[PATCH] Generic type subscription
Hi,
Regarding to the previous conversations [1]/messages/by-id/CA+q6zcV8qvGcDXurwwgUbwACV86Th7G80pnubg42e-p9gsSf=g@mail.gmail.com, [2]/messages/by-id/CA+q6zcX3mdxGCgdThzuySwH-ApyHHM-G4oB1R0fn0j2hZqqkLQ@mail.gmail.com, here is a patch (with some
improvements from Nikita Glukhov) for the generic type subscription. It
allows
to define type-specific subscription logic for any data type and has
implementations for the array and jsonb types. There are following changes
in this
patch:
* A new column for `pg_type` (`regproc typsubscription`) which points out a
type-specific subscription procedure for particular data type. It can be
none
(and it's a default value), which means that this data type doesn't
support
subscription.
* Type-specific code (e.g. any kind of verification, type coercion, actual
data extraction and update) in the array subscription implementation was
separated from generic code into `array_subscription` procedure. Generic
implementation assumes that subscription expression can be in form `[a]`
or
`[a:b]`, and there isn't any restrictions for type of `a` and `b`.
* Using the same api a new subscription logic was implemented for `jsonb`
type
in `jsonb_subscription` procedure. Several changes were introduced into
jsonb
functions just to be able to share common code.
I believe that this patch is more or less in good shape, so I would like to
know
what do you think about it? Feedback is welcome.
[1]: /messages/by-id/CA+q6zcV8qvGcDXurwwgUbwACV86Th7G80pnubg42e-p9gsSf=g@mail.gmail.com
/messages/by-id/CA+q6zcV8qvGcDXurwwgUbwACV86Th7G80pnubg42e-p9gsSf=g@mail.gmail.com
[2]: /messages/by-id/CA+q6zcX3mdxGCgdThzuySwH-ApyHHM-G4oB1R0fn0j2hZqqkLQ@mail.gmail.com
/messages/by-id/CA+q6zcX3mdxGCgdThzuySwH-ApyHHM-G4oB1R0fn0j2hZqqkLQ@mail.gmail.com
Attachments:
generic_type_subscription_v1.patchtext/x-patch; charset=US-ASCII; name=generic_type_subscription_v1.patchDownload
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 8ce24e0..c0fc651 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -2464,14 +2464,14 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
JumbleExpr(jstate, (Node *) expr->aggfilter);
}
break;
- case T_ArrayRef:
+ case T_SubscriptionRef:
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptionRef *sbsref = (SubscriptionRef *) node;
- JumbleExpr(jstate, (Node *) aref->refupperindexpr);
- JumbleExpr(jstate, (Node *) aref->reflowerindexpr);
- JumbleExpr(jstate, (Node *) aref->refexpr);
- JumbleExpr(jstate, (Node *) aref->refassgnexpr);
+ JumbleExpr(jstate, (Node *) sbsref->refupperindexpr);
+ JumbleExpr(jstate, (Node *) sbsref->reflowerindexpr);
+ JumbleExpr(jstate, (Node *) sbsref->refexpr);
+ JumbleExpr(jstate, (Node *) sbsref->refassgnexpr);
}
break;
case T_FuncExpr:
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index aaf9108..06b9f8d 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -137,7 +137,7 @@ static void deparseExpr(Expr *expr, deparse_expr_cxt *context);
static void deparseVar(Var *node, deparse_expr_cxt *context);
static void deparseConst(Const *node, deparse_expr_cxt *context);
static void deparseParam(Param *node, deparse_expr_cxt *context);
-static void deparseArrayRef(ArrayRef *node, deparse_expr_cxt *context);
+static void deparseSubscriptionRef(SubscriptionRef *node, deparse_expr_cxt *context);
static void deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context);
static void deparseOpExpr(OpExpr *node, deparse_expr_cxt *context);
static void deparseOperatorName(StringInfo buf, Form_pg_operator opform);
@@ -357,9 +357,9 @@ foreign_expr_walker(Node *node,
state = FDW_COLLATE_UNSAFE;
}
break;
- case T_ArrayRef:
+ case T_SubscriptionRef:
{
- ArrayRef *ar = (ArrayRef *) node;
+ SubscriptionRef *ar = (SubscriptionRef *) node;
/* Assignment should not be in restrictions. */
if (ar->refassgnexpr != NULL)
@@ -1799,8 +1799,8 @@ deparseExpr(Expr *node, deparse_expr_cxt *context)
case T_Param:
deparseParam((Param *) node, context);
break;
- case T_ArrayRef:
- deparseArrayRef((ArrayRef *) node, context);
+ case T_SubscriptionRef:
+ deparseSubscriptionRef((SubscriptionRef *) node, context);
break;
case T_FuncExpr:
deparseFuncExpr((FuncExpr *) node, context);
@@ -2020,7 +2020,7 @@ deparseParam(Param *node, deparse_expr_cxt *context)
* Deparse an array subscript expression.
*/
static void
-deparseArrayRef(ArrayRef *node, deparse_expr_cxt *context)
+deparseSubscriptionRef(SubscriptionRef *node, deparse_expr_cxt *context)
{
StringInfo buf = context->buf;
ListCell *lowlist_item;
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 4e09e06..7027b7a 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -7056,6 +7056,13 @@
</row>
<row>
+ <entry><structfield>typsubscription</structfield></entry>
+ <entry><type>regproc</type></entry>
+ <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+ <entry>Custom subscription function with type-specific logic, or 0 if this type doesn't support subscription.</entry>
+ </row>
+
+ <row>
<entry><structfield>typdefaultbin</structfield></entry>
<entry><type>pg_node_tree</type></entry>
<entry></entry>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 4383711..bc76576 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -70,6 +70,7 @@
<!ENTITY xplang SYSTEM "xplang.sgml">
<!ENTITY xoper SYSTEM "xoper.sgml">
<!ENTITY xtypes SYSTEM "xtypes.sgml">
+<!ENTITY xsubscription SYSTEM "xsubscription.sgml">
<!ENTITY plperl SYSTEM "plperl.sgml">
<!ENTITY plpython SYSTEM "plpython.sgml">
<!ENTITY plsql SYSTEM "plpgsql.sgml">
diff --git a/doc/src/sgml/json.sgml b/doc/src/sgml/json.sgml
index 3cf78d6..399e4f3 100644
--- a/doc/src/sgml/json.sgml
+++ b/doc/src/sgml/json.sgml
@@ -569,4 +569,29 @@ SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"tags": ["qu
compared using the default database collation.
</para>
</sect2>
+
+ <sect2 id="json-subscription">
+ <title>JSON subscription</title>
+ <para>
+ JSONB data type support array-style subscription expressions to extract or update particular element. An example of subscription syntax:
+<programlisting>
+-- Extract value by key
+SELECT ('{"a": 1}'::jsonb)['a'];
+
+-- Extract nested value by key path
+SELECT ('{"a": {"b": {"c": 1}}}'::jsonb)['a']['b']['c'];
+
+-- Extract element by index
+SELECT ('[1, "2", null]'::jsonb)['1'];
+
+-- Update value by key
+UPDATE table_name set jsonb_field['key'] = 1;
+
+-- Select records using where clause with subscription
+SELECT * from table_name where jsonb_field['key'] = '"value"';
+</programlisting>
+ </para>
+ </sect2>
+
+
</sect1>
diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index 5a09f19..8588065 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -54,6 +54,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
[ , ELEMENT = <replaceable class="parameter">element</replaceable> ]
[ , DELIMITER = <replaceable class="parameter">delimiter</replaceable> ]
[ , COLLATABLE = <replaceable class="parameter">collatable</replaceable> ]
+ [ , SUBSCRIPTION = <replaceable class="parameter">subscription_function</replaceable> ]
)
CREATE TYPE <replaceable class="parameter">name</replaceable>
@@ -194,7 +195,8 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
<replaceable class="parameter">send_function</replaceable>,
<replaceable class="parameter">type_modifier_input_function</replaceable>,
<replaceable class="parameter">type_modifier_output_function</replaceable> and
- <replaceable class="parameter">analyze_function</replaceable>
+ <replaceable class="parameter">analyze_function</replaceable>,
+ <replaceable class="parameter">subscription_function</replaceable>
are optional. Generally these functions have to be coded in C
or another low-level language.
</para>
@@ -451,6 +453,22 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
make use of the collation information; this does not happen
automatically merely by marking the type collatable.
</para>
+
+ <para>
+ The optional
+ <replaceable class="parameter">subscription_function</replaceable>
+ contains type-specific logic for subscription of the data type.
+ By default, there is no such function, which means that the data
+ type doesn't support subscription. The subscription function must be
+ declared to take a single argument of type <type>internal</>, and return
+ a <type>internal</> result. There are two examples of implementation for
+ subscription function in case of array
+ (<replaceable class="parameter">array_subscription</replaceable>)
+ and jsonb
+ (<replaceable class="parameter">jsonb_subscription</replaceable>)
+ types in <filename>src/backend/utils/adt/arrayfuncs.c</> and
+ <filename>src/backend/utils/adt/jsonfuncs.c</> corresponding.
+ </para>
</refsect2>
<refsect2>
@@ -766,6 +784,16 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">subscription_function</replaceable></term>
+ <listitem>
+ <para>
+ The name of a function that contains type-specific subscription logic for
+ the data type.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/doc/src/sgml/xsubscription.sgml b/doc/src/sgml/xsubscription.sgml
new file mode 100644
index e69de29..255ef66 100644
--- /dev/null
+++ b/doc/src/sgml/xsubscription.sgml
@@ -0,0 +1,98 @@
+<!-- doc/src/sgml/xsubscription.sgml -->
+
+ <sect1 id="xsubscription">
+ <title>User-defined subscription procedure</title>
+
+ <indexterm zone="xsubscription">
+ <primary>custom subscription</primary>
+ </indexterm>
+ When you define a new base type, you can also specify a custom procedure
+ to handle subscription expressions. It should contains logic for verification
+ and for extraction or update your data. For instance:
+
+<programlisting><![CDATA[
+typedef struct Custom
+{
+ int first;
+ int second;
+} Custom;
+
+Datum
+custom_subscription_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptionExecData *sbsdata = (SubscriptionExecData *) PG_GETARG_POINTER(1);
+ Custom *result = (Custom *) sbsdata->containerSource;
+
+ // Some extraction or update logic based on sbsdata
+}
+
+Datum
+custom_subscription_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptionRef *sbsref = (SubscriptionRef *) PG_GETARG_POINTER(0);
+
+ // Some verifications or type coersion
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+PG_FUNCTION_INFO_V1(custom_subscription);
+
+Datum
+custom_subscription(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1,
+ fcinfo->nargs);
+
+ if (op_type & SBS_VALIDATION)
+ return custom_subscription_prepare(&target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return custom_subscription_evaluate(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscription function: %d", op_type);
+}]]>
+</programlisting>
+
+ Then you can define a subscription procedure and a custom data type:
+
+<programlisting>
+CREATE FUNCTION custom_subscription(internal)
+ RETURNS internal
+ AS '<replaceable>filename</replaceable>'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE custom (
+ internallength = 4,
+ input = custom_in,
+ output = custom_out,
+ subscription = custom_subscription
+);
+</programlisting>
+
+ and use it as usual:
+
+<programlisting>
+CREATE TABLE test_subscription (
+ data custom,
+);
+
+INSERT INTO test_subscription VALUES ('(1, 2)');
+
+SELECT data[0] from test_subscription;
+
+UPDATE test_subscription SET data[1] = 3;
+</programlisting>
+
+ </para>
+
+ <para>
+ The examples of custom subscription implementation can be found in
+ <filename>subscription.sql</filename> and <filename>subscription.c</filename>
+ in the <filename>src/tutorial</> directory of the source distribution.
+ See the <filename>README</> file in that directory for instructions
+ about running the examples.
+ </para>
+
+</sect1>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index e997b57..08e23d7 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -977,7 +977,8 @@ AddNewRelationType(const char *typeName,
-1, /* typmod */
0, /* array dimensions for typBaseType */
false, /* Type NOT NULL */
- InvalidOid); /* rowtypes never have a collation */
+ InvalidOid, /* rowtypes never have a collation */
+ InvalidOid); /* typsubscription - none */
}
/* --------------------------------
@@ -1244,7 +1245,8 @@ heap_create_with_catalog(const char *relname,
-1, /* typmod */
0, /* array dimensions for typBaseType */
false, /* Type NOT NULL */
- InvalidOid); /* rowtypes never have a collation */
+ InvalidOid, /* rowtypes never have a collation */
+ F_ARRAY_SUBSCRIPTION); /* array implementation */
pfree(relarrayname);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 4b2d281..dabcb64 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -118,6 +118,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(-1);
values[Anum_pg_type_typndims - 1] = Int32GetDatum(0);
values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(InvalidOid);
+ values[Anum_pg_type_typsubscription - 1] = ObjectIdGetDatum(InvalidOid);
nulls[Anum_pg_type_typdefaultbin - 1] = true;
nulls[Anum_pg_type_typdefault - 1] = true;
nulls[Anum_pg_type_typacl - 1] = true;
@@ -166,6 +167,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
false,
InvalidOid,
InvalidOid,
+ InvalidOid,
NULL,
false);
@@ -224,7 +226,8 @@ TypeCreate(Oid newTypeOid,
int32 typeMod,
int32 typNDims, /* Array dimensions for baseType */
bool typeNotNull,
- Oid typeCollation)
+ Oid typeCollation,
+ Oid subscriptionProcedure)
{
Relation pg_type_desc;
Oid typeObjectId;
@@ -364,6 +367,7 @@ TypeCreate(Oid newTypeOid,
values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(typeMod);
values[Anum_pg_type_typndims - 1] = Int32GetDatum(typNDims);
values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(typeCollation);
+ values[Anum_pg_type_typsubscription - 1] = ObjectIdGetDatum(subscriptionProcedure);
/*
* initialize the default binary value for this type. Check for nulls of
@@ -484,6 +488,7 @@ TypeCreate(Oid newTypeOid,
isImplicitArray,
baseType,
typeCollation,
+ subscriptionProcedure,
(defaultTypeBin ?
stringToNode(defaultTypeBin) :
NULL),
@@ -530,6 +535,7 @@ GenerateTypeDependencies(Oid typeNamespace,
bool isImplicitArray,
Oid baseType,
Oid typeCollation,
+ Oid subscriptionProcedure,
Node *defaultExpr,
bool rebuild)
{
@@ -682,6 +688,14 @@ GenerateTypeDependencies(Oid typeNamespace,
/* Normal dependency on the default expression. */
if (defaultExpr)
recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL);
+
+ if (OidIsValid(subscriptionProcedure))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = subscriptionProcedure;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
}
/*
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index ce04211..05f77e9 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -94,6 +94,7 @@ static Oid findTypeSendFunction(List *procname, Oid typeOid);
static Oid findTypeTypmodinFunction(List *procname);
static Oid findTypeTypmodoutFunction(List *procname);
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
+static Oid findTypeSubscriptionFunction(List *procname, Oid typeOid);
static Oid findRangeSubOpclass(List *opcname, Oid subtype);
static Oid findRangeCanonicalFunction(List *procname, Oid typeOid);
static Oid findRangeSubtypeDiffFunction(List *procname, Oid subtype);
@@ -123,6 +124,7 @@ DefineType(List *names, List *parameters)
List *typmodinName = NIL;
List *typmodoutName = NIL;
List *analyzeName = NIL;
+ List *subscriptionName = NIL;
char category = TYPCATEGORY_USER;
bool preferred = false;
char delimiter = DEFAULT_TYPDELIM;
@@ -141,6 +143,7 @@ DefineType(List *names, List *parameters)
DefElem *typmodinNameEl = NULL;
DefElem *typmodoutNameEl = NULL;
DefElem *analyzeNameEl = NULL;
+ DefElem *subscriptionNameEl = NULL;
DefElem *categoryEl = NULL;
DefElem *preferredEl = NULL;
DefElem *delimiterEl = NULL;
@@ -163,6 +166,7 @@ DefineType(List *names, List *parameters)
Oid resulttype;
ListCell *pl;
ObjectAddress address;
+ Oid subscriptionOid = InvalidOid;
/*
* As of Postgres 8.4, we require superuser privilege to create a base
@@ -262,6 +266,9 @@ DefineType(List *names, List *parameters)
else if (pg_strcasecmp(defel->defname, "analyze") == 0 ||
pg_strcasecmp(defel->defname, "analyse") == 0)
defelp = &analyzeNameEl;
+ else if (pg_strcasecmp(defel->defname, "subscription") == 0 ||
+ pg_strcasecmp(defel->defname, "subscription") == 0)
+ defelp = &subscriptionNameEl;
else if (pg_strcasecmp(defel->defname, "category") == 0)
defelp = &categoryEl;
else if (pg_strcasecmp(defel->defname, "preferred") == 0)
@@ -330,6 +337,8 @@ DefineType(List *names, List *parameters)
typmodoutName = defGetQualifiedName(typmodoutNameEl);
if (analyzeNameEl)
analyzeName = defGetQualifiedName(analyzeNameEl);
+ if (subscriptionNameEl)
+ subscriptionName = defGetQualifiedName(subscriptionNameEl);
if (categoryEl)
{
char *p = defGetString(categoryEl);
@@ -511,6 +520,9 @@ DefineType(List *names, List *parameters)
if (analyzeName)
analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid);
+ if (subscriptionName)
+ subscriptionOid = findTypeSubscriptionFunction(subscriptionName, typoid);
+
/*
* Check permissions on functions. We choose to require the creator/owner
* of a type to also own the underlying functions. Since creating a type
@@ -630,7 +642,8 @@ DefineType(List *names, List *parameters)
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
false, /* Type NOT NULL */
- collation); /* type's collation */
+ collation, /* type's collation */
+ subscriptionOid); /* subscription procedure */
Assert(typoid == address.objectId);
/*
@@ -671,7 +684,8 @@ DefineType(List *names, List *parameters)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- collation); /* type's collation */
+ collation, /* type's collation */
+ F_ARRAY_SUBSCRIPTION);
pfree(array_type);
@@ -733,6 +747,7 @@ DefineDomain(CreateDomainStmt *stmt)
Oid receiveProcedure;
Oid sendProcedure;
Oid analyzeProcedure;
+ Oid subscriptionProcedure;
bool byValue;
char category;
char delimiter;
@@ -856,6 +871,9 @@ DefineDomain(CreateDomainStmt *stmt)
/* Analysis function */
analyzeProcedure = baseType->typanalyze;
+ /* Subscription function */
+ subscriptionProcedure = baseType->typsubscription;
+
/* Inherited default value */
datum = SysCacheGetAttr(TYPEOID, typeTup,
Anum_pg_type_typdefault, &isnull);
@@ -1057,7 +1075,8 @@ DefineDomain(CreateDomainStmt *stmt)
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
typNotNull, /* Type NOT NULL */
- domaincoll); /* type's collation */
+ domaincoll, /* type's collation */
+ subscriptionProcedure); /* subscription procedure */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
@@ -1169,7 +1188,8 @@ DefineEnum(CreateEnumStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* type's collation */
+ InvalidOid, /* type's collation */
+ InvalidOid); /* typsubscription - none */
/* Enter the enum's values into pg_enum */
EnumValuesCreate(enumTypeAddr.objectId, stmt->vals);
@@ -1209,7 +1229,8 @@ DefineEnum(CreateEnumStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* type's collation */
+ InvalidOid, /* type's collation */
+ F_ARRAY_SUBSCRIPTION); /* array subscription implementation */
pfree(enumArrayName);
@@ -1508,7 +1529,8 @@ DefineRange(CreateRangeStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* type's collation (ranges never have one) */
+ InvalidOid, /* type's collation (ranges never have one) */
+ InvalidOid); /* typsubscription - none */
Assert(typoid == address.objectId);
/* Create the entry in pg_range */
@@ -1550,7 +1572,8 @@ DefineRange(CreateRangeStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* typcollation */
+ InvalidOid, /* typcollation */
+ F_ARRAY_SUBSCRIPTION); /* array subscription implementation */
pfree(rangeArrayName);
@@ -1894,6 +1917,33 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
return procOid;
}
+static Oid
+findTypeSubscriptionFunction(List *procname, Oid typeOid)
+{
+ Oid argList[1];
+ Oid procOid;
+
+ /*
+ * Analyze functions always take one INTERNAL argument and return INTERNAL.
+ */
+ argList[0] = INTERNALOID;
+
+ procOid = LookupFuncName(procname, 1, argList, true);
+ if (!OidIsValid(procOid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("function %s does not exist",
+ func_signature_string(procname, 1, NIL, argList))));
+
+ if (get_func_rettype(procOid) != INTERNALOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("type subscription function %s must return type %s",
+ NameListToString(procname), "internal")));
+
+ return procOid;
+}
+
/*
* Find suitable support functions and opclasses for a range type.
*/
@@ -2248,6 +2298,7 @@ AlterDomainDefault(List *names, Node *defaultRaw)
false, /* a domain isn't an implicit array */
typTup->typbasetype,
typTup->typcollation,
+ typTup->typsubscription,
defaultExpr,
true); /* Rebuild is true */
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 743e7d6..da70901 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -55,6 +55,7 @@
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/lsyscache.h"
+#include "utils/syscache.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
#include "utils/typcache.h"
@@ -62,10 +63,9 @@
/* static function decls */
-static Datum ExecEvalArrayRef(ArrayRefExprState *astate,
+static Datum ExecEvalSubscriptionRef(SubscriptionRefExprState *astate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
-static bool isAssignmentIndirectionExpr(ExprState *exprstate);
static Datum ExecEvalAggref(AggrefExprState *aggref,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
@@ -251,39 +251,50 @@ static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
/*----------
- * ExecEvalArrayRef
+ * ExecEvalSubscriptionRef
*
- * This function takes an ArrayRef and returns the extracted Datum
- * if it's a simple reference, or the modified array value if it's
- * an array assignment (i.e., array element or slice insertion).
+ * This function takes a SubscriptionRef, extracts all information required
+ * for subscription and pass it to a particular subscription procedure,
+ * specified for this data type. As a result the extracted Datum will be
+ * returned if it's a simple reference, or the modified containers value if
+ * it's an containers assignment (i.e., containers element or slice
+ * insertion).
*
* NOTE: if we get a NULL result from a subscript expression, we return NULL
- * when it's an array reference, or raise an error when it's an assignment.
+ * when it's an containers reference, or raise an error when it's an assignment.
*----------
*/
+
static Datum
-ExecEvalArrayRef(ArrayRefExprState *astate,
- ExprContext *econtext,
- bool *isNull,
- ExprDoneCond *isDone)
+ExecEvalSubscriptionRef(SubscriptionRefExprState *sbstate,
+ ExprContext *econtext,
+ bool *isNull,
+ ExprDoneCond *isDone)
{
- ArrayRef *arrayRef = (ArrayRef *) astate->xprstate.expr;
- Datum array_source;
- bool isAssignment = (arrayRef->refassgnexpr != NULL);
- bool eisnull;
- ListCell *l;
- int i = 0,
- j = 0;
- IntArray upper,
- lower;
- bool upperProvided[MAXDIM],
- lowerProvided[MAXDIM];
- int *lIndex;
-
- array_source = ExecEvalExpr(astate->refexpr,
- econtext,
- isNull,
- isDone);
+ SubscriptionRef *sbsRef = (SubscriptionRef *) sbstate->xprstate.expr;
+ Oid containerType;
+ RegProcedure typsubscription;
+ bool isAssignment = (sbsRef->refassgnexpr != NULL);
+ bool eisnull;
+ Datum *upper = NULL,
+ *lower = NULL;
+ ListCell *l;
+ int i = 0,
+ j = 0;
+ SubscriptionExecData sbsdata;
+ bool upperProvided[MAXDIM],
+ lowerProvided[MAXDIM];
+
+ if (sbstate->refupperindexpr != NULL)
+ upper = (Datum *) palloc(sbstate->refupperindexpr->length * sizeof(Datum *));
+
+ if (sbstate->reflowerindexpr != NULL)
+ lower = (Datum *) palloc(sbstate->reflowerindexpr->length * sizeof(Datum *));
+
+ sbsdata.xprcontext = econtext;
+ sbsdata.isNull = isNull;
+ sbsdata.containerSource = ExecEvalExpr(sbstate->refexpr, econtext,
+ isNull, isDone);
/*
* If refexpr yields NULL, and it's a fetch, then result is NULL. In the
@@ -297,51 +308,49 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
return (Datum) NULL;
}
- foreach(l, astate->refupperindexpr)
+ foreach(l, sbstate->refupperindexpr)
{
ExprState *eltstate = (ExprState *) lfirst(l);
if (i >= MAXDIM)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ errmsg("number of container dimensions (%d) exceeds the maximum allowed (%d)",
i + 1, MAXDIM)));
if (eltstate == NULL)
{
- /* Slice bound is omitted, so use array's upper bound */
- Assert(astate->reflowerindexpr != NIL);
+ /* Slice bound is omitted, so use containers's upper bound */
+ Assert(sbstate->reflowerindexpr != NIL);
upperProvided[i++] = false;
continue;
}
+
upperProvided[i] = true;
+ upper[i++] = ExecEvalExpr(eltstate, econtext, &eisnull, NULL);
- upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate,
- econtext,
- &eisnull,
- NULL));
/* If any index expr yields NULL, result is NULL or error */
if (eisnull)
{
if (isAssignment)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
- errmsg("array subscript in assignment must not be null")));
+ errmsg("container subscript in assignment must not be null")));
*isNull = true;
return (Datum) NULL;
}
}
- if (astate->reflowerindexpr != NIL)
+ if (sbstate->reflowerindexpr != NIL)
{
- foreach(l, astate->reflowerindexpr)
+ foreach(l, sbstate->reflowerindexpr)
{
ExprState *eltstate = (ExprState *) lfirst(l);
if (j >= MAXDIM)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ errmsg("number of container dimensions (%d) exceeds the maximum allowed (%d)",
j + 1, MAXDIM)));
if (eltstate == NULL)
@@ -350,191 +359,45 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
lowerProvided[j++] = false;
continue;
}
+
lowerProvided[j] = true;
+ lower[j++] = ExecEvalExpr(eltstate, econtext, &eisnull, NULL);
- lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate,
- econtext,
- &eisnull,
- NULL));
/* If any index expr yields NULL, result is NULL or error */
if (eisnull)
{
if (isAssignment)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
- errmsg("array subscript in assignment must not be null")));
+ errmsg("container subscript in assignment must not be null")));
*isNull = true;
return (Datum) NULL;
}
}
+
/* this can't happen unless parser messed up */
if (i != j)
elog(ERROR, "upper and lower index lists are not same length");
- lIndex = lower.indx;
- }
- else
- lIndex = NULL;
-
- if (isAssignment)
- {
- Datum sourceData;
- Datum save_datum;
- bool save_isNull;
-
- /*
- * We might have a nested-assignment situation, in which the
- * refassgnexpr is itself a FieldStore or ArrayRef that needs to
- * obtain and modify the previous value of the array element or slice
- * being replaced. If so, we have to extract that value from the
- * array and pass it down via the econtext's caseValue. It's safe to
- * reuse the CASE mechanism because there cannot be a CASE between
- * here and where the value would be needed, and an array assignment
- * can't be within a CASE either. (So saving and restoring the
- * caseValue is just paranoia, but let's do it anyway.)
- *
- * Since fetching the old element might be a nontrivial expense, do it
- * only if the argument appears to actually need it.
- */
- save_datum = econtext->caseValue_datum;
- save_isNull = econtext->caseValue_isNull;
-
- if (isAssignmentIndirectionExpr(astate->refassgnexpr))
- {
- if (*isNull)
- {
- /* whole array is null, so any element or slice is too */
- econtext->caseValue_datum = (Datum) 0;
- econtext->caseValue_isNull = true;
- }
- else if (lIndex == NULL)
- {
- econtext->caseValue_datum =
- array_get_element(array_source, i,
- upper.indx,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign,
- &econtext->caseValue_isNull);
- }
- else
- {
- econtext->caseValue_datum =
- array_get_slice(array_source, i,
- upper.indx, lower.indx,
- upperProvided, lowerProvided,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
- econtext->caseValue_isNull = false;
- }
- }
- else
- {
- /* argument shouldn't need caseValue, but for safety set it null */
- econtext->caseValue_datum = (Datum) 0;
- econtext->caseValue_isNull = true;
- }
-
- /*
- * Evaluate the value to be assigned into the array.
- */
- sourceData = ExecEvalExpr(astate->refassgnexpr,
- econtext,
- &eisnull,
- NULL);
-
- econtext->caseValue_datum = save_datum;
- econtext->caseValue_isNull = save_isNull;
-
- /*
- * For an assignment to a fixed-length array type, both the original
- * array and the value to be assigned into it must be non-NULL, else
- * we punt and return the original array.
- */
- if (astate->refattrlength > 0) /* fixed-length array? */
- if (eisnull || *isNull)
- return array_source;
-
- /*
- * For assignment to varlena arrays, we handle a NULL original array
- * by substituting an empty (zero-dimensional) array; insertion of the
- * new element will result in a singleton array value. It does not
- * matter whether the new element is NULL.
- */
- if (*isNull)
- {
- array_source = PointerGetDatum(construct_empty_array(arrayRef->refelemtype));
- *isNull = false;
- }
-
- if (lIndex == NULL)
- return array_set_element(array_source, i,
- upper.indx,
- sourceData,
- eisnull,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
- else
- return array_set_slice(array_source, i,
- upper.indx, lower.indx,
- upperProvided, lowerProvided,
- sourceData,
- eisnull,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
}
- if (lIndex == NULL)
- return array_get_element(array_source, i,
- upper.indx,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign,
- isNull);
- else
- return array_get_slice(array_source, i,
- upper.indx, lower.indx,
- upperProvided, lowerProvided,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
-}
+ sbsdata.upper = upper;
+ sbsdata.upperProvided = upperProvided;
+ sbsdata.lower = lower;
+ sbsdata.lowerProvided = lowerProvided;
+ sbsdata.indexprNumber = i;
-/*
- * Helper for ExecEvalArrayRef: is expr a nested FieldStore or ArrayRef
- * that might need the old element value passed down?
- *
- * (We could use this in ExecEvalFieldStore too, but in that case passing
- * the old value is so cheap there's no need.)
- */
-static bool
-isAssignmentIndirectionExpr(ExprState *exprstate)
-{
- if (exprstate == NULL)
- return false; /* just paranoia */
- if (IsA(exprstate, FieldStoreState))
- {
- FieldStore *fstore = (FieldStore *) exprstate->expr;
+ containerType = getBaseTypeAndTypmod(sbsRef->refcontainertype, &sbsRef->reftypmod);
+ typsubscription = get_typsubscription(containerType);
- if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
- return true;
- }
- else if (IsA(exprstate, ArrayRefExprState))
- {
- ArrayRef *arrayRef = (ArrayRef *) exprstate->expr;
+ if (!OidIsValid(typsubscription))
+ /* this can't happen */
+ elog(ERROR, "can not find subscription procedure for type %s",
+ format_type_be(containerType));
- if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr))
- return true;
- }
- return false;
+ return OidFunctionCall3(typsubscription,
+ Int32GetDatum(SBS_EXEC),
+ PointerGetDatum(sbstate),
+ PointerGetDatum(&sbsdata));
}
/* ----------------------------------------------------------------
@@ -4332,7 +4195,7 @@ ExecEvalFieldStore(FieldStoreState *fstate,
/*
* Use the CaseTestExpr mechanism to pass down the old value of the
* field being replaced; this is needed in case the newval is itself a
- * FieldStore or ArrayRef that has to obtain and modify the old value.
+ * FieldStore or SubscriptionRef that has to obtain and modify the old value.
* It's safe to reuse the CASE mechanism because there cannot be a
* CASE between here and where the value would be needed, and a field
* assignment can't be within a CASE either. (So saving and restoring
@@ -4695,25 +4558,21 @@ ExecInitExpr(Expr *node, PlanState *parent)
state = (ExprState *) wfstate;
}
break;
- case T_ArrayRef:
+ case T_SubscriptionRef:
{
- ArrayRef *aref = (ArrayRef *) node;
- ArrayRefExprState *astate = makeNode(ArrayRefExprState);
+ SubscriptionRef *sbsref = (SubscriptionRef *) node;
+ SubscriptionRefExprState *astate = makeNode(SubscriptionRefExprState);
- astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayRef;
+ astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalSubscriptionRef;
astate->refupperindexpr = (List *)
- ExecInitExpr((Expr *) aref->refupperindexpr, parent);
+ ExecInitExpr((Expr *) sbsref->refupperindexpr, parent);
astate->reflowerindexpr = (List *)
- ExecInitExpr((Expr *) aref->reflowerindexpr, parent);
- astate->refexpr = ExecInitExpr(aref->refexpr, parent);
- astate->refassgnexpr = ExecInitExpr(aref->refassgnexpr,
+ ExecInitExpr((Expr *) sbsref->reflowerindexpr, parent);
+ astate->refexpr = ExecInitExpr(sbsref->refexpr, parent);
+ astate->refassgnexpr = ExecInitExpr(sbsref->refassgnexpr,
parent);
/* do one-time catalog lookups for type info */
- astate->refattrlength = get_typlen(aref->refarraytype);
- get_typlenbyvalalign(aref->refelemtype,
- &astate->refelemlength,
- &astate->refelembyval,
- &astate->refelemalign);
+ astate->refattrlength = get_typlen(sbsref->refcontainertype);
state = (ExprState *) astate;
}
break;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 1877fb4..a3b2daa 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1292,14 +1292,14 @@ _copyWindowFunc(const WindowFunc *from)
}
/*
- * _copyArrayRef
+ * _copySubscriptionRef
*/
-static ArrayRef *
-_copyArrayRef(const ArrayRef *from)
+static SubscriptionRef *
+_copySubscriptionRef(const SubscriptionRef *from)
{
- ArrayRef *newnode = makeNode(ArrayRef);
+ SubscriptionRef *newnode = makeNode(SubscriptionRef);
- COPY_SCALAR_FIELD(refarraytype);
+ COPY_SCALAR_FIELD(refcontainertype);
COPY_SCALAR_FIELD(refelemtype);
COPY_SCALAR_FIELD(reftypmod);
COPY_SCALAR_FIELD(refcollid);
@@ -4464,8 +4464,8 @@ copyObject(const void *from)
case T_WindowFunc:
retval = _copyWindowFunc(from);
break;
- case T_ArrayRef:
- retval = _copyArrayRef(from);
+ case T_SubscriptionRef:
+ retval = _copySubscriptionRef(from);
break;
case T_FuncExpr:
retval = _copyFuncExpr(from);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 448e1a9..a6e7228 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -244,9 +244,9 @@ _equalWindowFunc(const WindowFunc *a, const WindowFunc *b)
}
static bool
-_equalArrayRef(const ArrayRef *a, const ArrayRef *b)
+_equalSubscriptionRef(const SubscriptionRef *a, const SubscriptionRef *b)
{
- COMPARE_SCALAR_FIELD(refarraytype);
+ COMPARE_SCALAR_FIELD(refcontainertype);
COMPARE_SCALAR_FIELD(refelemtype);
COMPARE_SCALAR_FIELD(reftypmod);
COMPARE_SCALAR_FIELD(refcollid);
@@ -2775,8 +2775,8 @@ equal(const void *a, const void *b)
case T_WindowFunc:
retval = _equalWindowFunc(a, b);
break;
- case T_ArrayRef:
- retval = _equalArrayRef(a, b);
+ case T_SubscriptionRef:
+ retval = _equalSubscriptionRef(a, b);
break;
case T_FuncExpr:
retval = _equalFuncExpr(a, b);
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 3997441..a02719f 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -66,13 +66,13 @@ exprType(const Node *expr)
case T_WindowFunc:
type = ((const WindowFunc *) expr)->wintype;
break;
- case T_ArrayRef:
+ case T_SubscriptionRef:
{
- const ArrayRef *arrayref = (const ArrayRef *) expr;
+ const SubscriptionRef *arrayref = (const SubscriptionRef *) expr;
/* slice and/or store operations yield the array type */
if (arrayref->reflowerindexpr || arrayref->refassgnexpr)
- type = arrayref->refarraytype;
+ type = arrayref->refcontainertype;
else
type = arrayref->refelemtype;
}
@@ -284,9 +284,9 @@ exprTypmod(const Node *expr)
return ((const Const *) expr)->consttypmod;
case T_Param:
return ((const Param *) expr)->paramtypmod;
- case T_ArrayRef:
+ case T_SubscriptionRef:
/* typmod is the same for array or element */
- return ((const ArrayRef *) expr)->reftypmod;
+ return ((const SubscriptionRef *) expr)->reftypmod;
case T_FuncExpr:
{
int32 coercedTypmod;
@@ -772,8 +772,8 @@ exprCollation(const Node *expr)
case T_WindowFunc:
coll = ((const WindowFunc *) expr)->wincollid;
break;
- case T_ArrayRef:
- coll = ((const ArrayRef *) expr)->refcollid;
+ case T_SubscriptionRef:
+ coll = ((const SubscriptionRef *) expr)->refcollid;
break;
case T_FuncExpr:
coll = ((const FuncExpr *) expr)->funccollid;
@@ -1014,8 +1014,8 @@ exprSetCollation(Node *expr, Oid collation)
case T_WindowFunc:
((WindowFunc *) expr)->wincollid = collation;
break;
- case T_ArrayRef:
- ((ArrayRef *) expr)->refcollid = collation;
+ case T_SubscriptionRef:
+ ((SubscriptionRef *) expr)->refcollid = collation;
break;
case T_FuncExpr:
((FuncExpr *) expr)->funccollid = collation;
@@ -1237,9 +1237,9 @@ exprLocation(const Node *expr)
/* function name should always be the first thing */
loc = ((const WindowFunc *) expr)->location;
break;
- case T_ArrayRef:
+ case T_SubscriptionRef:
/* just use array argument's location */
- loc = exprLocation((Node *) ((const ArrayRef *) expr)->refexpr);
+ loc = exprLocation((Node *) ((const SubscriptionRef *) expr)->refexpr);
break;
case T_FuncExpr:
{
@@ -1926,21 +1926,21 @@ expression_tree_walker(Node *node,
return true;
}
break;
- case T_ArrayRef:
+ case T_SubscriptionRef:
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptionRef *sbsref = (SubscriptionRef *) node;
/* recurse directly for upper/lower array index lists */
- if (expression_tree_walker((Node *) aref->refupperindexpr,
+ if (expression_tree_walker((Node *) sbsref->refupperindexpr,
walker, context))
return true;
- if (expression_tree_walker((Node *) aref->reflowerindexpr,
+ if (expression_tree_walker((Node *) sbsref->reflowerindexpr,
walker, context))
return true;
/* walker must see the refexpr and refassgnexpr, however */
- if (walker(aref->refexpr, context))
+ if (walker(sbsref->refexpr, context))
return true;
- if (walker(aref->refassgnexpr, context))
+ if (walker(sbsref->refassgnexpr, context))
return true;
}
break;
@@ -2515,12 +2515,12 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
- case T_ArrayRef:
+ case T_SubscriptionRef:
{
- ArrayRef *arrayref = (ArrayRef *) node;
- ArrayRef *newnode;
+ SubscriptionRef *arrayref = (SubscriptionRef *) node;
+ SubscriptionRef *newnode;
- FLATCOPY(newnode, arrayref, ArrayRef);
+ FLATCOPY(newnode, arrayref, SubscriptionRef);
MUTATE(newnode->refupperindexpr, arrayref->refupperindexpr,
List *);
MUTATE(newnode->reflowerindexpr, arrayref->reflowerindexpr,
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 29b7712..10086d4 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1076,11 +1076,11 @@ _outWindowFunc(StringInfo str, const WindowFunc *node)
}
static void
-_outArrayRef(StringInfo str, const ArrayRef *node)
+_outSubscriptionRef(StringInfo str, const SubscriptionRef *node)
{
- WRITE_NODE_TYPE("ARRAYREF");
+ WRITE_NODE_TYPE("SUBSCRIPTIONREF");
- WRITE_OID_FIELD(refarraytype);
+ WRITE_OID_FIELD(refcontainertype);
WRITE_OID_FIELD(refelemtype);
WRITE_INT_FIELD(reftypmod);
WRITE_OID_FIELD(refcollid);
@@ -3457,8 +3457,8 @@ outNode(StringInfo str, const void *obj)
case T_WindowFunc:
_outWindowFunc(str, obj);
break;
- case T_ArrayRef:
- _outArrayRef(str, obj);
+ case T_SubscriptionRef:
+ _outSubscriptionRef(str, obj);
break;
case T_FuncExpr:
_outFuncExpr(str, obj);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 6f9a81e..96afe97 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -605,14 +605,14 @@ _readWindowFunc(void)
}
/*
- * _readArrayRef
+ * _readSubscriptionRef
*/
-static ArrayRef *
-_readArrayRef(void)
+static SubscriptionRef *
+_readSubscriptionRef(void)
{
- READ_LOCALS(ArrayRef);
+ READ_LOCALS(SubscriptionRef);
- READ_OID_FIELD(refarraytype);
+ READ_OID_FIELD(refcontainertype);
READ_OID_FIELD(refelemtype);
READ_INT_FIELD(reftypmod);
READ_OID_FIELD(refcollid);
@@ -2317,8 +2317,8 @@ parseNodeString(void)
return_value = _readGroupingFunc();
else if (MATCH("WINDOWFUNC", 10))
return_value = _readWindowFunc();
- else if (MATCH("ARRAYREF", 8))
- return_value = _readArrayRef();
+ else if (MATCH("SUBSCRIPTIONREF", 15))
+ return_value = _readSubscriptionRef();
else if (MATCH("FUNCEXPR", 8))
return_value = _readFuncExpr();
else if (MATCH("NAMEDARGEXPR", 12))
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 4496fde..10be05b 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -1329,10 +1329,10 @@ contain_nonstrict_functions_walker(Node *node, void *context)
/* a window function could return non-null with null input */
return true;
}
- if (IsA(node, ArrayRef))
+ if (IsA(node, SubscriptionRef))
{
/* array assignment is nonstrict, but subscripting is strict */
- if (((ArrayRef *) node)->refassgnexpr != NULL)
+ if (((SubscriptionRef *) node)->refassgnexpr != NULL)
return true;
/* else fall through to check args */
}
@@ -1512,7 +1512,7 @@ contain_leaked_vars_walker(Node *node, void *context)
case T_Var:
case T_Const:
case T_Param:
- case T_ArrayRef:
+ case T_SubscriptionRef:
case T_ArrayExpr:
case T_FieldSelect:
case T_FieldStore:
@@ -3573,7 +3573,7 @@ eval_const_expressions_mutator(Node *node,
* For any node type not handled above, we recurse using
* expression_tree_mutator, which will copy the node unchanged but try to
* simplify its arguments (if any) using this routine. For example: we
- * cannot eliminate an ArrayRef node, but we might be able to simplify
+ * cannot eliminate an SubscriptionRef node, but we might be able to simplify
* constant expressions in its subscripts.
*/
return expression_tree_mutator(node, eval_const_expressions_mutator,
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index eac86cc..b15fc6a 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -915,13 +915,13 @@ transformInsertRow(ParseState *pstate, List *exprlist,
expr = (Expr *) linitial(fstore->newvals);
}
- else if (IsA(expr, ArrayRef))
+ else if (IsA(expr, SubscriptionRef))
{
- ArrayRef *aref = (ArrayRef *) expr;
+ SubscriptionRef *sbsref = (SubscriptionRef *) expr;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
break;
- expr = aref->refassgnexpr;
+ expr = sbsref->refassgnexpr;
}
else
break;
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 63f7965..3005e47 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -461,13 +461,13 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
/* process subscripts before this field selection */
if (subscripts)
- result = (Node *) transformArraySubscripts(pstate,
- result,
- exprType(result),
- InvalidOid,
- exprTypmod(result),
- subscripts,
- NULL);
+ result = (Node *) transformContainerSubscripts(pstate,
+ result,
+ exprType(result),
+ InvalidOid,
+ exprTypmod(result),
+ subscripts,
+ NULL);
subscripts = NIL;
newresult = ParseFuncOrColumn(pstate,
@@ -482,13 +482,13 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
}
/* process trailing subscripts, if any */
if (subscripts)
- result = (Node *) transformArraySubscripts(pstate,
- result,
- exprType(result),
- InvalidOid,
- exprTypmod(result),
- subscripts,
- NULL);
+ result = (Node *) transformContainerSubscripts(pstate,
+ result,
+ exprType(result),
+ InvalidOid,
+ exprTypmod(result),
+ subscripts,
+ NULL);
return result;
}
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 62d2f71..3d11aa0 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -199,20 +199,24 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location)
return result;
}
/*
* transformArrayType()
- * Identify the types involved in a subscripting operation
+ * Identify the types involved in a subscripting operation for array
*
* On entry, arrayType/arrayTypmod identify the type of the input value
* to be subscripted (which could be a domain type). These are modified
* if necessary to identify the actual array type and typmod, and the
* array's element type is returned. An error is thrown if the input isn't
* an array type.
+ *
+ * NOTE: This part of type-specific code is not separated into type-specific
+ * subscription procedure for now, but it does not affect on the whole logic,
+ * since InvalidOid will be return in case of other types not an error.
+ * An error will appears only if a subscription procedure is not defined.
*/
Oid
-transformArrayType(Oid *arrayType, int32 *arrayTypmod)
+transformArrayType(Oid *containerType, int32 *containerTypmod)
{
- Oid origArrayType = *arrayType;
Oid elementType;
HeapTuple type_tuple_array;
Form_pg_type type_struct_array;
@@ -224,7 +228,7 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
* itself. (Note that we provide no method whereby the creator of a
* domain over an array type could hide its ability to be subscripted.)
*/
- *arrayType = getBaseTypeAndTypmod(*arrayType, arrayTypmod);
+ *containerType = getBaseTypeAndTypmod(*containerType, containerTypmod);
/*
* We treat int2vector and oidvector as though they were domains over
@@ -233,25 +237,20 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
* xxxvector type; so we want the result of a slice operation to be
* considered to be of the more general type.
*/
- if (*arrayType == INT2VECTOROID)
- *arrayType = INT2ARRAYOID;
- else if (*arrayType == OIDVECTOROID)
- *arrayType = OIDARRAYOID;
+ if (*containerType == INT2VECTOROID)
+ *containerType = INT2ARRAYOID;
+ else if (*containerType == OIDVECTOROID)
+ *containerType = OIDARRAYOID;
/* Get the type tuple for the array */
- type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*arrayType));
+ type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*containerType));
if (!HeapTupleIsValid(type_tuple_array))
- elog(ERROR, "cache lookup failed for type %u", *arrayType);
+ elog(ERROR, "cache lookup failed for type %u", *containerType);
type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array);
/* needn't check typisdefined since this will fail anyway */
elementType = type_struct_array->typelem;
- if (elementType == InvalidOid)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("cannot subscript type %s because it is not an array",
- format_type_be(origArrayType))));
ReleaseSysCache(type_tuple_array);
@@ -259,61 +258,80 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
}
/*
- * transformArraySubscripts()
- * Transform array subscripting. This is used for both
- * array fetch and array assignment.
+ * transformContainerSubscripts()
+ * Transform container subscripting. This is used for both
+ * container fetch and container assignment.
*
- * In an array fetch, we are given a source array value and we produce an
- * expression that represents the result of extracting a single array element
- * or an array slice.
+ * In a container fetch, we are given a source container value and we produce
+ * an expression that represents the result of extracting a single container
+ * element or a container slice.
*
- * In an array assignment, we are given a destination array value plus a
- * source value that is to be assigned to a single element or a slice of
- * that array. We produce an expression that represents the new array value
- * with the source data inserted into the right part of the array.
+ * In a container assignment, we are given a destination container value plus a
+ * source value that is to be assigned to a single element or a slice of that
+ * container. We produce an expression that represents the new container value
+ * with the source data inserted into the right part of the container.
*
- * For both cases, if the source array is of a domain-over-array type,
- * the result is of the base array type or its element type; essentially,
- * we must fold a domain to its base type before applying subscripting.
- * (Note that int2vector and oidvector are treated as domains here.)
+ * For both cases, this function contains only general subscription logic while
+ * type-specific logic (e.g. type verifications and coersion) is placend in
+ * separate procedure indicated by typsubscription. There is only one exception
+ * for now about domain-over-container, if the source container is of a
+ * domain-over-container type, the result is of the base container type or its
+ * element type; essentially, we must fold a domain to its base type before
+ * applying subscripting. (Note that int2vector and oidvector are treated as
+ * domains here.) If domain verification failed we assume, that element type
+ * must be the same as container type (e.g. in case of jsonb).
+ * An error will appear in case if current container type doesn't have a
+ * subscription procedure.
*
- * pstate Parse state
- * arrayBase Already-transformed expression for the array as a whole
- * arrayType OID of array's datatype (should match type of arrayBase,
- * or be the base type of arrayBase's domain type)
- * elementType OID of array's element type (fetch with transformArrayType,
- * or pass InvalidOid to do it here)
- * arrayTypMod typmod for the array (which is also typmod for the elements)
- * indirection Untransformed list of subscripts (must not be NIL)
- * assignFrom NULL for array fetch, else transformed expression for source.
+ * pstate Parse state
+ * containerBase Already-transformed expression for the container as a whole
+ * containerType OID of container's datatype (should match type of containerBase,
+ * or be the base type of containerBase's domain type)
+ * elementType OID of container's element type (fetch with transformArrayType,
+ * or pass InvalidOid to do it here)
+ * containerTypMod typmod for the container (which is also typmod for the elements)
+ * indirection Untransformed list of subscripts (must not be NIL)
+ * assignFrom NULL for container fetch, else transformed expression for source.
*/
-ArrayRef *
-transformArraySubscripts(ParseState *pstate,
- Node *arrayBase,
- Oid arrayType,
- Oid elementType,
- int32 arrayTypMod,
- List *indirection,
- Node *assignFrom)
+
+SubscriptionRef *
+transformContainerSubscripts(ParseState *pstate,
+ Node *containerBase,
+ Oid containerType,
+ Oid elementType,
+ int32 containerTypMod,
+ List *indirection,
+ Node *assignFrom)
{
- bool isSlice = false;
- List *upperIndexpr = NIL;
- List *lowerIndexpr = NIL;
- ListCell *idx;
- ArrayRef *aref;
+ bool isSlice = false;
+ List *upperIndexpr = NIL;
+ List *lowerIndexpr = NIL;
+ ListCell *idx;
+ SubscriptionRef *sbsref,
+ *prepared_sbsref;
+ RegProcedure typsubscription = get_typsubscription(containerType);
+
+ if (!OidIsValid(typsubscription))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("cannot subscript type %s because it does not support subscription",
+ format_type_be(containerType))));
/*
* Caller may or may not have bothered to determine elementType. Note
- * that if the caller did do so, arrayType/arrayTypMod must be as modified
+ * that if the caller did do so, containerType/containerTypMod must be as modified
* by transformArrayType, ie, smash domain to base type.
*/
if (!OidIsValid(elementType))
- elementType = transformArrayType(&arrayType, &arrayTypMod);
+ elementType = transformArrayType(&containerType, &containerTypMod);
+
+ if (!OidIsValid(elementType))
+ elementType = containerType;
/*
- * A list containing only simple subscripts refers to a single array
+ * A list containing only simple subscripts refers to a single container
* element. If any of the items are slice specifiers (lower:upper), then
- * the subscript expression means an array slice operation. In this case,
+ * the subscript expression means an container slice operation. In this case,
* we convert any non-slice items to slices by treating the single
* subscript as the upper bound and supplying an assumed lower bound of 1.
* We have to prescan the list to see if there are any slice items.
@@ -343,107 +361,37 @@ transformArraySubscripts(ParseState *pstate,
if (ai->lidx)
{
subexpr = transformExpr(pstate, ai->lidx, pstate->p_expr_kind);
- /* If it's not int4 already, try to coerce */
- subexpr = coerce_to_target_type(pstate,
- subexpr, exprType(subexpr),
- INT4OID, -1,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (subexpr == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array subscript must have type integer"),
- parser_errposition(pstate, exprLocation(ai->lidx))));
- }
- else if (!ai->is_slice)
- {
- /* Make a constant 1 */
- subexpr = (Node *) makeConst(INT4OID,
- -1,
- InvalidOid,
- sizeof(int32),
- Int32GetDatum(1),
- false,
- true); /* pass by value */
}
else
{
/* Slice with omitted lower bound, put NULL into the list */
subexpr = NULL;
}
- lowerIndexpr = lappend(lowerIndexpr, subexpr);
- }
- else
- Assert(ai->lidx == NULL && !ai->is_slice);
-
- if (ai->uidx)
- {
- subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
- /* If it's not int4 already, try to coerce */
- subexpr = coerce_to_target_type(pstate,
- subexpr, exprType(subexpr),
- INT4OID, -1,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (subexpr == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array subscript must have type integer"),
- parser_errposition(pstate, exprLocation(ai->uidx))));
- }
- else
- {
- /* Slice with omitted upper bound, put NULL into the list */
- Assert(isSlice && ai->is_slice);
- subexpr = NULL;
+ lowerIndexpr = lappend(lowerIndexpr, list_make2(subexpr, ai));
}
+ subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
upperIndexpr = lappend(upperIndexpr, subexpr);
}
/*
- * If doing an array store, coerce the source value to the right type.
- * (This should agree with the coercion done by transformAssignedExpr.)
+ * Ready to build the SubscriptionRef node.
*/
- if (assignFrom != NULL)
- {
- Oid typesource = exprType(assignFrom);
- Oid typeneeded = isSlice ? arrayType : elementType;
- Node *newFrom;
-
- newFrom = coerce_to_target_type(pstate,
- assignFrom, typesource,
- typeneeded, arrayTypMod,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (newFrom == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array assignment requires type %s"
- " but expression is of type %s",
- format_type_be(typeneeded),
- format_type_be(typesource)),
- errhint("You will need to rewrite or cast the expression."),
- parser_errposition(pstate, exprLocation(assignFrom))));
- assignFrom = newFrom;
- }
-
- /*
- * Ready to build the ArrayRef node.
- */
- aref = makeNode(ArrayRef);
- aref->refarraytype = arrayType;
- aref->refelemtype = elementType;
- aref->reftypmod = arrayTypMod;
+ sbsref = makeNode(SubscriptionRef);
+ sbsref->refcontainertype = containerType;
+ sbsref->refelemtype = elementType;
+ sbsref->reftypmod = containerTypMod;
/* refcollid will be set by parse_collate.c */
- aref->refupperindexpr = upperIndexpr;
- aref->reflowerindexpr = lowerIndexpr;
- aref->refexpr = (Expr *) arrayBase;
- aref->refassgnexpr = (Expr *) assignFrom;
+ sbsref->refupperindexpr = upperIndexpr;
+ sbsref->reflowerindexpr = lowerIndexpr;
+ sbsref->refexpr = (Expr *) containerBase;
+ sbsref->refassgnexpr = (Expr *) assignFrom;
+
+ prepared_sbsref = (SubscriptionRef *) OidFunctionCall3(typsubscription,
+ Int32GetDatum(SBS_VALIDATION),
+ PointerGetDatum(sbsref),
+ PointerGetDatum(pstate));
- return aref;
+ return prepared_sbsref;
}
/*
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index b7b82bf..026e463 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -759,41 +759,24 @@ transformAssignmentIndirection(ParseState *pstate,
/* base case: just coerce RHS to match target type ID */
- result = coerce_to_target_type(pstate,
- rhs, exprType(rhs),
- targetTypeId, targetTypMod,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
+ if (targetTypeId != InvalidOid)
+ result = coerce_to_target_type(pstate,
+ rhs, exprType(rhs),
+ targetTypeId, targetTypMod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ else
+ result = rhs;
+
if (result == NULL)
- {
- if (targetIsArray)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array assignment to \"%s\" requires type %s"
- " but expression is of type %s",
- targetName,
- format_type_be(targetTypeId),
- format_type_be(exprType(rhs))),
- errhint("You will need to rewrite or cast the expression."),
- parser_errposition(pstate, location)));
- else
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("subfield \"%s\" is of type %s"
- " but expression is of type %s",
- targetName,
- format_type_be(targetTypeId),
- format_type_be(exprType(rhs))),
- errhint("You will need to rewrite or cast the expression."),
- parser_errposition(pstate, location)));
- }
+ result = rhs;
return result;
}
/*
- * helper for transformAssignmentIndirection: process array assignment
+ * helper for transformAssignmentIndirection: process container assignment
*/
static Node *
transformAssignmentSubscripts(ParseState *pstate,
@@ -809,55 +792,55 @@ transformAssignmentSubscripts(ParseState *pstate,
int location)
{
Node *result;
- Oid arrayType;
- int32 arrayTypMod;
+ Oid containerType;
+ int32 containerTypMod;
Oid elementTypeId;
Oid typeNeeded;
Oid collationNeeded;
Assert(subscripts != NIL);
- /* Identify the actual array type and element type involved */
- arrayType = targetTypeId;
- arrayTypMod = targetTypMod;
- elementTypeId = transformArrayType(&arrayType, &arrayTypMod);
+ /* Identify the actual container type and element type involved */
+ containerType = targetTypeId;
+ containerTypMod = targetTypMod;
+ elementTypeId = transformArrayType(&containerType, &containerTypMod);
/* Identify type that RHS must provide */
- typeNeeded = isSlice ? arrayType : elementTypeId;
+ typeNeeded = isSlice ? containerType : elementTypeId;
/*
- * Array normally has same collation as elements, but there's an
- * exception: we might be subscripting a domain over an array type. In
+ * container normally has same collation as elements, but there's an
+ * exception: we might be subscripting a domain over an container type. In
* that case use collation of the base type.
*/
- if (arrayType == targetTypeId)
+ if (containerType == targetTypeId)
collationNeeded = targetCollation;
else
- collationNeeded = get_typcollation(arrayType);
+ collationNeeded = get_typcollation(containerType);
- /* recurse to create appropriate RHS for array assign */
+ /* recurse to create appropriate RHS for container assign */
rhs = transformAssignmentIndirection(pstate,
NULL,
targetName,
true,
typeNeeded,
- arrayTypMod,
+ containerTypMod,
collationNeeded,
next_indirection,
rhs,
location);
/* process subscripts */
- result = (Node *) transformArraySubscripts(pstate,
- basenode,
- arrayType,
- elementTypeId,
- arrayTypMod,
- subscripts,
- rhs);
-
- /* If target was a domain over array, need to coerce up to the domain */
- if (arrayType != targetTypeId)
+ result = (Node *) transformContainerSubscripts(pstate,
+ basenode,
+ containerType,
+ exprType(rhs),
+ containerTypMod,
+ subscripts,
+ rhs);
+
+ /* If target was a domain over container, need to coerce up to the domain */
+ if (containerType != targetTypeId)
{
Oid resulttype = exprType(result);
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index a22a11e..e3ca08b 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -895,7 +895,7 @@ process_matched_tle(TargetEntry *src_tle,
/*----------
* Multiple assignments to same attribute. Allow only if all are
- * FieldStore or ArrayRef assignment operations. This is a bit
+ * FieldStore or SubscriptionRef assignment operations. This is a bit
* tricky because what we may actually be looking at is a nest of
* such nodes; consider
* UPDATE tab SET col.fld1.subfld1 = x, col.fld2.subfld2 = y
@@ -903,7 +903,7 @@ process_matched_tle(TargetEntry *src_tle,
* FieldStore(col, fld1, FieldStore(placeholder, subfld1, x))
* FieldStore(col, fld2, FieldStore(placeholder, subfld2, y))
* However, we can ignore the substructure and just consider the top
- * FieldStore or ArrayRef from each assignment, because it works to
+ * FieldStore or SubscriptionRef from each assignment, because it works to
* combine these as
* FieldStore(FieldStore(col, fld1,
* FieldStore(placeholder, subfld1, x)),
@@ -913,7 +913,7 @@ process_matched_tle(TargetEntry *src_tle,
*
* For FieldStore, instead of nesting we can generate a single
* FieldStore with multiple target fields. We must nest when
- * ArrayRefs are involved though.
+ * SubscriptionRefs are involved though.
*----------
*/
src_expr = (Node *) src_tle->expr;
@@ -972,13 +972,13 @@ process_matched_tle(TargetEntry *src_tle,
}
newexpr = (Node *) fstore;
}
- else if (IsA(src_expr, ArrayRef))
+ else if (IsA(src_expr, SubscriptionRef))
{
- ArrayRef *aref = makeNode(ArrayRef);
+ SubscriptionRef *sbsref = makeNode(SubscriptionRef);
- memcpy(aref, src_expr, sizeof(ArrayRef));
- aref->refexpr = (Expr *) prior_expr;
- newexpr = (Node *) aref;
+ memcpy(sbsref, src_expr, sizeof(SubscriptionRef));
+ sbsref->refexpr = (Expr *) prior_expr;
+ newexpr = (Node *) sbsref;
}
else
{
@@ -1005,14 +1005,15 @@ get_assignment_input(Node *node)
return (Node *) fstore->arg;
}
- else if (IsA(node, ArrayRef))
+ else if (IsA(node, SubscriptionRef))
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptionRef *sbsref = (SubscriptionRef *) node;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
return NULL;
- return (Node *) aref->refexpr;
+ return (Node *) sbsref->refexpr;
}
+
return NULL;
}
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 8fbd850..3a0993e 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -24,6 +24,8 @@
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "libpq/pqformat.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
#include "utils/array.h"
#include "utils/arrayaccess.h"
#include "utils/builtins.h"
@@ -31,6 +33,8 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
+#include "parser/parse_node.h"
+#include "parser/parse_coerce.h"
/*
@@ -88,6 +92,7 @@ typedef struct ArrayIteratorData
static bool array_isspace(char ch);
static int ArrayCount(const char *str, int *dim, char typdelim);
+static bool isAssignmentIndirectionExpr(ExprState *exprstate);
static void ReadArrayStr(char *arrayStr, const char *origStr,
int nitems, int ndim, int *dim,
FmgrInfo *inputproc, Oid typioparam, int32 typmod,
@@ -157,7 +162,8 @@ static int width_bucket_array_variable(Datum operand,
ArrayType *thresholds,
Oid collation,
TypeCacheEntry *typentry);
-
+static Datum array_subscription_prepare(PG_FUNCTION_ARGS);
+static Datum array_subscription_evaluate(PG_FUNCTION_ARGS);
/*
* array_in :
@@ -6524,3 +6530,363 @@ width_bucket_array_variable(Datum operand,
return left;
}
+
+/*
+ * Helper for ExecEvalSubscriptionRef: is expr a nested FieldStore or SubscriptionRef
+ * that might need the old element value passed down?
+ *
+ * (We could use this in ExecEvalFieldStore too, but in that case passing
+ * the old value is so cheap there's no need.)
+ */
+static bool
+isAssignmentIndirectionExpr(ExprState *exprstate)
+{
+ if (exprstate == NULL)
+ return false; /* just paranoia */
+ if (IsA(exprstate, FieldStoreState))
+ {
+ FieldStore *fstore = (FieldStore *) exprstate->expr;
+
+ if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
+ return true;
+ }
+ else if (IsA(exprstate, SubscriptionRefExprState))
+ {
+ SubscriptionRef *array_ref = (SubscriptionRef *) exprstate->expr;
+
+ if (array_ref->refexpr && IsA(array_ref->refexpr, CaseTestExpr))
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Perform an actual data extraction or modification for the array
+ * subscription. As a result the extracted Datum or the modified containers
+ * value will be returned.
+ */
+Datum
+array_subscription_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptionRefExprState *sbstate = (SubscriptionRefExprState *) PG_GETARG_POINTER(0);
+ SubscriptionExecData *sbsdata = (SubscriptionExecData *) PG_GETARG_POINTER(1);
+ ExprContext *econtext = sbsdata->xprcontext;
+ bool *is_null = sbsdata->isNull;
+ SubscriptionRef *array_ref = (SubscriptionRef *) sbstate->xprstate.expr;
+ bool is_assignment = (array_ref->refassgnexpr != NULL);
+ bool is_slice = (array_ref->reflowerindexpr != NIL);
+ IntArray u_index, l_index;
+ bool eisnull;
+ int i = 0;
+
+ get_typlenbyvalalign(array_ref->refelemtype,
+ &sbstate->refelemlength,
+ &sbstate->refelembyval,
+ &sbstate->refelemalign);
+
+ for(i = 0; i < sbsdata->indexprNumber; i++)
+ u_index.indx[i] = DatumGetInt32(sbsdata->upper[i]);
+
+ if (is_slice)
+ {
+ for(i = 0; i < sbsdata->indexprNumber; i++)
+ l_index.indx[i] = DatumGetInt32(sbsdata->lower[i]);
+ }
+
+ if (is_assignment)
+ {
+ Datum sourceData;
+ Datum save_datum;
+ bool save_isNull;
+
+ /*
+ * We might have a nested-assignment situation, in which the
+ * refassgnexpr is itself a FieldStore or SubscriptionRef that needs to
+ * obtain and modify the previous value of the array element or slice
+ * being replaced. If so, we have to extract that value from the
+ * array and pass it down via the econtext's caseValue. It's safe to
+ * reuse the CASE mechanism because there cannot be a CASE between
+ * here and where the value would be needed, and an array assignment
+ * can't be within a CASE either. (So saving and restoring the
+ * caseValue is just paranoia, but let's do it anyway.)
+ *
+ * Since fetching the old element might be a nontrivial expense, do it
+ * only if the argument appears to actually need it.
+ */
+ save_datum = econtext->caseValue_datum;
+ save_isNull = econtext->caseValue_isNull;
+
+ if (isAssignmentIndirectionExpr(sbstate->refassgnexpr))
+ {
+ if (*is_null)
+ {
+ /* whole array is null, so any element or slice is too */
+ econtext->caseValue_datum = (Datum) 0;
+ econtext->caseValue_isNull = true;
+ }
+ else if (!is_slice)
+ {
+ econtext->caseValue_datum =
+ array_get_element(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign,
+ &econtext->caseValue_isNull);
+ }
+ else
+ {
+ econtext->caseValue_datum =
+ array_get_slice(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, l_index.indx,
+ sbsdata->upperProvided,
+ sbsdata->lowerProvided,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+ econtext->caseValue_isNull = false;
+ }
+ }
+ else
+ {
+ /* argument shouldn't need caseValue, but for safety set it null */
+ econtext->caseValue_datum = (Datum) 0;
+ econtext->caseValue_isNull = true;
+ }
+
+ /*
+ * Evaluate the value to be assigned into the array.
+ */
+ sourceData = ExecEvalExpr(sbstate->refassgnexpr,
+ econtext,
+ &eisnull,
+ NULL);
+
+ econtext->caseValue_datum = save_datum;
+ econtext->caseValue_isNull = save_isNull;
+
+ /*
+ * For an assignment to a fixed-length array type, both the original
+ * array and the value to be assigned into it must be non-NULL, else
+ * we punt and return the original array.
+ */
+ if (sbstate->refattrlength > 0) /* fixed-length array? */
+ if (eisnull || *is_null)
+ return sbsdata->containerSource;
+
+ /*
+ * For assignment to varlena arrays, we handle a NULL original array
+ * by substituting an empty (zero-dimensional) array; insertion of the
+ * new element will result in a singleton array value. It does not
+ * matter whether the new element is NULL.
+ */
+ if (*is_null)
+ {
+ sbsdata->containerSource = PointerGetDatum(construct_empty_array(array_ref->refelemtype));
+ *is_null = false;
+ }
+
+ if (!is_slice)
+ return array_set_element(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, sourceData, eisnull,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+ else
+ return array_set_slice(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, l_index.indx,
+ sbsdata->upperProvided,
+ sbsdata->lowerProvided,
+ sourceData, eisnull,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+ }
+
+ if (!is_slice)
+ return array_get_element(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign,
+ is_null);
+ else
+ return array_get_slice(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, l_index.indx,
+ sbsdata->upperProvided,
+ sbsdata->lowerProvided,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+}
+
+/*
+ * Perform preparation for the array subscription, mostly type verification
+ * and coersion. This function produces an expression that represents the
+ * result of extracting a single container element/container slice or the new
+ * container value with the source data inserted into the right part of the
+ * container.
+ */
+Datum
+array_subscription_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptionRef *sbsref = (SubscriptionRef *) PG_GETARG_POINTER(0);
+ ParseState *pstate = (ParseState *) PG_GETARG_POINTER(1);
+ Node *node = (Node *)sbsref;
+ Oid array_type = sbsref->refcontainertype;
+ int32 array_typ_mode = (int32) sbsref->reftypmod;
+ bool is_slice = sbsref->reflowerindexpr != NIL;
+ Oid typeneeded = InvalidOid,
+ typesource = InvalidOid;
+ Node *new_from;
+ Oid element_type_id;
+ Node *subexpr;
+ List *upperIndexpr = NIL;
+ List *lowerIndexpr = NIL;
+ ListCell *l;
+
+ element_type_id = transformArrayType(&array_type, &array_typ_mode);
+ sbsref->refelemtype = element_type_id;
+
+ foreach(l, sbsref->refupperindexpr)
+ {
+ subexpr = (Node *) lfirst(l);
+
+ if (subexpr == NULL)
+ {
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ continue;
+ }
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ INT4OID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("array subscript must have type integer"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ }
+
+ sbsref->refupperindexpr = upperIndexpr;
+
+ foreach(l, sbsref->reflowerindexpr)
+ {
+ List *expr_ai = (List *) lfirst(l);
+ A_Indices *ai = (A_Indices *) lfirst(list_tail(expr_ai));
+
+ subexpr = (Node *) lfirst(list_head(expr_ai));
+ if (subexpr == NULL && !ai->is_slice)
+ {
+ /* Make a constant 1 */
+ subexpr = (Node *) makeConst(INT4OID,
+ -1,
+ InvalidOid,
+ sizeof(int32),
+ Int32GetDatum(1),
+ false,
+ true); /* pass by value */
+ }
+
+ if (subexpr == NULL)
+ {
+ lowerIndexpr = lappend(lowerIndexpr, subexpr);
+ continue;
+ }
+
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ INT4OID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("array subscript must have type integer"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ lowerIndexpr = lappend(lowerIndexpr, subexpr);
+ }
+
+ sbsref->reflowerindexpr = lowerIndexpr;
+
+ if (sbsref->refassgnexpr != NULL)
+ {
+ new_from = coerce_to_target_type(pstate,
+ (Node *)sbsref->refassgnexpr, typesource,
+ typeneeded, sbsref->reftypmod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (new_from == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("array assignment requires type %s"
+ " but expression is of type %s",
+ format_type_be(typeneeded),
+ format_type_be(typesource)),
+ errhint("You will need to rewrite or cast the expression."),
+ parser_errposition(pstate, exprLocation((Node *)sbsref->refassgnexpr))));
+ sbsref->refassgnexpr = (Expr *)new_from;
+
+ if (array_type != sbsref->refcontainertype)
+ {
+ typesource = exprType((Node *)sbsref->refassgnexpr);
+ typesource = is_slice ? sbsref->refcontainertype : sbsref->refelemtype;
+
+ node = coerce_to_target_type(pstate,
+ node, array_type,
+ sbsref->refcontainertype, sbsref->reftypmod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+
+ /* can fail if we had int2vector/oidvector, but not for true domains */
+ if (node == NULL && node->type != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_CANNOT_COERCE),
+ errmsg("cannot cast type %s to %s",
+ format_type_be(array_type),
+ format_type_be(sbsref->refcontainertype)),
+ parser_errposition(pstate, 0)));
+
+ PG_RETURN_POINTER(node);
+ }
+
+ }
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+/*
+ * Handle array-type subscription logic.
+ */
+Datum
+array_subscription(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1,
+ fcinfo->nargs);
+
+ if (op_type & SBS_VALIDATION)
+ return array_subscription_prepare(&target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return array_subscription_evaluate(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscription function: %d", op_type);
+}
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index 987cfd1..27a5e89 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -1145,23 +1145,34 @@ to_jsonb(PG_FUNCTION_ARGS)
{
Datum val = PG_GETARG_DATUM(0);
Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
- JsonbInState result;
- JsonbTypeCategory tcategory;
- Oid outfuncoid;
+ JsonbValue *res = to_jsonb_worker(val, val_type);
+ PG_RETURN_POINTER(JsonbValueToJsonb(res));
+}
- if (val_type == InvalidOid)
+/*
+ * Do the actual conversion to jsonb for to_jsonb function. This logic is
+ * separated because it can be useful not only in here (e.g. we use it in
+ * jsonb subscription)
+ */
+JsonbValue *
+to_jsonb_worker(Datum source, Oid source_type)
+{
+ JsonbInState result;
+ JsonbTypeCategory tcategory;
+ Oid outfuncoid;
+
+ if (source_type == InvalidOid)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("could not determine input data type")));
- jsonb_categorize_type(val_type,
+ jsonb_categorize_type(source_type,
&tcategory, &outfuncoid);
memset(&result, 0, sizeof(JsonbInState));
- datum_to_jsonb(val, false, &result, tcategory, outfuncoid, false);
-
- PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
+ datum_to_jsonb(source, false, &result, tcategory, outfuncoid, false);
+ return result.res;
}
/*
diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c
index ddc34ce..5636a1c 100644
--- a/src/backend/utils/adt/jsonb_util.c
+++ b/src/backend/utils/adt/jsonb_util.c
@@ -61,6 +61,19 @@ static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate,
JsonbIteratorToken seq,
JsonbValue *scalarVal);
+JsonbValue *
+JsonbToJsonbValue(Jsonb *jsonb, JsonbValue *val)
+{
+ if (!val)
+ val = (JsonbValue *) palloc(sizeof(JsonbValue));
+
+ val->type = jbvBinary;
+ val->val.binary.data = &jsonb->root;
+ val->val.binary.len = VARSIZE(jsonb) - VARHDRSZ;
+
+ return val;
+}
+
/*
* Turn an in-memory JsonbValue into a Jsonb for on-disk storage.
*
@@ -530,9 +543,30 @@ pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq,
/* unpack the binary and add each piece to the pstate */
it = JsonbIteratorInit(jbval->val.binary.data);
+
+ if ((jbval->val.binary.data->header & JB_FSCALAR) && *pstate)
+ {
+ tok = JsonbIteratorNext(&it, &v, true);
+ Assert(tok == WJB_BEGIN_ARRAY);
+ Assert(v.type == jbvArray && v.val.array.rawScalar);
+
+ tok = JsonbIteratorNext(&it, &v, true);
+ Assert(tok == WJB_ELEM);
+
+ res = pushJsonbValueScalar(pstate, seq, &v);
+
+ tok = JsonbIteratorNext(&it, &v, true);
+ Assert(tok == WJB_END_ARRAY);
+ Assert(it == NULL);
+
+ return res;
+ }
+
while ((tok = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
res = pushJsonbValueScalar(pstate, tok,
- tok < WJB_BEGIN_ARRAY ? &v : NULL);
+ tok < WJB_BEGIN_ARRAY ||
+ (tok == WJB_BEGIN_ARRAY &&
+ v.val.array.rawScalar) ? &v : NULL);
return res;
}
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index a80a20e..9256079 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -23,6 +23,8 @@
#include "lib/stringinfo.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_coerce.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/hsearch.h"
@@ -32,6 +34,7 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
+#include "parser/parse_node.h"
/* Operations available for setPath */
#define JB_PATH_NOOP 0x0000
@@ -136,18 +139,21 @@ static JsonbValue *findJsonbValueFromContainerLen(JsonbContainer *container,
/* functions supporting jsonb_delete, jsonb_set and jsonb_concat */
static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
JsonbParseState **state);
+static Datum jsonb_set_element(Datum datum, Datum *path, int path_len,
+ Datum sourceData, Oid source_type);
+static Datum jsonb_get_element(Jsonb *jb, Datum *path, int npath,
+ bool *isnull, bool as_text);
static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len,
- JsonbParseState **st, int level, Jsonb *newval,
- int op_type);
+ JsonbParseState **st, int level, JsonbValue *newval, int op_type);
static void setPathObject(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len, JsonbParseState **st,
- int level,
- Jsonb *newval, uint32 npairs, int op_type);
+ int level, JsonbValue *newval, uint32 npairs, int op_type);
static void setPathArray(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len, JsonbParseState **st,
- int level, Jsonb *newval, uint32 nelems, int op_type);
-static void addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb);
+ int level, JsonbValue *newval, uint32 nelems, int op_type);
+static Datum jsonb_subscription_evaluate(PG_FUNCTION_ARGS);
+static Datum jsonb_subscription_prepare(PG_FUNCTION_ARGS);
/* state for json_object_keys */
typedef struct OkeysState
@@ -1171,16 +1177,11 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
Jsonb *jb = PG_GETARG_JSONB(0);
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
- Jsonb *res;
Datum *pathtext;
bool *pathnulls;
+ bool isnull;
int npath;
- int i;
- bool have_object = false,
- have_array = false;
- JsonbValue *jbvp = NULL;
- JsonbValue tv;
- JsonbContainer *container;
+ Datum res;
/*
* If the array contains any null elements, return NULL, on the grounds
@@ -1195,9 +1196,28 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
deconstruct_array(path, TEXTOID, -1, false, 'i',
&pathtext, &pathnulls, &npath);
- /* Identify whether we have object, array, or scalar at top-level */
- container = &jb->root;
+ res = jsonb_get_element(jb, pathtext, npath, &isnull, as_text);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(res);
+}
+static Datum
+jsonb_get_element(Jsonb *jb, Datum *path, int npath, bool *isnull, bool as_text)
+{
+ Jsonb *res;
+ JsonbContainer *container = &jb->root;
+ JsonbValue *jbvp = NULL;
+ JsonbValue tv;
+ int i;
+ bool have_object = false,
+ have_array = false;
+
+ *isnull = false;
+
+ /* Identify whether we have object, array, or scalar at top-level */
if (JB_ROOT_IS_OBJECT(jb))
have_object = true;
else if (JB_ROOT_IS_ARRAY(jb) && !JB_ROOT_IS_SCALAR(jb))
@@ -1222,14 +1242,14 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
if (as_text)
{
- PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL,
+ return PointerGetDatum(cstring_to_text(JsonbToCString(NULL,
container,
VARSIZE(jb))));
}
else
{
/* not text mode - just hand back the jsonb */
- PG_RETURN_JSONB(jb);
+ return JsonbGetDatum(jb);
}
}
@@ -1239,21 +1259,24 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
jbvp = findJsonbValueFromContainerLen(container,
JB_FOBJECT,
- VARDATA_ANY(pathtext[i]),
- VARSIZE_ANY_EXHDR(pathtext[i]));
+ VARDATA_ANY(path[i]),
+ VARSIZE_ANY_EXHDR(path[i]));
}
else if (have_array)
{
long lindex;
uint32 index;
- char *indextext = TextDatumGetCString(pathtext[i]);
+ char *indextext = TextDatumGetCString(path[i]);
char *endptr;
errno = 0;
lindex = strtol(indextext, &endptr, 10);
if (endptr == indextext || *endptr != '\0' || errno != 0 ||
lindex > INT_MAX || lindex < INT_MIN)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
if (lindex >= 0)
{
@@ -1271,7 +1294,10 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
nelements = container->header & JB_CMASK;
if (-lindex > nelements)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
else
index = nelements + lindex;
}
@@ -1281,11 +1307,15 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
else
{
/* scalar, extraction yields a null */
- PG_RETURN_NULL();
+ *isnull = true;
+ return PointerGetDatum(NULL);
}
if (jbvp == NULL)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
else if (i == npath - 1)
break;
@@ -1310,27 +1340,57 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
/* special-case outputs for string and null values */
if (jbvp->type == jbvString)
- PG_RETURN_TEXT_P(cstring_to_text_with_len(jbvp->val.string.val,
- jbvp->val.string.len));
+ return PointerGetDatum(
+ cstring_to_text_with_len(jbvp->val.string.val,
+ jbvp->val.string.len));
if (jbvp->type == jbvNull)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
}
res = JsonbValueToJsonb(jbvp);
if (as_text)
{
- PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL,
+ return PointerGetDatum(cstring_to_text(JsonbToCString(NULL,
&res->root,
VARSIZE(res))));
}
else
{
/* not text mode - just hand back the jsonb */
- PG_RETURN_JSONB(res);
+ return JsonbGetDatum(res);
}
}
+Datum
+jsonb_set_element(Datum jsonbdatum, Datum *path, int path_len,
+ Datum sourceData, Oid source_type)
+{
+ Jsonb *jb = DatumGetJsonb(jsonbdatum);
+ JsonbValue *newval,
+ *res;
+ JsonbParseState *state = NULL;
+ JsonbIterator *it;
+ bool *path_nulls = palloc0(path_len * sizeof(bool));
+
+ newval = to_jsonb_worker(sourceData, source_type);
+
+ if (newval->type == jbvArray && newval->val.array.rawScalar)
+ *newval = newval->val.array.elems[0];
+
+ it = JsonbIteratorInit(&jb->root);
+
+ res = setPath(&it, path, path_nulls, path_len, &state, 0,
+ newval, JB_PATH_CREATE);
+
+ pfree(path_nulls);
+
+ PG_RETURN_JSONB(JsonbValueToJsonb(res));
+}
+
/*
* SQL function json_array_length(json) -> int
*/
@@ -3286,57 +3346,6 @@ jsonb_strip_nulls(PG_FUNCTION_ARGS)
}
/*
- * Add values from the jsonb to the parse state.
- *
- * If the parse state container is an object, the jsonb is pushed as
- * a value, not a key.
- *
- * This needs to be done using an iterator because pushJsonbValue doesn't
- * like getting jbvBinary values, so we can't just push jb as a whole.
- */
-static void
-addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb)
-{
- JsonbIterator *it;
- JsonbValue *o = &(*jbps)->contVal;
- JsonbValue v;
- JsonbIteratorToken type;
-
- it = JsonbIteratorInit(&jb->root);
-
- Assert(o->type == jbvArray || o->type == jbvObject);
-
- if (JB_ROOT_IS_SCALAR(jb))
- {
- (void) JsonbIteratorNext(&it, &v, false); /* skip array header */
- (void) JsonbIteratorNext(&it, &v, false); /* fetch scalar value */
-
- switch (o->type)
- {
- case jbvArray:
- (void) pushJsonbValue(jbps, WJB_ELEM, &v);
- break;
- case jbvObject:
- (void) pushJsonbValue(jbps, WJB_VALUE, &v);
- break;
- default:
- elog(ERROR, "unexpected parent of nested structure");
- }
- }
- else
- {
- while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
- {
- if (type == WJB_KEY || type == WJB_VALUE || type == WJB_ELEM)
- (void) pushJsonbValue(jbps, type, &v);
- else
- (void) pushJsonbValue(jbps, type, NULL);
- }
- }
-
-}
-
-/*
* SQL function jsonb_pretty (jsonb)
*
* Pretty-printed text for the jsonb
@@ -3522,7 +3531,9 @@ jsonb_set(PG_FUNCTION_ARGS)
{
Jsonb *in = PG_GETARG_JSONB(0);
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
- Jsonb *newval = PG_GETARG_JSONB(2);
+ Jsonb *newjsonb = PG_GETARG_JSONB(2);
+ JsonbValue newvalbuf;
+ JsonbValue *newval = JsonbToJsonbValue(newjsonb, &newvalbuf);
bool create = PG_GETARG_BOOL(3);
JsonbValue *res = NULL;
Datum *path_elems;
@@ -3614,7 +3625,9 @@ jsonb_insert(PG_FUNCTION_ARGS)
{
Jsonb *in = PG_GETARG_JSONB(0);
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
- Jsonb *newval = PG_GETARG_JSONB(2);
+ Jsonb *newjsonb = PG_GETARG_JSONB(2);
+ JsonbValue newvalbuf;
+ JsonbValue *newval = JsonbToJsonbValue(newjsonb, &newvalbuf);
bool after = PG_GETARG_BOOL(3);
JsonbValue *res = NULL;
Datum *path_elems;
@@ -3774,10 +3787,10 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
* All path elements before the last must already exist
* whatever bits in op_type are set, or nothing is done.
*/
-static JsonbValue *
+JsonbValue *
setPath(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len,
- JsonbParseState **st, int level, Jsonb *newval, int op_type)
+ JsonbParseState **st, int level, JsonbValue *newval, int op_type)
{
JsonbValue v;
JsonbIteratorToken r;
@@ -3830,11 +3843,11 @@ setPath(JsonbIterator **it, Datum *path_elems,
static void
setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
int path_len, JsonbParseState **st, int level,
- Jsonb *newval, uint32 npairs, int op_type)
+ JsonbValue *newval, uint32 npairs, int op_type)
{
- JsonbValue v;
int i;
- JsonbValue k;
+ JsonbValue k,
+ v;
bool done = false;
if (level >= path_len || path_nulls[level])
@@ -3851,7 +3864,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
newkey.val.string.val = VARDATA_ANY(path_elems[level]);
(void) pushJsonbValue(st, WJB_KEY, &newkey);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_VALUE, newval);
}
for (i = 0; i < npairs; i++)
@@ -3882,7 +3895,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
if (!(op_type & JB_PATH_DELETE))
{
(void) pushJsonbValue(st, WJB_KEY, &k);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_VALUE, newval);
}
done = true;
}
@@ -3905,7 +3918,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
newkey.val.string.val = VARDATA_ANY(path_elems[level]);
(void) pushJsonbValue(st, WJB_KEY, &newkey);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_VALUE, newval);
}
(void) pushJsonbValue(st, r, &k);
@@ -3937,9 +3950,9 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
static void
setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
int path_len, JsonbParseState **st, int level,
- Jsonb *newval, uint32 nelems, int op_type)
+ JsonbValue *newval, uint32 nelems, int op_type)
{
- JsonbValue v;
+ JsonbValue v, *new = (JsonbValue *) newval;
int idx,
i;
bool done = false;
@@ -3984,8 +3997,8 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
if ((idx == INT_MIN || nelems == 0) && (level == path_len - 1) &&
(op_type & JB_PATH_CREATE_OR_INSERT))
{
- Assert(newval != NULL);
- addJsonbToParseState(st, newval);
+ Assert(new != NULL);
+ (void) pushJsonbValue(st, WJB_ELEM, new);
done = true;
}
@@ -4001,7 +4014,7 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
r = JsonbIteratorNext(it, &v, true); /* skip */
if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_CREATE))
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, new);
/*
* We should keep current value only in case of
@@ -4012,13 +4025,13 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
(void) pushJsonbValue(st, r, &v);
if (op_type & JB_PATH_INSERT_AFTER)
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, new);
done = true;
}
else
(void) setPath(it, path_elems, path_nulls, path_len,
- st, level + 1, newval, op_type);
+ st, level + 1, new, op_type);
}
else
{
@@ -4046,8 +4059,169 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
if (op_type & JB_PATH_CREATE_OR_INSERT && !done &&
level == path_len - 1 && i == nelems - 1)
{
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, new);
}
}
}
}
+
+/*
+ * Perform an actual data extraction or modification for the jsonb
+ * subscription. As a result the extracted Datum or the modified containers
+ * value will be returned.
+ */
+Datum
+jsonb_subscription_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptionRefExprState *sbstate = (SubscriptionRefExprState *) PG_GETARG_POINTER(0);
+ SubscriptionExecData *sbsdata = (SubscriptionExecData *) PG_GETARG_POINTER(1);
+ SubscriptionRef *jsonb_ref = (SubscriptionRef *) sbstate->xprstate.expr;
+ bool *is_null = sbsdata->isNull;
+ bool is_assignment = (jsonb_ref->refassgnexpr != NULL);
+
+ if (is_assignment)
+ {
+ ExprContext *econtext = sbsdata->xprcontext;
+ Datum sourceData;
+ Datum save_datum;
+ bool save_isNull;
+ bool eisnull;
+
+ /*
+ * We might have a nested-assignment situation, in which the
+ * refassgnexpr is itself a FieldStore or SubscriptionRef that needs to
+ * obtain and modify the previous value of the array element or slice
+ * being replaced. If so, we have to extract that value from the
+ * array and pass it down via the econtext's caseValue. It's safe to
+ * reuse the CASE mechanism because there cannot be a CASE between
+ * here and where the value would be needed, and an array assignment
+ * can't be within a CASE either. (So saving and restoring the
+ * caseValue is just paranoia, but let's do it anyway.)
+ *
+ * Since fetching the old element might be a nontrivial expense, do it
+ * only if the argument appears to actually need it.
+ */
+ save_datum = econtext->caseValue_datum;
+ save_isNull = econtext->caseValue_isNull;
+
+ /*
+ * Evaluate the value to be assigned into the array.
+ */
+ sourceData = ExecEvalExpr(sbstate->refassgnexpr,
+ econtext,
+ &eisnull,
+ NULL);
+
+ econtext->caseValue_datum = save_datum;
+ econtext->caseValue_isNull = save_isNull;
+
+ /*
+ * For an assignment to a fixed-length array type, both the original
+ * array and the value to be assigned into it must be non-NULL, else
+ * we punt and return the original array.
+ */
+ if (sbstate->refattrlength > 0) /* fixed-length array? */
+ if (eisnull || *is_null)
+ return sbsdata->containerSource;
+
+ /*
+ * For assignment to varlena arrays, we handle a NULL original array
+ * by substituting an empty (zero-dimensional) array; insertion of the
+ * new element will result in a singleton array value. It does not
+ * matter whether the new element is NULL.
+ */
+ if (*is_null)
+ {
+ sbsdata->containerSource =
+ PointerGetDatum(construct_empty_array(jsonb_ref->refelemtype));
+ *is_null = false;
+ }
+
+ return jsonb_set_element(sbsdata->containerSource,
+ sbsdata->upper,
+ sbsdata->indexprNumber,
+ sourceData,
+ jsonb_ref->refelemtype);
+ }
+ else
+ return jsonb_get_element(DatumGetJsonb(sbsdata->containerSource),
+ sbsdata->upper,
+ sbsdata->indexprNumber,
+ is_null,
+ false);
+}
+
+/*
+ * Perform preparation for the jsonb subscription. Since there are not any
+ * particular restrictions for this kind of subscription, we will verify that
+ * it is not a slice operation. This function produces an expression that
+ * represents the result of extracting a single container element or the new
+ * container value with the source data inserted into the right part of the
+ * container.
+ */
+Datum
+jsonb_subscription_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptionRef *sbsref = (SubscriptionRef *) PG_GETARG_POINTER(0);
+ ParseState *pstate = (ParseState *) PG_GETARG_POINTER(1);
+ List *upperIndexpr = NIL;
+ ListCell *l;
+
+ if (sbsref->reflowerindexpr != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("jsonb subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *)lfirst(sbsref->reflowerindexpr->head))))));
+
+ foreach(l, sbsref->refupperindexpr)
+ {
+ Node *subexpr = (Node *) lfirst(l);
+
+ Assert(subexpr != NULL);
+
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("jsonb subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *) lfirst(sbsref->refupperindexpr->head))))));
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ TEXTOID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("jsonb subscript must have text type"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ }
+
+ sbsref->refupperindexpr = upperIndexpr;
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+/*
+ * Handle jsonb-type subscription logic.
+ */
+Datum
+jsonb_subscription(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1,
+ fcinfo->nargs);
+
+ if (op_type & SBS_VALIDATION)
+ return jsonb_subscription_prepare(&target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return jsonb_subscription_evaluate(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscription function: %d", op_type);
+}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 8a81d7a..18dab1e 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -439,7 +439,7 @@ static void get_tablesample_def(TableSampleClause *tablesample,
static void get_opclass_name(Oid opclass, Oid actual_datatype,
StringInfo buf);
static Node *processIndirection(Node *node, deparse_context *context);
-static void printSubscripts(ArrayRef *aref, deparse_context *context);
+static void printSubscripts(SubscriptionRef *aref, deparse_context *context);
static char *get_relation_name(Oid relid);
static char *generate_relation_name(Oid relid, List *namespaces);
static char *generate_qualified_relation_name(Oid relid);
@@ -5828,7 +5828,7 @@ get_update_query_targetlist_def(Query *query, List *targetList,
{
/*
* We must dig down into the expr to see if it's a PARAM_MULTIEXPR
- * Param. That could be buried under FieldStores and ArrayRefs
+ * Param. That could be buried under FieldStores and SubscriptionRefs
* (cf processIndirection()), and underneath those there could be
* an implicit type coercion.
*/
@@ -5841,13 +5841,13 @@ get_update_query_targetlist_def(Query *query, List *targetList,
expr = (Node *) linitial(fstore->newvals);
}
- else if (IsA(expr, ArrayRef))
+ else if (IsA(expr, SubscriptionRef))
{
- ArrayRef *aref = (ArrayRef *) expr;
+ SubscriptionRef *sbsref = (SubscriptionRef *) expr;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
break;
- expr = (Node *) aref->refassgnexpr;
+ expr = (Node *) sbsref->refassgnexpr;
}
else
break;
@@ -6879,7 +6879,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
/* single words: always simple */
return true;
- case T_ArrayRef:
+ case T_SubscriptionRef:
case T_ArrayExpr:
case T_RowExpr:
case T_CoalesceExpr:
@@ -6996,7 +6996,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
return true; /* own parentheses */
}
case T_BoolExpr: /* lower precedence */
- case T_ArrayRef: /* other separators */
+ case T_SubscriptionRef: /* other separators */
case T_ArrayExpr: /* other separators */
case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */
@@ -7046,7 +7046,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
return false;
return true; /* own parentheses */
}
- case T_ArrayRef: /* other separators */
+ case T_SubscriptionRef: /* other separators */
case T_ArrayExpr: /* other separators */
case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */
@@ -7232,9 +7232,9 @@ get_rule_expr(Node *node, deparse_context *context,
get_windowfunc_expr((WindowFunc *) node, context);
break;
- case T_ArrayRef:
+ case T_SubscriptionRef:
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptionRef *sbsref = (SubscriptionRef *) node;
bool need_parens;
/*
@@ -7245,24 +7245,24 @@ get_rule_expr(Node *node, deparse_context *context,
* here too, and display only the assignment source
* expression.
*/
- if (IsA(aref->refexpr, CaseTestExpr))
+ if (IsA(sbsref->refexpr, CaseTestExpr))
{
- Assert(aref->refassgnexpr);
- get_rule_expr((Node *) aref->refassgnexpr,
+ Assert(sbsref->refassgnexpr);
+ get_rule_expr((Node *) sbsref->refassgnexpr,
context, showimplicit);
break;
}
/*
* Parenthesize the argument unless it's a simple Var or a
- * FieldSelect. (In particular, if it's another ArrayRef, we
+ * FieldSelect. (In particular, if it's another SubscriptionRef, we
* *must* parenthesize to avoid confusion.)
*/
- need_parens = !IsA(aref->refexpr, Var) &&
- !IsA(aref->refexpr, FieldSelect);
+ need_parens = !IsA(sbsref->refexpr, Var) &&
+ !IsA(sbsref->refexpr, FieldSelect);
if (need_parens)
appendStringInfoChar(buf, '(');
- get_rule_expr((Node *) aref->refexpr, context, showimplicit);
+ get_rule_expr((Node *) sbsref->refexpr, context, showimplicit);
if (need_parens)
appendStringInfoChar(buf, ')');
@@ -7275,7 +7275,7 @@ get_rule_expr(Node *node, deparse_context *context,
* EXPLAIN tries to print the targetlist of a plan resulting
* from such a statement.
*/
- if (aref->refassgnexpr)
+ if (sbsref->refassgnexpr)
{
Node *refassgnexpr;
@@ -7292,7 +7292,7 @@ get_rule_expr(Node *node, deparse_context *context,
else
{
/* Just an ordinary array fetch, so print subscripts */
- printSubscripts(aref, context);
+ printSubscripts(sbsref, context);
}
}
break;
@@ -7491,12 +7491,12 @@ get_rule_expr(Node *node, deparse_context *context,
bool need_parens;
/*
- * Parenthesize the argument unless it's an ArrayRef or
+ * Parenthesize the argument unless it's an SubscriptionRef or
* another FieldSelect. Note in particular that it would be
* WRONG to not parenthesize a Var argument; simplicity is not
* the issue here, having the right number of names is.
*/
- need_parens = !IsA(arg, ArrayRef) &&!IsA(arg, FieldSelect);
+ need_parens = !IsA(arg, SubscriptionRef) && !IsA(arg, FieldSelect);
if (need_parens)
appendStringInfoChar(buf, '(');
get_rule_expr(arg, context, true);
@@ -9610,7 +9610,7 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
/*
* processIndirection - take care of array and subfield assignment
*
- * We strip any top-level FieldStore or assignment ArrayRef nodes that
+ * We strip any top-level FieldStore or assignment SubscriptionRef nodes that
* appear in the input, printing them as decoration for the base column
* name (which we assume the caller just printed). Return the subexpression
* that's to be assigned.
@@ -9652,19 +9652,19 @@ processIndirection(Node *node, deparse_context *context)
*/
node = (Node *) linitial(fstore->newvals);
}
- else if (IsA(node, ArrayRef))
+ else if (IsA(node, SubscriptionRef))
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptionRef *sbsref = (SubscriptionRef *) node;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
break;
- printSubscripts(aref, context);
+ printSubscripts(sbsref, context);
/*
* We ignore refexpr since it should be an uninteresting reference
* to the target column or subcolumn.
*/
- node = (Node *) aref->refassgnexpr;
+ node = (Node *) sbsref->refassgnexpr;
}
else
break;
@@ -9674,14 +9674,14 @@ processIndirection(Node *node, deparse_context *context)
}
static void
-printSubscripts(ArrayRef *aref, deparse_context *context)
+printSubscripts(SubscriptionRef *sbsref, deparse_context *context)
{
StringInfo buf = context->buf;
ListCell *lowlist_item;
ListCell *uplist_item;
- lowlist_item = list_head(aref->reflowerindexpr); /* could be NULL */
- foreach(uplist_item, aref->refupperindexpr)
+ lowlist_item = list_head(sbsref->reflowerindexpr); /* could be NULL */
+ foreach(uplist_item, sbsref->refupperindexpr)
{
appendStringInfoChar(buf, '[');
if (lowlist_item)
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 13ae6ad..ad8f334 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -3061,3 +3061,23 @@ get_range_subtype(Oid rangeOid)
else
return InvalidOid;
}
+
+/*
+ * get_typsubscription
+ *
+ * Given the type OID, return the type's typsubscription procedure, if any.
+ */
+RegProcedure
+get_typsubscription(Oid typid)
+{
+ HeapTuple tp;
+ RegProcedure result = InvalidOid;
+
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (HeapTupleIsValid(tp))
+ {
+ result = ((Form_pg_type) GETSTRUCT(tp))->typsubscription;
+ ReleaseSysCache(tp);
+ }
+ return result;
+}
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 5d179ae..b71d833 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -1398,3 +1398,22 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
return tupdesc;
}
+
+FunctionCallInfoData
+get_slice_arguments(FunctionCallInfo fcinfo, int begin, int end)
+{
+ FunctionCallInfoData sliced_fcinfo;
+ int i;
+
+ InitFunctionCallInfoData(sliced_fcinfo, fcinfo->flinfo,
+ fcinfo->nargs - 1, fcinfo->fncollation,
+ NULL, NULL);
+
+ for(i = begin; i < end; i++)
+ {
+ sliced_fcinfo.arg[i - begin] = fcinfo->arg[i];
+ sliced_fcinfo.argnull[i - begin] = false;
+ }
+
+ return sliced_fcinfo;
+}
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index e57b81c..23bf824 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -143,7 +143,7 @@ typedef FormData_pg_class *Form_pg_class;
* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId;
* similarly, "1" in relminmxid stands for FirstMultiXactId
*/
-DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f f f t n 3 1 _null_ _null_ ));
+DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 31 0 t f f f f f f t n 3 1 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 21 0 f f f f f f f t n 3 1 _null_ _null_ ));
DESCR("");
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index e2d08ba..4796d22 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -5344,6 +5344,12 @@ DESCR("pg_controldata recovery state information as a function");
DATA(insert OID = 3444 ( pg_control_init PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,23,23,23,23,23,23,23,16,16,16,23}" "{o,o,o,o,o,o,o,o,o,o,o,o,o}" "{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,bigint_timestamps,float4_pass_by_value,float8_pass_by_value,data_page_checksum_version}" _null_ _null_ pg_control_init _null_ _null_ _null_ ));
DESCR("pg_controldata init state information as a function");
+/* type subscription support */
+DATA(insert OID = 3343 ( jsonb_subscription PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 "2281" "2281" _null_ _null_ _null_ _null_ _null_ jsonb_subscription _null_ _null_ _null_ ));
+DESCR("Jsonb subscription logic");
+DATA(insert OID = 3344 ( array_subscription PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 "2281" "2281" _null_ _null_ _null_ _null_ _null_ array_subscription _null_ _null_ _null_ ));
+DESCR("Array subscription logic");
+
/*
* Symbolic values for provolatile column: these indicate whether the result
* of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 162239c..b54d136 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -199,6 +199,12 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO
*/
Oid typcollation;
+ /*
+ * Type specific subscription logic. If typsubscription is none, it means
+ * that this type doesn't support subscription.
+ */
+ regproc typsubscription;
+
#ifdef CATALOG_VARLEN /* variable-length fields start here */
/*
@@ -236,7 +242,7 @@ typedef FormData_pg_type *Form_pg_type;
* compiler constants for pg_type
* ----------------
*/
-#define Natts_pg_type 30
+#define Natts_pg_type 31
#define Anum_pg_type_typname 1
#define Anum_pg_type_typnamespace 2
#define Anum_pg_type_typowner 3
@@ -264,10 +270,10 @@ typedef FormData_pg_type *Form_pg_type;
#define Anum_pg_type_typtypmod 25
#define Anum_pg_type_typndims 26
#define Anum_pg_type_typcollation 27
-#define Anum_pg_type_typdefaultbin 28
-#define Anum_pg_type_typdefault 29
-#define Anum_pg_type_typacl 30
-
+#define Anum_pg_type_typsubscription 28
+#define Anum_pg_type_typdefaultbin 29
+#define Anum_pg_type_typdefault 30
+#define Anum_pg_type_typacl 31
/* ----------------
* initial contents of pg_type
@@ -283,94 +289,94 @@ typedef FormData_pg_type *Form_pg_type;
*/
/* OIDS 1 - 99 */
-DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("boolean, 'true'/'false'");
#define BOOLOID 16
-DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("variable-length string, binary values escaped");
#define BYTEAOID 17
-DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("single character");
#define CHAROID 18
-DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("63-byte type for storing system identifiers");
#define NAMEOID 19
-DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("~18 digit integer, 8-byte storage");
#define INT8OID 20
-DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("-32 thousand to 32 thousand, 2-byte storage");
#define INT2OID 21
-DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("array of int2, used in system tables");
#define INT2VECTOROID 22
-DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("-2 billion to 2 billion integer, 4-byte storage");
#define INT4OID 23
-DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered procedure");
#define REGPROCOID 24
-DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("variable-length string, no limit specified");
#define TEXTOID 25
-DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("object identifier(oid), maximum 4 billion");
#define OIDOID 26
-DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("(block, offset), physical location of tuple");
#define TIDOID 27
-DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("transaction id");
#define XIDOID 28
-DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("command identifier type, sequence in transaction id");
#define CIDOID 29
-DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("array of oids, used in system tables");
#define OIDVECTOROID 30
/* hand-built rowtype entries for bootstrapped catalogs */
/* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */
-DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
+DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
+DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
+DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
/* OIDS 100 - 199 */
-DATA(insert OID = 114 ( json PGNSP PGUID -1 f b U f t \054 0 0 199 json_in json_out json_recv json_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 114 ( json PGNSP PGUID -1 f b U f t \054 0 0 199 json_in json_out json_recv json_send - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define JSONOID 114
-DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("XML content");
#define XMLOID 142
-DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 199 ( _json PGNSP PGUID -1 f b A f t \054 0 114 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 199 ( _json PGNSP PGUID -1 f b A f t \054 0 114 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
-DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("string representing an internal node tree");
#define PGNODETREEOID 194
-DATA(insert OID = 32 ( pg_ddl_command PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 pg_ddl_command_in pg_ddl_command_out pg_ddl_command_recv pg_ddl_command_send - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 32 ( pg_ddl_command PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 pg_ddl_command_in pg_ddl_command_out pg_ddl_command_recv pg_ddl_command_send - - - ALIGNOF_POINTER p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("internal type for passing CollectedCommand");
#define PGDDLCOMMANDOID 32
/* OIDS 200 - 299 */
-DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("storage manager");
/* OIDS 300 - 399 */
@@ -380,276 +386,276 @@ DESCR("storage manager");
/* OIDS 500 - 599 */
/* OIDS 600 - 699 */
-DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("geometric point '(x, y)'");
#define POINTOID 600
-DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("geometric line segment '(pt1,pt2)'");
#define LSEGOID 601
-DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("geometric path '(pt1,...)'");
#define PATHOID 602
-DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("geometric box '(lower left,upper right)'");
#define BOXOID 603
-DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("geometric polygon '(pt1,...)'");
#define POLYGONOID 604
-DATA(insert OID = 628 ( line PGNSP PGUID 24 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 628 ( line PGNSP PGUID 24 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("geometric line");
#define LINEOID 628
-DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* OIDS 700 - 799 */
-DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("single-precision floating point number, 4-byte storage");
#define FLOAT4OID 700
-DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("double-precision floating point number, 8-byte storage");
#define FLOAT8OID 701
-DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("absolute, limited-range date and time (Unix system time)");
#define ABSTIMEOID 702
-DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("relative, limited-range time interval (Unix delta time)");
#define RELTIMEOID 703
-DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("(abstime,abstime), time interval");
#define TINTERVALOID 704
-DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("");
#define UNKNOWNOID 705
-DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("geometric circle '(center,radius)'");
#define CIRCLEOID 718
-DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("monetary amounts, $d,ddd.cc");
#define CASHOID 790
-DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* OIDS 800 - 899 */
-DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDROID 829
-DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("IP address/netmask, host address, netmask optional");
#define INETOID 869
-DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("network IP address/netmask, network address");
#define CIDROID 650
/* OIDS 900 - 999 */
/* OIDS 1000 - 1099 */
-DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
#define INT2ARRAYOID 1005
-DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
#define INT4ARRAYOID 1007
-DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 100 array_subscription _null_ _null_ _null_ ));
#define TEXTARRAYOID 1009
-DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
#define OIDARRAYOID 1028
-DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
-DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
-DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout array_typanalyze i x f 0 -1 0 100 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout array_typanalyze i x f 0 -1 0 100 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
#define FLOAT4ARRAYOID 1021
-DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("access control list");
#define ACLITEMOID 1033
-DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
#define CSTRINGARRAYOID 1263
-DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("char(length), blank-padded string, fixed storage length");
#define BPCHAROID 1042
-DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("varchar(length), non-blank-padded string, variable storage length");
#define VARCHAROID 1043
-DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("date");
#define DATEOID 1082
-DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("time of day");
#define TIMEOID 1083
/* OIDS 1100 - 1199 */
-DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("date and time");
#define TIMESTAMPOID 1114
-DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("date and time with time zone");
#define TIMESTAMPTZOID 1184
-DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("@ <number> <units>, time interval");
#define INTERVALOID 1186
-DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* OIDS 1200 - 1299 */
-DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("time of day with time zone");
#define TIMETZOID 1266
-DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* OIDS 1500 - 1599 */
-DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("fixed-length bit string");
#define BITOID 1560
-DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("variable-length bit string");
#define VARBITOID 1562
-DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* OIDS 1600 - 1699 */
/* OIDS 1700 - 1799 */
-DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("numeric(precision, decimal), arbitrary precision number");
#define NUMERICOID 1700
-DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("reference to cursor (portal name)");
#define REFCURSOROID 1790
/* OIDS 2200 - 2299 */
-DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
-DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered procedure (with args)");
#define REGPROCEDUREOID 2202
-DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered operator");
#define REGOPEROID 2203
-DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered operator (with args)");
#define REGOPERATOROID 2204
-DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered class");
#define REGCLASSOID 2205
-DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered type");
#define REGTYPEOID 2206
-DATA(insert OID = 4096 ( regrole PGNSP PGUID 4 t b N f t \054 0 0 4097 regrolein regroleout regrolerecv regrolesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4096 ( regrole PGNSP PGUID 4 t b N f t \054 0 0 4097 regrolein regroleout regrolerecv regrolesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered role");
#define REGROLEOID 4096
-DATA(insert OID = 4089 ( regnamespace PGNSP PGUID 4 t b N f t \054 0 0 4090 regnamespacein regnamespaceout regnamespacerecv regnamespacesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4089 ( regnamespace PGNSP PGUID 4 t b N f t \054 0 0 4090 regnamespacein regnamespaceout regnamespacerecv regnamespacesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered namespace");
#define REGNAMESPACEOID 4089
-DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
#define REGTYPEARRAYOID 2211
-DATA(insert OID = 4097 ( _regrole PGNSP PGUID -1 f b A f t \054 0 4096 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 4090 ( _regnamespace PGNSP PGUID -1 f b A f t \054 0 4089 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4097 ( _regrole PGNSP PGUID -1 f b A f t \054 0 4096 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 4090 ( _regnamespace PGNSP PGUID -1 f b A f t \054 0 4089 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* uuid */
-DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("UUID datatype");
#define UUIDOID 2950
-DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* pg_lsn */
-DATA(insert OID = 3220 ( pg_lsn PGNSP PGUID 8 FLOAT8PASSBYVAL b U f t \054 0 0 3221 pg_lsn_in pg_lsn_out pg_lsn_recv pg_lsn_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3220 ( pg_lsn PGNSP PGUID 8 FLOAT8PASSBYVAL b U f t \054 0 0 3221 pg_lsn_in pg_lsn_out pg_lsn_recv pg_lsn_send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("PostgreSQL LSN datatype");
#define LSNOID 3220
-DATA(insert OID = 3221 ( _pg_lsn PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3221 ( _pg_lsn PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* text search */
-DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("text representation for text search");
#define TSVECTOROID 3614
-DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("GiST index internal text representation for text search");
#define GTSVECTOROID 3642
-DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("query representation for text search");
#define TSQUERYOID 3615
-DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered text search configuration");
#define REGCONFIGOID 3734
-DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered text search dictionary");
#define REGDICTIONARYOID 3769
-DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* jsonb */
-DATA(insert OID = 3802 ( jsonb PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3802 ( jsonb PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 jsonb_subscription _null_ _null_ _null_ ));
DESCR("Binary JSON");
#define JSONBOID 3802
-DATA(insert OID = 3807 ( _jsonb PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3807 ( _jsonb PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
-DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("txid snapshot");
-DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* range types */
-DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of integers");
#define INT4RANGEOID 3904
-DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of numerics");
-DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of timestamps without time zone");
-DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of timestamps with time zone");
-DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of dates");
-DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of bigints");
-DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/*
* pseudo-types
@@ -664,41 +670,41 @@ DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 arr
* but there is now support for it in records and arrays. Perhaps we should
* just treat it as a regular base type?
*/
-DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define RECORDOID 2249
-DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
#define RECORDARRAYOID 2287
-DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define CSTRINGOID 2275
-DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYOID 2276
-DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYARRAYOID 2277
-DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out void_recv void_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out void_recv void_send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define VOIDOID 2278
-DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define TRIGGEROID 2279
-DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define EVTTRIGGEROID 3838
-DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
-DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define INTERNALOID 2281
-DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define OPAQUEOID 2282
-DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYELEMENTOID 2283
-DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYNONARRAYOID 2776
-DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYENUMOID 3500
-DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define FDW_HANDLEROID 3115
-DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define INDEX_AM_HANDLEROID 325
-DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define TSM_HANDLEROID 3310
-DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYRANGEOID 3831
diff --git a/src/include/catalog/pg_type_fn.h b/src/include/catalog/pg_type_fn.h
index d7bbfdb..947cad0 100644
--- a/src/include/catalog/pg_type_fn.h
+++ b/src/include/catalog/pg_type_fn.h
@@ -52,7 +52,8 @@ extern ObjectAddress TypeCreate(Oid newTypeOid,
int32 typeMod,
int32 typNDims,
bool typeNotNull,
- Oid typeCollation);
+ Oid typeCollation,
+ Oid subscriptionProcedure);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
@@ -70,6 +71,7 @@ extern void GenerateTypeDependencies(Oid typeNamespace,
bool isImplicitArray,
Oid baseType,
Oid typeCollation,
+ Oid subscriptionProcedure,
Node *defaultExpr,
bool rebuild);
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index e73a824..760d45f 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -183,6 +183,7 @@ extern TupleDesc build_function_result_tupdesc_d(Datum proallargtypes,
Datum proargmodes,
Datum proargnames);
extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
+extern FunctionCallInfoData get_slice_arguments(FunctionCallInfo fcinfo, int begin, int end);
/*----------
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index e7fd7bd..027400d 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -643,25 +643,46 @@ typedef struct WindowFuncExprState
} WindowFuncExprState;
/* ----------------
- * ArrayRefExprState node
+ * SubscriptionRefExprState node
*
* Note: array types can be fixed-length (typlen > 0), but only when the
* element type is itself fixed-length. Otherwise they are varlena structures
* and have typlen = -1. In any case, an array type is never pass-by-value.
* ----------------
*/
-typedef struct ArrayRefExprState
+typedef struct SubscriptionRefExprState
{
ExprState xprstate;
List *refupperindexpr; /* states for child nodes */
List *reflowerindexpr;
ExprState *refexpr;
ExprState *refassgnexpr;
- int16 refattrlength; /* typlen of array type */
- int16 refelemlength; /* typlen of the array element type */
+ int16 refattrlength; /* typlen of container type */
+ int16 refelemlength; /* typlen of the container element type */
bool refelembyval; /* is the element type pass-by-value? */
char refelemalign; /* typalign of the element type */
-} ArrayRefExprState;
+} SubscriptionRefExprState;
+
+/* ---------------------------------
+ * Subscription exec information
+ *
+ * It contains all information which is required to perform type-specific data
+ * extraction or modification. This information will be gathered in
+ * `ExecEvalSubscriptionRef` function and passed to `typsubscription`
+ * procedure.
+ * ---------------------------------
+ */
+typedef struct SubscriptionExecData
+{
+ ExprContext *xprcontext; /* econtext for subscription */
+ bool *isNull;
+ Datum *upper; /* upper boundary for subscription */
+ Datum *lower; /* lower boundary for subscription */
+ bool *upperProvided;
+ bool *lowerProvided;
+ Datum containerSource;
+ int indexprNumber;
+} SubscriptionExecData;
/* ----------------
* FuncExprState node
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 2f7efa8..890b873 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -140,7 +140,7 @@ typedef enum NodeTag
T_Aggref,
T_GroupingFunc,
T_WindowFunc,
- T_ArrayRef,
+ T_SubscriptionRef,
T_FuncExpr,
T_NamedArgExpr,
T_OpExpr,
@@ -194,7 +194,7 @@ typedef enum NodeTag
T_AggrefExprState,
T_GroupingFuncExprState,
T_WindowFuncExprState,
- T_ArrayRefExprState,
+ T_SubscriptionRefExprState,
T_FuncExprState,
T_ScalarArrayOpExprState,
T_BoolExprState,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 65510b0..6b30b90 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -350,18 +350,18 @@ typedef struct WindowFunc
} WindowFunc;
/* ----------------
- * ArrayRef: describes an array subscripting operation
- *
- * An ArrayRef can describe fetching a single element from an array,
- * fetching a subarray (array slice), storing a single element into
- * an array, or storing a slice. The "store" cases work with an
- * initial array value and a source value that is inserted into the
- * appropriate part of the array; the result of the operation is an
- * entire new modified array value.
- *
- * If reflowerindexpr = NIL, then we are fetching or storing a single array
- * element at the subscripts given by refupperindexpr. Otherwise we are
- * fetching or storing an array slice, that is a rectangular subarray
+ * SubscriptionRef: describes a subscripting operation over a container
+ *
+ * An SubscriptionRef can describe fetching a single element from a container,
+ * fetching a part of container (e.g. array slice), storing a single element into
+ * a container, or storing a slice. The "store" cases work with an
+ * initial container value and a source value that is inserted into the
+ * appropriate part of the container; the result of the operation is an
+ * entire new modified container value.
+ *
+ * If reflowerindexpr = NIL, then we are fetching or storing a single container
+ * element at the subscripts given by refupperindexpr. Otherwise we are
+ * fetching or storing a container slice, that is a rectangular subcontainer
* with lower and upper bounds given by the index expressions.
* reflowerindexpr must be the same length as refupperindexpr when it
* is not NIL.
@@ -373,27 +373,27 @@ typedef struct WindowFunc
* element; but it is the array type when doing subarray fetch or either
* type of store.
*
- * Note: for the cases where an array is returned, if refexpr yields a R/W
- * expanded array, then the implementation is allowed to modify that object
+ * Note: for the cases where a container is returned, if refexpr yields a R/W
+ * expanded container, then the implementation is allowed to modify that object
* in-place and return the same object.)
* ----------------
*/
-typedef struct ArrayRef
+typedef struct SubscriptionRef
{
Expr xpr;
- Oid refarraytype; /* type of the array proper */
- Oid refelemtype; /* type of the array elements */
- int32 reftypmod; /* typmod of the array (and elements too) */
- Oid refcollid; /* OID of collation, or InvalidOid if none */
- List *refupperindexpr;/* expressions that evaluate to upper array
- * indexes */
- List *reflowerindexpr;/* expressions that evaluate to lower array
- * indexes, or NIL for single array element */
- Expr *refexpr; /* the expression that evaluates to an array
- * value */
- Expr *refassgnexpr; /* expression for the source value, or NULL if
- * fetch */
-} ArrayRef;
+ Oid refcontainertype; /* type of the container proper */
+ Oid refelemtype; /* type of the container elements */
+ int32 reftypmod; /* typmod of the container (and elements too) */
+ Oid refcollid; /* OID of collation, or InvalidOid if none */
+ List *refupperindexpr; /* expressions that evaluate to upper container
+ * indexes */
+ List *reflowerindexpr; /* expressions that evaluate to lower container
+ * indexes, or NIL for single container element */
+ Expr *refexpr; /* the expression that evaluates to an container
+ * value */
+ Expr *refassgnexpr; /* expression for the source value, or NULL if
+ * fetch */
+} SubscriptionRef;
/*
* CoercionContext - distinguishes the allowed set of type casts
@@ -734,7 +734,7 @@ typedef struct FieldSelect
*
* FieldStore represents the operation of modifying one field in a tuple
* value, yielding a new tuple value (the input is not touched!). Like
- * the assign case of ArrayRef, this is used to implement UPDATE of a
+ * the assign case of SubscriptionRef, this is used to implement UPDATE of a
* portion of a column.
*
* A single FieldStore can actually represent updates of several different
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index e3e359c..ddf9288 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -223,12 +223,17 @@ extern void cancel_parser_errposition_callback(ParseCallbackState *pcbstate);
extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno,
int location);
-extern Oid transformArrayType(Oid *arrayType, int32 *arrayTypmod);
-extern ArrayRef *transformArraySubscripts(ParseState *pstate,
- Node *arrayBase,
- Oid arrayType,
+extern Oid transformArrayType(Oid *containerType, int32 *containerTypmod);
+
+/* Type of a stage in case of type-specific subscription procedure */
+#define SBS_VALIDATION 0x0001
+#define SBS_EXEC 0x0002
+
+extern SubscriptionRef *transformContainerSubscripts(ParseState *pstate,
+ Node *containerBase,
+ Oid containerType,
Oid elementType,
- int32 arrayTypMod,
+ int32 containerTypMod,
List *indirection,
Node *assignFrom);
extern Const *make_const(ParseState *pstate, Value *value, int location);
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index 6164f11..64dd619 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -502,4 +502,9 @@ extern Datum array_positions(PG_FUNCTION_ARGS);
*/
extern Datum array_typanalyze(PG_FUNCTION_ARGS);
+/*
+ * prototypes for functions with array subscription logic
+ */
+extern Datum array_subscription(PG_FUNCTION_ARGS);
+
#endif /* ARRAY_H */
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index fa52afc..5bc3afe 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -352,6 +352,7 @@ extern Datum jsonb_typeof(PG_FUNCTION_ARGS);
/* generator routines */
extern Datum to_jsonb(PG_FUNCTION_ARGS);
+extern JsonbValue *to_jsonb_worker(Datum source, Oid source_type);
extern Datum jsonb_build_object(PG_FUNCTION_ARGS);
extern Datum jsonb_build_object_noargs(PG_FUNCTION_ARGS);
@@ -425,6 +426,7 @@ extern JsonbValue *pushJsonbValue(JsonbParseState **pstate,
extern JsonbIterator *JsonbIteratorInit(JsonbContainer *container);
extern JsonbIteratorToken JsonbIteratorNext(JsonbIterator **it, JsonbValue *val,
bool skipNested);
+extern JsonbValue *JsonbToJsonbValue(Jsonb *jsonb, JsonbValue *val);
extern Jsonb *JsonbValueToJsonb(JsonbValue *val);
extern bool JsonbDeepContains(JsonbIterator **val,
JsonbIterator **mContained);
@@ -437,4 +439,7 @@ extern char *JsonbToCStringIndent(StringInfo out, JsonbContainer *in,
int estimated_len);
+/* Jsonb subscription logic */
+extern Datum jsonb_subscription(PG_FUNCTION_ARGS);
+
#endif /* __JSONB_H__ */
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index dcb8980..474957d 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -159,6 +159,7 @@ extern void free_attstatsslot(Oid atttype,
extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
+extern RegProcedure get_typsubscription(Oid typid);
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
/* type_is_array_domain accepts both plain arrays and domains over arrays */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index f9b3b22..5ef5867 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -4746,7 +4746,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
/*
* Evaluate the subscripts, switch into left-to-right order.
- * Like ExecEvalArrayRef(), complain if any subscript is null.
+ * Like ExecEvalSubscriptionRef(), complain if any subscript is null.
*/
for (i = 0; i < nsubscripts; i++)
{
@@ -4794,7 +4794,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
* fixed-length array types we skip the assignment. We can't
* support assignment of a null entry into a fixed-length
* array, either, so that's a no-op too. This is all ugly but
- * corresponds to the current behavior of ExecEvalArrayRef().
+ * corresponds to the current behavior of ExecEvalSubscriptionRef().
*/
if (arrayelem->arraytyplen > 0 && /* fixed-length array? */
(oldarrayisnull || isNull))
@@ -6502,9 +6502,9 @@ exec_simple_check_node(Node *node)
case T_Param:
return TRUE;
- case T_ArrayRef:
+ case T_SubscriptionRef:
{
- ArrayRef *expr = (ArrayRef *) node;
+ SubscriptionRef *expr = (SubscriptionRef *) node;
if (!exec_simple_check_node((Node *) expr->refupperindexpr))
return FALSE;
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
index a6d25de..0d6baad 100644
--- a/src/test/regress/expected/jsonb.out
+++ b/src/test/regress/expected/jsonb.out
@@ -3441,3 +3441,211 @@ HINT: Try using the function jsonb_set to replace key value.
select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"', true);
ERROR: cannot replace existing key
HINT: Try using the function jsonb_set to replace key value.
+-- jsonb subscript
+select ('123'::jsonb)['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('123'::jsonb)[0];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": 1}'::jsonb)['a'];
+ jsonb
+-------
+ 1
+(1 row)
+
+select ('{"a": 1}'::jsonb)[0];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": 1}'::jsonb)['not_exist'];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[0];
+ jsonb
+-------
+ 1
+(1 row)
+
+select ('[1, "2", null]'::jsonb)['1'];
+ jsonb
+-------
+ "2"
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[1.0];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[2];
+ jsonb
+-------
+ null
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[3];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[-2];
+ jsonb
+-------
+ "2"
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[1]['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[1][0];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['b'];
+ jsonb
+-------
+ "c"
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'];
+ jsonb
+-----------
+ [1, 2, 3]
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'][1];
+ jsonb
+-------
+ 2
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d']['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1'];
+ jsonb
+---------------
+ {"a2": "aaa"}
+(1 row)
+
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2'];
+ jsonb
+-------
+ "aaa"
+(1 row)
+
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']['a3'];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'];
+ jsonb
+-----------------------
+ ["aaa", "bbb", "ccc"]
+(1 row)
+
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'][2];
+ jsonb
+-------
+ "ccc"
+(1 row)
+
+create TEMP TABLE test_jsonb_subscript (
+ id int,
+ test_json jsonb
+);
+insert into test_jsonb_subscript values
+(1, '{}'), -- empty jsonb
+(2, '{"key": "value"}'); -- jsonb with data
+-- update empty jsonb
+update test_jsonb_subscript set test_json['a'] = 1 where id = 1;
+select * from test_jsonb_subscript;
+ id | test_json
+----+------------------
+ 2 | {"key": "value"}
+ 1 | {"a": 1}
+(2 rows)
+
+-- update jsonb with some data
+update test_jsonb_subscript set test_json['a'] = 1 where id = 2;
+select * from test_jsonb_subscript;
+ id | test_json
+----+--------------------------
+ 1 | {"a": 1}
+ 2 | {"a": 1, "key": "value"}
+(2 rows)
+
+-- replace jsonb
+update test_jsonb_subscript set test_json['a'] = 'test';
+select * from test_jsonb_subscript;
+ id | test_json
+----+-------------------------------
+ 1 | {"a": "test"}
+ 2 | {"a": "test", "key": "value"}
+(2 rows)
+
+-- replace by object
+update test_jsonb_subscript set test_json['a'] = '{"b": 1}'::jsonb;
+select * from test_jsonb_subscript;
+ id | test_json
+----+---------------------------------
+ 1 | {"a": {"b": 1}}
+ 2 | {"a": {"b": 1}, "key": "value"}
+(2 rows)
+
+-- replace by array
+update test_jsonb_subscript set test_json['a'] = '[1, 2, 3]'::jsonb;
+select * from test_jsonb_subscript;
+ id | test_json
+----+----------------------------------
+ 1 | {"a": [1, 2, 3]}
+ 2 | {"a": [1, 2, 3], "key": "value"}
+(2 rows)
+
+-- use jsonb subscription in where clause
+select * from test_jsonb_subscript where test_json['key'] = '"value"';
+ id | test_json
+----+----------------------------------
+ 2 | {"a": [1, 2, 3], "key": "value"}
+(1 row)
+
+select * from test_jsonb_subscript where test_json['key_doesnt_exists'] = '"value"';
+ id | test_json
+----+-----------
+(0 rows)
+
+select * from test_jsonb_subscript where test_json['key'] = '"wrong_value"';
+ id | test_json
+----+-----------
+(0 rows)
+
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
index b84bd70..3ec66ed 100644
--- a/src/test/regress/sql/jsonb.sql
+++ b/src/test/regress/sql/jsonb.sql
@@ -867,3 +867,63 @@ select jsonb_insert('{"a": {"b": "value"}}', '{a, c}', '"new_value"', true);
select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"');
select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"', true);
+
+-- jsonb subscript
+select ('123'::jsonb)['a'];
+select ('123'::jsonb)[0];
+select ('{"a": 1}'::jsonb)['a'];
+select ('{"a": 1}'::jsonb)[0];
+select ('{"a": 1}'::jsonb)['not_exist'];
+select ('[1, "2", null]'::jsonb)['a'];
+select ('[1, "2", null]'::jsonb)[0];
+select ('[1, "2", null]'::jsonb)['1'];
+select ('[1, "2", null]'::jsonb)[1.0];
+select ('[1, "2", null]'::jsonb)[2];
+select ('[1, "2", null]'::jsonb)[3];
+select ('[1, "2", null]'::jsonb)[-2];
+select ('[1, "2", null]'::jsonb)[1]['a'];
+select ('[1, "2", null]'::jsonb)[1][0];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['b'];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'][1];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d']['a'];
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1'];
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2'];
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']['a3'];
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'];
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'][2];
+
+create TEMP TABLE test_jsonb_subscript (
+ id int,
+ test_json jsonb
+);
+
+insert into test_jsonb_subscript values
+(1, '{}'), -- empty jsonb
+(2, '{"key": "value"}'); -- jsonb with data
+
+-- update empty jsonb
+update test_jsonb_subscript set test_json['a'] = 1 where id = 1;
+select * from test_jsonb_subscript;
+
+-- update jsonb with some data
+update test_jsonb_subscript set test_json['a'] = 1 where id = 2;
+select * from test_jsonb_subscript;
+
+-- replace jsonb
+update test_jsonb_subscript set test_json['a'] = 'test';
+select * from test_jsonb_subscript;
+
+-- replace by object
+update test_jsonb_subscript set test_json['a'] = '{"b": 1}'::jsonb;
+select * from test_jsonb_subscript;
+
+-- replace by array
+update test_jsonb_subscript set test_json['a'] = '[1, 2, 3]'::jsonb;
+select * from test_jsonb_subscript;
+
+-- use jsonb subscription in where clause
+select * from test_jsonb_subscript where test_json['key'] = '"value"';
+select * from test_jsonb_subscript where test_json['key_doesnt_exists'] = '"value"';
+select * from test_jsonb_subscript where test_json['key'] = '"wrong_value"';
+
diff --git a/src/tutorial/Makefile b/src/tutorial/Makefile
index 16dc390..cf8082a 100644
--- a/src/tutorial/Makefile
+++ b/src/tutorial/Makefile
@@ -13,8 +13,8 @@
#
#-------------------------------------------------------------------------
-MODULES = complex funcs
-DATA_built = advanced.sql basics.sql complex.sql funcs.sql syscat.sql
+MODULES = complex funcs subscription
+DATA_built = advanced.sql basics.sql complex.sql funcs.sql syscat.sql subscription.sql
ifdef NO_PGXS
subdir = src/tutorial
diff --git a/src/tutorial/subscription.c b/src/tutorial/subscription.c
new file mode 100644
index e69de29..4abe3a7 100644
--- /dev/null
+++ b/src/tutorial/subscription.c
@@ -0,0 +1,230 @@
+/*
+ * src/tutorial/subscription.c
+ *
+ ******************************************************************************
+ This file contains routines that can be bound to a Postgres backend and
+ called by the backend in the process of processing queries. The calling
+ format for these routines is dictated by Postgres architecture.
+******************************************************************************/
+
+#include "postgres.h"
+
+#include "catalog/pg_type.h"
+#include "executor/executor.h"
+#include "nodes/execnodes.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_node.h"
+#include "utils/array.h"
+#include "fmgr.h"
+#include "funcapi.h"
+
+PG_MODULE_MAGIC;
+
+typedef struct Custom
+{
+ int first;
+ int second;
+} Custom;
+
+
+/*****************************************************************************
+ * Input/Output functions
+ *****************************************************************************/
+
+PG_FUNCTION_INFO_V1(custom_in);
+
+Datum
+custom_in(PG_FUNCTION_ARGS)
+{
+ char *str = PG_GETARG_CSTRING(0);
+ int firstValue,
+ secondValue;
+ Custom *result;
+
+ if (sscanf(str, " ( %d , %d )", &firstValue, &secondValue) != 2)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid input syntax for complex: \"%s\"",
+ str)));
+
+
+ result = (Custom *) palloc(sizeof(Custom));
+ result->first = firstValue;
+ result->second = secondValue;
+ PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(custom_out);
+
+Datum
+custom_out(PG_FUNCTION_ARGS)
+{
+ Custom *custom = (Custom *) PG_GETARG_POINTER(0);
+ char *result;
+
+ result = psprintf("(%d, %d)", custom->first, custom->second);
+ PG_RETURN_CSTRING(result);
+}
+
+/*****************************************************************************
+ * Custom subscription logic functions
+ *****************************************************************************/
+
+Datum
+custom_subscription_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptionRefExprState *sbstate = (SubscriptionRefExprState *) PG_GETARG_POINTER(0);
+ SubscriptionExecData *sbsdata = (SubscriptionExecData *) PG_GETARG_POINTER(1);
+ SubscriptionRef *custom_ref = (SubscriptionRef *) sbstate->xprstate.expr;
+ Custom *result = (Custom *) sbsdata->containerSource;
+ bool *is_null = sbsdata->isNull;
+ bool is_assignment = (custom_ref->refassgnexpr != NULL);
+
+ int index;
+
+ if (sbsdata->indexprNumber != 1)
+ ereport(ERROR, (errmsg("custom does not support nested subscription")));
+
+ index = DatumGetInt32(sbsdata->upper[0]);
+
+ if (is_assignment)
+ {
+ ExprContext *econtext = sbsdata->xprcontext;
+ Datum sourceData;
+ Datum save_datum;
+ bool save_isNull;
+ bool eisnull;
+
+ /*
+ * We might have a nested-assignment situation, in which the
+ * refassgnexpr is itself a FieldStore or SubscriptionRef that needs to
+ * obtain and modify the previous value of the array element or slice
+ * being replaced. If so, we have to extract that value from the
+ * array and pass it down via the econtext's caseValue. It's safe to
+ * reuse the CASE mechanism because there cannot be a CASE between
+ * here and where the value would be needed, and an array assignment
+ * can't be within a CASE either. (So saving and restoring the
+ * caseValue is just paranoia, but let's do it anyway.)
+ *
+ * Since fetching the old element might be a nontrivial expense, do it
+ * only if the argument appears to actually need it.
+ */
+ save_datum = econtext->caseValue_datum;
+ save_isNull = econtext->caseValue_isNull;
+
+ /*
+ * Evaluate the value to be assigned into the container.
+ */
+ sourceData = ExecEvalExpr(sbstate->refassgnexpr,
+ econtext,
+ &eisnull,
+ NULL);
+
+ econtext->caseValue_datum = save_datum;
+ econtext->caseValue_isNull = save_isNull;
+
+ /*
+ * For an assignment to a fixed-length array type, both the original
+ * array and the value to be assigned into it must be non-NULL, else
+ * we punt and return the original array.
+ */
+ if (sbstate->refattrlength > 0) /* fixed-length container? */
+ if (eisnull || *is_null)
+ return sbsdata->containerSource;
+
+ /*
+ * For assignment to varlena container, we handle a NULL original array
+ * by substituting an empty (zero-dimensional) array; insertion of the
+ * new element will result in a singleton array value. It does not
+ * matter whether the new element is NULL.
+ */
+ if (*is_null)
+ {
+ sbsdata->containerSource =
+ PointerGetDatum(construct_empty_array(custom_ref->refelemtype));
+ *is_null = false;
+ }
+
+ if (index == 1)
+ result->first = DatumGetInt32(sourceData);
+ else
+ result->second = DatumGetInt32(sourceData);
+
+ PG_RETURN_POINTER(result);
+ }
+ else
+ {
+ if (index == 1)
+ PG_RETURN_INT32(result->first);
+ else
+ PG_RETURN_INT32(result->second);
+ }
+}
+
+Datum
+custom_subscription_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptionRef *sbsref = (SubscriptionRef *) PG_GETARG_POINTER(0);
+ ParseState *pstate = (ParseState *) PG_GETARG_POINTER(1);
+ List *upperIndexpr = NIL;
+ ListCell *l;
+
+ if (sbsref->reflowerindexpr != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("custom subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *)lfirst(sbsref->reflowerindexpr->head))))));
+
+ foreach(l, sbsref->refupperindexpr)
+ {
+ Node *subexpr = (Node *) lfirst(l);
+
+ Assert(subexpr != NULL);
+
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("custom subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *) lfirst(sbsref->refupperindexpr->head))))));
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ INT4OID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("custom subscript must have int type"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ }
+
+ sbsref->refupperindexpr = upperIndexpr;
+ sbsref->refelemtype = INT4OID;
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+PG_FUNCTION_INFO_V1(custom_subscription);
+
+Datum
+custom_subscription(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1,
+ fcinfo->nargs);
+
+ if (op_type & SBS_VALIDATION)
+ return custom_subscription_prepare(&target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return custom_subscription_evaluate(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscription function: %d", op_type);
+}
diff --git a/src/tutorial/subscription.source b/src/tutorial/subscription.source
new file mode 100644
index e69de29..3bdf0ea 100644
--- /dev/null
+++ b/src/tutorial/subscription.source
@@ -0,0 +1,71 @@
+---------------------------------------------------------------------------
+--
+-- subscription.sql-
+-- This file shows how to create a new subscription procedure for
+-- user-defined type.
+--
+--
+-- Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+-- Portions Copyright (c) 1994, Regents of the University of California
+--
+-- src/tutorial/subscription.source
+--
+---------------------------------------------------------------------------
+
+-----------------------------
+-- Creating a new type:
+-- We are going to create a new type called 'complex' which represents
+-- complex numbers.
+-- A user-defined type must have an input and an output function, and
+-- optionally can have binary input and output functions. All of these
+-- are usually user-defined C functions.
+-----------------------------
+
+-- Assume the user defined functions are in /home/erthalion/programms/postgresql-master/src/tutorial/complex$DLSUFFIX
+-- (we do not want to assume this is in the dynamic loader search path).
+-- Look at $PWD/complex.c for the source. Note that we declare all of
+-- them as STRICT, so we do not need to cope with NULL inputs in the
+-- C code. We also mark them IMMUTABLE, since they always return the
+-- same outputs given the same inputs.
+
+-- the input function 'complex_in' takes a null-terminated string (the
+-- textual representation of the type) and turns it into the internal
+-- (in memory) representation. You will get a message telling you 'complex'
+-- does not exist yet but that's okay.
+
+CREATE FUNCTION custom_in(cstring)
+ RETURNS custom
+ AS '_OBJWD_/subscription'
+ LANGUAGE C IMMUTABLE STRICT;
+
+-- the output function 'complex_out' takes the internal representation and
+-- converts it into the textual representation.
+
+CREATE FUNCTION custom_out(custom)
+ RETURNS cstring
+ AS '_OBJWD_/subscription'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION custom_subscription(internal)
+ RETURNS internal
+ AS '_OBJWD_/subscription'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE custom (
+ internallength = 8,
+ input = custom_in,
+ output = custom_out,
+ subscription = custom_subscription
+);
+
+-- we can use it in a table
+
+CREATE TABLE test_subscription (
+ data custom,
+);
+
+INSERT INTO test_subscription VALUES ('(1, 2)');
+
+SELECT data[0] from test_subscription;
+
+UPDATE test_subscription SET data[1] = 3;
On 9/9/16 6:29 AM, Dmitry Dolgov wrote:
Regarding to the previous conversations [1], [2], here is a patch (with some
improvements from Nikita Glukhov) for the generic type subscription.
Awesome! Please make sure to add it to the Commit Fest app.
--
Jim Nasby, Data Architect, Blue Treble Consulting, Austin TX
Experts in Analytics, Data Architecture and PostgreSQL
Data in Trouble? Get it in Treble! http://BlueTreble.com
855-TREBLE2 (855-873-2532) mobile: 512-569-9461
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Fri, 9 Sep 2016 18:29:23 +0700
Dmitry Dolgov <9erthalion6@gmail.com> wrote:
Hi,
Regarding to the previous conversations [1], [2], here is a patch
(with some improvements from Nikita Glukhov) for the generic type
subscription. It allows
to define type-specific subscription logic for any data type and has
implementations for the array and jsonb types. There are following
changes in this
patch:
I've tried to compile this patch with current state of master (commit
51c3e9fade76c12) and found out that, when configured with
--enable-cassert, it doesn't pass make check.
LOG: server process (PID 4643) was terminated by signal 6: Aborted
DETAIL: Failed process was running:
update test_jsonb_subscript set test_json['a'] = '{"b":
1}'::jsonb;
--
Victor
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
I've tried to compile this patch with current state of master (commit
51c3e9fade76c12) and found out that, when configured with
--enable-cassert,
it doesn't pass make check.
Thanks for the feedback. Yes, unexpectedly for me, `ExecEvalExpr` can return
expanded `jbvArray` and `jbvObject` instead `jbvBinary` in both cases. It's
interesting, that this doesn't break anything, but obviously violates
the `pushJsonbValueScalar` semantics. I don't think `ExecEvalExpr` should be
changed for jsonb, we can handle this situation in `pushJsonbValue`
instead. I've
attached a new version of patch with this modification.
On 27 September 2016 at 19:08, Victor Wagner <vitus@wagner.pp.ru> wrote:
Show quoted text
On Fri, 9 Sep 2016 18:29:23 +0700
Dmitry Dolgov <9erthalion6@gmail.com> wrote:Hi,
Regarding to the previous conversations [1], [2], here is a patch
(with some improvements from Nikita Glukhov) for the generic type
subscription. It allows
to define type-specific subscription logic for any data type and has
implementations for the array and jsonb types. There are following
changes in this
patch:I've tried to compile this patch with current state of master (commit
51c3e9fade76c12) and found out that, when configured with
--enable-cassert, it doesn't pass make check.LOG: server process (PID 4643) was terminated by signal 6: Aborted
DETAIL: Failed process was running:
update test_jsonb_subscript set test_json['a'] = '{"b":
1}'::jsonb;--
Victor--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Attachments:
generic_type_subscription_v2.patchtext/x-patch; charset=US-ASCII; name=generic_type_subscription_v2.patchDownload
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 8ce24e0..c0fc651 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -2464,14 +2464,14 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
JumbleExpr(jstate, (Node *) expr->aggfilter);
}
break;
- case T_ArrayRef:
+ case T_SubscriptionRef:
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptionRef *sbsref = (SubscriptionRef *) node;
- JumbleExpr(jstate, (Node *) aref->refupperindexpr);
- JumbleExpr(jstate, (Node *) aref->reflowerindexpr);
- JumbleExpr(jstate, (Node *) aref->refexpr);
- JumbleExpr(jstate, (Node *) aref->refassgnexpr);
+ JumbleExpr(jstate, (Node *) sbsref->refupperindexpr);
+ JumbleExpr(jstate, (Node *) sbsref->reflowerindexpr);
+ JumbleExpr(jstate, (Node *) sbsref->refexpr);
+ JumbleExpr(jstate, (Node *) sbsref->refassgnexpr);
}
break;
case T_FuncExpr:
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 691658f..51d654d 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -137,7 +137,7 @@ static void deparseExpr(Expr *expr, deparse_expr_cxt *context);
static void deparseVar(Var *node, deparse_expr_cxt *context);
static void deparseConst(Const *node, deparse_expr_cxt *context);
static void deparseParam(Param *node, deparse_expr_cxt *context);
-static void deparseArrayRef(ArrayRef *node, deparse_expr_cxt *context);
+static void deparseSubscriptionRef(SubscriptionRef *node, deparse_expr_cxt *context);
static void deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context);
static void deparseOpExpr(OpExpr *node, deparse_expr_cxt *context);
static void deparseOperatorName(StringInfo buf, Form_pg_operator opform);
@@ -358,9 +358,9 @@ foreign_expr_walker(Node *node,
state = FDW_COLLATE_UNSAFE;
}
break;
- case T_ArrayRef:
+ case T_SubscriptionRef:
{
- ArrayRef *ar = (ArrayRef *) node;
+ SubscriptionRef *ar = (SubscriptionRef *) node;
/* Assignment should not be in restrictions. */
if (ar->refassgnexpr != NULL)
@@ -1822,8 +1822,8 @@ deparseExpr(Expr *node, deparse_expr_cxt *context)
case T_Param:
deparseParam((Param *) node, context);
break;
- case T_ArrayRef:
- deparseArrayRef((ArrayRef *) node, context);
+ case T_SubscriptionRef:
+ deparseSubscriptionRef((SubscriptionRef *) node, context);
break;
case T_FuncExpr:
deparseFuncExpr((FuncExpr *) node, context);
@@ -2043,7 +2043,7 @@ deparseParam(Param *node, deparse_expr_cxt *context)
* Deparse an array subscript expression.
*/
static void
-deparseArrayRef(ArrayRef *node, deparse_expr_cxt *context)
+deparseSubscriptionRef(SubscriptionRef *node, deparse_expr_cxt *context)
{
StringInfo buf = context->buf;
ListCell *lowlist_item;
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 29738b0..c6fe247 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -7056,6 +7056,13 @@
</row>
<row>
+ <entry><structfield>typsubscription</structfield></entry>
+ <entry><type>regproc</type></entry>
+ <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+ <entry>Custom subscription function with type-specific logic, or 0 if this type doesn't support subscription.</entry>
+ </row>
+
+ <row>
<entry><structfield>typdefaultbin</structfield></entry>
<entry><type>pg_node_tree</type></entry>
<entry></entry>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 69649a7..ff6a2c2 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -71,6 +71,7 @@
<!ENTITY xplang SYSTEM "xplang.sgml">
<!ENTITY xoper SYSTEM "xoper.sgml">
<!ENTITY xtypes SYSTEM "xtypes.sgml">
+<!ENTITY xsubscription SYSTEM "xsubscription.sgml">
<!ENTITY plperl SYSTEM "plperl.sgml">
<!ENTITY plpython SYSTEM "plpython.sgml">
<!ENTITY plsql SYSTEM "plpgsql.sgml">
diff --git a/doc/src/sgml/json.sgml b/doc/src/sgml/json.sgml
index 3cf78d6..399e4f3 100644
--- a/doc/src/sgml/json.sgml
+++ b/doc/src/sgml/json.sgml
@@ -569,4 +569,29 @@ SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"tags": ["qu
compared using the default database collation.
</para>
</sect2>
+
+ <sect2 id="json-subscription">
+ <title>JSON subscription</title>
+ <para>
+ JSONB data type support array-style subscription expressions to extract or update particular element. An example of subscription syntax:
+<programlisting>
+-- Extract value by key
+SELECT ('{"a": 1}'::jsonb)['a'];
+
+-- Extract nested value by key path
+SELECT ('{"a": {"b": {"c": 1}}}'::jsonb)['a']['b']['c'];
+
+-- Extract element by index
+SELECT ('[1, "2", null]'::jsonb)['1'];
+
+-- Update value by key
+UPDATE table_name set jsonb_field['key'] = 1;
+
+-- Select records using where clause with subscription
+SELECT * from table_name where jsonb_field['key'] = '"value"';
+</programlisting>
+ </para>
+ </sect2>
+
+
</sect1>
diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index 5a09f19..8588065 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -54,6 +54,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
[ , ELEMENT = <replaceable class="parameter">element</replaceable> ]
[ , DELIMITER = <replaceable class="parameter">delimiter</replaceable> ]
[ , COLLATABLE = <replaceable class="parameter">collatable</replaceable> ]
+ [ , SUBSCRIPTION = <replaceable class="parameter">subscription_function</replaceable> ]
)
CREATE TYPE <replaceable class="parameter">name</replaceable>
@@ -194,7 +195,8 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
<replaceable class="parameter">send_function</replaceable>,
<replaceable class="parameter">type_modifier_input_function</replaceable>,
<replaceable class="parameter">type_modifier_output_function</replaceable> and
- <replaceable class="parameter">analyze_function</replaceable>
+ <replaceable class="parameter">analyze_function</replaceable>,
+ <replaceable class="parameter">subscription_function</replaceable>
are optional. Generally these functions have to be coded in C
or another low-level language.
</para>
@@ -451,6 +453,22 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
make use of the collation information; this does not happen
automatically merely by marking the type collatable.
</para>
+
+ <para>
+ The optional
+ <replaceable class="parameter">subscription_function</replaceable>
+ contains type-specific logic for subscription of the data type.
+ By default, there is no such function, which means that the data
+ type doesn't support subscription. The subscription function must be
+ declared to take a single argument of type <type>internal</>, and return
+ a <type>internal</> result. There are two examples of implementation for
+ subscription function in case of array
+ (<replaceable class="parameter">array_subscription</replaceable>)
+ and jsonb
+ (<replaceable class="parameter">jsonb_subscription</replaceable>)
+ types in <filename>src/backend/utils/adt/arrayfuncs.c</> and
+ <filename>src/backend/utils/adt/jsonfuncs.c</> corresponding.
+ </para>
</refsect2>
<refsect2>
@@ -766,6 +784,16 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">subscription_function</replaceable></term>
+ <listitem>
+ <para>
+ The name of a function that contains type-specific subscription logic for
+ the data type.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/doc/src/sgml/xsubscription.sgml b/doc/src/sgml/xsubscription.sgml
new file mode 100644
index 0000000..255ef66
--- /dev/null
+++ b/doc/src/sgml/xsubscription.sgml
@@ -0,0 +1,98 @@
+<!-- doc/src/sgml/xsubscription.sgml -->
+
+ <sect1 id="xsubscription">
+ <title>User-defined subscription procedure</title>
+
+ <indexterm zone="xsubscription">
+ <primary>custom subscription</primary>
+ </indexterm>
+ When you define a new base type, you can also specify a custom procedure
+ to handle subscription expressions. It should contains logic for verification
+ and for extraction or update your data. For instance:
+
+<programlisting><![CDATA[
+typedef struct Custom
+{
+ int first;
+ int second;
+} Custom;
+
+Datum
+custom_subscription_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptionExecData *sbsdata = (SubscriptionExecData *) PG_GETARG_POINTER(1);
+ Custom *result = (Custom *) sbsdata->containerSource;
+
+ // Some extraction or update logic based on sbsdata
+}
+
+Datum
+custom_subscription_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptionRef *sbsref = (SubscriptionRef *) PG_GETARG_POINTER(0);
+
+ // Some verifications or type coersion
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+PG_FUNCTION_INFO_V1(custom_subscription);
+
+Datum
+custom_subscription(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1,
+ fcinfo->nargs);
+
+ if (op_type & SBS_VALIDATION)
+ return custom_subscription_prepare(&target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return custom_subscription_evaluate(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscription function: %d", op_type);
+}]]>
+</programlisting>
+
+ Then you can define a subscription procedure and a custom data type:
+
+<programlisting>
+CREATE FUNCTION custom_subscription(internal)
+ RETURNS internal
+ AS '<replaceable>filename</replaceable>'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE custom (
+ internallength = 4,
+ input = custom_in,
+ output = custom_out,
+ subscription = custom_subscription
+);
+</programlisting>
+
+ and use it as usual:
+
+<programlisting>
+CREATE TABLE test_subscription (
+ data custom,
+);
+
+INSERT INTO test_subscription VALUES ('(1, 2)');
+
+SELECT data[0] from test_subscription;
+
+UPDATE test_subscription SET data[1] = 3;
+</programlisting>
+
+ </para>
+
+ <para>
+ The examples of custom subscription implementation can be found in
+ <filename>subscription.sql</filename> and <filename>subscription.c</filename>
+ in the <filename>src/tutorial</> directory of the source distribution.
+ See the <filename>README</> file in that directory for instructions
+ about running the examples.
+ </para>
+
+</sect1>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index dbd6094..73f89d3 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -977,7 +977,8 @@ AddNewRelationType(const char *typeName,
-1, /* typmod */
0, /* array dimensions for typBaseType */
false, /* Type NOT NULL */
- InvalidOid); /* rowtypes never have a collation */
+ InvalidOid, /* rowtypes never have a collation */
+ InvalidOid); /* typsubscription - none */
}
/* --------------------------------
@@ -1244,7 +1245,8 @@ heap_create_with_catalog(const char *relname,
-1, /* typmod */
0, /* array dimensions for typBaseType */
false, /* Type NOT NULL */
- InvalidOid); /* rowtypes never have a collation */
+ InvalidOid, /* rowtypes never have a collation */
+ F_ARRAY_SUBSCRIPTION); /* array implementation */
pfree(relarrayname);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 4b2d281..dabcb64 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -118,6 +118,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(-1);
values[Anum_pg_type_typndims - 1] = Int32GetDatum(0);
values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(InvalidOid);
+ values[Anum_pg_type_typsubscription - 1] = ObjectIdGetDatum(InvalidOid);
nulls[Anum_pg_type_typdefaultbin - 1] = true;
nulls[Anum_pg_type_typdefault - 1] = true;
nulls[Anum_pg_type_typacl - 1] = true;
@@ -166,6 +167,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
false,
InvalidOid,
InvalidOid,
+ InvalidOid,
NULL,
false);
@@ -224,7 +226,8 @@ TypeCreate(Oid newTypeOid,
int32 typeMod,
int32 typNDims, /* Array dimensions for baseType */
bool typeNotNull,
- Oid typeCollation)
+ Oid typeCollation,
+ Oid subscriptionProcedure)
{
Relation pg_type_desc;
Oid typeObjectId;
@@ -364,6 +367,7 @@ TypeCreate(Oid newTypeOid,
values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(typeMod);
values[Anum_pg_type_typndims - 1] = Int32GetDatum(typNDims);
values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(typeCollation);
+ values[Anum_pg_type_typsubscription - 1] = ObjectIdGetDatum(subscriptionProcedure);
/*
* initialize the default binary value for this type. Check for nulls of
@@ -484,6 +488,7 @@ TypeCreate(Oid newTypeOid,
isImplicitArray,
baseType,
typeCollation,
+ subscriptionProcedure,
(defaultTypeBin ?
stringToNode(defaultTypeBin) :
NULL),
@@ -530,6 +535,7 @@ GenerateTypeDependencies(Oid typeNamespace,
bool isImplicitArray,
Oid baseType,
Oid typeCollation,
+ Oid subscriptionProcedure,
Node *defaultExpr,
bool rebuild)
{
@@ -682,6 +688,14 @@ GenerateTypeDependencies(Oid typeNamespace,
/* Normal dependency on the default expression. */
if (defaultExpr)
recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL);
+
+ if (OidIsValid(subscriptionProcedure))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = subscriptionProcedure;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
}
/*
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 056933a..c3497e0 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -94,6 +94,7 @@ static Oid findTypeSendFunction(List *procname, Oid typeOid);
static Oid findTypeTypmodinFunction(List *procname);
static Oid findTypeTypmodoutFunction(List *procname);
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
+static Oid findTypeSubscriptionFunction(List *procname, Oid typeOid);
static Oid findRangeSubOpclass(List *opcname, Oid subtype);
static Oid findRangeCanonicalFunction(List *procname, Oid typeOid);
static Oid findRangeSubtypeDiffFunction(List *procname, Oid subtype);
@@ -123,6 +124,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
List *typmodinName = NIL;
List *typmodoutName = NIL;
List *analyzeName = NIL;
+ List *subscriptionName = NIL;
char category = TYPCATEGORY_USER;
bool preferred = false;
char delimiter = DEFAULT_TYPDELIM;
@@ -141,6 +143,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
DefElem *typmodinNameEl = NULL;
DefElem *typmodoutNameEl = NULL;
DefElem *analyzeNameEl = NULL;
+ DefElem *subscriptionNameEl = NULL;
DefElem *categoryEl = NULL;
DefElem *preferredEl = NULL;
DefElem *delimiterEl = NULL;
@@ -163,6 +166,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
Oid resulttype;
ListCell *pl;
ObjectAddress address;
+ Oid subscriptionOid = InvalidOid;
/*
* As of Postgres 8.4, we require superuser privilege to create a base
@@ -262,6 +266,9 @@ DefineType(ParseState *pstate, List *names, List *parameters)
else if (pg_strcasecmp(defel->defname, "analyze") == 0 ||
pg_strcasecmp(defel->defname, "analyse") == 0)
defelp = &analyzeNameEl;
+ else if (pg_strcasecmp(defel->defname, "subscription") == 0 ||
+ pg_strcasecmp(defel->defname, "subscription") == 0)
+ defelp = &subscriptionNameEl;
else if (pg_strcasecmp(defel->defname, "category") == 0)
defelp = &categoryEl;
else if (pg_strcasecmp(defel->defname, "preferred") == 0)
@@ -332,6 +339,8 @@ DefineType(ParseState *pstate, List *names, List *parameters)
typmodoutName = defGetQualifiedName(typmodoutNameEl);
if (analyzeNameEl)
analyzeName = defGetQualifiedName(analyzeNameEl);
+ if (subscriptionNameEl)
+ subscriptionName = defGetQualifiedName(subscriptionNameEl);
if (categoryEl)
{
char *p = defGetString(categoryEl);
@@ -513,6 +522,9 @@ DefineType(ParseState *pstate, List *names, List *parameters)
if (analyzeName)
analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid);
+ if (subscriptionName)
+ subscriptionOid = findTypeSubscriptionFunction(subscriptionName, typoid);
+
/*
* Check permissions on functions. We choose to require the creator/owner
* of a type to also own the underlying functions. Since creating a type
@@ -632,7 +644,8 @@ DefineType(ParseState *pstate, List *names, List *parameters)
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
false, /* Type NOT NULL */
- collation); /* type's collation */
+ collation, /* type's collation */
+ subscriptionOid); /* subscription procedure */
Assert(typoid == address.objectId);
/*
@@ -673,7 +686,8 @@ DefineType(ParseState *pstate, List *names, List *parameters)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- collation); /* type's collation */
+ collation, /* type's collation */
+ F_ARRAY_SUBSCRIPTION);
pfree(array_type);
@@ -735,6 +749,7 @@ DefineDomain(CreateDomainStmt *stmt)
Oid receiveProcedure;
Oid sendProcedure;
Oid analyzeProcedure;
+ Oid subscriptionProcedure;
bool byValue;
char category;
char delimiter;
@@ -858,6 +873,9 @@ DefineDomain(CreateDomainStmt *stmt)
/* Analysis function */
analyzeProcedure = baseType->typanalyze;
+ /* Subscription function */
+ subscriptionProcedure = baseType->typsubscription;
+
/* Inherited default value */
datum = SysCacheGetAttr(TYPEOID, typeTup,
Anum_pg_type_typdefault, &isnull);
@@ -1059,7 +1077,8 @@ DefineDomain(CreateDomainStmt *stmt)
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
typNotNull, /* Type NOT NULL */
- domaincoll); /* type's collation */
+ domaincoll, /* type's collation */
+ subscriptionProcedure); /* subscription procedure */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
@@ -1171,7 +1190,8 @@ DefineEnum(CreateEnumStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* type's collation */
+ InvalidOid, /* type's collation */
+ InvalidOid); /* typsubscription - none */
/* Enter the enum's values into pg_enum */
EnumValuesCreate(enumTypeAddr.objectId, stmt->vals);
@@ -1211,7 +1231,8 @@ DefineEnum(CreateEnumStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* type's collation */
+ InvalidOid, /* type's collation */
+ F_ARRAY_SUBSCRIPTION); /* array subscription implementation */
pfree(enumArrayName);
@@ -1499,7 +1520,8 @@ DefineRange(CreateRangeStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* type's collation (ranges never have one) */
+ InvalidOid, /* type's collation (ranges never have one) */
+ InvalidOid); /* typsubscription - none */
Assert(typoid == address.objectId);
/* Create the entry in pg_range */
@@ -1541,7 +1563,8 @@ DefineRange(CreateRangeStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* typcollation */
+ InvalidOid, /* typcollation */
+ F_ARRAY_SUBSCRIPTION); /* array subscription implementation */
pfree(rangeArrayName);
@@ -1885,6 +1908,33 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
return procOid;
}
+static Oid
+findTypeSubscriptionFunction(List *procname, Oid typeOid)
+{
+ Oid argList[1];
+ Oid procOid;
+
+ /*
+ * Analyze functions always take one INTERNAL argument and return INTERNAL.
+ */
+ argList[0] = INTERNALOID;
+
+ procOid = LookupFuncName(procname, 1, argList, true);
+ if (!OidIsValid(procOid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("function %s does not exist",
+ func_signature_string(procname, 1, NIL, argList))));
+
+ if (get_func_rettype(procOid) != INTERNALOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("type subscription function %s must return type %s",
+ NameListToString(procname), "internal")));
+
+ return procOid;
+}
+
/*
* Find suitable support functions and opclasses for a range type.
*/
@@ -2239,6 +2289,7 @@ AlterDomainDefault(List *names, Node *defaultRaw)
false, /* a domain isn't an implicit array */
typTup->typbasetype,
typTup->typcollation,
+ typTup->typsubscription,
defaultExpr,
true); /* Rebuild is true */
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 743e7d6..da70901 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -55,6 +55,7 @@
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/lsyscache.h"
+#include "utils/syscache.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
#include "utils/typcache.h"
@@ -62,10 +63,9 @@
/* static function decls */
-static Datum ExecEvalArrayRef(ArrayRefExprState *astate,
+static Datum ExecEvalSubscriptionRef(SubscriptionRefExprState *astate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
-static bool isAssignmentIndirectionExpr(ExprState *exprstate);
static Datum ExecEvalAggref(AggrefExprState *aggref,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
@@ -251,39 +251,50 @@ static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
/*----------
- * ExecEvalArrayRef
+ * ExecEvalSubscriptionRef
*
- * This function takes an ArrayRef and returns the extracted Datum
- * if it's a simple reference, or the modified array value if it's
- * an array assignment (i.e., array element or slice insertion).
+ * This function takes a SubscriptionRef, extracts all information required
+ * for subscription and pass it to a particular subscription procedure,
+ * specified for this data type. As a result the extracted Datum will be
+ * returned if it's a simple reference, or the modified containers value if
+ * it's an containers assignment (i.e., containers element or slice
+ * insertion).
*
* NOTE: if we get a NULL result from a subscript expression, we return NULL
- * when it's an array reference, or raise an error when it's an assignment.
+ * when it's an containers reference, or raise an error when it's an assignment.
*----------
*/
+
static Datum
-ExecEvalArrayRef(ArrayRefExprState *astate,
- ExprContext *econtext,
- bool *isNull,
- ExprDoneCond *isDone)
+ExecEvalSubscriptionRef(SubscriptionRefExprState *sbstate,
+ ExprContext *econtext,
+ bool *isNull,
+ ExprDoneCond *isDone)
{
- ArrayRef *arrayRef = (ArrayRef *) astate->xprstate.expr;
- Datum array_source;
- bool isAssignment = (arrayRef->refassgnexpr != NULL);
- bool eisnull;
- ListCell *l;
- int i = 0,
- j = 0;
- IntArray upper,
- lower;
- bool upperProvided[MAXDIM],
- lowerProvided[MAXDIM];
- int *lIndex;
-
- array_source = ExecEvalExpr(astate->refexpr,
- econtext,
- isNull,
- isDone);
+ SubscriptionRef *sbsRef = (SubscriptionRef *) sbstate->xprstate.expr;
+ Oid containerType;
+ RegProcedure typsubscription;
+ bool isAssignment = (sbsRef->refassgnexpr != NULL);
+ bool eisnull;
+ Datum *upper = NULL,
+ *lower = NULL;
+ ListCell *l;
+ int i = 0,
+ j = 0;
+ SubscriptionExecData sbsdata;
+ bool upperProvided[MAXDIM],
+ lowerProvided[MAXDIM];
+
+ if (sbstate->refupperindexpr != NULL)
+ upper = (Datum *) palloc(sbstate->refupperindexpr->length * sizeof(Datum *));
+
+ if (sbstate->reflowerindexpr != NULL)
+ lower = (Datum *) palloc(sbstate->reflowerindexpr->length * sizeof(Datum *));
+
+ sbsdata.xprcontext = econtext;
+ sbsdata.isNull = isNull;
+ sbsdata.containerSource = ExecEvalExpr(sbstate->refexpr, econtext,
+ isNull, isDone);
/*
* If refexpr yields NULL, and it's a fetch, then result is NULL. In the
@@ -297,51 +308,49 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
return (Datum) NULL;
}
- foreach(l, astate->refupperindexpr)
+ foreach(l, sbstate->refupperindexpr)
{
ExprState *eltstate = (ExprState *) lfirst(l);
if (i >= MAXDIM)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ errmsg("number of container dimensions (%d) exceeds the maximum allowed (%d)",
i + 1, MAXDIM)));
if (eltstate == NULL)
{
- /* Slice bound is omitted, so use array's upper bound */
- Assert(astate->reflowerindexpr != NIL);
+ /* Slice bound is omitted, so use containers's upper bound */
+ Assert(sbstate->reflowerindexpr != NIL);
upperProvided[i++] = false;
continue;
}
+
upperProvided[i] = true;
+ upper[i++] = ExecEvalExpr(eltstate, econtext, &eisnull, NULL);
- upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate,
- econtext,
- &eisnull,
- NULL));
/* If any index expr yields NULL, result is NULL or error */
if (eisnull)
{
if (isAssignment)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
- errmsg("array subscript in assignment must not be null")));
+ errmsg("container subscript in assignment must not be null")));
*isNull = true;
return (Datum) NULL;
}
}
- if (astate->reflowerindexpr != NIL)
+ if (sbstate->reflowerindexpr != NIL)
{
- foreach(l, astate->reflowerindexpr)
+ foreach(l, sbstate->reflowerindexpr)
{
ExprState *eltstate = (ExprState *) lfirst(l);
if (j >= MAXDIM)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ errmsg("number of container dimensions (%d) exceeds the maximum allowed (%d)",
j + 1, MAXDIM)));
if (eltstate == NULL)
@@ -350,191 +359,45 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
lowerProvided[j++] = false;
continue;
}
+
lowerProvided[j] = true;
+ lower[j++] = ExecEvalExpr(eltstate, econtext, &eisnull, NULL);
- lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate,
- econtext,
- &eisnull,
- NULL));
/* If any index expr yields NULL, result is NULL or error */
if (eisnull)
{
if (isAssignment)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
- errmsg("array subscript in assignment must not be null")));
+ errmsg("container subscript in assignment must not be null")));
*isNull = true;
return (Datum) NULL;
}
}
+
/* this can't happen unless parser messed up */
if (i != j)
elog(ERROR, "upper and lower index lists are not same length");
- lIndex = lower.indx;
- }
- else
- lIndex = NULL;
-
- if (isAssignment)
- {
- Datum sourceData;
- Datum save_datum;
- bool save_isNull;
-
- /*
- * We might have a nested-assignment situation, in which the
- * refassgnexpr is itself a FieldStore or ArrayRef that needs to
- * obtain and modify the previous value of the array element or slice
- * being replaced. If so, we have to extract that value from the
- * array and pass it down via the econtext's caseValue. It's safe to
- * reuse the CASE mechanism because there cannot be a CASE between
- * here and where the value would be needed, and an array assignment
- * can't be within a CASE either. (So saving and restoring the
- * caseValue is just paranoia, but let's do it anyway.)
- *
- * Since fetching the old element might be a nontrivial expense, do it
- * only if the argument appears to actually need it.
- */
- save_datum = econtext->caseValue_datum;
- save_isNull = econtext->caseValue_isNull;
-
- if (isAssignmentIndirectionExpr(astate->refassgnexpr))
- {
- if (*isNull)
- {
- /* whole array is null, so any element or slice is too */
- econtext->caseValue_datum = (Datum) 0;
- econtext->caseValue_isNull = true;
- }
- else if (lIndex == NULL)
- {
- econtext->caseValue_datum =
- array_get_element(array_source, i,
- upper.indx,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign,
- &econtext->caseValue_isNull);
- }
- else
- {
- econtext->caseValue_datum =
- array_get_slice(array_source, i,
- upper.indx, lower.indx,
- upperProvided, lowerProvided,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
- econtext->caseValue_isNull = false;
- }
- }
- else
- {
- /* argument shouldn't need caseValue, but for safety set it null */
- econtext->caseValue_datum = (Datum) 0;
- econtext->caseValue_isNull = true;
- }
-
- /*
- * Evaluate the value to be assigned into the array.
- */
- sourceData = ExecEvalExpr(astate->refassgnexpr,
- econtext,
- &eisnull,
- NULL);
-
- econtext->caseValue_datum = save_datum;
- econtext->caseValue_isNull = save_isNull;
-
- /*
- * For an assignment to a fixed-length array type, both the original
- * array and the value to be assigned into it must be non-NULL, else
- * we punt and return the original array.
- */
- if (astate->refattrlength > 0) /* fixed-length array? */
- if (eisnull || *isNull)
- return array_source;
-
- /*
- * For assignment to varlena arrays, we handle a NULL original array
- * by substituting an empty (zero-dimensional) array; insertion of the
- * new element will result in a singleton array value. It does not
- * matter whether the new element is NULL.
- */
- if (*isNull)
- {
- array_source = PointerGetDatum(construct_empty_array(arrayRef->refelemtype));
- *isNull = false;
- }
-
- if (lIndex == NULL)
- return array_set_element(array_source, i,
- upper.indx,
- sourceData,
- eisnull,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
- else
- return array_set_slice(array_source, i,
- upper.indx, lower.indx,
- upperProvided, lowerProvided,
- sourceData,
- eisnull,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
}
- if (lIndex == NULL)
- return array_get_element(array_source, i,
- upper.indx,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign,
- isNull);
- else
- return array_get_slice(array_source, i,
- upper.indx, lower.indx,
- upperProvided, lowerProvided,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
-}
+ sbsdata.upper = upper;
+ sbsdata.upperProvided = upperProvided;
+ sbsdata.lower = lower;
+ sbsdata.lowerProvided = lowerProvided;
+ sbsdata.indexprNumber = i;
-/*
- * Helper for ExecEvalArrayRef: is expr a nested FieldStore or ArrayRef
- * that might need the old element value passed down?
- *
- * (We could use this in ExecEvalFieldStore too, but in that case passing
- * the old value is so cheap there's no need.)
- */
-static bool
-isAssignmentIndirectionExpr(ExprState *exprstate)
-{
- if (exprstate == NULL)
- return false; /* just paranoia */
- if (IsA(exprstate, FieldStoreState))
- {
- FieldStore *fstore = (FieldStore *) exprstate->expr;
+ containerType = getBaseTypeAndTypmod(sbsRef->refcontainertype, &sbsRef->reftypmod);
+ typsubscription = get_typsubscription(containerType);
- if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
- return true;
- }
- else if (IsA(exprstate, ArrayRefExprState))
- {
- ArrayRef *arrayRef = (ArrayRef *) exprstate->expr;
+ if (!OidIsValid(typsubscription))
+ /* this can't happen */
+ elog(ERROR, "can not find subscription procedure for type %s",
+ format_type_be(containerType));
- if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr))
- return true;
- }
- return false;
+ return OidFunctionCall3(typsubscription,
+ Int32GetDatum(SBS_EXEC),
+ PointerGetDatum(sbstate),
+ PointerGetDatum(&sbsdata));
}
/* ----------------------------------------------------------------
@@ -4332,7 +4195,7 @@ ExecEvalFieldStore(FieldStoreState *fstate,
/*
* Use the CaseTestExpr mechanism to pass down the old value of the
* field being replaced; this is needed in case the newval is itself a
- * FieldStore or ArrayRef that has to obtain and modify the old value.
+ * FieldStore or SubscriptionRef that has to obtain and modify the old value.
* It's safe to reuse the CASE mechanism because there cannot be a
* CASE between here and where the value would be needed, and a field
* assignment can't be within a CASE either. (So saving and restoring
@@ -4695,25 +4558,21 @@ ExecInitExpr(Expr *node, PlanState *parent)
state = (ExprState *) wfstate;
}
break;
- case T_ArrayRef:
+ case T_SubscriptionRef:
{
- ArrayRef *aref = (ArrayRef *) node;
- ArrayRefExprState *astate = makeNode(ArrayRefExprState);
+ SubscriptionRef *sbsref = (SubscriptionRef *) node;
+ SubscriptionRefExprState *astate = makeNode(SubscriptionRefExprState);
- astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayRef;
+ astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalSubscriptionRef;
astate->refupperindexpr = (List *)
- ExecInitExpr((Expr *) aref->refupperindexpr, parent);
+ ExecInitExpr((Expr *) sbsref->refupperindexpr, parent);
astate->reflowerindexpr = (List *)
- ExecInitExpr((Expr *) aref->reflowerindexpr, parent);
- astate->refexpr = ExecInitExpr(aref->refexpr, parent);
- astate->refassgnexpr = ExecInitExpr(aref->refassgnexpr,
+ ExecInitExpr((Expr *) sbsref->reflowerindexpr, parent);
+ astate->refexpr = ExecInitExpr(sbsref->refexpr, parent);
+ astate->refassgnexpr = ExecInitExpr(sbsref->refassgnexpr,
parent);
/* do one-time catalog lookups for type info */
- astate->refattrlength = get_typlen(aref->refarraytype);
- get_typlenbyvalalign(aref->refelemtype,
- &astate->refelemlength,
- &astate->refelembyval,
- &astate->refelemalign);
+ astate->refattrlength = get_typlen(sbsref->refcontainertype);
state = (ExprState *) astate;
}
break;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 71714bc..7e0f48b 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1292,14 +1292,14 @@ _copyWindowFunc(const WindowFunc *from)
}
/*
- * _copyArrayRef
+ * _copySubscriptionRef
*/
-static ArrayRef *
-_copyArrayRef(const ArrayRef *from)
+static SubscriptionRef *
+_copySubscriptionRef(const SubscriptionRef *from)
{
- ArrayRef *newnode = makeNode(ArrayRef);
+ SubscriptionRef *newnode = makeNode(SubscriptionRef);
- COPY_SCALAR_FIELD(refarraytype);
+ COPY_SCALAR_FIELD(refcontainertype);
COPY_SCALAR_FIELD(refelemtype);
COPY_SCALAR_FIELD(reftypmod);
COPY_SCALAR_FIELD(refcollid);
@@ -4467,8 +4467,8 @@ copyObject(const void *from)
case T_WindowFunc:
retval = _copyWindowFunc(from);
break;
- case T_ArrayRef:
- retval = _copyArrayRef(from);
+ case T_SubscriptionRef:
+ retval = _copySubscriptionRef(from);
break;
case T_FuncExpr:
retval = _copyFuncExpr(from);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 29a090f..c680122 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -244,9 +244,9 @@ _equalWindowFunc(const WindowFunc *a, const WindowFunc *b)
}
static bool
-_equalArrayRef(const ArrayRef *a, const ArrayRef *b)
+_equalSubscriptionRef(const SubscriptionRef *a, const SubscriptionRef *b)
{
- COMPARE_SCALAR_FIELD(refarraytype);
+ COMPARE_SCALAR_FIELD(refcontainertype);
COMPARE_SCALAR_FIELD(refelemtype);
COMPARE_SCALAR_FIELD(reftypmod);
COMPARE_SCALAR_FIELD(refcollid);
@@ -2779,8 +2779,8 @@ equal(const void *a, const void *b)
case T_WindowFunc:
retval = _equalWindowFunc(a, b);
break;
- case T_ArrayRef:
- retval = _equalArrayRef(a, b);
+ case T_SubscriptionRef:
+ retval = _equalSubscriptionRef(a, b);
break;
case T_FuncExpr:
retval = _equalFuncExpr(a, b);
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 3997441..a02719f 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -66,13 +66,13 @@ exprType(const Node *expr)
case T_WindowFunc:
type = ((const WindowFunc *) expr)->wintype;
break;
- case T_ArrayRef:
+ case T_SubscriptionRef:
{
- const ArrayRef *arrayref = (const ArrayRef *) expr;
+ const SubscriptionRef *arrayref = (const SubscriptionRef *) expr;
/* slice and/or store operations yield the array type */
if (arrayref->reflowerindexpr || arrayref->refassgnexpr)
- type = arrayref->refarraytype;
+ type = arrayref->refcontainertype;
else
type = arrayref->refelemtype;
}
@@ -284,9 +284,9 @@ exprTypmod(const Node *expr)
return ((const Const *) expr)->consttypmod;
case T_Param:
return ((const Param *) expr)->paramtypmod;
- case T_ArrayRef:
+ case T_SubscriptionRef:
/* typmod is the same for array or element */
- return ((const ArrayRef *) expr)->reftypmod;
+ return ((const SubscriptionRef *) expr)->reftypmod;
case T_FuncExpr:
{
int32 coercedTypmod;
@@ -772,8 +772,8 @@ exprCollation(const Node *expr)
case T_WindowFunc:
coll = ((const WindowFunc *) expr)->wincollid;
break;
- case T_ArrayRef:
- coll = ((const ArrayRef *) expr)->refcollid;
+ case T_SubscriptionRef:
+ coll = ((const SubscriptionRef *) expr)->refcollid;
break;
case T_FuncExpr:
coll = ((const FuncExpr *) expr)->funccollid;
@@ -1014,8 +1014,8 @@ exprSetCollation(Node *expr, Oid collation)
case T_WindowFunc:
((WindowFunc *) expr)->wincollid = collation;
break;
- case T_ArrayRef:
- ((ArrayRef *) expr)->refcollid = collation;
+ case T_SubscriptionRef:
+ ((SubscriptionRef *) expr)->refcollid = collation;
break;
case T_FuncExpr:
((FuncExpr *) expr)->funccollid = collation;
@@ -1237,9 +1237,9 @@ exprLocation(const Node *expr)
/* function name should always be the first thing */
loc = ((const WindowFunc *) expr)->location;
break;
- case T_ArrayRef:
+ case T_SubscriptionRef:
/* just use array argument's location */
- loc = exprLocation((Node *) ((const ArrayRef *) expr)->refexpr);
+ loc = exprLocation((Node *) ((const SubscriptionRef *) expr)->refexpr);
break;
case T_FuncExpr:
{
@@ -1926,21 +1926,21 @@ expression_tree_walker(Node *node,
return true;
}
break;
- case T_ArrayRef:
+ case T_SubscriptionRef:
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptionRef *sbsref = (SubscriptionRef *) node;
/* recurse directly for upper/lower array index lists */
- if (expression_tree_walker((Node *) aref->refupperindexpr,
+ if (expression_tree_walker((Node *) sbsref->refupperindexpr,
walker, context))
return true;
- if (expression_tree_walker((Node *) aref->reflowerindexpr,
+ if (expression_tree_walker((Node *) sbsref->reflowerindexpr,
walker, context))
return true;
/* walker must see the refexpr and refassgnexpr, however */
- if (walker(aref->refexpr, context))
+ if (walker(sbsref->refexpr, context))
return true;
- if (walker(aref->refassgnexpr, context))
+ if (walker(sbsref->refassgnexpr, context))
return true;
}
break;
@@ -2515,12 +2515,12 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
- case T_ArrayRef:
+ case T_SubscriptionRef:
{
- ArrayRef *arrayref = (ArrayRef *) node;
- ArrayRef *newnode;
+ SubscriptionRef *arrayref = (SubscriptionRef *) node;
+ SubscriptionRef *newnode;
- FLATCOPY(newnode, arrayref, ArrayRef);
+ FLATCOPY(newnode, arrayref, SubscriptionRef);
MUTATE(newnode->refupperindexpr, arrayref->refupperindexpr,
List *);
MUTATE(newnode->reflowerindexpr, arrayref->reflowerindexpr,
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index ae86954..4616c4d 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1062,11 +1062,11 @@ _outWindowFunc(StringInfo str, const WindowFunc *node)
}
static void
-_outArrayRef(StringInfo str, const ArrayRef *node)
+_outSubscriptionRef(StringInfo str, const SubscriptionRef *node)
{
- WRITE_NODE_TYPE("ARRAYREF");
+ WRITE_NODE_TYPE("SUBSCRIPTIONREF");
- WRITE_OID_FIELD(refarraytype);
+ WRITE_OID_FIELD(refcontainertype);
WRITE_OID_FIELD(refelemtype);
WRITE_INT_FIELD(reftypmod);
WRITE_OID_FIELD(refcollid);
@@ -3445,8 +3445,8 @@ outNode(StringInfo str, const void *obj)
case T_WindowFunc:
_outWindowFunc(str, obj);
break;
- case T_ArrayRef:
- _outArrayRef(str, obj);
+ case T_SubscriptionRef:
+ _outSubscriptionRef(str, obj);
break;
case T_FuncExpr:
_outFuncExpr(str, obj);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 917e6c8..280ae6e 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -606,14 +606,14 @@ _readWindowFunc(void)
}
/*
- * _readArrayRef
+ * _readSubscriptionRef
*/
-static ArrayRef *
-_readArrayRef(void)
+static SubscriptionRef *
+_readSubscriptionRef(void)
{
- READ_LOCALS(ArrayRef);
+ READ_LOCALS(SubscriptionRef);
- READ_OID_FIELD(refarraytype);
+ READ_OID_FIELD(refcontainertype);
READ_OID_FIELD(refelemtype);
READ_INT_FIELD(reftypmod);
READ_OID_FIELD(refcollid);
@@ -2319,8 +2319,8 @@ parseNodeString(void)
return_value = _readGroupingFunc();
else if (MATCH("WINDOWFUNC", 10))
return_value = _readWindowFunc();
- else if (MATCH("ARRAYREF", 8))
- return_value = _readArrayRef();
+ else if (MATCH("SUBSCRIPTIONREF", 15))
+ return_value = _readSubscriptionRef();
else if (MATCH("FUNCEXPR", 8))
return_value = _readFuncExpr();
else if (MATCH("NAMEDARGEXPR", 12))
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 663ffe0..2c7b738 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -1329,10 +1329,10 @@ contain_nonstrict_functions_walker(Node *node, void *context)
/* a window function could return non-null with null input */
return true;
}
- if (IsA(node, ArrayRef))
+ if (IsA(node, SubscriptionRef))
{
/* array assignment is nonstrict, but subscripting is strict */
- if (((ArrayRef *) node)->refassgnexpr != NULL)
+ if (((SubscriptionRef *) node)->refassgnexpr != NULL)
return true;
/* else fall through to check args */
}
@@ -1512,7 +1512,7 @@ contain_leaked_vars_walker(Node *node, void *context)
case T_Var:
case T_Const:
case T_Param:
- case T_ArrayRef:
+ case T_SubscriptionRef:
case T_ArrayExpr:
case T_FieldSelect:
case T_FieldStore:
@@ -3573,7 +3573,7 @@ eval_const_expressions_mutator(Node *node,
* For any node type not handled above, we recurse using
* expression_tree_mutator, which will copy the node unchanged but try to
* simplify its arguments (if any) using this routine. For example: we
- * cannot eliminate an ArrayRef node, but we might be able to simplify
+ * cannot eliminate an SubscriptionRef node, but we might be able to simplify
* constant expressions in its subscripts.
*/
return expression_tree_mutator(node, eval_const_expressions_mutator,
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 6901e08..c8f2eae 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -917,13 +917,13 @@ transformInsertRow(ParseState *pstate, List *exprlist,
expr = (Expr *) linitial(fstore->newvals);
}
- else if (IsA(expr, ArrayRef))
+ else if (IsA(expr, SubscriptionRef))
{
- ArrayRef *aref = (ArrayRef *) expr;
+ SubscriptionRef *sbsref = (SubscriptionRef *) expr;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
break;
- expr = aref->refassgnexpr;
+ expr = sbsref->refassgnexpr;
}
else
break;
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 63f7965..3005e47 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -461,13 +461,13 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
/* process subscripts before this field selection */
if (subscripts)
- result = (Node *) transformArraySubscripts(pstate,
- result,
- exprType(result),
- InvalidOid,
- exprTypmod(result),
- subscripts,
- NULL);
+ result = (Node *) transformContainerSubscripts(pstate,
+ result,
+ exprType(result),
+ InvalidOid,
+ exprTypmod(result),
+ subscripts,
+ NULL);
subscripts = NIL;
newresult = ParseFuncOrColumn(pstate,
@@ -482,13 +482,13 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
}
/* process trailing subscripts, if any */
if (subscripts)
- result = (Node *) transformArraySubscripts(pstate,
- result,
- exprType(result),
- InvalidOid,
- exprTypmod(result),
- subscripts,
- NULL);
+ result = (Node *) transformContainerSubscripts(pstate,
+ result,
+ exprType(result),
+ InvalidOid,
+ exprTypmod(result),
+ subscripts,
+ NULL);
return result;
}
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 62d2f71..948fb8b 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -201,18 +201,22 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location)
/*
* transformArrayType()
- * Identify the types involved in a subscripting operation
+ * Identify the types involved in a subscripting operation for array
*
* On entry, arrayType/arrayTypmod identify the type of the input value
* to be subscripted (which could be a domain type). These are modified
* if necessary to identify the actual array type and typmod, and the
* array's element type is returned. An error is thrown if the input isn't
* an array type.
+ *
+ * NOTE: This part of type-specific code is not separated into type-specific
+ * subscription procedure for now, but it does not affect on the whole logic,
+ * since InvalidOid will be return in case of other types not an error.
+ * An error will appears only if a subscription procedure is not defined.
*/
Oid
-transformArrayType(Oid *arrayType, int32 *arrayTypmod)
+transformArrayType(Oid *containerType, int32 *containerTypmod)
{
- Oid origArrayType = *arrayType;
Oid elementType;
HeapTuple type_tuple_array;
Form_pg_type type_struct_array;
@@ -224,7 +228,7 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
* itself. (Note that we provide no method whereby the creator of a
* domain over an array type could hide its ability to be subscripted.)
*/
- *arrayType = getBaseTypeAndTypmod(*arrayType, arrayTypmod);
+ *containerType = getBaseTypeAndTypmod(*containerType, containerTypmod);
/*
* We treat int2vector and oidvector as though they were domains over
@@ -233,25 +237,20 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
* xxxvector type; so we want the result of a slice operation to be
* considered to be of the more general type.
*/
- if (*arrayType == INT2VECTOROID)
- *arrayType = INT2ARRAYOID;
- else if (*arrayType == OIDVECTOROID)
- *arrayType = OIDARRAYOID;
+ if (*containerType == INT2VECTOROID)
+ *containerType = INT2ARRAYOID;
+ else if (*containerType == OIDVECTOROID)
+ *containerType = OIDARRAYOID;
/* Get the type tuple for the array */
- type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*arrayType));
+ type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*containerType));
if (!HeapTupleIsValid(type_tuple_array))
- elog(ERROR, "cache lookup failed for type %u", *arrayType);
+ elog(ERROR, "cache lookup failed for type %u", *containerType);
type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array);
/* needn't check typisdefined since this will fail anyway */
elementType = type_struct_array->typelem;
- if (elementType == InvalidOid)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("cannot subscript type %s because it is not an array",
- format_type_be(origArrayType))));
ReleaseSysCache(type_tuple_array);
@@ -259,61 +258,80 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
}
/*
- * transformArraySubscripts()
- * Transform array subscripting. This is used for both
- * array fetch and array assignment.
+ * transformContainerSubscripts()
+ * Transform container subscripting. This is used for both
+ * container fetch and container assignment.
*
- * In an array fetch, we are given a source array value and we produce an
- * expression that represents the result of extracting a single array element
- * or an array slice.
+ * In a container fetch, we are given a source container value and we produce
+ * an expression that represents the result of extracting a single container
+ * element or a container slice.
*
- * In an array assignment, we are given a destination array value plus a
- * source value that is to be assigned to a single element or a slice of
- * that array. We produce an expression that represents the new array value
- * with the source data inserted into the right part of the array.
+ * In a container assignment, we are given a destination container value plus a
+ * source value that is to be assigned to a single element or a slice of that
+ * container. We produce an expression that represents the new container value
+ * with the source data inserted into the right part of the container.
*
- * For both cases, if the source array is of a domain-over-array type,
- * the result is of the base array type or its element type; essentially,
- * we must fold a domain to its base type before applying subscripting.
- * (Note that int2vector and oidvector are treated as domains here.)
+ * For both cases, this function contains only general subscription logic while
+ * type-specific logic (e.g. type verifications and coersion) is placend in
+ * separate procedure indicated by typsubscription. There is only one exception
+ * for now about domain-over-container, if the source container is of a
+ * domain-over-container type, the result is of the base container type or its
+ * element type; essentially, we must fold a domain to its base type before
+ * applying subscripting. (Note that int2vector and oidvector are treated as
+ * domains here.) If domain verification failed we assume, that element type
+ * must be the same as container type (e.g. in case of jsonb).
+ * An error will appear in case if current container type doesn't have a
+ * subscription procedure.
*
- * pstate Parse state
- * arrayBase Already-transformed expression for the array as a whole
- * arrayType OID of array's datatype (should match type of arrayBase,
- * or be the base type of arrayBase's domain type)
- * elementType OID of array's element type (fetch with transformArrayType,
- * or pass InvalidOid to do it here)
- * arrayTypMod typmod for the array (which is also typmod for the elements)
- * indirection Untransformed list of subscripts (must not be NIL)
- * assignFrom NULL for array fetch, else transformed expression for source.
+ * pstate Parse state
+ * containerBase Already-transformed expression for the container as a whole
+ * containerType OID of container's datatype (should match type of containerBase,
+ * or be the base type of containerBase's domain type)
+ * elementType OID of container's element type (fetch with transformArrayType,
+ * or pass InvalidOid to do it here)
+ * containerTypMod typmod for the container (which is also typmod for the elements)
+ * indirection Untransformed list of subscripts (must not be NIL)
+ * assignFrom NULL for container fetch, else transformed expression for source.
*/
-ArrayRef *
-transformArraySubscripts(ParseState *pstate,
- Node *arrayBase,
- Oid arrayType,
- Oid elementType,
- int32 arrayTypMod,
- List *indirection,
- Node *assignFrom)
+
+SubscriptionRef *
+transformContainerSubscripts(ParseState *pstate,
+ Node *containerBase,
+ Oid containerType,
+ Oid elementType,
+ int32 containerTypMod,
+ List *indirection,
+ Node *assignFrom)
{
- bool isSlice = false;
- List *upperIndexpr = NIL;
- List *lowerIndexpr = NIL;
- ListCell *idx;
- ArrayRef *aref;
+ bool isSlice = false;
+ List *upperIndexpr = NIL;
+ List *lowerIndexpr = NIL;
+ ListCell *idx;
+ SubscriptionRef *sbsref,
+ *prepared_sbsref;
+ RegProcedure typsubscription = get_typsubscription(containerType);
+
+ if (!OidIsValid(typsubscription))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("cannot subscript type %s because it does not support subscription",
+ format_type_be(containerType))));
/*
* Caller may or may not have bothered to determine elementType. Note
- * that if the caller did do so, arrayType/arrayTypMod must be as modified
+ * that if the caller did do so, containerType/containerTypMod must be as modified
* by transformArrayType, ie, smash domain to base type.
*/
if (!OidIsValid(elementType))
- elementType = transformArrayType(&arrayType, &arrayTypMod);
+ elementType = transformArrayType(&containerType, &containerTypMod);
+
+ if (!OidIsValid(elementType))
+ elementType = containerType;
/*
- * A list containing only simple subscripts refers to a single array
+ * A list containing only simple subscripts refers to a single container
* element. If any of the items are slice specifiers (lower:upper), then
- * the subscript expression means an array slice operation. In this case,
+ * the subscript expression means an container slice operation. In this case,
* we convert any non-slice items to slices by treating the single
* subscript as the upper bound and supplying an assumed lower bound of 1.
* We have to prescan the list to see if there are any slice items.
@@ -343,107 +361,37 @@ transformArraySubscripts(ParseState *pstate,
if (ai->lidx)
{
subexpr = transformExpr(pstate, ai->lidx, pstate->p_expr_kind);
- /* If it's not int4 already, try to coerce */
- subexpr = coerce_to_target_type(pstate,
- subexpr, exprType(subexpr),
- INT4OID, -1,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (subexpr == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array subscript must have type integer"),
- parser_errposition(pstate, exprLocation(ai->lidx))));
- }
- else if (!ai->is_slice)
- {
- /* Make a constant 1 */
- subexpr = (Node *) makeConst(INT4OID,
- -1,
- InvalidOid,
- sizeof(int32),
- Int32GetDatum(1),
- false,
- true); /* pass by value */
}
else
{
/* Slice with omitted lower bound, put NULL into the list */
subexpr = NULL;
}
- lowerIndexpr = lappend(lowerIndexpr, subexpr);
- }
- else
- Assert(ai->lidx == NULL && !ai->is_slice);
-
- if (ai->uidx)
- {
- subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
- /* If it's not int4 already, try to coerce */
- subexpr = coerce_to_target_type(pstate,
- subexpr, exprType(subexpr),
- INT4OID, -1,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (subexpr == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array subscript must have type integer"),
- parser_errposition(pstate, exprLocation(ai->uidx))));
- }
- else
- {
- /* Slice with omitted upper bound, put NULL into the list */
- Assert(isSlice && ai->is_slice);
- subexpr = NULL;
+ lowerIndexpr = lappend(lowerIndexpr, list_make2(subexpr, ai));
}
+ subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
upperIndexpr = lappend(upperIndexpr, subexpr);
}
/*
- * If doing an array store, coerce the source value to the right type.
- * (This should agree with the coercion done by transformAssignedExpr.)
- */
- if (assignFrom != NULL)
- {
- Oid typesource = exprType(assignFrom);
- Oid typeneeded = isSlice ? arrayType : elementType;
- Node *newFrom;
-
- newFrom = coerce_to_target_type(pstate,
- assignFrom, typesource,
- typeneeded, arrayTypMod,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (newFrom == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array assignment requires type %s"
- " but expression is of type %s",
- format_type_be(typeneeded),
- format_type_be(typesource)),
- errhint("You will need to rewrite or cast the expression."),
- parser_errposition(pstate, exprLocation(assignFrom))));
- assignFrom = newFrom;
- }
-
- /*
- * Ready to build the ArrayRef node.
+ * Ready to build the SubscriptionRef node.
*/
- aref = makeNode(ArrayRef);
- aref->refarraytype = arrayType;
- aref->refelemtype = elementType;
- aref->reftypmod = arrayTypMod;
+ sbsref = makeNode(SubscriptionRef);
+ sbsref->refcontainertype = containerType;
+ sbsref->refelemtype = elementType;
+ sbsref->reftypmod = containerTypMod;
/* refcollid will be set by parse_collate.c */
- aref->refupperindexpr = upperIndexpr;
- aref->reflowerindexpr = lowerIndexpr;
- aref->refexpr = (Expr *) arrayBase;
- aref->refassgnexpr = (Expr *) assignFrom;
+ sbsref->refupperindexpr = upperIndexpr;
+ sbsref->reflowerindexpr = lowerIndexpr;
+ sbsref->refexpr = (Expr *) containerBase;
+ sbsref->refassgnexpr = (Expr *) assignFrom;
+
+ prepared_sbsref = (SubscriptionRef *) OidFunctionCall3(typsubscription,
+ Int32GetDatum(SBS_VALIDATION),
+ PointerGetDatum(sbsref),
+ PointerGetDatum(pstate));
- return aref;
+ return prepared_sbsref;
}
/*
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index b7b82bf..026e463 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -759,41 +759,24 @@ transformAssignmentIndirection(ParseState *pstate,
/* base case: just coerce RHS to match target type ID */
- result = coerce_to_target_type(pstate,
- rhs, exprType(rhs),
- targetTypeId, targetTypMod,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
+ if (targetTypeId != InvalidOid)
+ result = coerce_to_target_type(pstate,
+ rhs, exprType(rhs),
+ targetTypeId, targetTypMod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ else
+ result = rhs;
+
if (result == NULL)
- {
- if (targetIsArray)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array assignment to \"%s\" requires type %s"
- " but expression is of type %s",
- targetName,
- format_type_be(targetTypeId),
- format_type_be(exprType(rhs))),
- errhint("You will need to rewrite or cast the expression."),
- parser_errposition(pstate, location)));
- else
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("subfield \"%s\" is of type %s"
- " but expression is of type %s",
- targetName,
- format_type_be(targetTypeId),
- format_type_be(exprType(rhs))),
- errhint("You will need to rewrite or cast the expression."),
- parser_errposition(pstate, location)));
- }
+ result = rhs;
return result;
}
/*
- * helper for transformAssignmentIndirection: process array assignment
+ * helper for transformAssignmentIndirection: process container assignment
*/
static Node *
transformAssignmentSubscripts(ParseState *pstate,
@@ -809,55 +792,55 @@ transformAssignmentSubscripts(ParseState *pstate,
int location)
{
Node *result;
- Oid arrayType;
- int32 arrayTypMod;
+ Oid containerType;
+ int32 containerTypMod;
Oid elementTypeId;
Oid typeNeeded;
Oid collationNeeded;
Assert(subscripts != NIL);
- /* Identify the actual array type and element type involved */
- arrayType = targetTypeId;
- arrayTypMod = targetTypMod;
- elementTypeId = transformArrayType(&arrayType, &arrayTypMod);
+ /* Identify the actual container type and element type involved */
+ containerType = targetTypeId;
+ containerTypMod = targetTypMod;
+ elementTypeId = transformArrayType(&containerType, &containerTypMod);
/* Identify type that RHS must provide */
- typeNeeded = isSlice ? arrayType : elementTypeId;
+ typeNeeded = isSlice ? containerType : elementTypeId;
/*
- * Array normally has same collation as elements, but there's an
- * exception: we might be subscripting a domain over an array type. In
+ * container normally has same collation as elements, but there's an
+ * exception: we might be subscripting a domain over an container type. In
* that case use collation of the base type.
*/
- if (arrayType == targetTypeId)
+ if (containerType == targetTypeId)
collationNeeded = targetCollation;
else
- collationNeeded = get_typcollation(arrayType);
+ collationNeeded = get_typcollation(containerType);
- /* recurse to create appropriate RHS for array assign */
+ /* recurse to create appropriate RHS for container assign */
rhs = transformAssignmentIndirection(pstate,
NULL,
targetName,
true,
typeNeeded,
- arrayTypMod,
+ containerTypMod,
collationNeeded,
next_indirection,
rhs,
location);
/* process subscripts */
- result = (Node *) transformArraySubscripts(pstate,
- basenode,
- arrayType,
- elementTypeId,
- arrayTypMod,
- subscripts,
- rhs);
-
- /* If target was a domain over array, need to coerce up to the domain */
- if (arrayType != targetTypeId)
+ result = (Node *) transformContainerSubscripts(pstate,
+ basenode,
+ containerType,
+ exprType(rhs),
+ containerTypMod,
+ subscripts,
+ rhs);
+
+ /* If target was a domain over container, need to coerce up to the domain */
+ if (containerType != targetTypeId)
{
Oid resulttype = exprType(result);
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index b828e3c..44b4c04 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -895,7 +895,7 @@ process_matched_tle(TargetEntry *src_tle,
/*----------
* Multiple assignments to same attribute. Allow only if all are
- * FieldStore or ArrayRef assignment operations. This is a bit
+ * FieldStore or SubscriptionRef assignment operations. This is a bit
* tricky because what we may actually be looking at is a nest of
* such nodes; consider
* UPDATE tab SET col.fld1.subfld1 = x, col.fld2.subfld2 = y
@@ -903,7 +903,7 @@ process_matched_tle(TargetEntry *src_tle,
* FieldStore(col, fld1, FieldStore(placeholder, subfld1, x))
* FieldStore(col, fld2, FieldStore(placeholder, subfld2, y))
* However, we can ignore the substructure and just consider the top
- * FieldStore or ArrayRef from each assignment, because it works to
+ * FieldStore or SubscriptionRef from each assignment, because it works to
* combine these as
* FieldStore(FieldStore(col, fld1,
* FieldStore(placeholder, subfld1, x)),
@@ -913,7 +913,7 @@ process_matched_tle(TargetEntry *src_tle,
*
* For FieldStore, instead of nesting we can generate a single
* FieldStore with multiple target fields. We must nest when
- * ArrayRefs are involved though.
+ * SubscriptionRefs are involved though.
*----------
*/
src_expr = (Node *) src_tle->expr;
@@ -972,13 +972,13 @@ process_matched_tle(TargetEntry *src_tle,
}
newexpr = (Node *) fstore;
}
- else if (IsA(src_expr, ArrayRef))
+ else if (IsA(src_expr, SubscriptionRef))
{
- ArrayRef *aref = makeNode(ArrayRef);
+ SubscriptionRef *sbsref = makeNode(SubscriptionRef);
- memcpy(aref, src_expr, sizeof(ArrayRef));
- aref->refexpr = (Expr *) prior_expr;
- newexpr = (Node *) aref;
+ memcpy(sbsref, src_expr, sizeof(SubscriptionRef));
+ sbsref->refexpr = (Expr *) prior_expr;
+ newexpr = (Node *) sbsref;
}
else
{
@@ -1005,14 +1005,15 @@ get_assignment_input(Node *node)
return (Node *) fstore->arg;
}
- else if (IsA(node, ArrayRef))
+ else if (IsA(node, SubscriptionRef))
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptionRef *sbsref = (SubscriptionRef *) node;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
return NULL;
- return (Node *) aref->refexpr;
+ return (Node *) sbsref->refexpr;
}
+
return NULL;
}
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 1db7bf0..1894e70 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -24,6 +24,8 @@
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "libpq/pqformat.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
#include "utils/array.h"
#include "utils/arrayaccess.h"
#include "utils/builtins.h"
@@ -31,6 +33,8 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
+#include "parser/parse_node.h"
+#include "parser/parse_coerce.h"
/*
@@ -88,6 +92,7 @@ typedef struct ArrayIteratorData
static bool array_isspace(char ch);
static int ArrayCount(const char *str, int *dim, char typdelim);
+static bool isAssignmentIndirectionExpr(ExprState *exprstate);
static void ReadArrayStr(char *arrayStr, const char *origStr,
int nitems, int ndim, int *dim,
FmgrInfo *inputproc, Oid typioparam, int32 typmod,
@@ -157,7 +162,8 @@ static int width_bucket_array_variable(Datum operand,
ArrayType *thresholds,
Oid collation,
TypeCacheEntry *typentry);
-
+static Datum array_subscription_prepare(PG_FUNCTION_ARGS);
+static Datum array_subscription_evaluate(PG_FUNCTION_ARGS);
/*
* array_in :
@@ -6520,3 +6526,363 @@ width_bucket_array_variable(Datum operand,
return left;
}
+
+/*
+ * Helper for ExecEvalSubscriptionRef: is expr a nested FieldStore or SubscriptionRef
+ * that might need the old element value passed down?
+ *
+ * (We could use this in ExecEvalFieldStore too, but in that case passing
+ * the old value is so cheap there's no need.)
+ */
+static bool
+isAssignmentIndirectionExpr(ExprState *exprstate)
+{
+ if (exprstate == NULL)
+ return false; /* just paranoia */
+ if (IsA(exprstate, FieldStoreState))
+ {
+ FieldStore *fstore = (FieldStore *) exprstate->expr;
+
+ if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
+ return true;
+ }
+ else if (IsA(exprstate, SubscriptionRefExprState))
+ {
+ SubscriptionRef *array_ref = (SubscriptionRef *) exprstate->expr;
+
+ if (array_ref->refexpr && IsA(array_ref->refexpr, CaseTestExpr))
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Perform an actual data extraction or modification for the array
+ * subscription. As a result the extracted Datum or the modified containers
+ * value will be returned.
+ */
+Datum
+array_subscription_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptionRefExprState *sbstate = (SubscriptionRefExprState *) PG_GETARG_POINTER(0);
+ SubscriptionExecData *sbsdata = (SubscriptionExecData *) PG_GETARG_POINTER(1);
+ ExprContext *econtext = sbsdata->xprcontext;
+ bool *is_null = sbsdata->isNull;
+ SubscriptionRef *array_ref = (SubscriptionRef *) sbstate->xprstate.expr;
+ bool is_assignment = (array_ref->refassgnexpr != NULL);
+ bool is_slice = (array_ref->reflowerindexpr != NIL);
+ IntArray u_index, l_index;
+ bool eisnull;
+ int i = 0;
+
+ get_typlenbyvalalign(array_ref->refelemtype,
+ &sbstate->refelemlength,
+ &sbstate->refelembyval,
+ &sbstate->refelemalign);
+
+ for(i = 0; i < sbsdata->indexprNumber; i++)
+ u_index.indx[i] = DatumGetInt32(sbsdata->upper[i]);
+
+ if (is_slice)
+ {
+ for(i = 0; i < sbsdata->indexprNumber; i++)
+ l_index.indx[i] = DatumGetInt32(sbsdata->lower[i]);
+ }
+
+ if (is_assignment)
+ {
+ Datum sourceData;
+ Datum save_datum;
+ bool save_isNull;
+
+ /*
+ * We might have a nested-assignment situation, in which the
+ * refassgnexpr is itself a FieldStore or SubscriptionRef that needs to
+ * obtain and modify the previous value of the array element or slice
+ * being replaced. If so, we have to extract that value from the
+ * array and pass it down via the econtext's caseValue. It's safe to
+ * reuse the CASE mechanism because there cannot be a CASE between
+ * here and where the value would be needed, and an array assignment
+ * can't be within a CASE either. (So saving and restoring the
+ * caseValue is just paranoia, but let's do it anyway.)
+ *
+ * Since fetching the old element might be a nontrivial expense, do it
+ * only if the argument appears to actually need it.
+ */
+ save_datum = econtext->caseValue_datum;
+ save_isNull = econtext->caseValue_isNull;
+
+ if (isAssignmentIndirectionExpr(sbstate->refassgnexpr))
+ {
+ if (*is_null)
+ {
+ /* whole array is null, so any element or slice is too */
+ econtext->caseValue_datum = (Datum) 0;
+ econtext->caseValue_isNull = true;
+ }
+ else if (!is_slice)
+ {
+ econtext->caseValue_datum =
+ array_get_element(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign,
+ &econtext->caseValue_isNull);
+ }
+ else
+ {
+ econtext->caseValue_datum =
+ array_get_slice(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, l_index.indx,
+ sbsdata->upperProvided,
+ sbsdata->lowerProvided,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+ econtext->caseValue_isNull = false;
+ }
+ }
+ else
+ {
+ /* argument shouldn't need caseValue, but for safety set it null */
+ econtext->caseValue_datum = (Datum) 0;
+ econtext->caseValue_isNull = true;
+ }
+
+ /*
+ * Evaluate the value to be assigned into the array.
+ */
+ sourceData = ExecEvalExpr(sbstate->refassgnexpr,
+ econtext,
+ &eisnull,
+ NULL);
+
+ econtext->caseValue_datum = save_datum;
+ econtext->caseValue_isNull = save_isNull;
+
+ /*
+ * For an assignment to a fixed-length array type, both the original
+ * array and the value to be assigned into it must be non-NULL, else
+ * we punt and return the original array.
+ */
+ if (sbstate->refattrlength > 0) /* fixed-length array? */
+ if (eisnull || *is_null)
+ return sbsdata->containerSource;
+
+ /*
+ * For assignment to varlena arrays, we handle a NULL original array
+ * by substituting an empty (zero-dimensional) array; insertion of the
+ * new element will result in a singleton array value. It does not
+ * matter whether the new element is NULL.
+ */
+ if (*is_null)
+ {
+ sbsdata->containerSource = PointerGetDatum(construct_empty_array(array_ref->refelemtype));
+ *is_null = false;
+ }
+
+ if (!is_slice)
+ return array_set_element(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, sourceData, eisnull,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+ else
+ return array_set_slice(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, l_index.indx,
+ sbsdata->upperProvided,
+ sbsdata->lowerProvided,
+ sourceData, eisnull,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+ }
+
+ if (!is_slice)
+ return array_get_element(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign,
+ is_null);
+ else
+ return array_get_slice(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, l_index.indx,
+ sbsdata->upperProvided,
+ sbsdata->lowerProvided,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+}
+
+/*
+ * Perform preparation for the array subscription, mostly type verification
+ * and coersion. This function produces an expression that represents the
+ * result of extracting a single container element/container slice or the new
+ * container value with the source data inserted into the right part of the
+ * container.
+ */
+Datum
+array_subscription_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptionRef *sbsref = (SubscriptionRef *) PG_GETARG_POINTER(0);
+ ParseState *pstate = (ParseState *) PG_GETARG_POINTER(1);
+ Node *node = (Node *)sbsref;
+ Oid array_type = sbsref->refcontainertype;
+ int32 array_typ_mode = (int32) sbsref->reftypmod;
+ bool is_slice = sbsref->reflowerindexpr != NIL;
+ Oid typeneeded = InvalidOid,
+ typesource = InvalidOid;
+ Node *new_from;
+ Oid element_type_id;
+ Node *subexpr;
+ List *upperIndexpr = NIL;
+ List *lowerIndexpr = NIL;
+ ListCell *l;
+
+ element_type_id = transformArrayType(&array_type, &array_typ_mode);
+ sbsref->refelemtype = element_type_id;
+
+ foreach(l, sbsref->refupperindexpr)
+ {
+ subexpr = (Node *) lfirst(l);
+
+ if (subexpr == NULL)
+ {
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ continue;
+ }
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ INT4OID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("array subscript must have type integer"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ }
+
+ sbsref->refupperindexpr = upperIndexpr;
+
+ foreach(l, sbsref->reflowerindexpr)
+ {
+ List *expr_ai = (List *) lfirst(l);
+ A_Indices *ai = (A_Indices *) lfirst(list_tail(expr_ai));
+
+ subexpr = (Node *) lfirst(list_head(expr_ai));
+ if (subexpr == NULL && !ai->is_slice)
+ {
+ /* Make a constant 1 */
+ subexpr = (Node *) makeConst(INT4OID,
+ -1,
+ InvalidOid,
+ sizeof(int32),
+ Int32GetDatum(1),
+ false,
+ true); /* pass by value */
+ }
+
+ if (subexpr == NULL)
+ {
+ lowerIndexpr = lappend(lowerIndexpr, subexpr);
+ continue;
+ }
+
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ INT4OID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("array subscript must have type integer"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ lowerIndexpr = lappend(lowerIndexpr, subexpr);
+ }
+
+ sbsref->reflowerindexpr = lowerIndexpr;
+
+ if (sbsref->refassgnexpr != NULL)
+ {
+ new_from = coerce_to_target_type(pstate,
+ (Node *)sbsref->refassgnexpr, typesource,
+ typeneeded, sbsref->reftypmod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (new_from == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("array assignment requires type %s"
+ " but expression is of type %s",
+ format_type_be(typeneeded),
+ format_type_be(typesource)),
+ errhint("You will need to rewrite or cast the expression."),
+ parser_errposition(pstate, exprLocation((Node *)sbsref->refassgnexpr))));
+ sbsref->refassgnexpr = (Expr *)new_from;
+
+ if (array_type != sbsref->refcontainertype)
+ {
+ typesource = exprType((Node *)sbsref->refassgnexpr);
+ typesource = is_slice ? sbsref->refcontainertype : sbsref->refelemtype;
+
+ node = coerce_to_target_type(pstate,
+ node, array_type,
+ sbsref->refcontainertype, sbsref->reftypmod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+
+ /* can fail if we had int2vector/oidvector, but not for true domains */
+ if (node == NULL && node->type != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_CANNOT_COERCE),
+ errmsg("cannot cast type %s to %s",
+ format_type_be(array_type),
+ format_type_be(sbsref->refcontainertype)),
+ parser_errposition(pstate, 0)));
+
+ PG_RETURN_POINTER(node);
+ }
+
+ }
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+/*
+ * Handle array-type subscription logic.
+ */
+Datum
+array_subscription(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1,
+ fcinfo->nargs);
+
+ if (op_type & SBS_VALIDATION)
+ return array_subscription_prepare(&target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return array_subscription_evaluate(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscription function: %d", op_type);
+}
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index 987cfd1..27a5e89 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -1145,23 +1145,34 @@ to_jsonb(PG_FUNCTION_ARGS)
{
Datum val = PG_GETARG_DATUM(0);
Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
- JsonbInState result;
- JsonbTypeCategory tcategory;
- Oid outfuncoid;
+ JsonbValue *res = to_jsonb_worker(val, val_type);
+ PG_RETURN_POINTER(JsonbValueToJsonb(res));
+}
- if (val_type == InvalidOid)
+/*
+ * Do the actual conversion to jsonb for to_jsonb function. This logic is
+ * separated because it can be useful not only in here (e.g. we use it in
+ * jsonb subscription)
+ */
+JsonbValue *
+to_jsonb_worker(Datum source, Oid source_type)
+{
+ JsonbInState result;
+ JsonbTypeCategory tcategory;
+ Oid outfuncoid;
+
+ if (source_type == InvalidOid)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("could not determine input data type")));
- jsonb_categorize_type(val_type,
+ jsonb_categorize_type(source_type,
&tcategory, &outfuncoid);
memset(&result, 0, sizeof(JsonbInState));
- datum_to_jsonb(val, false, &result, tcategory, outfuncoid, false);
-
- PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
+ datum_to_jsonb(source, false, &result, tcategory, outfuncoid, false);
+ return result.res;
}
/*
diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c
index ddc34ce..f192f72 100644
--- a/src/backend/utils/adt/jsonb_util.c
+++ b/src/backend/utils/adt/jsonb_util.c
@@ -61,6 +61,19 @@ static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate,
JsonbIteratorToken seq,
JsonbValue *scalarVal);
+JsonbValue *
+JsonbToJsonbValue(Jsonb *jsonb, JsonbValue *val)
+{
+ if (!val)
+ val = (JsonbValue *) palloc(sizeof(JsonbValue));
+
+ val->type = jbvBinary;
+ val->val.binary.data = &jsonb->root;
+ val->val.binary.len = VARSIZE(jsonb) - VARHDRSZ;
+
+ return val;
+}
+
/*
* Turn an in-memory JsonbValue into a Jsonb for on-disk storage.
*
@@ -520,6 +533,30 @@ pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq,
JsonbValue *res = NULL;
JsonbValue v;
JsonbIteratorToken tok;
+ int i;
+
+ if (jbval && (seq == WJB_ELEM || seq == WJB_VALUE) && jbval->type == jbvObject)
+ {
+ pushJsonbValue(pstate, WJB_BEGIN_OBJECT, NULL);
+ for (i = 0; i < jbval->val.object.nPairs; i++)
+ {
+ pushJsonbValue(pstate, WJB_KEY, &jbval->val.object.pairs[i].key);
+ pushJsonbValue(pstate, WJB_VALUE, &jbval->val.object.pairs[i].value);
+ }
+
+ return pushJsonbValue(pstate, WJB_END_OBJECT, NULL);
+ }
+
+ if (jbval && (seq == WJB_ELEM || seq == WJB_VALUE) && jbval->type == jbvArray)
+ {
+ pushJsonbValue(pstate, WJB_BEGIN_ARRAY, NULL);
+ for (i = 0; i < jbval->val.array.nElems; i++)
+ {
+ pushJsonbValue(pstate, WJB_ELEM, &jbval->val.array.elems[i]);
+ }
+
+ return pushJsonbValue(pstate, WJB_END_ARRAY, NULL);
+ }
if (!jbval || (seq != WJB_ELEM && seq != WJB_VALUE) ||
jbval->type != jbvBinary)
@@ -530,9 +567,30 @@ pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq,
/* unpack the binary and add each piece to the pstate */
it = JsonbIteratorInit(jbval->val.binary.data);
+
+ if ((jbval->val.binary.data->header & JB_FSCALAR) && *pstate)
+ {
+ tok = JsonbIteratorNext(&it, &v, true);
+ Assert(tok == WJB_BEGIN_ARRAY);
+ Assert(v.type == jbvArray && v.val.array.rawScalar);
+
+ tok = JsonbIteratorNext(&it, &v, true);
+ Assert(tok == WJB_ELEM);
+
+ res = pushJsonbValueScalar(pstate, seq, &v);
+
+ tok = JsonbIteratorNext(&it, &v, true);
+ Assert(tok == WJB_END_ARRAY);
+ Assert(it == NULL);
+
+ return res;
+ }
+
while ((tok = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
res = pushJsonbValueScalar(pstate, tok,
- tok < WJB_BEGIN_ARRAY ? &v : NULL);
+ tok < WJB_BEGIN_ARRAY ||
+ (tok == WJB_BEGIN_ARRAY &&
+ v.val.array.rawScalar) ? &v : NULL);
return res;
}
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 996007d..177c1ad 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -23,6 +23,8 @@
#include "lib/stringinfo.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_coerce.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/hsearch.h"
@@ -32,6 +34,7 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
+#include "parser/parse_node.h"
/* Operations available for setPath */
#define JB_PATH_NOOP 0x0000
@@ -136,18 +139,21 @@ static JsonbValue *findJsonbValueFromContainerLen(JsonbContainer *container,
/* functions supporting jsonb_delete, jsonb_set and jsonb_concat */
static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
JsonbParseState **state);
+static Datum jsonb_set_element(Datum datum, Datum *path, int path_len,
+ Datum sourceData, Oid source_type);
+static Datum jsonb_get_element(Jsonb *jb, Datum *path, int npath,
+ bool *isnull, bool as_text);
static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len,
- JsonbParseState **st, int level, Jsonb *newval,
- int op_type);
+ JsonbParseState **st, int level, JsonbValue *newval, int op_type);
static void setPathObject(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len, JsonbParseState **st,
- int level,
- Jsonb *newval, uint32 npairs, int op_type);
+ int level, JsonbValue *newval, uint32 npairs, int op_type);
static void setPathArray(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len, JsonbParseState **st,
- int level, Jsonb *newval, uint32 nelems, int op_type);
-static void addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb);
+ int level, JsonbValue *newval, uint32 nelems, int op_type);
+static Datum jsonb_subscription_evaluate(PG_FUNCTION_ARGS);
+static Datum jsonb_subscription_prepare(PG_FUNCTION_ARGS);
/* state for json_object_keys */
typedef struct OkeysState
@@ -1171,16 +1177,11 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
Jsonb *jb = PG_GETARG_JSONB(0);
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
- Jsonb *res;
Datum *pathtext;
bool *pathnulls;
+ bool isnull;
int npath;
- int i;
- bool have_object = false,
- have_array = false;
- JsonbValue *jbvp = NULL;
- JsonbValue tv;
- JsonbContainer *container;
+ Datum res;
/*
* If the array contains any null elements, return NULL, on the grounds
@@ -1195,9 +1196,28 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
deconstruct_array(path, TEXTOID, -1, false, 'i',
&pathtext, &pathnulls, &npath);
- /* Identify whether we have object, array, or scalar at top-level */
- container = &jb->root;
+ res = jsonb_get_element(jb, pathtext, npath, &isnull, as_text);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(res);
+}
+static Datum
+jsonb_get_element(Jsonb *jb, Datum *path, int npath, bool *isnull, bool as_text)
+{
+ Jsonb *res;
+ JsonbContainer *container = &jb->root;
+ JsonbValue *jbvp = NULL;
+ JsonbValue tv;
+ int i;
+ bool have_object = false,
+ have_array = false;
+
+ *isnull = false;
+
+ /* Identify whether we have object, array, or scalar at top-level */
if (JB_ROOT_IS_OBJECT(jb))
have_object = true;
else if (JB_ROOT_IS_ARRAY(jb) && !JB_ROOT_IS_SCALAR(jb))
@@ -1222,14 +1242,14 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
if (as_text)
{
- PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL,
+ return PointerGetDatum(cstring_to_text(JsonbToCString(NULL,
container,
VARSIZE(jb))));
}
else
{
/* not text mode - just hand back the jsonb */
- PG_RETURN_JSONB(jb);
+ return JsonbGetDatum(jb);
}
}
@@ -1239,21 +1259,24 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
jbvp = findJsonbValueFromContainerLen(container,
JB_FOBJECT,
- VARDATA_ANY(pathtext[i]),
- VARSIZE_ANY_EXHDR(pathtext[i]));
+ VARDATA_ANY(path[i]),
+ VARSIZE_ANY_EXHDR(path[i]));
}
else if (have_array)
{
long lindex;
uint32 index;
- char *indextext = TextDatumGetCString(pathtext[i]);
+ char *indextext = TextDatumGetCString(path[i]);
char *endptr;
errno = 0;
lindex = strtol(indextext, &endptr, 10);
if (endptr == indextext || *endptr != '\0' || errno != 0 ||
lindex > INT_MAX || lindex < INT_MIN)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
if (lindex >= 0)
{
@@ -1271,7 +1294,10 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
nelements = container->header & JB_CMASK;
if (-lindex > nelements)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
else
index = nelements + lindex;
}
@@ -1281,11 +1307,15 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
else
{
/* scalar, extraction yields a null */
- PG_RETURN_NULL();
+ *isnull = true;
+ return PointerGetDatum(NULL);
}
if (jbvp == NULL)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
else if (i == npath - 1)
break;
@@ -1310,27 +1340,57 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
/* special-case outputs for string and null values */
if (jbvp->type == jbvString)
- PG_RETURN_TEXT_P(cstring_to_text_with_len(jbvp->val.string.val,
- jbvp->val.string.len));
+ return PointerGetDatum(
+ cstring_to_text_with_len(jbvp->val.string.val,
+ jbvp->val.string.len));
if (jbvp->type == jbvNull)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
}
res = JsonbValueToJsonb(jbvp);
if (as_text)
{
- PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL,
+ return PointerGetDatum(cstring_to_text(JsonbToCString(NULL,
&res->root,
VARSIZE(res))));
}
else
{
/* not text mode - just hand back the jsonb */
- PG_RETURN_JSONB(res);
+ return JsonbGetDatum(res);
}
}
+Datum
+jsonb_set_element(Datum jsonbdatum, Datum *path, int path_len,
+ Datum sourceData, Oid source_type)
+{
+ Jsonb *jb = DatumGetJsonb(jsonbdatum);
+ JsonbValue *newval,
+ *res;
+ JsonbParseState *state = NULL;
+ JsonbIterator *it;
+ bool *path_nulls = palloc0(path_len * sizeof(bool));
+
+ newval = to_jsonb_worker(sourceData, source_type);
+
+ if (newval->type == jbvArray && newval->val.array.rawScalar)
+ *newval = newval->val.array.elems[0];
+
+ it = JsonbIteratorInit(&jb->root);
+
+ res = setPath(&it, path, path_nulls, path_len, &state, 0,
+ newval, JB_PATH_CREATE);
+
+ pfree(path_nulls);
+
+ PG_RETURN_JSONB(JsonbValueToJsonb(res));
+}
+
/*
* SQL function json_array_length(json) -> int
*/
@@ -3278,57 +3338,6 @@ jsonb_strip_nulls(PG_FUNCTION_ARGS)
}
/*
- * Add values from the jsonb to the parse state.
- *
- * If the parse state container is an object, the jsonb is pushed as
- * a value, not a key.
- *
- * This needs to be done using an iterator because pushJsonbValue doesn't
- * like getting jbvBinary values, so we can't just push jb as a whole.
- */
-static void
-addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb)
-{
- JsonbIterator *it;
- JsonbValue *o = &(*jbps)->contVal;
- JsonbValue v;
- JsonbIteratorToken type;
-
- it = JsonbIteratorInit(&jb->root);
-
- Assert(o->type == jbvArray || o->type == jbvObject);
-
- if (JB_ROOT_IS_SCALAR(jb))
- {
- (void) JsonbIteratorNext(&it, &v, false); /* skip array header */
- (void) JsonbIteratorNext(&it, &v, false); /* fetch scalar value */
-
- switch (o->type)
- {
- case jbvArray:
- (void) pushJsonbValue(jbps, WJB_ELEM, &v);
- break;
- case jbvObject:
- (void) pushJsonbValue(jbps, WJB_VALUE, &v);
- break;
- default:
- elog(ERROR, "unexpected parent of nested structure");
- }
- }
- else
- {
- while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
- {
- if (type == WJB_KEY || type == WJB_VALUE || type == WJB_ELEM)
- (void) pushJsonbValue(jbps, type, &v);
- else
- (void) pushJsonbValue(jbps, type, NULL);
- }
- }
-
-}
-
-/*
* SQL function jsonb_pretty (jsonb)
*
* Pretty-printed text for the jsonb
@@ -3514,7 +3523,9 @@ jsonb_set(PG_FUNCTION_ARGS)
{
Jsonb *in = PG_GETARG_JSONB(0);
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
- Jsonb *newval = PG_GETARG_JSONB(2);
+ Jsonb *newjsonb = PG_GETARG_JSONB(2);
+ JsonbValue newvalbuf;
+ JsonbValue *newval = JsonbToJsonbValue(newjsonb, &newvalbuf);
bool create = PG_GETARG_BOOL(3);
JsonbValue *res = NULL;
Datum *path_elems;
@@ -3606,7 +3617,9 @@ jsonb_insert(PG_FUNCTION_ARGS)
{
Jsonb *in = PG_GETARG_JSONB(0);
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
- Jsonb *newval = PG_GETARG_JSONB(2);
+ Jsonb *newjsonb = PG_GETARG_JSONB(2);
+ JsonbValue newvalbuf;
+ JsonbValue *newval = JsonbToJsonbValue(newjsonb, &newvalbuf);
bool after = PG_GETARG_BOOL(3);
JsonbValue *res = NULL;
Datum *path_elems;
@@ -3766,10 +3779,10 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
* All path elements before the last must already exist
* whatever bits in op_type are set, or nothing is done.
*/
-static JsonbValue *
+JsonbValue *
setPath(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len,
- JsonbParseState **st, int level, Jsonb *newval, int op_type)
+ JsonbParseState **st, int level, JsonbValue *newval, int op_type)
{
JsonbValue v;
JsonbIteratorToken r;
@@ -3822,11 +3835,11 @@ setPath(JsonbIterator **it, Datum *path_elems,
static void
setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
int path_len, JsonbParseState **st, int level,
- Jsonb *newval, uint32 npairs, int op_type)
+ JsonbValue *newval, uint32 npairs, int op_type)
{
- JsonbValue v;
int i;
- JsonbValue k;
+ JsonbValue k,
+ v;
bool done = false;
if (level >= path_len || path_nulls[level])
@@ -3843,7 +3856,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
newkey.val.string.val = VARDATA_ANY(path_elems[level]);
(void) pushJsonbValue(st, WJB_KEY, &newkey);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_VALUE, newval);
}
for (i = 0; i < npairs; i++)
@@ -3874,7 +3887,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
if (!(op_type & JB_PATH_DELETE))
{
(void) pushJsonbValue(st, WJB_KEY, &k);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_VALUE, newval);
}
done = true;
}
@@ -3897,7 +3910,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
newkey.val.string.val = VARDATA_ANY(path_elems[level]);
(void) pushJsonbValue(st, WJB_KEY, &newkey);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_VALUE, newval);
}
(void) pushJsonbValue(st, r, &k);
@@ -3929,9 +3942,9 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
static void
setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
int path_len, JsonbParseState **st, int level,
- Jsonb *newval, uint32 nelems, int op_type)
+ JsonbValue *newval, uint32 nelems, int op_type)
{
- JsonbValue v;
+ JsonbValue v, *new = (JsonbValue *) newval;
int idx,
i;
bool done = false;
@@ -3976,8 +3989,8 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
if ((idx == INT_MIN || nelems == 0) && (level == path_len - 1) &&
(op_type & JB_PATH_CREATE_OR_INSERT))
{
- Assert(newval != NULL);
- addJsonbToParseState(st, newval);
+ Assert(new != NULL);
+ (void) pushJsonbValue(st, WJB_ELEM, new);
done = true;
}
@@ -3993,7 +4006,7 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
r = JsonbIteratorNext(it, &v, true); /* skip */
if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_CREATE))
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, new);
/*
* We should keep current value only in case of
@@ -4004,13 +4017,13 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
(void) pushJsonbValue(st, r, &v);
if (op_type & JB_PATH_INSERT_AFTER)
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, new);
done = true;
}
else
(void) setPath(it, path_elems, path_nulls, path_len,
- st, level + 1, newval, op_type);
+ st, level + 1, new, op_type);
}
else
{
@@ -4038,8 +4051,169 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
if (op_type & JB_PATH_CREATE_OR_INSERT && !done &&
level == path_len - 1 && i == nelems - 1)
{
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, new);
}
}
}
}
+
+/*
+ * Perform an actual data extraction or modification for the jsonb
+ * subscription. As a result the extracted Datum or the modified containers
+ * value will be returned.
+ */
+Datum
+jsonb_subscription_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptionRefExprState *sbstate = (SubscriptionRefExprState *) PG_GETARG_POINTER(0);
+ SubscriptionExecData *sbsdata = (SubscriptionExecData *) PG_GETARG_POINTER(1);
+ SubscriptionRef *jsonb_ref = (SubscriptionRef *) sbstate->xprstate.expr;
+ bool *is_null = sbsdata->isNull;
+ bool is_assignment = (jsonb_ref->refassgnexpr != NULL);
+
+ if (is_assignment)
+ {
+ ExprContext *econtext = sbsdata->xprcontext;
+ Datum sourceData;
+ Datum save_datum;
+ bool save_isNull;
+ bool eisnull;
+
+ /*
+ * We might have a nested-assignment situation, in which the
+ * refassgnexpr is itself a FieldStore or SubscriptionRef that needs to
+ * obtain and modify the previous value of the array element or slice
+ * being replaced. If so, we have to extract that value from the
+ * array and pass it down via the econtext's caseValue. It's safe to
+ * reuse the CASE mechanism because there cannot be a CASE between
+ * here and where the value would be needed, and an array assignment
+ * can't be within a CASE either. (So saving and restoring the
+ * caseValue is just paranoia, but let's do it anyway.)
+ *
+ * Since fetching the old element might be a nontrivial expense, do it
+ * only if the argument appears to actually need it.
+ */
+ save_datum = econtext->caseValue_datum;
+ save_isNull = econtext->caseValue_isNull;
+
+ /*
+ * Evaluate the value to be assigned into the array.
+ */
+ sourceData = ExecEvalExpr(sbstate->refassgnexpr,
+ econtext,
+ &eisnull,
+ NULL);
+
+ econtext->caseValue_datum = save_datum;
+ econtext->caseValue_isNull = save_isNull;
+
+ /*
+ * For an assignment to a fixed-length array type, both the original
+ * array and the value to be assigned into it must be non-NULL, else
+ * we punt and return the original array.
+ */
+ if (sbstate->refattrlength > 0) /* fixed-length array? */
+ if (eisnull || *is_null)
+ return sbsdata->containerSource;
+
+ /*
+ * For assignment to varlena arrays, we handle a NULL original array
+ * by substituting an empty (zero-dimensional) array; insertion of the
+ * new element will result in a singleton array value. It does not
+ * matter whether the new element is NULL.
+ */
+ if (*is_null)
+ {
+ sbsdata->containerSource =
+ PointerGetDatum(construct_empty_array(jsonb_ref->refelemtype));
+ *is_null = false;
+ }
+
+ return jsonb_set_element(sbsdata->containerSource,
+ sbsdata->upper,
+ sbsdata->indexprNumber,
+ sourceData,
+ jsonb_ref->refelemtype);
+ }
+ else
+ return jsonb_get_element(DatumGetJsonb(sbsdata->containerSource),
+ sbsdata->upper,
+ sbsdata->indexprNumber,
+ is_null,
+ false);
+}
+
+/*
+ * Perform preparation for the jsonb subscription. Since there are not any
+ * particular restrictions for this kind of subscription, we will verify that
+ * it is not a slice operation. This function produces an expression that
+ * represents the result of extracting a single container element or the new
+ * container value with the source data inserted into the right part of the
+ * container.
+ */
+Datum
+jsonb_subscription_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptionRef *sbsref = (SubscriptionRef *) PG_GETARG_POINTER(0);
+ ParseState *pstate = (ParseState *) PG_GETARG_POINTER(1);
+ List *upperIndexpr = NIL;
+ ListCell *l;
+
+ if (sbsref->reflowerindexpr != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("jsonb subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *)lfirst(sbsref->reflowerindexpr->head))))));
+
+ foreach(l, sbsref->refupperindexpr)
+ {
+ Node *subexpr = (Node *) lfirst(l);
+
+ Assert(subexpr != NULL);
+
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("jsonb subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *) lfirst(sbsref->refupperindexpr->head))))));
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ TEXTOID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("jsonb subscript must have text type"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ }
+
+ sbsref->refupperindexpr = upperIndexpr;
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+/*
+ * Handle jsonb-type subscription logic.
+ */
+Datum
+jsonb_subscription(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1,
+ fcinfo->nargs);
+
+ if (op_type & SBS_VALIDATION)
+ return jsonb_subscription_prepare(&target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return jsonb_subscription_evaluate(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscription function: %d", op_type);
+}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 8a81d7a..18dab1e 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -439,7 +439,7 @@ static void get_tablesample_def(TableSampleClause *tablesample,
static void get_opclass_name(Oid opclass, Oid actual_datatype,
StringInfo buf);
static Node *processIndirection(Node *node, deparse_context *context);
-static void printSubscripts(ArrayRef *aref, deparse_context *context);
+static void printSubscripts(SubscriptionRef *aref, deparse_context *context);
static char *get_relation_name(Oid relid);
static char *generate_relation_name(Oid relid, List *namespaces);
static char *generate_qualified_relation_name(Oid relid);
@@ -5828,7 +5828,7 @@ get_update_query_targetlist_def(Query *query, List *targetList,
{
/*
* We must dig down into the expr to see if it's a PARAM_MULTIEXPR
- * Param. That could be buried under FieldStores and ArrayRefs
+ * Param. That could be buried under FieldStores and SubscriptionRefs
* (cf processIndirection()), and underneath those there could be
* an implicit type coercion.
*/
@@ -5841,13 +5841,13 @@ get_update_query_targetlist_def(Query *query, List *targetList,
expr = (Node *) linitial(fstore->newvals);
}
- else if (IsA(expr, ArrayRef))
+ else if (IsA(expr, SubscriptionRef))
{
- ArrayRef *aref = (ArrayRef *) expr;
+ SubscriptionRef *sbsref = (SubscriptionRef *) expr;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
break;
- expr = (Node *) aref->refassgnexpr;
+ expr = (Node *) sbsref->refassgnexpr;
}
else
break;
@@ -6879,7 +6879,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
/* single words: always simple */
return true;
- case T_ArrayRef:
+ case T_SubscriptionRef:
case T_ArrayExpr:
case T_RowExpr:
case T_CoalesceExpr:
@@ -6996,7 +6996,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
return true; /* own parentheses */
}
case T_BoolExpr: /* lower precedence */
- case T_ArrayRef: /* other separators */
+ case T_SubscriptionRef: /* other separators */
case T_ArrayExpr: /* other separators */
case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */
@@ -7046,7 +7046,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
return false;
return true; /* own parentheses */
}
- case T_ArrayRef: /* other separators */
+ case T_SubscriptionRef: /* other separators */
case T_ArrayExpr: /* other separators */
case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */
@@ -7232,9 +7232,9 @@ get_rule_expr(Node *node, deparse_context *context,
get_windowfunc_expr((WindowFunc *) node, context);
break;
- case T_ArrayRef:
+ case T_SubscriptionRef:
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptionRef *sbsref = (SubscriptionRef *) node;
bool need_parens;
/*
@@ -7245,24 +7245,24 @@ get_rule_expr(Node *node, deparse_context *context,
* here too, and display only the assignment source
* expression.
*/
- if (IsA(aref->refexpr, CaseTestExpr))
+ if (IsA(sbsref->refexpr, CaseTestExpr))
{
- Assert(aref->refassgnexpr);
- get_rule_expr((Node *) aref->refassgnexpr,
+ Assert(sbsref->refassgnexpr);
+ get_rule_expr((Node *) sbsref->refassgnexpr,
context, showimplicit);
break;
}
/*
* Parenthesize the argument unless it's a simple Var or a
- * FieldSelect. (In particular, if it's another ArrayRef, we
+ * FieldSelect. (In particular, if it's another SubscriptionRef, we
* *must* parenthesize to avoid confusion.)
*/
- need_parens = !IsA(aref->refexpr, Var) &&
- !IsA(aref->refexpr, FieldSelect);
+ need_parens = !IsA(sbsref->refexpr, Var) &&
+ !IsA(sbsref->refexpr, FieldSelect);
if (need_parens)
appendStringInfoChar(buf, '(');
- get_rule_expr((Node *) aref->refexpr, context, showimplicit);
+ get_rule_expr((Node *) sbsref->refexpr, context, showimplicit);
if (need_parens)
appendStringInfoChar(buf, ')');
@@ -7275,7 +7275,7 @@ get_rule_expr(Node *node, deparse_context *context,
* EXPLAIN tries to print the targetlist of a plan resulting
* from such a statement.
*/
- if (aref->refassgnexpr)
+ if (sbsref->refassgnexpr)
{
Node *refassgnexpr;
@@ -7292,7 +7292,7 @@ get_rule_expr(Node *node, deparse_context *context,
else
{
/* Just an ordinary array fetch, so print subscripts */
- printSubscripts(aref, context);
+ printSubscripts(sbsref, context);
}
}
break;
@@ -7491,12 +7491,12 @@ get_rule_expr(Node *node, deparse_context *context,
bool need_parens;
/*
- * Parenthesize the argument unless it's an ArrayRef or
+ * Parenthesize the argument unless it's an SubscriptionRef or
* another FieldSelect. Note in particular that it would be
* WRONG to not parenthesize a Var argument; simplicity is not
* the issue here, having the right number of names is.
*/
- need_parens = !IsA(arg, ArrayRef) &&!IsA(arg, FieldSelect);
+ need_parens = !IsA(arg, SubscriptionRef) && !IsA(arg, FieldSelect);
if (need_parens)
appendStringInfoChar(buf, '(');
get_rule_expr(arg, context, true);
@@ -9610,7 +9610,7 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
/*
* processIndirection - take care of array and subfield assignment
*
- * We strip any top-level FieldStore or assignment ArrayRef nodes that
+ * We strip any top-level FieldStore or assignment SubscriptionRef nodes that
* appear in the input, printing them as decoration for the base column
* name (which we assume the caller just printed). Return the subexpression
* that's to be assigned.
@@ -9652,19 +9652,19 @@ processIndirection(Node *node, deparse_context *context)
*/
node = (Node *) linitial(fstore->newvals);
}
- else if (IsA(node, ArrayRef))
+ else if (IsA(node, SubscriptionRef))
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptionRef *sbsref = (SubscriptionRef *) node;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
break;
- printSubscripts(aref, context);
+ printSubscripts(sbsref, context);
/*
* We ignore refexpr since it should be an uninteresting reference
* to the target column or subcolumn.
*/
- node = (Node *) aref->refassgnexpr;
+ node = (Node *) sbsref->refassgnexpr;
}
else
break;
@@ -9674,14 +9674,14 @@ processIndirection(Node *node, deparse_context *context)
}
static void
-printSubscripts(ArrayRef *aref, deparse_context *context)
+printSubscripts(SubscriptionRef *sbsref, deparse_context *context)
{
StringInfo buf = context->buf;
ListCell *lowlist_item;
ListCell *uplist_item;
- lowlist_item = list_head(aref->reflowerindexpr); /* could be NULL */
- foreach(uplist_item, aref->refupperindexpr)
+ lowlist_item = list_head(sbsref->reflowerindexpr); /* could be NULL */
+ foreach(uplist_item, sbsref->refupperindexpr)
{
appendStringInfoChar(buf, '[');
if (lowlist_item)
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 13ae6ad..ad8f334 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -3061,3 +3061,23 @@ get_range_subtype(Oid rangeOid)
else
return InvalidOid;
}
+
+/*
+ * get_typsubscription
+ *
+ * Given the type OID, return the type's typsubscription procedure, if any.
+ */
+RegProcedure
+get_typsubscription(Oid typid)
+{
+ HeapTuple tp;
+ RegProcedure result = InvalidOid;
+
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (HeapTupleIsValid(tp))
+ {
+ result = ((Form_pg_type) GETSTRUCT(tp))->typsubscription;
+ ReleaseSysCache(tp);
+ }
+ return result;
+}
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 5d49fe5..837f033 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -1396,3 +1396,22 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
return tupdesc;
}
+
+FunctionCallInfoData
+get_slice_arguments(FunctionCallInfo fcinfo, int begin, int end)
+{
+ FunctionCallInfoData sliced_fcinfo;
+ int i;
+
+ InitFunctionCallInfoData(sliced_fcinfo, fcinfo->flinfo,
+ fcinfo->nargs - 1, fcinfo->fncollation,
+ NULL, NULL);
+
+ for(i = begin; i < end; i++)
+ {
+ sliced_fcinfo.arg[i - begin] = fcinfo->arg[i];
+ sliced_fcinfo.argnull[i - begin] = false;
+ }
+
+ return sliced_fcinfo;
+}
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index e57b81c..23bf824 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -143,7 +143,7 @@ typedef FormData_pg_class *Form_pg_class;
* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId;
* similarly, "1" in relminmxid stands for FirstMultiXactId
*/
-DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f f f t n 3 1 _null_ _null_ ));
+DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 31 0 t f f f f f f t n 3 1 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 21 0 f f f f f f f t n 3 1 _null_ _null_ ));
DESCR("");
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index e2d08ba..4796d22 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -5344,6 +5344,12 @@ DESCR("pg_controldata recovery state information as a function");
DATA(insert OID = 3444 ( pg_control_init PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,23,23,23,23,23,23,23,16,16,16,23}" "{o,o,o,o,o,o,o,o,o,o,o,o,o}" "{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,bigint_timestamps,float4_pass_by_value,float8_pass_by_value,data_page_checksum_version}" _null_ _null_ pg_control_init _null_ _null_ _null_ ));
DESCR("pg_controldata init state information as a function");
+/* type subscription support */
+DATA(insert OID = 3343 ( jsonb_subscription PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 "2281" "2281" _null_ _null_ _null_ _null_ _null_ jsonb_subscription _null_ _null_ _null_ ));
+DESCR("Jsonb subscription logic");
+DATA(insert OID = 3344 ( array_subscription PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 "2281" "2281" _null_ _null_ _null_ _null_ _null_ array_subscription _null_ _null_ _null_ ));
+DESCR("Array subscription logic");
+
/*
* Symbolic values for provolatile column: these indicate whether the result
* of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 162239c..b54d136 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -199,6 +199,12 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO
*/
Oid typcollation;
+ /*
+ * Type specific subscription logic. If typsubscription is none, it means
+ * that this type doesn't support subscription.
+ */
+ regproc typsubscription;
+
#ifdef CATALOG_VARLEN /* variable-length fields start here */
/*
@@ -236,7 +242,7 @@ typedef FormData_pg_type *Form_pg_type;
* compiler constants for pg_type
* ----------------
*/
-#define Natts_pg_type 30
+#define Natts_pg_type 31
#define Anum_pg_type_typname 1
#define Anum_pg_type_typnamespace 2
#define Anum_pg_type_typowner 3
@@ -264,10 +270,10 @@ typedef FormData_pg_type *Form_pg_type;
#define Anum_pg_type_typtypmod 25
#define Anum_pg_type_typndims 26
#define Anum_pg_type_typcollation 27
-#define Anum_pg_type_typdefaultbin 28
-#define Anum_pg_type_typdefault 29
-#define Anum_pg_type_typacl 30
-
+#define Anum_pg_type_typsubscription 28
+#define Anum_pg_type_typdefaultbin 29
+#define Anum_pg_type_typdefault 30
+#define Anum_pg_type_typacl 31
/* ----------------
* initial contents of pg_type
@@ -283,94 +289,94 @@ typedef FormData_pg_type *Form_pg_type;
*/
/* OIDS 1 - 99 */
-DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("boolean, 'true'/'false'");
#define BOOLOID 16
-DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("variable-length string, binary values escaped");
#define BYTEAOID 17
-DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("single character");
#define CHAROID 18
-DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("63-byte type for storing system identifiers");
#define NAMEOID 19
-DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("~18 digit integer, 8-byte storage");
#define INT8OID 20
-DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("-32 thousand to 32 thousand, 2-byte storage");
#define INT2OID 21
-DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("array of int2, used in system tables");
#define INT2VECTOROID 22
-DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("-2 billion to 2 billion integer, 4-byte storage");
#define INT4OID 23
-DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered procedure");
#define REGPROCOID 24
-DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("variable-length string, no limit specified");
#define TEXTOID 25
-DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("object identifier(oid), maximum 4 billion");
#define OIDOID 26
-DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("(block, offset), physical location of tuple");
#define TIDOID 27
-DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("transaction id");
#define XIDOID 28
-DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("command identifier type, sequence in transaction id");
#define CIDOID 29
-DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("array of oids, used in system tables");
#define OIDVECTOROID 30
/* hand-built rowtype entries for bootstrapped catalogs */
/* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */
-DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
+DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
+DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
+DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
/* OIDS 100 - 199 */
-DATA(insert OID = 114 ( json PGNSP PGUID -1 f b U f t \054 0 0 199 json_in json_out json_recv json_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 114 ( json PGNSP PGUID -1 f b U f t \054 0 0 199 json_in json_out json_recv json_send - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define JSONOID 114
-DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("XML content");
#define XMLOID 142
-DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 199 ( _json PGNSP PGUID -1 f b A f t \054 0 114 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 199 ( _json PGNSP PGUID -1 f b A f t \054 0 114 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
-DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("string representing an internal node tree");
#define PGNODETREEOID 194
-DATA(insert OID = 32 ( pg_ddl_command PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 pg_ddl_command_in pg_ddl_command_out pg_ddl_command_recv pg_ddl_command_send - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 32 ( pg_ddl_command PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 pg_ddl_command_in pg_ddl_command_out pg_ddl_command_recv pg_ddl_command_send - - - ALIGNOF_POINTER p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("internal type for passing CollectedCommand");
#define PGDDLCOMMANDOID 32
/* OIDS 200 - 299 */
-DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("storage manager");
/* OIDS 300 - 399 */
@@ -380,276 +386,276 @@ DESCR("storage manager");
/* OIDS 500 - 599 */
/* OIDS 600 - 699 */
-DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("geometric point '(x, y)'");
#define POINTOID 600
-DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("geometric line segment '(pt1,pt2)'");
#define LSEGOID 601
-DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("geometric path '(pt1,...)'");
#define PATHOID 602
-DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("geometric box '(lower left,upper right)'");
#define BOXOID 603
-DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("geometric polygon '(pt1,...)'");
#define POLYGONOID 604
-DATA(insert OID = 628 ( line PGNSP PGUID 24 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 628 ( line PGNSP PGUID 24 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("geometric line");
#define LINEOID 628
-DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* OIDS 700 - 799 */
-DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("single-precision floating point number, 4-byte storage");
#define FLOAT4OID 700
-DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("double-precision floating point number, 8-byte storage");
#define FLOAT8OID 701
-DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("absolute, limited-range date and time (Unix system time)");
#define ABSTIMEOID 702
-DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("relative, limited-range time interval (Unix delta time)");
#define RELTIMEOID 703
-DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("(abstime,abstime), time interval");
#define TINTERVALOID 704
-DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("");
#define UNKNOWNOID 705
-DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("geometric circle '(center,radius)'");
#define CIRCLEOID 718
-DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("monetary amounts, $d,ddd.cc");
#define CASHOID 790
-DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* OIDS 800 - 899 */
-DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDROID 829
-DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("IP address/netmask, host address, netmask optional");
#define INETOID 869
-DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("network IP address/netmask, network address");
#define CIDROID 650
/* OIDS 900 - 999 */
/* OIDS 1000 - 1099 */
-DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
#define INT2ARRAYOID 1005
-DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
#define INT4ARRAYOID 1007
-DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 100 array_subscription _null_ _null_ _null_ ));
#define TEXTARRAYOID 1009
-DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
#define OIDARRAYOID 1028
-DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
-DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
-DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout array_typanalyze i x f 0 -1 0 100 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout array_typanalyze i x f 0 -1 0 100 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
#define FLOAT4ARRAYOID 1021
-DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("access control list");
#define ACLITEMOID 1033
-DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
#define CSTRINGARRAYOID 1263
-DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("char(length), blank-padded string, fixed storage length");
#define BPCHAROID 1042
-DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("varchar(length), non-blank-padded string, variable storage length");
#define VARCHAROID 1043
-DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("date");
#define DATEOID 1082
-DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("time of day");
#define TIMEOID 1083
/* OIDS 1100 - 1199 */
-DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("date and time");
#define TIMESTAMPOID 1114
-DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("date and time with time zone");
#define TIMESTAMPTZOID 1184
-DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("@ <number> <units>, time interval");
#define INTERVALOID 1186
-DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* OIDS 1200 - 1299 */
-DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("time of day with time zone");
#define TIMETZOID 1266
-DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* OIDS 1500 - 1599 */
-DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("fixed-length bit string");
#define BITOID 1560
-DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("variable-length bit string");
#define VARBITOID 1562
-DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* OIDS 1600 - 1699 */
/* OIDS 1700 - 1799 */
-DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("numeric(precision, decimal), arbitrary precision number");
#define NUMERICOID 1700
-DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("reference to cursor (portal name)");
#define REFCURSOROID 1790
/* OIDS 2200 - 2299 */
-DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
-DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered procedure (with args)");
#define REGPROCEDUREOID 2202
-DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered operator");
#define REGOPEROID 2203
-DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered operator (with args)");
#define REGOPERATOROID 2204
-DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered class");
#define REGCLASSOID 2205
-DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered type");
#define REGTYPEOID 2206
-DATA(insert OID = 4096 ( regrole PGNSP PGUID 4 t b N f t \054 0 0 4097 regrolein regroleout regrolerecv regrolesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4096 ( regrole PGNSP PGUID 4 t b N f t \054 0 0 4097 regrolein regroleout regrolerecv regrolesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered role");
#define REGROLEOID 4096
-DATA(insert OID = 4089 ( regnamespace PGNSP PGUID 4 t b N f t \054 0 0 4090 regnamespacein regnamespaceout regnamespacerecv regnamespacesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4089 ( regnamespace PGNSP PGUID 4 t b N f t \054 0 0 4090 regnamespacein regnamespaceout regnamespacerecv regnamespacesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered namespace");
#define REGNAMESPACEOID 4089
-DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
#define REGTYPEARRAYOID 2211
-DATA(insert OID = 4097 ( _regrole PGNSP PGUID -1 f b A f t \054 0 4096 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 4090 ( _regnamespace PGNSP PGUID -1 f b A f t \054 0 4089 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4097 ( _regrole PGNSP PGUID -1 f b A f t \054 0 4096 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 4090 ( _regnamespace PGNSP PGUID -1 f b A f t \054 0 4089 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* uuid */
-DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("UUID datatype");
#define UUIDOID 2950
-DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* pg_lsn */
-DATA(insert OID = 3220 ( pg_lsn PGNSP PGUID 8 FLOAT8PASSBYVAL b U f t \054 0 0 3221 pg_lsn_in pg_lsn_out pg_lsn_recv pg_lsn_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3220 ( pg_lsn PGNSP PGUID 8 FLOAT8PASSBYVAL b U f t \054 0 0 3221 pg_lsn_in pg_lsn_out pg_lsn_recv pg_lsn_send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("PostgreSQL LSN datatype");
#define LSNOID 3220
-DATA(insert OID = 3221 ( _pg_lsn PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3221 ( _pg_lsn PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* text search */
-DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("text representation for text search");
#define TSVECTOROID 3614
-DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("GiST index internal text representation for text search");
#define GTSVECTOROID 3642
-DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("query representation for text search");
#define TSQUERYOID 3615
-DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered text search configuration");
#define REGCONFIGOID 3734
-DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered text search dictionary");
#define REGDICTIONARYOID 3769
-DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* jsonb */
-DATA(insert OID = 3802 ( jsonb PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3802 ( jsonb PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 jsonb_subscription _null_ _null_ _null_ ));
DESCR("Binary JSON");
#define JSONBOID 3802
-DATA(insert OID = 3807 ( _jsonb PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3807 ( _jsonb PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
-DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("txid snapshot");
-DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* range types */
-DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of integers");
#define INT4RANGEOID 3904
-DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of numerics");
-DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of timestamps without time zone");
-DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of timestamps with time zone");
-DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of dates");
-DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of bigints");
-DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/*
* pseudo-types
@@ -664,41 +670,41 @@ DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 arr
* but there is now support for it in records and arrays. Perhaps we should
* just treat it as a regular base type?
*/
-DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define RECORDOID 2249
-DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
#define RECORDARRAYOID 2287
-DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define CSTRINGOID 2275
-DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYOID 2276
-DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYARRAYOID 2277
-DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out void_recv void_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out void_recv void_send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define VOIDOID 2278
-DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define TRIGGEROID 2279
-DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define EVTTRIGGEROID 3838
-DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
-DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define INTERNALOID 2281
-DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define OPAQUEOID 2282
-DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYELEMENTOID 2283
-DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYNONARRAYOID 2776
-DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYENUMOID 3500
-DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define FDW_HANDLEROID 3115
-DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define INDEX_AM_HANDLEROID 325
-DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define TSM_HANDLEROID 3310
-DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYRANGEOID 3831
diff --git a/src/include/catalog/pg_type_fn.h b/src/include/catalog/pg_type_fn.h
index d7bbfdb..947cad0 100644
--- a/src/include/catalog/pg_type_fn.h
+++ b/src/include/catalog/pg_type_fn.h
@@ -52,7 +52,8 @@ extern ObjectAddress TypeCreate(Oid newTypeOid,
int32 typeMod,
int32 typNDims,
bool typeNotNull,
- Oid typeCollation);
+ Oid typeCollation,
+ Oid subscriptionProcedure);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
@@ -70,6 +71,7 @@ extern void GenerateTypeDependencies(Oid typeNamespace,
bool isImplicitArray,
Oid baseType,
Oid typeCollation,
+ Oid subscriptionProcedure,
Node *defaultExpr,
bool rebuild);
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index e73a824..760d45f 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -183,6 +183,7 @@ extern TupleDesc build_function_result_tupdesc_d(Datum proallargtypes,
Datum proargmodes,
Datum proargnames);
extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
+extern FunctionCallInfoData get_slice_arguments(FunctionCallInfo fcinfo, int begin, int end);
/*----------
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 4fa3661..1f548ea 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -644,25 +644,46 @@ typedef struct WindowFuncExprState
} WindowFuncExprState;
/* ----------------
- * ArrayRefExprState node
+ * SubscriptionRefExprState node
*
* Note: array types can be fixed-length (typlen > 0), but only when the
* element type is itself fixed-length. Otherwise they are varlena structures
* and have typlen = -1. In any case, an array type is never pass-by-value.
* ----------------
*/
-typedef struct ArrayRefExprState
+typedef struct SubscriptionRefExprState
{
ExprState xprstate;
List *refupperindexpr; /* states for child nodes */
List *reflowerindexpr;
ExprState *refexpr;
ExprState *refassgnexpr;
- int16 refattrlength; /* typlen of array type */
- int16 refelemlength; /* typlen of the array element type */
+ int16 refattrlength; /* typlen of container type */
+ int16 refelemlength; /* typlen of the container element type */
bool refelembyval; /* is the element type pass-by-value? */
char refelemalign; /* typalign of the element type */
-} ArrayRefExprState;
+} SubscriptionRefExprState;
+
+/* ---------------------------------
+ * Subscription exec information
+ *
+ * It contains all information which is required to perform type-specific data
+ * extraction or modification. This information will be gathered in
+ * `ExecEvalSubscriptionRef` function and passed to `typsubscription`
+ * procedure.
+ * ---------------------------------
+ */
+typedef struct SubscriptionExecData
+{
+ ExprContext *xprcontext; /* econtext for subscription */
+ bool *isNull;
+ Datum *upper; /* upper boundary for subscription */
+ Datum *lower; /* lower boundary for subscription */
+ bool *upperProvided;
+ bool *lowerProvided;
+ Datum containerSource;
+ int indexprNumber;
+} SubscriptionExecData;
/* ----------------
* FuncExprState node
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 88297bb..a80285b 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -140,7 +140,7 @@ typedef enum NodeTag
T_Aggref,
T_GroupingFunc,
T_WindowFunc,
- T_ArrayRef,
+ T_SubscriptionRef,
T_FuncExpr,
T_NamedArgExpr,
T_OpExpr,
@@ -194,7 +194,7 @@ typedef enum NodeTag
T_AggrefExprState,
T_GroupingFuncExprState,
T_WindowFuncExprState,
- T_ArrayRefExprState,
+ T_SubscriptionRefExprState,
T_FuncExprState,
T_ScalarArrayOpExprState,
T_BoolExprState,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 65510b0..6b30b90 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -350,18 +350,18 @@ typedef struct WindowFunc
} WindowFunc;
/* ----------------
- * ArrayRef: describes an array subscripting operation
- *
- * An ArrayRef can describe fetching a single element from an array,
- * fetching a subarray (array slice), storing a single element into
- * an array, or storing a slice. The "store" cases work with an
- * initial array value and a source value that is inserted into the
- * appropriate part of the array; the result of the operation is an
- * entire new modified array value.
- *
- * If reflowerindexpr = NIL, then we are fetching or storing a single array
- * element at the subscripts given by refupperindexpr. Otherwise we are
- * fetching or storing an array slice, that is a rectangular subarray
+ * SubscriptionRef: describes a subscripting operation over a container
+ *
+ * An SubscriptionRef can describe fetching a single element from a container,
+ * fetching a part of container (e.g. array slice), storing a single element into
+ * a container, or storing a slice. The "store" cases work with an
+ * initial container value and a source value that is inserted into the
+ * appropriate part of the container; the result of the operation is an
+ * entire new modified container value.
+ *
+ * If reflowerindexpr = NIL, then we are fetching or storing a single container
+ * element at the subscripts given by refupperindexpr. Otherwise we are
+ * fetching or storing a container slice, that is a rectangular subcontainer
* with lower and upper bounds given by the index expressions.
* reflowerindexpr must be the same length as refupperindexpr when it
* is not NIL.
@@ -373,27 +373,27 @@ typedef struct WindowFunc
* element; but it is the array type when doing subarray fetch or either
* type of store.
*
- * Note: for the cases where an array is returned, if refexpr yields a R/W
- * expanded array, then the implementation is allowed to modify that object
+ * Note: for the cases where a container is returned, if refexpr yields a R/W
+ * expanded container, then the implementation is allowed to modify that object
* in-place and return the same object.)
* ----------------
*/
-typedef struct ArrayRef
+typedef struct SubscriptionRef
{
Expr xpr;
- Oid refarraytype; /* type of the array proper */
- Oid refelemtype; /* type of the array elements */
- int32 reftypmod; /* typmod of the array (and elements too) */
- Oid refcollid; /* OID of collation, or InvalidOid if none */
- List *refupperindexpr;/* expressions that evaluate to upper array
- * indexes */
- List *reflowerindexpr;/* expressions that evaluate to lower array
- * indexes, or NIL for single array element */
- Expr *refexpr; /* the expression that evaluates to an array
- * value */
- Expr *refassgnexpr; /* expression for the source value, or NULL if
- * fetch */
-} ArrayRef;
+ Oid refcontainertype; /* type of the container proper */
+ Oid refelemtype; /* type of the container elements */
+ int32 reftypmod; /* typmod of the container (and elements too) */
+ Oid refcollid; /* OID of collation, or InvalidOid if none */
+ List *refupperindexpr; /* expressions that evaluate to upper container
+ * indexes */
+ List *reflowerindexpr; /* expressions that evaluate to lower container
+ * indexes, or NIL for single container element */
+ Expr *refexpr; /* the expression that evaluates to an container
+ * value */
+ Expr *refassgnexpr; /* expression for the source value, or NULL if
+ * fetch */
+} SubscriptionRef;
/*
* CoercionContext - distinguishes the allowed set of type casts
@@ -734,7 +734,7 @@ typedef struct FieldSelect
*
* FieldStore represents the operation of modifying one field in a tuple
* value, yielding a new tuple value (the input is not touched!). Like
- * the assign case of ArrayRef, this is used to implement UPDATE of a
+ * the assign case of SubscriptionRef, this is used to implement UPDATE of a
* portion of a column.
*
* A single FieldStore can actually represent updates of several different
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 6633586..49418ae 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -224,12 +224,17 @@ extern void cancel_parser_errposition_callback(ParseCallbackState *pcbstate);
extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno,
int location);
-extern Oid transformArrayType(Oid *arrayType, int32 *arrayTypmod);
-extern ArrayRef *transformArraySubscripts(ParseState *pstate,
- Node *arrayBase,
- Oid arrayType,
+extern Oid transformArrayType(Oid *containerType, int32 *containerTypmod);
+
+/* Type of a stage in case of type-specific subscription procedure */
+#define SBS_VALIDATION 0x0001
+#define SBS_EXEC 0x0002
+
+extern SubscriptionRef *transformContainerSubscripts(ParseState *pstate,
+ Node *containerBase,
+ Oid containerType,
Oid elementType,
- int32 arrayTypMod,
+ int32 containerTypMod,
List *indirection,
Node *assignFrom);
extern Const *make_const(ParseState *pstate, Value *value, int location);
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index 6164f11..64dd619 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -502,4 +502,9 @@ extern Datum array_positions(PG_FUNCTION_ARGS);
*/
extern Datum array_typanalyze(PG_FUNCTION_ARGS);
+/*
+ * prototypes for functions with array subscription logic
+ */
+extern Datum array_subscription(PG_FUNCTION_ARGS);
+
#endif /* ARRAY_H */
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index fa52afc..5bc3afe 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -352,6 +352,7 @@ extern Datum jsonb_typeof(PG_FUNCTION_ARGS);
/* generator routines */
extern Datum to_jsonb(PG_FUNCTION_ARGS);
+extern JsonbValue *to_jsonb_worker(Datum source, Oid source_type);
extern Datum jsonb_build_object(PG_FUNCTION_ARGS);
extern Datum jsonb_build_object_noargs(PG_FUNCTION_ARGS);
@@ -425,6 +426,7 @@ extern JsonbValue *pushJsonbValue(JsonbParseState **pstate,
extern JsonbIterator *JsonbIteratorInit(JsonbContainer *container);
extern JsonbIteratorToken JsonbIteratorNext(JsonbIterator **it, JsonbValue *val,
bool skipNested);
+extern JsonbValue *JsonbToJsonbValue(Jsonb *jsonb, JsonbValue *val);
extern Jsonb *JsonbValueToJsonb(JsonbValue *val);
extern bool JsonbDeepContains(JsonbIterator **val,
JsonbIterator **mContained);
@@ -437,4 +439,7 @@ extern char *JsonbToCStringIndent(StringInfo out, JsonbContainer *in,
int estimated_len);
+/* Jsonb subscription logic */
+extern Datum jsonb_subscription(PG_FUNCTION_ARGS);
+
#endif /* __JSONB_H__ */
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index dcb8980..474957d 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -159,6 +159,7 @@ extern void free_attstatsslot(Oid atttype,
extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
+extern RegProcedure get_typsubscription(Oid typid);
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
/* type_is_array_domain accepts both plain arrays and domains over arrays */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 470cf93..692ffbe 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -4742,7 +4742,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
/*
* Evaluate the subscripts, switch into left-to-right order.
- * Like ExecEvalArrayRef(), complain if any subscript is null.
+ * Like ExecEvalSubscriptionRef(), complain if any subscript is null.
*/
for (i = 0; i < nsubscripts; i++)
{
@@ -4790,7 +4790,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
* fixed-length array types we skip the assignment. We can't
* support assignment of a null entry into a fixed-length
* array, either, so that's a no-op too. This is all ugly but
- * corresponds to the current behavior of ExecEvalArrayRef().
+ * corresponds to the current behavior of ExecEvalSubscriptionRef().
*/
if (arrayelem->arraytyplen > 0 && /* fixed-length array? */
(oldarrayisnull || isNull))
@@ -6498,9 +6498,9 @@ exec_simple_check_node(Node *node)
case T_Param:
return TRUE;
- case T_ArrayRef:
+ case T_SubscriptionRef:
{
- ArrayRef *expr = (ArrayRef *) node;
+ SubscriptionRef *expr = (SubscriptionRef *) node;
if (!exec_simple_check_node((Node *) expr->refupperindexpr))
return FALSE;
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
index a6d25de..0d6baad 100644
--- a/src/test/regress/expected/jsonb.out
+++ b/src/test/regress/expected/jsonb.out
@@ -3441,3 +3441,211 @@ HINT: Try using the function jsonb_set to replace key value.
select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"', true);
ERROR: cannot replace existing key
HINT: Try using the function jsonb_set to replace key value.
+-- jsonb subscript
+select ('123'::jsonb)['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('123'::jsonb)[0];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": 1}'::jsonb)['a'];
+ jsonb
+-------
+ 1
+(1 row)
+
+select ('{"a": 1}'::jsonb)[0];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": 1}'::jsonb)['not_exist'];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[0];
+ jsonb
+-------
+ 1
+(1 row)
+
+select ('[1, "2", null]'::jsonb)['1'];
+ jsonb
+-------
+ "2"
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[1.0];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[2];
+ jsonb
+-------
+ null
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[3];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[-2];
+ jsonb
+-------
+ "2"
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[1]['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[1][0];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['b'];
+ jsonb
+-------
+ "c"
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'];
+ jsonb
+-----------
+ [1, 2, 3]
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'][1];
+ jsonb
+-------
+ 2
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d']['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1'];
+ jsonb
+---------------
+ {"a2": "aaa"}
+(1 row)
+
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2'];
+ jsonb
+-------
+ "aaa"
+(1 row)
+
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']['a3'];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'];
+ jsonb
+-----------------------
+ ["aaa", "bbb", "ccc"]
+(1 row)
+
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'][2];
+ jsonb
+-------
+ "ccc"
+(1 row)
+
+create TEMP TABLE test_jsonb_subscript (
+ id int,
+ test_json jsonb
+);
+insert into test_jsonb_subscript values
+(1, '{}'), -- empty jsonb
+(2, '{"key": "value"}'); -- jsonb with data
+-- update empty jsonb
+update test_jsonb_subscript set test_json['a'] = 1 where id = 1;
+select * from test_jsonb_subscript;
+ id | test_json
+----+------------------
+ 2 | {"key": "value"}
+ 1 | {"a": 1}
+(2 rows)
+
+-- update jsonb with some data
+update test_jsonb_subscript set test_json['a'] = 1 where id = 2;
+select * from test_jsonb_subscript;
+ id | test_json
+----+--------------------------
+ 1 | {"a": 1}
+ 2 | {"a": 1, "key": "value"}
+(2 rows)
+
+-- replace jsonb
+update test_jsonb_subscript set test_json['a'] = 'test';
+select * from test_jsonb_subscript;
+ id | test_json
+----+-------------------------------
+ 1 | {"a": "test"}
+ 2 | {"a": "test", "key": "value"}
+(2 rows)
+
+-- replace by object
+update test_jsonb_subscript set test_json['a'] = '{"b": 1}'::jsonb;
+select * from test_jsonb_subscript;
+ id | test_json
+----+---------------------------------
+ 1 | {"a": {"b": 1}}
+ 2 | {"a": {"b": 1}, "key": "value"}
+(2 rows)
+
+-- replace by array
+update test_jsonb_subscript set test_json['a'] = '[1, 2, 3]'::jsonb;
+select * from test_jsonb_subscript;
+ id | test_json
+----+----------------------------------
+ 1 | {"a": [1, 2, 3]}
+ 2 | {"a": [1, 2, 3], "key": "value"}
+(2 rows)
+
+-- use jsonb subscription in where clause
+select * from test_jsonb_subscript where test_json['key'] = '"value"';
+ id | test_json
+----+----------------------------------
+ 2 | {"a": [1, 2, 3], "key": "value"}
+(1 row)
+
+select * from test_jsonb_subscript where test_json['key_doesnt_exists'] = '"value"';
+ id | test_json
+----+-----------
+(0 rows)
+
+select * from test_jsonb_subscript where test_json['key'] = '"wrong_value"';
+ id | test_json
+----+-----------
+(0 rows)
+
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
index b84bd70..3ec66ed 100644
--- a/src/test/regress/sql/jsonb.sql
+++ b/src/test/regress/sql/jsonb.sql
@@ -867,3 +867,63 @@ select jsonb_insert('{"a": {"b": "value"}}', '{a, c}', '"new_value"', true);
select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"');
select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"', true);
+
+-- jsonb subscript
+select ('123'::jsonb)['a'];
+select ('123'::jsonb)[0];
+select ('{"a": 1}'::jsonb)['a'];
+select ('{"a": 1}'::jsonb)[0];
+select ('{"a": 1}'::jsonb)['not_exist'];
+select ('[1, "2", null]'::jsonb)['a'];
+select ('[1, "2", null]'::jsonb)[0];
+select ('[1, "2", null]'::jsonb)['1'];
+select ('[1, "2", null]'::jsonb)[1.0];
+select ('[1, "2", null]'::jsonb)[2];
+select ('[1, "2", null]'::jsonb)[3];
+select ('[1, "2", null]'::jsonb)[-2];
+select ('[1, "2", null]'::jsonb)[1]['a'];
+select ('[1, "2", null]'::jsonb)[1][0];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['b'];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'][1];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d']['a'];
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1'];
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2'];
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']['a3'];
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'];
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'][2];
+
+create TEMP TABLE test_jsonb_subscript (
+ id int,
+ test_json jsonb
+);
+
+insert into test_jsonb_subscript values
+(1, '{}'), -- empty jsonb
+(2, '{"key": "value"}'); -- jsonb with data
+
+-- update empty jsonb
+update test_jsonb_subscript set test_json['a'] = 1 where id = 1;
+select * from test_jsonb_subscript;
+
+-- update jsonb with some data
+update test_jsonb_subscript set test_json['a'] = 1 where id = 2;
+select * from test_jsonb_subscript;
+
+-- replace jsonb
+update test_jsonb_subscript set test_json['a'] = 'test';
+select * from test_jsonb_subscript;
+
+-- replace by object
+update test_jsonb_subscript set test_json['a'] = '{"b": 1}'::jsonb;
+select * from test_jsonb_subscript;
+
+-- replace by array
+update test_jsonb_subscript set test_json['a'] = '[1, 2, 3]'::jsonb;
+select * from test_jsonb_subscript;
+
+-- use jsonb subscription in where clause
+select * from test_jsonb_subscript where test_json['key'] = '"value"';
+select * from test_jsonb_subscript where test_json['key_doesnt_exists'] = '"value"';
+select * from test_jsonb_subscript where test_json['key'] = '"wrong_value"';
+
diff --git a/src/tutorial/Makefile b/src/tutorial/Makefile
index 16dc390..cf8082a 100644
--- a/src/tutorial/Makefile
+++ b/src/tutorial/Makefile
@@ -13,8 +13,8 @@
#
#-------------------------------------------------------------------------
-MODULES = complex funcs
-DATA_built = advanced.sql basics.sql complex.sql funcs.sql syscat.sql
+MODULES = complex funcs subscription
+DATA_built = advanced.sql basics.sql complex.sql funcs.sql syscat.sql subscription.sql
ifdef NO_PGXS
subdir = src/tutorial
diff --git a/src/tutorial/subscription.c b/src/tutorial/subscription.c
new file mode 100644
index 0000000..4abe3a7
--- /dev/null
+++ b/src/tutorial/subscription.c
@@ -0,0 +1,230 @@
+/*
+ * src/tutorial/subscription.c
+ *
+ ******************************************************************************
+ This file contains routines that can be bound to a Postgres backend and
+ called by the backend in the process of processing queries. The calling
+ format for these routines is dictated by Postgres architecture.
+******************************************************************************/
+
+#include "postgres.h"
+
+#include "catalog/pg_type.h"
+#include "executor/executor.h"
+#include "nodes/execnodes.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_node.h"
+#include "utils/array.h"
+#include "fmgr.h"
+#include "funcapi.h"
+
+PG_MODULE_MAGIC;
+
+typedef struct Custom
+{
+ int first;
+ int second;
+} Custom;
+
+
+/*****************************************************************************
+ * Input/Output functions
+ *****************************************************************************/
+
+PG_FUNCTION_INFO_V1(custom_in);
+
+Datum
+custom_in(PG_FUNCTION_ARGS)
+{
+ char *str = PG_GETARG_CSTRING(0);
+ int firstValue,
+ secondValue;
+ Custom *result;
+
+ if (sscanf(str, " ( %d , %d )", &firstValue, &secondValue) != 2)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid input syntax for complex: \"%s\"",
+ str)));
+
+
+ result = (Custom *) palloc(sizeof(Custom));
+ result->first = firstValue;
+ result->second = secondValue;
+ PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(custom_out);
+
+Datum
+custom_out(PG_FUNCTION_ARGS)
+{
+ Custom *custom = (Custom *) PG_GETARG_POINTER(0);
+ char *result;
+
+ result = psprintf("(%d, %d)", custom->first, custom->second);
+ PG_RETURN_CSTRING(result);
+}
+
+/*****************************************************************************
+ * Custom subscription logic functions
+ *****************************************************************************/
+
+Datum
+custom_subscription_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptionRefExprState *sbstate = (SubscriptionRefExprState *) PG_GETARG_POINTER(0);
+ SubscriptionExecData *sbsdata = (SubscriptionExecData *) PG_GETARG_POINTER(1);
+ SubscriptionRef *custom_ref = (SubscriptionRef *) sbstate->xprstate.expr;
+ Custom *result = (Custom *) sbsdata->containerSource;
+ bool *is_null = sbsdata->isNull;
+ bool is_assignment = (custom_ref->refassgnexpr != NULL);
+
+ int index;
+
+ if (sbsdata->indexprNumber != 1)
+ ereport(ERROR, (errmsg("custom does not support nested subscription")));
+
+ index = DatumGetInt32(sbsdata->upper[0]);
+
+ if (is_assignment)
+ {
+ ExprContext *econtext = sbsdata->xprcontext;
+ Datum sourceData;
+ Datum save_datum;
+ bool save_isNull;
+ bool eisnull;
+
+ /*
+ * We might have a nested-assignment situation, in which the
+ * refassgnexpr is itself a FieldStore or SubscriptionRef that needs to
+ * obtain and modify the previous value of the array element or slice
+ * being replaced. If so, we have to extract that value from the
+ * array and pass it down via the econtext's caseValue. It's safe to
+ * reuse the CASE mechanism because there cannot be a CASE between
+ * here and where the value would be needed, and an array assignment
+ * can't be within a CASE either. (So saving and restoring the
+ * caseValue is just paranoia, but let's do it anyway.)
+ *
+ * Since fetching the old element might be a nontrivial expense, do it
+ * only if the argument appears to actually need it.
+ */
+ save_datum = econtext->caseValue_datum;
+ save_isNull = econtext->caseValue_isNull;
+
+ /*
+ * Evaluate the value to be assigned into the container.
+ */
+ sourceData = ExecEvalExpr(sbstate->refassgnexpr,
+ econtext,
+ &eisnull,
+ NULL);
+
+ econtext->caseValue_datum = save_datum;
+ econtext->caseValue_isNull = save_isNull;
+
+ /*
+ * For an assignment to a fixed-length array type, both the original
+ * array and the value to be assigned into it must be non-NULL, else
+ * we punt and return the original array.
+ */
+ if (sbstate->refattrlength > 0) /* fixed-length container? */
+ if (eisnull || *is_null)
+ return sbsdata->containerSource;
+
+ /*
+ * For assignment to varlena container, we handle a NULL original array
+ * by substituting an empty (zero-dimensional) array; insertion of the
+ * new element will result in a singleton array value. It does not
+ * matter whether the new element is NULL.
+ */
+ if (*is_null)
+ {
+ sbsdata->containerSource =
+ PointerGetDatum(construct_empty_array(custom_ref->refelemtype));
+ *is_null = false;
+ }
+
+ if (index == 1)
+ result->first = DatumGetInt32(sourceData);
+ else
+ result->second = DatumGetInt32(sourceData);
+
+ PG_RETURN_POINTER(result);
+ }
+ else
+ {
+ if (index == 1)
+ PG_RETURN_INT32(result->first);
+ else
+ PG_RETURN_INT32(result->second);
+ }
+}
+
+Datum
+custom_subscription_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptionRef *sbsref = (SubscriptionRef *) PG_GETARG_POINTER(0);
+ ParseState *pstate = (ParseState *) PG_GETARG_POINTER(1);
+ List *upperIndexpr = NIL;
+ ListCell *l;
+
+ if (sbsref->reflowerindexpr != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("custom subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *)lfirst(sbsref->reflowerindexpr->head))))));
+
+ foreach(l, sbsref->refupperindexpr)
+ {
+ Node *subexpr = (Node *) lfirst(l);
+
+ Assert(subexpr != NULL);
+
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("custom subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *) lfirst(sbsref->refupperindexpr->head))))));
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ INT4OID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("custom subscript must have int type"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ }
+
+ sbsref->refupperindexpr = upperIndexpr;
+ sbsref->refelemtype = INT4OID;
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+PG_FUNCTION_INFO_V1(custom_subscription);
+
+Datum
+custom_subscription(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1,
+ fcinfo->nargs);
+
+ if (op_type & SBS_VALIDATION)
+ return custom_subscription_prepare(&target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return custom_subscription_evaluate(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscription function: %d", op_type);
+}
diff --git a/src/tutorial/subscription.source b/src/tutorial/subscription.source
new file mode 100644
index 0000000..7479fa7
--- /dev/null
+++ b/src/tutorial/subscription.source
@@ -0,0 +1,71 @@
+---------------------------------------------------------------------------
+--
+-- subscription.sql-
+-- This file shows how to create a new subscription procedure for
+-- user-defined type.
+--
+--
+-- Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+-- Portions Copyright (c) 1994, Regents of the University of California
+--
+-- src/tutorial/subscription.source
+--
+---------------------------------------------------------------------------
+
+-----------------------------
+-- Creating a new type:
+-- We are going to create a new type called 'complex' which represents
+-- complex numbers.
+-- A user-defined type must have an input and an output function, and
+-- optionally can have binary input and output functions. All of these
+-- are usually user-defined C functions.
+-----------------------------
+
+-- Assume the user defined functions are in /home/erthalion/programms/postgresql-master/src/tutorial/complex$DLSUFFIX
+-- (we do not want to assume this is in the dynamic loader search path).
+-- Look at $PWD/complex.c for the source. Note that we declare all of
+-- them as STRICT, so we do not need to cope with NULL inputs in the
+-- C code. We also mark them IMMUTABLE, since they always return the
+-- same outputs given the same inputs.
+
+-- the input function 'complex_in' takes a null-terminated string (the
+-- textual representation of the type) and turns it into the internal
+-- (in memory) representation. You will get a message telling you 'complex'
+-- does not exist yet but that's okay.
+
+CREATE FUNCTION custom_in(cstring)
+ RETURNS custom
+ AS '_OBJWD_/subscription'
+ LANGUAGE C IMMUTABLE STRICT;
+
+-- the output function 'complex_out' takes the internal representation and
+-- converts it into the textual representation.
+
+CREATE FUNCTION custom_out(custom)
+ RETURNS cstring
+ AS '_OBJWD_/subscription'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION custom_subscription(internal)
+ RETURNS internal
+ AS '_OBJWD_/subscription'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE custom (
+ internallength = 8,
+ input = custom_in,
+ output = custom_out,
+ subscription = custom_subscription
+);
+
+-- we can use it in a table
+
+CREATE TABLE test_subscription (
+ data custom,
+);
+
+INSERT INTO test_subscription VALUES ('(1, 2)');
+
+SELECT data[0] from test_subscription;
+
+UPDATE test_subscription SET data[1] = 3;
On Sat, 1 Oct 2016 16:52:34 +0700
Dmitry Dolgov <9erthalion6@gmail.com> wrote:
I've tried to compile this patch with current state of master
(commit 51c3e9fade76c12) and found out that, when configured with--enable-cassert,
it doesn't pass make check.
Thanks for the feedback. Yes, unexpectedly for me, `ExecEvalExpr` can
return expanded `jbvArray` and `jbvObject` instead `jbvBinary` in
both cases. It's interesting, that this doesn't break anything, but
obviously violates the `pushJsonbValueScalar` semantics. I don't
think `ExecEvalExpr` should be changed for jsonb, we can handle this
situation in `pushJsonbValue` instead. I've
attached a new version of patch with this modification.
Thanks for you quick responce. I've not yet thoroughly investigated new
patch, but already noticed some minor promlems:
Git complains about whitespace in this version of patch:
$ git apply ../generic_type_subscription_v2.patch
../generic_type_subscription_v2.patch:218: tab in indent.
int first;
../generic_type_subscription_v2.patch:219: tab in indent.
int second;
../generic_type_subscription_v2.patch:225: tab in indent.
SubscriptionExecData *sbsdata = (SubscriptionExecData *) PG_GETARG_POINTER(1);
../generic_type_subscription_v2.patch:226: tab in indent.
Custom *result = (Custom *) sbsdata->containerSource;
../generic_type_subscription_v2.patch:234: tab in indent.
SubscriptionRef *sbsref = (SubscriptionRef *) PG_GETARG_POINTER(0);
warning: squelched 29 whitespace errors
warning: 34 lines add whitespace errors.
It doesn't prevent me from further testing of patch, but worth noticing.
--
Victor
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Sat, Oct 1, 2016 at 12:52 PM, Dmitry Dolgov <9erthalion6@gmail.com>
wrote:
I've tried to compile this patch with current state of master (commit
51c3e9fade76c12) and found out that, when configured with--enable-cassert,
it doesn't pass make check.
Thanks for the feedback. Yes, unexpectedly for me, `ExecEvalExpr` can
return
expanded `jbvArray` and `jbvObject` instead `jbvBinary` in both cases. It's
interesting, that this doesn't break anything, but obviously violates
the `pushJsonbValueScalar` semantics. I don't think `ExecEvalExpr` should
be
changed for jsonb, we can handle this situation in `pushJsonbValue`
instead. I've
attached a new version of patch with this modification.
have you ever run 'make check' ?
=========================
53 of 168 tests failed.
=========================
Show quoted text
On 27 September 2016 at 19:08, Victor Wagner <vitus@wagner.pp.ru> wrote:
On Fri, 9 Sep 2016 18:29:23 +0700
Dmitry Dolgov <9erthalion6@gmail.com> wrote:Hi,
Regarding to the previous conversations [1], [2], here is a patch
(with some improvements from Nikita Glukhov) for the generic type
subscription. It allows
to define type-specific subscription logic for any data type and has
implementations for the array and jsonb types. There are following
changes in this
patch:I've tried to compile this patch with current state of master (commit
51c3e9fade76c12) and found out that, when configured with
--enable-cassert, it doesn't pass make check.LOG: server process (PID 4643) was terminated by signal 6: Aborted
DETAIL: Failed process was running:
update test_jsonb_subscript set test_json['a'] = '{"b":
1}'::jsonb;--
Victor--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 5 October 2016 at 03:00, Oleg Bartunov <obartunov@gmail.com> wrote:
have you ever run 'make check' ?
=========================
53 of 168 tests failed.
=========================
Sure, how else could I write tests for this feature? But right now on my
machine
everything is ok (the same for `make installcheck`):
$ make check
....
=======================
All 168 tests passed.
=======================
On Wed, Oct 5, 2016 at 6:48 AM, Dmitry Dolgov <9erthalion6@gmail.com> wrote:
On 5 October 2016 at 03:00, Oleg Bartunov <obartunov@gmail.com> wrote:
have you ever run 'make check' ?
=========================
53 of 168 tests failed.
=========================Sure, how else could I write tests for this feature? But right now on my
machine
everything is ok (the same for `make installcheck`):$ make check
....
=======================
All 168 tests passed.
=======================
Oops, something was wrong in my setup :)
Hello,
On 04.10.2016 11:28, Victor Wagner wrote:
Git complains about whitespace in this version of patch:
$ git apply ../generic_type_subscription_v2.patch
../generic_type_subscription_v2.patch:218: tab in indent.
int first;
../generic_type_subscription_v2.patch:219: tab in indent.
int second;
../generic_type_subscription_v2.patch:225: tab in indent.
SubscriptionExecData *sbsdata = (SubscriptionExecData *) PG_GETARG_POINTER(1);
../generic_type_subscription_v2.patch:226: tab in indent.
Custom *result = (Custom *) sbsdata->containerSource;
../generic_type_subscription_v2.patch:234: tab in indent.
SubscriptionRef *sbsref = (SubscriptionRef *) PG_GETARG_POINTER(0);
warning: squelched 29 whitespace errors
warning: 34 lines add whitespace errors.It doesn't prevent me from further testing of patch, but worth noticing.
I agree with Victor. In sgml files whitespaces instead of tabs are
usually used.
I've looked at your patch to make some review.
"subscription"
--------------
The term "subscription" is confusing me. I'm not native english speaker.
But I suppose that this term is not related with the "subscript". So
maybe you should use the "subscripting" or "container" (because you
already use the "container" term in the code). For example:
T_SubscriptingRef
SubscriptingRef
deparseSubscriptingRef()
xsubscripting.sgml
CREATE TYPE custom (
internallength = 4,
input = custom_in,
output = custom_out,
subscripting = custom_subscripting
);
etc.
Subscripting interface
----------------------
In tests I see the query:
+select ('[1, "2", null]'::jsonb)['1']; + jsonb +------- + "2" +(1 row)
Here '1' is used as a second item index. But from the last discussion
/messages/by-id/55D24517.8080609@agliodbs.com it
should be a key:
There is one ambiguous case you need to address:
testjson = '{ "a" : { } }'
SET testjson['a']['a1']['1'] = 42
... so in this case, is '1' a key, or the first item of an array? how
do we determine that? How does the user assign something to an array?
And should return null. Is this right? Or this behaviour was not
accepted in discussion? I didn't find it.
+Datum +custom_subscription(PG_FUNCTION_ARGS) +{ + int op_type = PG_GETARG_INT32(0); + FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1, + fcinfo->nargs); + + if (op_type & SBS_VALIDATION) + return custom_subscription_prepare(&target_fcinfo); + + if (op_type & SBS_EXEC) + return custom_subscription_evaluate(&target_fcinfo); + + elog(ERROR, "incorrect op_type for subscription function: %d", op_type); +}
I'm not sure but maybe we should use here two different functions? For
example:
Datum
custom_subscripting_validate(PG_FUNCTION_ARGS)
{
}
Datum
custom_subscripting_evaluate(PG_FUNCTION_ARGS)
{
}
CREATE TYPE custom (
internallength = 8,
input = custom_in,
output = custom_out,
subscripting_validate = custom_subscripting_validate,
subscripting_evalute = custom_subscripting_evaluate,
);
What do you think?
Documentation
-------------
+<!-- doc/src/sgml/xsubscription.sgml --> + + <sect1 id="xsubscription"> + <title>User-defined subscription procedure</title>
There is the extra whitespace before <sect1>.
+ </indexterm> + When you define a new base type, you can also specify a custom procedure + to handle subscription expressions. It should contains logic for verification + and for extraction or update your data. For instance:
I suppose a <para> tag is missed after the </indexterm>.
+</programlisting> + + </para> + + <para>
So </para> is redundant here.
Code
----
+static Oid +findTypeSubscriptionFunction(List *procname, Oid typeOid) +{ + Oid argList[1];
Here typeOid argument is not used. Is it should be here?
+ExecEvalSubscriptionRef(SubscriptionRefExprState *sbstate,
I think in this function local arguments have a lot of tabs. It is
normal if not all variables is aligned on the same line. A lot of tabs
are occur in other functions too. Because of this some lines exceed 80
characters.
+ if (sbstate->refupperindexpr != NULL) + upper = (Datum *) palloc(sbstate->refupperindexpr->length * sizeof(Datum *)); + + if (sbstate->reflowerindexpr != NULL) + lower = (Datum *) palloc(sbstate->reflowerindexpr->length * sizeof(Datum *));
Could we use palloc() before the "foreach(l, sbstate->refupperindexpr)"
? I think it could be a little optimization.
-transformArrayType(Oid *arrayType, int32 *arrayTypmod) +transformArrayType(Oid *containerType, int32 *containerTypmod)
I think name of the function is confusing. Maybe use
transformContainerType()?
+JsonbValue * +JsonbToJsonbValue(Jsonb *jsonb, JsonbValue *val) +{
In this function we could palloc JsonbValue every time and don't use val
argument. And maybe better to copy all JsonbContainer from jsonb->root
using memcpy(). Instead of assigning reference to jsonb->root. As is the
case in JsonbValueToJsonb().
It is necessary to remove the notice about JsonbToJsonbValue() in
JsonbValueToJsonb() comments.
-static JsonbValue * +JsonbValue * setPath(JsonbIterator **it, Datum *path_elems,
Why did you remove static keyword? setPath() is still static.
- JsonbValue v; + JsonbValue v, *new = (JsonbValue *) newval;
Is declaration of "new" variable necessary here? I think it is extra
declaration. Also its name may bring some problems. For example, there
is a thread where guys try to port PostgreSQL to C++.
--
Artur Zakirov
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 5 October 2016 at 22:59, Artur Zakirov <a.zakirov@postgrespro.ru> wrote:
I've looked at your patch to make some review.
Thanks for the feedback.
On 04.10.2016 11:28, Victor Wagner wrote: Git complains about whitespace
in
this version of patch:
Fixed.
The term "subscription" is confusing me
Yes, you're right. "container" is too general I think, so I renamed
everything
to "subscripting".
Here '1' is used as a second item index. But from the last discussion
/messages/by-id/55D24517.8080609@agliodbs.com it
should be a key
Well, I missed that, since I used already implemented function "setPath",
and
this function implies that "all path elements before the last must already
exist", so in this case:
select jsonb_set('{"a": {}}', '{a, a1, 1}', '42');
nothing will be changed. I agree, we can implement this as discussed, but
we need
to do it inside "setPath", so it will be like "globally".
I'm not sure but maybe we should use here two different functions?
I thought about that, but finally decided to keep changes into "pg_type" as
small as possible (anyway, these two functions will serve one logical
purpose).
Here typeOid argument is not used. Is it should be here?
No, it shouldn't, fixed. It's interesting, that the same is correct for
"findTypeAnalyzeFunction" (I used this function as an example).
I think name of the function is confusing. Maybe use
transformContainerType()?
The point is that "transformArrayType" still contains some array-specific
code,
although it doesn't affect to any other type. So I think it's not completely
correct to use "transformContainerType" name.
Why did you remove static keyword? setPath() is still static
Is declaration of "new" variable necessary here?
I changed it back.
Here is a new version of patch.
Attachments:
generic_type_subscription_v3.patchtext/x-patch; charset=US-ASCII; name=generic_type_subscription_v3.patchDownload
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 8ce24e0..d9c9d64 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -2464,14 +2464,14 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
JumbleExpr(jstate, (Node *) expr->aggfilter);
}
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
- JumbleExpr(jstate, (Node *) aref->refupperindexpr);
- JumbleExpr(jstate, (Node *) aref->reflowerindexpr);
- JumbleExpr(jstate, (Node *) aref->refexpr);
- JumbleExpr(jstate, (Node *) aref->refassgnexpr);
+ JumbleExpr(jstate, (Node *) sbsref->refupperindexpr);
+ JumbleExpr(jstate, (Node *) sbsref->reflowerindexpr);
+ JumbleExpr(jstate, (Node *) sbsref->refexpr);
+ JumbleExpr(jstate, (Node *) sbsref->refassgnexpr);
}
break;
case T_FuncExpr:
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 691658f..7c43585 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -137,7 +137,7 @@ static void deparseExpr(Expr *expr, deparse_expr_cxt *context);
static void deparseVar(Var *node, deparse_expr_cxt *context);
static void deparseConst(Const *node, deparse_expr_cxt *context);
static void deparseParam(Param *node, deparse_expr_cxt *context);
-static void deparseArrayRef(ArrayRef *node, deparse_expr_cxt *context);
+static void deparseSubscriptingRef(SubscriptingRef *node, deparse_expr_cxt *context);
static void deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context);
static void deparseOpExpr(OpExpr *node, deparse_expr_cxt *context);
static void deparseOperatorName(StringInfo buf, Form_pg_operator opform);
@@ -358,9 +358,9 @@ foreign_expr_walker(Node *node,
state = FDW_COLLATE_UNSAFE;
}
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *ar = (ArrayRef *) node;
+ SubscriptingRef *ar = (SubscriptingRef *) node;
/* Assignment should not be in restrictions. */
if (ar->refassgnexpr != NULL)
@@ -1822,8 +1822,8 @@ deparseExpr(Expr *node, deparse_expr_cxt *context)
case T_Param:
deparseParam((Param *) node, context);
break;
- case T_ArrayRef:
- deparseArrayRef((ArrayRef *) node, context);
+ case T_SubscriptingRef:
+ deparseSubscriptingRef((SubscriptingRef *) node, context);
break;
case T_FuncExpr:
deparseFuncExpr((FuncExpr *) node, context);
@@ -2043,7 +2043,7 @@ deparseParam(Param *node, deparse_expr_cxt *context)
* Deparse an array subscript expression.
*/
static void
-deparseArrayRef(ArrayRef *node, deparse_expr_cxt *context)
+deparseSubscriptingRef(SubscriptingRef *node, deparse_expr_cxt *context)
{
StringInfo buf = context->buf;
ListCell *lowlist_item;
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 29738b0..c6fe247 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -7056,6 +7056,13 @@
</row>
<row>
+ <entry><structfield>typsubscription</structfield></entry>
+ <entry><type>regproc</type></entry>
+ <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+ <entry>Custom subscription function with type-specific logic, or 0 if this type doesn't support subscription.</entry>
+ </row>
+
+ <row>
<entry><structfield>typdefaultbin</structfield></entry>
<entry><type>pg_node_tree</type></entry>
<entry></entry>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 69649a7..ff6a2c2 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -71,6 +71,7 @@
<!ENTITY xplang SYSTEM "xplang.sgml">
<!ENTITY xoper SYSTEM "xoper.sgml">
<!ENTITY xtypes SYSTEM "xtypes.sgml">
+<!ENTITY xsubscription SYSTEM "xsubscription.sgml">
<!ENTITY plperl SYSTEM "plperl.sgml">
<!ENTITY plpython SYSTEM "plpython.sgml">
<!ENTITY plsql SYSTEM "plpgsql.sgml">
diff --git a/doc/src/sgml/json.sgml b/doc/src/sgml/json.sgml
index 3cf78d6..399e4f3 100644
--- a/doc/src/sgml/json.sgml
+++ b/doc/src/sgml/json.sgml
@@ -569,4 +569,29 @@ SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"tags": ["qu
compared using the default database collation.
</para>
</sect2>
+
+ <sect2 id="json-subscription">
+ <title>JSON subscription</title>
+ <para>
+ JSONB data type support array-style subscription expressions to extract or update particular element. An example of subscription syntax:
+<programlisting>
+-- Extract value by key
+SELECT ('{"a": 1}'::jsonb)['a'];
+
+-- Extract nested value by key path
+SELECT ('{"a": {"b": {"c": 1}}}'::jsonb)['a']['b']['c'];
+
+-- Extract element by index
+SELECT ('[1, "2", null]'::jsonb)['1'];
+
+-- Update value by key
+UPDATE table_name set jsonb_field['key'] = 1;
+
+-- Select records using where clause with subscription
+SELECT * from table_name where jsonb_field['key'] = '"value"';
+</programlisting>
+ </para>
+ </sect2>
+
+
</sect1>
diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index 5a09f19..8588065 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -54,6 +54,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
[ , ELEMENT = <replaceable class="parameter">element</replaceable> ]
[ , DELIMITER = <replaceable class="parameter">delimiter</replaceable> ]
[ , COLLATABLE = <replaceable class="parameter">collatable</replaceable> ]
+ [ , SUBSCRIPTION = <replaceable class="parameter">subscription_function</replaceable> ]
)
CREATE TYPE <replaceable class="parameter">name</replaceable>
@@ -194,7 +195,8 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
<replaceable class="parameter">send_function</replaceable>,
<replaceable class="parameter">type_modifier_input_function</replaceable>,
<replaceable class="parameter">type_modifier_output_function</replaceable> and
- <replaceable class="parameter">analyze_function</replaceable>
+ <replaceable class="parameter">analyze_function</replaceable>,
+ <replaceable class="parameter">subscription_function</replaceable>
are optional. Generally these functions have to be coded in C
or another low-level language.
</para>
@@ -451,6 +453,22 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
make use of the collation information; this does not happen
automatically merely by marking the type collatable.
</para>
+
+ <para>
+ The optional
+ <replaceable class="parameter">subscription_function</replaceable>
+ contains type-specific logic for subscription of the data type.
+ By default, there is no such function, which means that the data
+ type doesn't support subscription. The subscription function must be
+ declared to take a single argument of type <type>internal</>, and return
+ a <type>internal</> result. There are two examples of implementation for
+ subscription function in case of array
+ (<replaceable class="parameter">array_subscription</replaceable>)
+ and jsonb
+ (<replaceable class="parameter">jsonb_subscription</replaceable>)
+ types in <filename>src/backend/utils/adt/arrayfuncs.c</> and
+ <filename>src/backend/utils/adt/jsonfuncs.c</> corresponding.
+ </para>
</refsect2>
<refsect2>
@@ -766,6 +784,16 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">subscription_function</replaceable></term>
+ <listitem>
+ <para>
+ The name of a function that contains type-specific subscription logic for
+ the data type.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/doc/src/sgml/xsubscription.sgml b/doc/src/sgml/xsubscription.sgml
new file mode 100644
index 0000000..091f8c9
--- /dev/null
+++ b/doc/src/sgml/xsubscription.sgml
@@ -0,0 +1,100 @@
+<!-- doc/src/sgml/xsubscription.sgml -->
+
+ <sect1 id="xsubscription">
+ <title>User-defined subscription procedure</title>
+
+ <indexterm zone="xsubscription">
+ <primary>custom subscription</primary>
+ </indexterm>
+ <para>
+ When you define a new base type, you can also specify a custom procedure
+ to handle subscription expressions. It should contains logic for verification
+ and for extraction or update your data. For instance:
+</para>
+<programlisting><![CDATA[
+typedef struct Custom
+{
+ int first;
+ int second;
+} Custom;
+
+Datum
+custom_subscription_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptingExecData *sbsdata = (SubscriptingExecData *) PG_GETARG_POINTER(1);
+ Custom *result = (Custom *) sbsdata->containerSource;
+
+ // Some extraction or update logic based on sbsdata
+}
+
+Datum
+custom_subscription_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptingRef *sbsref = (SubscriptingRef *) PG_GETARG_POINTER(0);
+
+ // Some verifications or type coersion
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+PG_FUNCTION_INFO_V1(custom_subscription);
+
+Datum
+custom_subscription(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1,
+ fcinfo->nargs);
+
+ if (op_type & SBS_VALIDATION)
+ return custom_subscription_prepare(&target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return custom_subscription_evaluate(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscription function: %d", op_type);
+}]]>
+</programlisting>
+
+<para>
+ Then you can define a subscription procedure and a custom data type:
+</para>
+<programlisting>
+CREATE FUNCTION custom_subscription(internal)
+ RETURNS internal
+ AS '<replaceable>filename</replaceable>'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE custom (
+ internallength = 4,
+ input = custom_in,
+ output = custom_out,
+ subscription = custom_subscription
+);
+</programlisting>
+
+<para>
+ and use it as usual:
+</para>
+<programlisting>
+CREATE TABLE test_subscription (
+ data custom,
+);
+
+INSERT INTO test_subscription VALUES ('(1, 2)');
+
+SELECT data[0] from test_subscription;
+
+UPDATE test_subscription SET data[1] = 3;
+</programlisting>
+
+
+ <para>
+ The examples of custom subscription implementation can be found in
+ <filename>subscription.sql</filename> and <filename>subscription.c</filename>
+ in the <filename>src/tutorial</> directory of the source distribution.
+ See the <filename>README</> file in that directory for instructions
+ about running the examples.
+ </para>
+
+</sect1>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 0cf7b9e..ab8d88d 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -978,7 +978,8 @@ AddNewRelationType(const char *typeName,
-1, /* typmod */
0, /* array dimensions for typBaseType */
false, /* Type NOT NULL */
- InvalidOid); /* rowtypes never have a collation */
+ InvalidOid, /* rowtypes never have a collation */
+ InvalidOid); /* typsubscription - none */
}
/* --------------------------------
@@ -1245,7 +1246,8 @@ heap_create_with_catalog(const char *relname,
-1, /* typmod */
0, /* array dimensions for typBaseType */
false, /* Type NOT NULL */
- InvalidOid); /* rowtypes never have a collation */
+ InvalidOid, /* rowtypes never have a collation */
+ F_ARRAY_SUBSCRIPTION); /* array implementation */
pfree(relarrayname);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 4b2d281..dabcb64 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -118,6 +118,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(-1);
values[Anum_pg_type_typndims - 1] = Int32GetDatum(0);
values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(InvalidOid);
+ values[Anum_pg_type_typsubscription - 1] = ObjectIdGetDatum(InvalidOid);
nulls[Anum_pg_type_typdefaultbin - 1] = true;
nulls[Anum_pg_type_typdefault - 1] = true;
nulls[Anum_pg_type_typacl - 1] = true;
@@ -166,6 +167,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
false,
InvalidOid,
InvalidOid,
+ InvalidOid,
NULL,
false);
@@ -224,7 +226,8 @@ TypeCreate(Oid newTypeOid,
int32 typeMod,
int32 typNDims, /* Array dimensions for baseType */
bool typeNotNull,
- Oid typeCollation)
+ Oid typeCollation,
+ Oid subscriptionProcedure)
{
Relation pg_type_desc;
Oid typeObjectId;
@@ -364,6 +367,7 @@ TypeCreate(Oid newTypeOid,
values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(typeMod);
values[Anum_pg_type_typndims - 1] = Int32GetDatum(typNDims);
values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(typeCollation);
+ values[Anum_pg_type_typsubscription - 1] = ObjectIdGetDatum(subscriptionProcedure);
/*
* initialize the default binary value for this type. Check for nulls of
@@ -484,6 +488,7 @@ TypeCreate(Oid newTypeOid,
isImplicitArray,
baseType,
typeCollation,
+ subscriptionProcedure,
(defaultTypeBin ?
stringToNode(defaultTypeBin) :
NULL),
@@ -530,6 +535,7 @@ GenerateTypeDependencies(Oid typeNamespace,
bool isImplicitArray,
Oid baseType,
Oid typeCollation,
+ Oid subscriptionProcedure,
Node *defaultExpr,
bool rebuild)
{
@@ -682,6 +688,14 @@ GenerateTypeDependencies(Oid typeNamespace,
/* Normal dependency on the default expression. */
if (defaultExpr)
recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL);
+
+ if (OidIsValid(subscriptionProcedure))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = subscriptionProcedure;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
}
/*
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 056933a..ad17c87 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -94,6 +94,7 @@ static Oid findTypeSendFunction(List *procname, Oid typeOid);
static Oid findTypeTypmodinFunction(List *procname);
static Oid findTypeTypmodoutFunction(List *procname);
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
+static Oid findTypeSubscriptingFunction(List *procname);
static Oid findRangeSubOpclass(List *opcname, Oid subtype);
static Oid findRangeCanonicalFunction(List *procname, Oid typeOid);
static Oid findRangeSubtypeDiffFunction(List *procname, Oid subtype);
@@ -123,6 +124,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
List *typmodinName = NIL;
List *typmodoutName = NIL;
List *analyzeName = NIL;
+ List *subscriptionName = NIL;
char category = TYPCATEGORY_USER;
bool preferred = false;
char delimiter = DEFAULT_TYPDELIM;
@@ -141,6 +143,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
DefElem *typmodinNameEl = NULL;
DefElem *typmodoutNameEl = NULL;
DefElem *analyzeNameEl = NULL;
+ DefElem *subscriptionNameEl = NULL;
DefElem *categoryEl = NULL;
DefElem *preferredEl = NULL;
DefElem *delimiterEl = NULL;
@@ -163,6 +166,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
Oid resulttype;
ListCell *pl;
ObjectAddress address;
+ Oid subscriptionOid = InvalidOid;
/*
* As of Postgres 8.4, we require superuser privilege to create a base
@@ -262,6 +266,9 @@ DefineType(ParseState *pstate, List *names, List *parameters)
else if (pg_strcasecmp(defel->defname, "analyze") == 0 ||
pg_strcasecmp(defel->defname, "analyse") == 0)
defelp = &analyzeNameEl;
+ else if (pg_strcasecmp(defel->defname, "subscription") == 0 ||
+ pg_strcasecmp(defel->defname, "subscription") == 0)
+ defelp = &subscriptionNameEl;
else if (pg_strcasecmp(defel->defname, "category") == 0)
defelp = &categoryEl;
else if (pg_strcasecmp(defel->defname, "preferred") == 0)
@@ -332,6 +339,8 @@ DefineType(ParseState *pstate, List *names, List *parameters)
typmodoutName = defGetQualifiedName(typmodoutNameEl);
if (analyzeNameEl)
analyzeName = defGetQualifiedName(analyzeNameEl);
+ if (subscriptionNameEl)
+ subscriptionName = defGetQualifiedName(subscriptionNameEl);
if (categoryEl)
{
char *p = defGetString(categoryEl);
@@ -513,6 +522,9 @@ DefineType(ParseState *pstate, List *names, List *parameters)
if (analyzeName)
analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid);
+ if (subscriptionName)
+ subscriptionOid = findTypeSubscriptingFunction(subscriptionName);
+
/*
* Check permissions on functions. We choose to require the creator/owner
* of a type to also own the underlying functions. Since creating a type
@@ -632,7 +644,8 @@ DefineType(ParseState *pstate, List *names, List *parameters)
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
false, /* Type NOT NULL */
- collation); /* type's collation */
+ collation, /* type's collation */
+ subscriptionOid); /* subscription procedure */
Assert(typoid == address.objectId);
/*
@@ -673,7 +686,8 @@ DefineType(ParseState *pstate, List *names, List *parameters)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- collation); /* type's collation */
+ collation, /* type's collation */
+ F_ARRAY_SUBSCRIPTION);
pfree(array_type);
@@ -735,6 +749,7 @@ DefineDomain(CreateDomainStmt *stmt)
Oid receiveProcedure;
Oid sendProcedure;
Oid analyzeProcedure;
+ Oid subscriptionProcedure;
bool byValue;
char category;
char delimiter;
@@ -858,6 +873,9 @@ DefineDomain(CreateDomainStmt *stmt)
/* Analysis function */
analyzeProcedure = baseType->typanalyze;
+ /* Subscripting function */
+ subscriptionProcedure = baseType->typsubscription;
+
/* Inherited default value */
datum = SysCacheGetAttr(TYPEOID, typeTup,
Anum_pg_type_typdefault, &isnull);
@@ -1059,7 +1077,8 @@ DefineDomain(CreateDomainStmt *stmt)
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
typNotNull, /* Type NOT NULL */
- domaincoll); /* type's collation */
+ domaincoll, /* type's collation */
+ subscriptionProcedure); /* subscription procedure */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
@@ -1171,7 +1190,8 @@ DefineEnum(CreateEnumStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* type's collation */
+ InvalidOid, /* type's collation */
+ InvalidOid); /* typsubscription - none */
/* Enter the enum's values into pg_enum */
EnumValuesCreate(enumTypeAddr.objectId, stmt->vals);
@@ -1211,7 +1231,8 @@ DefineEnum(CreateEnumStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* type's collation */
+ InvalidOid, /* type's collation */
+ F_ARRAY_SUBSCRIPTION); /* array subscription implementation */
pfree(enumArrayName);
@@ -1499,7 +1520,8 @@ DefineRange(CreateRangeStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* type's collation (ranges never have one) */
+ InvalidOid, /* type's collation (ranges never have one) */
+ InvalidOid); /* typsubscription - none */
Assert(typoid == address.objectId);
/* Create the entry in pg_range */
@@ -1541,7 +1563,8 @@ DefineRange(CreateRangeStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* typcollation */
+ InvalidOid, /* typcollation */
+ F_ARRAY_SUBSCRIPTION); /* array subscription implementation */
pfree(rangeArrayName);
@@ -1885,6 +1908,33 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
return procOid;
}
+static Oid
+findTypeSubscriptingFunction(List *procname)
+{
+ Oid argList[1];
+ Oid procOid;
+
+ /*
+ * Subscripting functions always take one INTERNAL argument and return INTERNAL.
+ */
+ argList[0] = INTERNALOID;
+
+ procOid = LookupFuncName(procname, 1, argList, true);
+ if (!OidIsValid(procOid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("function %s does not exist",
+ func_signature_string(procname, 1, NIL, argList))));
+
+ if (get_func_rettype(procOid) != INTERNALOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("type subscription function %s must return type %s",
+ NameListToString(procname), "internal")));
+
+ return procOid;
+}
+
/*
* Find suitable support functions and opclasses for a range type.
*/
@@ -2239,6 +2289,7 @@ AlterDomainDefault(List *names, Node *defaultRaw)
false, /* a domain isn't an implicit array */
typTup->typbasetype,
typTup->typcollation,
+ typTup->typsubscription,
defaultExpr,
true); /* Rebuild is true */
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 743e7d6..49a2e4a 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -55,6 +55,7 @@
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/lsyscache.h"
+#include "utils/syscache.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
#include "utils/typcache.h"
@@ -62,10 +63,9 @@
/* static function decls */
-static Datum ExecEvalArrayRef(ArrayRefExprState *astate,
+static Datum ExecEvalSubscriptingRef(SubscriptingRefExprState *astate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
-static bool isAssignmentIndirectionExpr(ExprState *exprstate);
static Datum ExecEvalAggref(AggrefExprState *aggref,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
@@ -251,39 +251,48 @@ static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
/*----------
- * ExecEvalArrayRef
+ * ExecEvalSubscriptingRef
*
- * This function takes an ArrayRef and returns the extracted Datum
- * if it's a simple reference, or the modified array value if it's
- * an array assignment (i.e., array element or slice insertion).
+ * This function takes a SubscriptingRef, extracts all information required
+ * for subscription and pass it to a particular subscription procedure,
+ * specified for this data type. As a result the extracted Datum will be
+ * returned if it's a simple reference, or the modified containers value if
+ * it's an containers assignment (i.e., containers element or slice
+ * insertion).
*
* NOTE: if we get a NULL result from a subscript expression, we return NULL
- * when it's an array reference, or raise an error when it's an assignment.
+ * when it's an containers reference, or raise an error when it's an assignment.
*----------
*/
+
static Datum
-ExecEvalArrayRef(ArrayRefExprState *astate,
- ExprContext *econtext,
- bool *isNull,
- ExprDoneCond *isDone)
+ExecEvalSubscriptingRef(SubscriptingRefExprState *sbstate, ExprContext *econtext,
+ bool *isNull, ExprDoneCond *isDone)
{
- ArrayRef *arrayRef = (ArrayRef *) astate->xprstate.expr;
- Datum array_source;
- bool isAssignment = (arrayRef->refassgnexpr != NULL);
- bool eisnull;
- ListCell *l;
- int i = 0,
- j = 0;
- IntArray upper,
- lower;
- bool upperProvided[MAXDIM],
- lowerProvided[MAXDIM];
- int *lIndex;
-
- array_source = ExecEvalExpr(astate->refexpr,
- econtext,
- isNull,
- isDone);
+ SubscriptingRef *sbsRef = (SubscriptingRef *) sbstate->xprstate.expr;
+ Oid containerType;
+ RegProcedure typsubscription;
+ bool isAssignment = (sbsRef->refassgnexpr != NULL);
+ bool eisnull;
+ Datum *upper = NULL,
+ *lower = NULL;
+ ListCell *l;
+ int i = 0,
+ j = 0;
+ SubscriptingExecData sbsdata;
+ bool upperProvided[MAXDIM],
+ lowerProvided[MAXDIM];
+
+ if (sbstate->refupperindexpr != NULL)
+ upper = (Datum *) palloc(sbstate->refupperindexpr->length * sizeof(Datum *));
+
+ if (sbstate->reflowerindexpr != NULL)
+ lower = (Datum *) palloc(sbstate->reflowerindexpr->length * sizeof(Datum *));
+
+ sbsdata.xprcontext = econtext;
+ sbsdata.isNull = isNull;
+ sbsdata.containerSource = ExecEvalExpr(sbstate->refexpr, econtext,
+ isNull, isDone);
/*
* If refexpr yields NULL, and it's a fetch, then result is NULL. In the
@@ -297,51 +306,49 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
return (Datum) NULL;
}
- foreach(l, astate->refupperindexpr)
+ foreach(l, sbstate->refupperindexpr)
{
ExprState *eltstate = (ExprState *) lfirst(l);
if (i >= MAXDIM)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ errmsg("number of container dimensions (%d) exceeds the maximum allowed (%d)",
i + 1, MAXDIM)));
if (eltstate == NULL)
{
- /* Slice bound is omitted, so use array's upper bound */
- Assert(astate->reflowerindexpr != NIL);
+ /* Slice bound is omitted, so use containers's upper bound */
+ Assert(sbstate->reflowerindexpr != NIL);
upperProvided[i++] = false;
continue;
}
+
upperProvided[i] = true;
+ upper[i++] = ExecEvalExpr(eltstate, econtext, &eisnull, NULL);
- upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate,
- econtext,
- &eisnull,
- NULL));
/* If any index expr yields NULL, result is NULL or error */
if (eisnull)
{
if (isAssignment)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
- errmsg("array subscript in assignment must not be null")));
+ errmsg("container subscript in assignment must not be null")));
*isNull = true;
return (Datum) NULL;
}
}
- if (astate->reflowerindexpr != NIL)
+ if (sbstate->reflowerindexpr != NIL)
{
- foreach(l, astate->reflowerindexpr)
+ foreach(l, sbstate->reflowerindexpr)
{
ExprState *eltstate = (ExprState *) lfirst(l);
if (j >= MAXDIM)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ errmsg("number of container dimensions (%d) exceeds the maximum allowed (%d)",
j + 1, MAXDIM)));
if (eltstate == NULL)
@@ -350,191 +357,45 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
lowerProvided[j++] = false;
continue;
}
+
lowerProvided[j] = true;
+ lower[j++] = ExecEvalExpr(eltstate, econtext, &eisnull, NULL);
- lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate,
- econtext,
- &eisnull,
- NULL));
/* If any index expr yields NULL, result is NULL or error */
if (eisnull)
{
if (isAssignment)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
- errmsg("array subscript in assignment must not be null")));
+ errmsg("container subscript in assignment must not be null")));
*isNull = true;
return (Datum) NULL;
}
}
+
/* this can't happen unless parser messed up */
if (i != j)
elog(ERROR, "upper and lower index lists are not same length");
- lIndex = lower.indx;
- }
- else
- lIndex = NULL;
-
- if (isAssignment)
- {
- Datum sourceData;
- Datum save_datum;
- bool save_isNull;
-
- /*
- * We might have a nested-assignment situation, in which the
- * refassgnexpr is itself a FieldStore or ArrayRef that needs to
- * obtain and modify the previous value of the array element or slice
- * being replaced. If so, we have to extract that value from the
- * array and pass it down via the econtext's caseValue. It's safe to
- * reuse the CASE mechanism because there cannot be a CASE between
- * here and where the value would be needed, and an array assignment
- * can't be within a CASE either. (So saving and restoring the
- * caseValue is just paranoia, but let's do it anyway.)
- *
- * Since fetching the old element might be a nontrivial expense, do it
- * only if the argument appears to actually need it.
- */
- save_datum = econtext->caseValue_datum;
- save_isNull = econtext->caseValue_isNull;
-
- if (isAssignmentIndirectionExpr(astate->refassgnexpr))
- {
- if (*isNull)
- {
- /* whole array is null, so any element or slice is too */
- econtext->caseValue_datum = (Datum) 0;
- econtext->caseValue_isNull = true;
- }
- else if (lIndex == NULL)
- {
- econtext->caseValue_datum =
- array_get_element(array_source, i,
- upper.indx,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign,
- &econtext->caseValue_isNull);
- }
- else
- {
- econtext->caseValue_datum =
- array_get_slice(array_source, i,
- upper.indx, lower.indx,
- upperProvided, lowerProvided,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
- econtext->caseValue_isNull = false;
- }
- }
- else
- {
- /* argument shouldn't need caseValue, but for safety set it null */
- econtext->caseValue_datum = (Datum) 0;
- econtext->caseValue_isNull = true;
- }
-
- /*
- * Evaluate the value to be assigned into the array.
- */
- sourceData = ExecEvalExpr(astate->refassgnexpr,
- econtext,
- &eisnull,
- NULL);
-
- econtext->caseValue_datum = save_datum;
- econtext->caseValue_isNull = save_isNull;
-
- /*
- * For an assignment to a fixed-length array type, both the original
- * array and the value to be assigned into it must be non-NULL, else
- * we punt and return the original array.
- */
- if (astate->refattrlength > 0) /* fixed-length array? */
- if (eisnull || *isNull)
- return array_source;
-
- /*
- * For assignment to varlena arrays, we handle a NULL original array
- * by substituting an empty (zero-dimensional) array; insertion of the
- * new element will result in a singleton array value. It does not
- * matter whether the new element is NULL.
- */
- if (*isNull)
- {
- array_source = PointerGetDatum(construct_empty_array(arrayRef->refelemtype));
- *isNull = false;
- }
-
- if (lIndex == NULL)
- return array_set_element(array_source, i,
- upper.indx,
- sourceData,
- eisnull,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
- else
- return array_set_slice(array_source, i,
- upper.indx, lower.indx,
- upperProvided, lowerProvided,
- sourceData,
- eisnull,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
}
- if (lIndex == NULL)
- return array_get_element(array_source, i,
- upper.indx,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign,
- isNull);
- else
- return array_get_slice(array_source, i,
- upper.indx, lower.indx,
- upperProvided, lowerProvided,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
-}
+ sbsdata.upper = upper;
+ sbsdata.upperProvided = upperProvided;
+ sbsdata.lower = lower;
+ sbsdata.lowerProvided = lowerProvided;
+ sbsdata.indexprNumber = i;
-/*
- * Helper for ExecEvalArrayRef: is expr a nested FieldStore or ArrayRef
- * that might need the old element value passed down?
- *
- * (We could use this in ExecEvalFieldStore too, but in that case passing
- * the old value is so cheap there's no need.)
- */
-static bool
-isAssignmentIndirectionExpr(ExprState *exprstate)
-{
- if (exprstate == NULL)
- return false; /* just paranoia */
- if (IsA(exprstate, FieldStoreState))
- {
- FieldStore *fstore = (FieldStore *) exprstate->expr;
+ containerType = getBaseTypeAndTypmod(sbsRef->refcontainertype, &sbsRef->reftypmod);
+ typsubscription = get_typsubscription(containerType);
- if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
- return true;
- }
- else if (IsA(exprstate, ArrayRefExprState))
- {
- ArrayRef *arrayRef = (ArrayRef *) exprstate->expr;
+ if (!OidIsValid(typsubscription))
+ /* this can't happen */
+ elog(ERROR, "can not find subscription procedure for type %s",
+ format_type_be(containerType));
- if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr))
- return true;
- }
- return false;
+ return OidFunctionCall3(typsubscription,
+ Int32GetDatum(SBS_EXEC),
+ PointerGetDatum(sbstate),
+ PointerGetDatum(&sbsdata));
}
/* ----------------------------------------------------------------
@@ -4332,7 +4193,7 @@ ExecEvalFieldStore(FieldStoreState *fstate,
/*
* Use the CaseTestExpr mechanism to pass down the old value of the
* field being replaced; this is needed in case the newval is itself a
- * FieldStore or ArrayRef that has to obtain and modify the old value.
+ * FieldStore or SubscriptingRef that has to obtain and modify the old value.
* It's safe to reuse the CASE mechanism because there cannot be a
* CASE between here and where the value would be needed, and a field
* assignment can't be within a CASE either. (So saving and restoring
@@ -4695,25 +4556,21 @@ ExecInitExpr(Expr *node, PlanState *parent)
state = (ExprState *) wfstate;
}
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *aref = (ArrayRef *) node;
- ArrayRefExprState *astate = makeNode(ArrayRefExprState);
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
+ SubscriptingRefExprState *astate = makeNode(SubscriptingRefExprState);
- astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayRef;
+ astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalSubscriptingRef;
astate->refupperindexpr = (List *)
- ExecInitExpr((Expr *) aref->refupperindexpr, parent);
+ ExecInitExpr((Expr *) sbsref->refupperindexpr, parent);
astate->reflowerindexpr = (List *)
- ExecInitExpr((Expr *) aref->reflowerindexpr, parent);
- astate->refexpr = ExecInitExpr(aref->refexpr, parent);
- astate->refassgnexpr = ExecInitExpr(aref->refassgnexpr,
+ ExecInitExpr((Expr *) sbsref->reflowerindexpr, parent);
+ astate->refexpr = ExecInitExpr(sbsref->refexpr, parent);
+ astate->refassgnexpr = ExecInitExpr(sbsref->refassgnexpr,
parent);
/* do one-time catalog lookups for type info */
- astate->refattrlength = get_typlen(aref->refarraytype);
- get_typlenbyvalalign(aref->refelemtype,
- &astate->refelemlength,
- &astate->refelembyval,
- &astate->refelemalign);
+ astate->refattrlength = get_typlen(sbsref->refcontainertype);
state = (ExprState *) astate;
}
break;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 71714bc..01f3777 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1292,14 +1292,14 @@ _copyWindowFunc(const WindowFunc *from)
}
/*
- * _copyArrayRef
+ * _copySubscriptingRef
*/
-static ArrayRef *
-_copyArrayRef(const ArrayRef *from)
+static SubscriptingRef *
+_copySubscriptingRef(const SubscriptingRef *from)
{
- ArrayRef *newnode = makeNode(ArrayRef);
+ SubscriptingRef *newnode = makeNode(SubscriptingRef);
- COPY_SCALAR_FIELD(refarraytype);
+ COPY_SCALAR_FIELD(refcontainertype);
COPY_SCALAR_FIELD(refelemtype);
COPY_SCALAR_FIELD(reftypmod);
COPY_SCALAR_FIELD(refcollid);
@@ -4467,8 +4467,8 @@ copyObject(const void *from)
case T_WindowFunc:
retval = _copyWindowFunc(from);
break;
- case T_ArrayRef:
- retval = _copyArrayRef(from);
+ case T_SubscriptingRef:
+ retval = _copySubscriptingRef(from);
break;
case T_FuncExpr:
retval = _copyFuncExpr(from);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 29a090f..4f359b5 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -244,9 +244,9 @@ _equalWindowFunc(const WindowFunc *a, const WindowFunc *b)
}
static bool
-_equalArrayRef(const ArrayRef *a, const ArrayRef *b)
+_equalSubscriptingRef(const SubscriptingRef *a, const SubscriptingRef *b)
{
- COMPARE_SCALAR_FIELD(refarraytype);
+ COMPARE_SCALAR_FIELD(refcontainertype);
COMPARE_SCALAR_FIELD(refelemtype);
COMPARE_SCALAR_FIELD(reftypmod);
COMPARE_SCALAR_FIELD(refcollid);
@@ -2779,8 +2779,8 @@ equal(const void *a, const void *b)
case T_WindowFunc:
retval = _equalWindowFunc(a, b);
break;
- case T_ArrayRef:
- retval = _equalArrayRef(a, b);
+ case T_SubscriptingRef:
+ retval = _equalSubscriptingRef(a, b);
break;
case T_FuncExpr:
retval = _equalFuncExpr(a, b);
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 3997441..28b7cb7 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -66,13 +66,13 @@ exprType(const Node *expr)
case T_WindowFunc:
type = ((const WindowFunc *) expr)->wintype;
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- const ArrayRef *arrayref = (const ArrayRef *) expr;
+ const SubscriptingRef *arrayref = (const SubscriptingRef *) expr;
/* slice and/or store operations yield the array type */
if (arrayref->reflowerindexpr || arrayref->refassgnexpr)
- type = arrayref->refarraytype;
+ type = arrayref->refcontainertype;
else
type = arrayref->refelemtype;
}
@@ -284,9 +284,9 @@ exprTypmod(const Node *expr)
return ((const Const *) expr)->consttypmod;
case T_Param:
return ((const Param *) expr)->paramtypmod;
- case T_ArrayRef:
+ case T_SubscriptingRef:
/* typmod is the same for array or element */
- return ((const ArrayRef *) expr)->reftypmod;
+ return ((const SubscriptingRef *) expr)->reftypmod;
case T_FuncExpr:
{
int32 coercedTypmod;
@@ -772,8 +772,8 @@ exprCollation(const Node *expr)
case T_WindowFunc:
coll = ((const WindowFunc *) expr)->wincollid;
break;
- case T_ArrayRef:
- coll = ((const ArrayRef *) expr)->refcollid;
+ case T_SubscriptingRef:
+ coll = ((const SubscriptingRef *) expr)->refcollid;
break;
case T_FuncExpr:
coll = ((const FuncExpr *) expr)->funccollid;
@@ -1014,8 +1014,8 @@ exprSetCollation(Node *expr, Oid collation)
case T_WindowFunc:
((WindowFunc *) expr)->wincollid = collation;
break;
- case T_ArrayRef:
- ((ArrayRef *) expr)->refcollid = collation;
+ case T_SubscriptingRef:
+ ((SubscriptingRef *) expr)->refcollid = collation;
break;
case T_FuncExpr:
((FuncExpr *) expr)->funccollid = collation;
@@ -1237,9 +1237,9 @@ exprLocation(const Node *expr)
/* function name should always be the first thing */
loc = ((const WindowFunc *) expr)->location;
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
/* just use array argument's location */
- loc = exprLocation((Node *) ((const ArrayRef *) expr)->refexpr);
+ loc = exprLocation((Node *) ((const SubscriptingRef *) expr)->refexpr);
break;
case T_FuncExpr:
{
@@ -1926,21 +1926,21 @@ expression_tree_walker(Node *node,
return true;
}
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
/* recurse directly for upper/lower array index lists */
- if (expression_tree_walker((Node *) aref->refupperindexpr,
+ if (expression_tree_walker((Node *) sbsref->refupperindexpr,
walker, context))
return true;
- if (expression_tree_walker((Node *) aref->reflowerindexpr,
+ if (expression_tree_walker((Node *) sbsref->reflowerindexpr,
walker, context))
return true;
/* walker must see the refexpr and refassgnexpr, however */
- if (walker(aref->refexpr, context))
+ if (walker(sbsref->refexpr, context))
return true;
- if (walker(aref->refassgnexpr, context))
+ if (walker(sbsref->refassgnexpr, context))
return true;
}
break;
@@ -2515,12 +2515,12 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *arrayref = (ArrayRef *) node;
- ArrayRef *newnode;
+ SubscriptingRef *arrayref = (SubscriptingRef *) node;
+ SubscriptingRef *newnode;
- FLATCOPY(newnode, arrayref, ArrayRef);
+ FLATCOPY(newnode, arrayref, SubscriptingRef);
MUTATE(newnode->refupperindexpr, arrayref->refupperindexpr,
List *);
MUTATE(newnode->reflowerindexpr, arrayref->reflowerindexpr,
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index ae86954..48f1265 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1062,11 +1062,11 @@ _outWindowFunc(StringInfo str, const WindowFunc *node)
}
static void
-_outArrayRef(StringInfo str, const ArrayRef *node)
+_outSubscriptingRef(StringInfo str, const SubscriptingRef *node)
{
- WRITE_NODE_TYPE("ARRAYREF");
+ WRITE_NODE_TYPE("SUBSCRIPTINGREF");
- WRITE_OID_FIELD(refarraytype);
+ WRITE_OID_FIELD(refcontainertype);
WRITE_OID_FIELD(refelemtype);
WRITE_INT_FIELD(reftypmod);
WRITE_OID_FIELD(refcollid);
@@ -3445,8 +3445,8 @@ outNode(StringInfo str, const void *obj)
case T_WindowFunc:
_outWindowFunc(str, obj);
break;
- case T_ArrayRef:
- _outArrayRef(str, obj);
+ case T_SubscriptingRef:
+ _outSubscriptingRef(str, obj);
break;
case T_FuncExpr:
_outFuncExpr(str, obj);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 917e6c8..4fae35c 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -606,14 +606,14 @@ _readWindowFunc(void)
}
/*
- * _readArrayRef
+ * _readSubscriptingRef
*/
-static ArrayRef *
-_readArrayRef(void)
+static SubscriptingRef *
+_readSubscriptingRef(void)
{
- READ_LOCALS(ArrayRef);
+ READ_LOCALS(SubscriptingRef);
- READ_OID_FIELD(refarraytype);
+ READ_OID_FIELD(refcontainertype);
READ_OID_FIELD(refelemtype);
READ_INT_FIELD(reftypmod);
READ_OID_FIELD(refcollid);
@@ -2319,8 +2319,8 @@ parseNodeString(void)
return_value = _readGroupingFunc();
else if (MATCH("WINDOWFUNC", 10))
return_value = _readWindowFunc();
- else if (MATCH("ARRAYREF", 8))
- return_value = _readArrayRef();
+ else if (MATCH("SUBSCRIPTINGREF", 15))
+ return_value = _readSubscriptingRef();
else if (MATCH("FUNCEXPR", 8))
return_value = _readFuncExpr();
else if (MATCH("NAMEDARGEXPR", 12))
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 663ffe0..6e041e2 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -1329,10 +1329,10 @@ contain_nonstrict_functions_walker(Node *node, void *context)
/* a window function could return non-null with null input */
return true;
}
- if (IsA(node, ArrayRef))
+ if (IsA(node, SubscriptingRef))
{
/* array assignment is nonstrict, but subscripting is strict */
- if (((ArrayRef *) node)->refassgnexpr != NULL)
+ if (((SubscriptingRef *) node)->refassgnexpr != NULL)
return true;
/* else fall through to check args */
}
@@ -1512,7 +1512,7 @@ contain_leaked_vars_walker(Node *node, void *context)
case T_Var:
case T_Const:
case T_Param:
- case T_ArrayRef:
+ case T_SubscriptingRef:
case T_ArrayExpr:
case T_FieldSelect:
case T_FieldStore:
@@ -3573,7 +3573,7 @@ eval_const_expressions_mutator(Node *node,
* For any node type not handled above, we recurse using
* expression_tree_mutator, which will copy the node unchanged but try to
* simplify its arguments (if any) using this routine. For example: we
- * cannot eliminate an ArrayRef node, but we might be able to simplify
+ * cannot eliminate an SubscriptingRef node, but we might be able to simplify
* constant expressions in its subscripts.
*/
return expression_tree_mutator(node, eval_const_expressions_mutator,
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 6901e08..37bf24b 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -917,13 +917,13 @@ transformInsertRow(ParseState *pstate, List *exprlist,
expr = (Expr *) linitial(fstore->newvals);
}
- else if (IsA(expr, ArrayRef))
+ else if (IsA(expr, SubscriptingRef))
{
- ArrayRef *aref = (ArrayRef *) expr;
+ SubscriptingRef *sbsref = (SubscriptingRef *) expr;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
break;
- expr = aref->refassgnexpr;
+ expr = sbsref->refassgnexpr;
}
else
break;
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 63f7965..3048a59 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -461,13 +461,13 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
/* process subscripts before this field selection */
if (subscripts)
- result = (Node *) transformArraySubscripts(pstate,
- result,
- exprType(result),
- InvalidOid,
- exprTypmod(result),
- subscripts,
- NULL);
+ result = (Node *) transformContainerSubscripts(pstate,
+ result,
+ exprType(result),
+ InvalidOid,
+ exprTypmod(result),
+ subscripts,
+ NULL);
subscripts = NIL;
newresult = ParseFuncOrColumn(pstate,
@@ -482,13 +482,13 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
}
/* process trailing subscripts, if any */
if (subscripts)
- result = (Node *) transformArraySubscripts(pstate,
- result,
- exprType(result),
- InvalidOid,
- exprTypmod(result),
- subscripts,
- NULL);
+ result = (Node *) transformContainerSubscripts(pstate,
+ result,
+ exprType(result),
+ InvalidOid,
+ exprTypmod(result),
+ subscripts,
+ NULL);
return result;
}
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 62d2f71..6918a27 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -201,18 +201,22 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location)
/*
* transformArrayType()
- * Identify the types involved in a subscripting operation
+ * Identify the types involved in a subscripting operation for array
*
* On entry, arrayType/arrayTypmod identify the type of the input value
* to be subscripted (which could be a domain type). These are modified
* if necessary to identify the actual array type and typmod, and the
* array's element type is returned. An error is thrown if the input isn't
* an array type.
+ *
+ * NOTE: This part of type-specific code is not separated into type-specific
+ * subscription procedure for now, but it does not affect on the whole logic,
+ * since InvalidOid will be return in case of other types not an error.
+ * An error will appears only if a subscription procedure is not defined.
*/
Oid
-transformArrayType(Oid *arrayType, int32 *arrayTypmod)
+transformArrayType(Oid *containerType, int32 *containerTypmod)
{
- Oid origArrayType = *arrayType;
Oid elementType;
HeapTuple type_tuple_array;
Form_pg_type type_struct_array;
@@ -224,7 +228,7 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
* itself. (Note that we provide no method whereby the creator of a
* domain over an array type could hide its ability to be subscripted.)
*/
- *arrayType = getBaseTypeAndTypmod(*arrayType, arrayTypmod);
+ *containerType = getBaseTypeAndTypmod(*containerType, containerTypmod);
/*
* We treat int2vector and oidvector as though they were domains over
@@ -233,25 +237,20 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
* xxxvector type; so we want the result of a slice operation to be
* considered to be of the more general type.
*/
- if (*arrayType == INT2VECTOROID)
- *arrayType = INT2ARRAYOID;
- else if (*arrayType == OIDVECTOROID)
- *arrayType = OIDARRAYOID;
+ if (*containerType == INT2VECTOROID)
+ *containerType = INT2ARRAYOID;
+ else if (*containerType == OIDVECTOROID)
+ *containerType = OIDARRAYOID;
/* Get the type tuple for the array */
- type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*arrayType));
+ type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*containerType));
if (!HeapTupleIsValid(type_tuple_array))
- elog(ERROR, "cache lookup failed for type %u", *arrayType);
+ elog(ERROR, "cache lookup failed for type %u", *containerType);
type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array);
/* needn't check typisdefined since this will fail anyway */
elementType = type_struct_array->typelem;
- if (elementType == InvalidOid)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("cannot subscript type %s because it is not an array",
- format_type_be(origArrayType))));
ReleaseSysCache(type_tuple_array);
@@ -259,61 +258,80 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
}
/*
- * transformArraySubscripts()
- * Transform array subscripting. This is used for both
- * array fetch and array assignment.
+ * transformContainerSubscripts()
+ * Transform container subscripting. This is used for both
+ * container fetch and container assignment.
*
- * In an array fetch, we are given a source array value and we produce an
- * expression that represents the result of extracting a single array element
- * or an array slice.
+ * In a container fetch, we are given a source container value and we produce
+ * an expression that represents the result of extracting a single container
+ * element or a container slice.
*
- * In an array assignment, we are given a destination array value plus a
- * source value that is to be assigned to a single element or a slice of
- * that array. We produce an expression that represents the new array value
- * with the source data inserted into the right part of the array.
+ * In a container assignment, we are given a destination container value plus a
+ * source value that is to be assigned to a single element or a slice of that
+ * container. We produce an expression that represents the new container value
+ * with the source data inserted into the right part of the container.
*
- * For both cases, if the source array is of a domain-over-array type,
- * the result is of the base array type or its element type; essentially,
- * we must fold a domain to its base type before applying subscripting.
- * (Note that int2vector and oidvector are treated as domains here.)
+ * For both cases, this function contains only general subscription logic while
+ * type-specific logic (e.g. type verifications and coersion) is placend in
+ * separate procedure indicated by typsubscription. There is only one exception
+ * for now about domain-over-container, if the source container is of a
+ * domain-over-container type, the result is of the base container type or its
+ * element type; essentially, we must fold a domain to its base type before
+ * applying subscripting. (Note that int2vector and oidvector are treated as
+ * domains here.) If domain verification failed we assume, that element type
+ * must be the same as container type (e.g. in case of jsonb).
+ * An error will appear in case if current container type doesn't have a
+ * subscription procedure.
*
- * pstate Parse state
- * arrayBase Already-transformed expression for the array as a whole
- * arrayType OID of array's datatype (should match type of arrayBase,
- * or be the base type of arrayBase's domain type)
- * elementType OID of array's element type (fetch with transformArrayType,
- * or pass InvalidOid to do it here)
- * arrayTypMod typmod for the array (which is also typmod for the elements)
- * indirection Untransformed list of subscripts (must not be NIL)
- * assignFrom NULL for array fetch, else transformed expression for source.
+ * pstate Parse state
+ * containerBase Already-transformed expression for the container as a whole
+ * containerType OID of container's datatype (should match type of containerBase,
+ * or be the base type of containerBase's domain type)
+ * elementType OID of container's element type (fetch with transformArrayType,
+ * or pass InvalidOid to do it here)
+ * containerTypMod typmod for the container (which is also typmod for the elements)
+ * indirection Untransformed list of subscripts (must not be NIL)
+ * assignFrom NULL for container fetch, else transformed expression for source.
*/
-ArrayRef *
-transformArraySubscripts(ParseState *pstate,
- Node *arrayBase,
- Oid arrayType,
- Oid elementType,
- int32 arrayTypMod,
- List *indirection,
- Node *assignFrom)
+
+SubscriptingRef *
+transformContainerSubscripts(ParseState *pstate,
+ Node *containerBase,
+ Oid containerType,
+ Oid elementType,
+ int32 containerTypMod,
+ List *indirection,
+ Node *assignFrom)
{
- bool isSlice = false;
- List *upperIndexpr = NIL;
- List *lowerIndexpr = NIL;
- ListCell *idx;
- ArrayRef *aref;
+ bool isSlice = false;
+ List *upperIndexpr = NIL;
+ List *lowerIndexpr = NIL;
+ ListCell *idx;
+ SubscriptingRef *sbsref,
+ *prepared_sbsref;
+ RegProcedure typsubscription = get_typsubscription(containerType);
+
+ if (!OidIsValid(typsubscription))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("cannot subscript type %s because it does not support subscription",
+ format_type_be(containerType))));
/*
* Caller may or may not have bothered to determine elementType. Note
- * that if the caller did do so, arrayType/arrayTypMod must be as modified
+ * that if the caller did do so, containerType/containerTypMod must be as modified
* by transformArrayType, ie, smash domain to base type.
*/
if (!OidIsValid(elementType))
- elementType = transformArrayType(&arrayType, &arrayTypMod);
+ elementType = transformArrayType(&containerType, &containerTypMod);
+
+ if (!OidIsValid(elementType))
+ elementType = containerType;
/*
- * A list containing only simple subscripts refers to a single array
+ * A list containing only simple subscripts refers to a single container
* element. If any of the items are slice specifiers (lower:upper), then
- * the subscript expression means an array slice operation. In this case,
+ * the subscript expression means an container slice operation. In this case,
* we convert any non-slice items to slices by treating the single
* subscript as the upper bound and supplying an assumed lower bound of 1.
* We have to prescan the list to see if there are any slice items.
@@ -343,107 +361,37 @@ transformArraySubscripts(ParseState *pstate,
if (ai->lidx)
{
subexpr = transformExpr(pstate, ai->lidx, pstate->p_expr_kind);
- /* If it's not int4 already, try to coerce */
- subexpr = coerce_to_target_type(pstate,
- subexpr, exprType(subexpr),
- INT4OID, -1,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (subexpr == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array subscript must have type integer"),
- parser_errposition(pstate, exprLocation(ai->lidx))));
- }
- else if (!ai->is_slice)
- {
- /* Make a constant 1 */
- subexpr = (Node *) makeConst(INT4OID,
- -1,
- InvalidOid,
- sizeof(int32),
- Int32GetDatum(1),
- false,
- true); /* pass by value */
}
else
{
/* Slice with omitted lower bound, put NULL into the list */
subexpr = NULL;
}
- lowerIndexpr = lappend(lowerIndexpr, subexpr);
- }
- else
- Assert(ai->lidx == NULL && !ai->is_slice);
-
- if (ai->uidx)
- {
- subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
- /* If it's not int4 already, try to coerce */
- subexpr = coerce_to_target_type(pstate,
- subexpr, exprType(subexpr),
- INT4OID, -1,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (subexpr == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array subscript must have type integer"),
- parser_errposition(pstate, exprLocation(ai->uidx))));
- }
- else
- {
- /* Slice with omitted upper bound, put NULL into the list */
- Assert(isSlice && ai->is_slice);
- subexpr = NULL;
+ lowerIndexpr = lappend(lowerIndexpr, list_make2(subexpr, ai));
}
+ subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
upperIndexpr = lappend(upperIndexpr, subexpr);
}
/*
- * If doing an array store, coerce the source value to the right type.
- * (This should agree with the coercion done by transformAssignedExpr.)
- */
- if (assignFrom != NULL)
- {
- Oid typesource = exprType(assignFrom);
- Oid typeneeded = isSlice ? arrayType : elementType;
- Node *newFrom;
-
- newFrom = coerce_to_target_type(pstate,
- assignFrom, typesource,
- typeneeded, arrayTypMod,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (newFrom == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array assignment requires type %s"
- " but expression is of type %s",
- format_type_be(typeneeded),
- format_type_be(typesource)),
- errhint("You will need to rewrite or cast the expression."),
- parser_errposition(pstate, exprLocation(assignFrom))));
- assignFrom = newFrom;
- }
-
- /*
- * Ready to build the ArrayRef node.
+ * Ready to build the SubscriptingRef node.
*/
- aref = makeNode(ArrayRef);
- aref->refarraytype = arrayType;
- aref->refelemtype = elementType;
- aref->reftypmod = arrayTypMod;
+ sbsref = makeNode(SubscriptingRef);
+ sbsref->refcontainertype = containerType;
+ sbsref->refelemtype = elementType;
+ sbsref->reftypmod = containerTypMod;
/* refcollid will be set by parse_collate.c */
- aref->refupperindexpr = upperIndexpr;
- aref->reflowerindexpr = lowerIndexpr;
- aref->refexpr = (Expr *) arrayBase;
- aref->refassgnexpr = (Expr *) assignFrom;
+ sbsref->refupperindexpr = upperIndexpr;
+ sbsref->reflowerindexpr = lowerIndexpr;
+ sbsref->refexpr = (Expr *) containerBase;
+ sbsref->refassgnexpr = (Expr *) assignFrom;
+
+ prepared_sbsref = (SubscriptingRef *) OidFunctionCall3(typsubscription,
+ Int32GetDatum(SBS_VALIDATION),
+ PointerGetDatum(sbsref),
+ PointerGetDatum(pstate));
- return aref;
+ return prepared_sbsref;
}
/*
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index b7b82bf..e2b0741 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -759,41 +759,24 @@ transformAssignmentIndirection(ParseState *pstate,
/* base case: just coerce RHS to match target type ID */
- result = coerce_to_target_type(pstate,
- rhs, exprType(rhs),
- targetTypeId, targetTypMod,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
+ if (targetTypeId != InvalidOid)
+ result = coerce_to_target_type(pstate,
+ rhs, exprType(rhs),
+ targetTypeId, targetTypMod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ else
+ result = rhs;
+
if (result == NULL)
- {
- if (targetIsArray)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array assignment to \"%s\" requires type %s"
- " but expression is of type %s",
- targetName,
- format_type_be(targetTypeId),
- format_type_be(exprType(rhs))),
- errhint("You will need to rewrite or cast the expression."),
- parser_errposition(pstate, location)));
- else
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("subfield \"%s\" is of type %s"
- " but expression is of type %s",
- targetName,
- format_type_be(targetTypeId),
- format_type_be(exprType(rhs))),
- errhint("You will need to rewrite or cast the expression."),
- parser_errposition(pstate, location)));
- }
+ result = rhs;
return result;
}
/*
- * helper for transformAssignmentIndirection: process array assignment
+ * helper for transformAssignmentIndirection: process container assignment
*/
static Node *
transformAssignmentSubscripts(ParseState *pstate,
@@ -809,55 +792,55 @@ transformAssignmentSubscripts(ParseState *pstate,
int location)
{
Node *result;
- Oid arrayType;
- int32 arrayTypMod;
+ Oid containerType;
+ int32 containerTypMod;
Oid elementTypeId;
Oid typeNeeded;
Oid collationNeeded;
Assert(subscripts != NIL);
- /* Identify the actual array type and element type involved */
- arrayType = targetTypeId;
- arrayTypMod = targetTypMod;
- elementTypeId = transformArrayType(&arrayType, &arrayTypMod);
+ /* Identify the actual container type and element type involved */
+ containerType = targetTypeId;
+ containerTypMod = targetTypMod;
+ elementTypeId = transformArrayType(&containerType, &containerTypMod);
/* Identify type that RHS must provide */
- typeNeeded = isSlice ? arrayType : elementTypeId;
+ typeNeeded = isSlice ? containerType : elementTypeId;
/*
- * Array normally has same collation as elements, but there's an
- * exception: we might be subscripting a domain over an array type. In
+ * container normally has same collation as elements, but there's an
+ * exception: we might be subscripting a domain over an container type. In
* that case use collation of the base type.
*/
- if (arrayType == targetTypeId)
+ if (containerType == targetTypeId)
collationNeeded = targetCollation;
else
- collationNeeded = get_typcollation(arrayType);
+ collationNeeded = get_typcollation(containerType);
- /* recurse to create appropriate RHS for array assign */
+ /* recurse to create appropriate RHS for container assign */
rhs = transformAssignmentIndirection(pstate,
NULL,
targetName,
true,
typeNeeded,
- arrayTypMod,
+ containerTypMod,
collationNeeded,
next_indirection,
rhs,
location);
/* process subscripts */
- result = (Node *) transformArraySubscripts(pstate,
- basenode,
- arrayType,
- elementTypeId,
- arrayTypMod,
- subscripts,
- rhs);
-
- /* If target was a domain over array, need to coerce up to the domain */
- if (arrayType != targetTypeId)
+ result = (Node *) transformContainerSubscripts(pstate,
+ basenode,
+ containerType,
+ exprType(rhs),
+ containerTypMod,
+ subscripts,
+ rhs);
+
+ /* If target was a domain over container, need to coerce up to the domain */
+ if (containerType != targetTypeId)
{
Oid resulttype = exprType(result);
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index b828e3c..c09ac03 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -895,7 +895,7 @@ process_matched_tle(TargetEntry *src_tle,
/*----------
* Multiple assignments to same attribute. Allow only if all are
- * FieldStore or ArrayRef assignment operations. This is a bit
+ * FieldStore or SubscriptingRef assignment operations. This is a bit
* tricky because what we may actually be looking at is a nest of
* such nodes; consider
* UPDATE tab SET col.fld1.subfld1 = x, col.fld2.subfld2 = y
@@ -903,7 +903,7 @@ process_matched_tle(TargetEntry *src_tle,
* FieldStore(col, fld1, FieldStore(placeholder, subfld1, x))
* FieldStore(col, fld2, FieldStore(placeholder, subfld2, y))
* However, we can ignore the substructure and just consider the top
- * FieldStore or ArrayRef from each assignment, because it works to
+ * FieldStore or SubscriptingRef from each assignment, because it works to
* combine these as
* FieldStore(FieldStore(col, fld1,
* FieldStore(placeholder, subfld1, x)),
@@ -913,7 +913,7 @@ process_matched_tle(TargetEntry *src_tle,
*
* For FieldStore, instead of nesting we can generate a single
* FieldStore with multiple target fields. We must nest when
- * ArrayRefs are involved though.
+ * SubscriptingRefs are involved though.
*----------
*/
src_expr = (Node *) src_tle->expr;
@@ -972,13 +972,13 @@ process_matched_tle(TargetEntry *src_tle,
}
newexpr = (Node *) fstore;
}
- else if (IsA(src_expr, ArrayRef))
+ else if (IsA(src_expr, SubscriptingRef))
{
- ArrayRef *aref = makeNode(ArrayRef);
+ SubscriptingRef *sbsref = makeNode(SubscriptingRef);
- memcpy(aref, src_expr, sizeof(ArrayRef));
- aref->refexpr = (Expr *) prior_expr;
- newexpr = (Node *) aref;
+ memcpy(sbsref, src_expr, sizeof(SubscriptingRef));
+ sbsref->refexpr = (Expr *) prior_expr;
+ newexpr = (Node *) sbsref;
}
else
{
@@ -1005,14 +1005,15 @@ get_assignment_input(Node *node)
return (Node *) fstore->arg;
}
- else if (IsA(node, ArrayRef))
+ else if (IsA(node, SubscriptingRef))
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
return NULL;
- return (Node *) aref->refexpr;
+ return (Node *) sbsref->refexpr;
}
+
return NULL;
}
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 1db7bf0..4db7bd4 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -24,6 +24,8 @@
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "libpq/pqformat.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
#include "utils/array.h"
#include "utils/arrayaccess.h"
#include "utils/builtins.h"
@@ -31,6 +33,8 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
+#include "parser/parse_node.h"
+#include "parser/parse_coerce.h"
/*
@@ -88,6 +92,7 @@ typedef struct ArrayIteratorData
static bool array_isspace(char ch);
static int ArrayCount(const char *str, int *dim, char typdelim);
+static bool isAssignmentIndirectionExpr(ExprState *exprstate);
static void ReadArrayStr(char *arrayStr, const char *origStr,
int nitems, int ndim, int *dim,
FmgrInfo *inputproc, Oid typioparam, int32 typmod,
@@ -157,7 +162,8 @@ static int width_bucket_array_variable(Datum operand,
ArrayType *thresholds,
Oid collation,
TypeCacheEntry *typentry);
-
+static Datum array_subscription_prepare(PG_FUNCTION_ARGS);
+static Datum array_subscription_evaluate(PG_FUNCTION_ARGS);
/*
* array_in :
@@ -6520,3 +6526,363 @@ width_bucket_array_variable(Datum operand,
return left;
}
+
+/*
+ * Helper for ExecEvalSubscriptingRef: is expr a nested FieldStore or SubscriptingRef
+ * that might need the old element value passed down?
+ *
+ * (We could use this in ExecEvalFieldStore too, but in that case passing
+ * the old value is so cheap there's no need.)
+ */
+static bool
+isAssignmentIndirectionExpr(ExprState *exprstate)
+{
+ if (exprstate == NULL)
+ return false; /* just paranoia */
+ if (IsA(exprstate, FieldStoreState))
+ {
+ FieldStore *fstore = (FieldStore *) exprstate->expr;
+
+ if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
+ return true;
+ }
+ else if (IsA(exprstate, SubscriptingRefExprState))
+ {
+ SubscriptingRef *array_ref = (SubscriptingRef *) exprstate->expr;
+
+ if (array_ref->refexpr && IsA(array_ref->refexpr, CaseTestExpr))
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Perform an actual data extraction or modification for the array
+ * subscription. As a result the extracted Datum or the modified containers
+ * value will be returned.
+ */
+Datum
+array_subscription_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptingRefExprState *sbstate = (SubscriptingRefExprState *) PG_GETARG_POINTER(0);
+ SubscriptingExecData *sbsdata = (SubscriptingExecData *) PG_GETARG_POINTER(1);
+ ExprContext *econtext = sbsdata->xprcontext;
+ bool *is_null = sbsdata->isNull;
+ SubscriptingRef *array_ref = (SubscriptingRef *) sbstate->xprstate.expr;
+ bool is_assignment = (array_ref->refassgnexpr != NULL);
+ bool is_slice = (array_ref->reflowerindexpr != NIL);
+ IntArray u_index, l_index;
+ bool eisnull;
+ int i = 0;
+
+ get_typlenbyvalalign(array_ref->refelemtype,
+ &sbstate->refelemlength,
+ &sbstate->refelembyval,
+ &sbstate->refelemalign);
+
+ for(i = 0; i < sbsdata->indexprNumber; i++)
+ u_index.indx[i] = DatumGetInt32(sbsdata->upper[i]);
+
+ if (is_slice)
+ {
+ for(i = 0; i < sbsdata->indexprNumber; i++)
+ l_index.indx[i] = DatumGetInt32(sbsdata->lower[i]);
+ }
+
+ if (is_assignment)
+ {
+ Datum sourceData;
+ Datum save_datum;
+ bool save_isNull;
+
+ /*
+ * We might have a nested-assignment situation, in which the
+ * refassgnexpr is itself a FieldStore or SubscriptingRef that needs to
+ * obtain and modify the previous value of the array element or slice
+ * being replaced. If so, we have to extract that value from the
+ * array and pass it down via the econtext's caseValue. It's safe to
+ * reuse the CASE mechanism because there cannot be a CASE between
+ * here and where the value would be needed, and an array assignment
+ * can't be within a CASE either. (So saving and restoring the
+ * caseValue is just paranoia, but let's do it anyway.)
+ *
+ * Since fetching the old element might be a nontrivial expense, do it
+ * only if the argument appears to actually need it.
+ */
+ save_datum = econtext->caseValue_datum;
+ save_isNull = econtext->caseValue_isNull;
+
+ if (isAssignmentIndirectionExpr(sbstate->refassgnexpr))
+ {
+ if (*is_null)
+ {
+ /* whole array is null, so any element or slice is too */
+ econtext->caseValue_datum = (Datum) 0;
+ econtext->caseValue_isNull = true;
+ }
+ else if (!is_slice)
+ {
+ econtext->caseValue_datum =
+ array_get_element(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign,
+ &econtext->caseValue_isNull);
+ }
+ else
+ {
+ econtext->caseValue_datum =
+ array_get_slice(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, l_index.indx,
+ sbsdata->upperProvided,
+ sbsdata->lowerProvided,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+ econtext->caseValue_isNull = false;
+ }
+ }
+ else
+ {
+ /* argument shouldn't need caseValue, but for safety set it null */
+ econtext->caseValue_datum = (Datum) 0;
+ econtext->caseValue_isNull = true;
+ }
+
+ /*
+ * Evaluate the value to be assigned into the array.
+ */
+ sourceData = ExecEvalExpr(sbstate->refassgnexpr,
+ econtext,
+ &eisnull,
+ NULL);
+
+ econtext->caseValue_datum = save_datum;
+ econtext->caseValue_isNull = save_isNull;
+
+ /*
+ * For an assignment to a fixed-length array type, both the original
+ * array and the value to be assigned into it must be non-NULL, else
+ * we punt and return the original array.
+ */
+ if (sbstate->refattrlength > 0) /* fixed-length array? */
+ if (eisnull || *is_null)
+ return sbsdata->containerSource;
+
+ /*
+ * For assignment to varlena arrays, we handle a NULL original array
+ * by substituting an empty (zero-dimensional) array; insertion of the
+ * new element will result in a singleton array value. It does not
+ * matter whether the new element is NULL.
+ */
+ if (*is_null)
+ {
+ sbsdata->containerSource = PointerGetDatum(construct_empty_array(array_ref->refelemtype));
+ *is_null = false;
+ }
+
+ if (!is_slice)
+ return array_set_element(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, sourceData, eisnull,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+ else
+ return array_set_slice(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, l_index.indx,
+ sbsdata->upperProvided,
+ sbsdata->lowerProvided,
+ sourceData, eisnull,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+ }
+
+ if (!is_slice)
+ return array_get_element(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign,
+ is_null);
+ else
+ return array_get_slice(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, l_index.indx,
+ sbsdata->upperProvided,
+ sbsdata->lowerProvided,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+}
+
+/*
+ * Perform preparation for the array subscription, mostly type verification
+ * and coersion. This function produces an expression that represents the
+ * result of extracting a single container element/container slice or the new
+ * container value with the source data inserted into the right part of the
+ * container.
+ */
+Datum
+array_subscription_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptingRef *sbsref = (SubscriptingRef *) PG_GETARG_POINTER(0);
+ ParseState *pstate = (ParseState *) PG_GETARG_POINTER(1);
+ Node *node = (Node *)sbsref;
+ Oid array_type = sbsref->refcontainertype;
+ int32 array_typ_mode = (int32) sbsref->reftypmod;
+ bool is_slice = sbsref->reflowerindexpr != NIL;
+ Oid typeneeded = InvalidOid,
+ typesource = InvalidOid;
+ Node *new_from;
+ Oid element_type_id;
+ Node *subexpr;
+ List *upperIndexpr = NIL;
+ List *lowerIndexpr = NIL;
+ ListCell *l;
+
+ element_type_id = transformArrayType(&array_type, &array_typ_mode);
+ sbsref->refelemtype = element_type_id;
+
+ foreach(l, sbsref->refupperindexpr)
+ {
+ subexpr = (Node *) lfirst(l);
+
+ if (subexpr == NULL)
+ {
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ continue;
+ }
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ INT4OID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("array subscript must have type integer"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ }
+
+ sbsref->refupperindexpr = upperIndexpr;
+
+ foreach(l, sbsref->reflowerindexpr)
+ {
+ List *expr_ai = (List *) lfirst(l);
+ A_Indices *ai = (A_Indices *) lfirst(list_tail(expr_ai));
+
+ subexpr = (Node *) lfirst(list_head(expr_ai));
+ if (subexpr == NULL && !ai->is_slice)
+ {
+ /* Make a constant 1 */
+ subexpr = (Node *) makeConst(INT4OID,
+ -1,
+ InvalidOid,
+ sizeof(int32),
+ Int32GetDatum(1),
+ false,
+ true); /* pass by value */
+ }
+
+ if (subexpr == NULL)
+ {
+ lowerIndexpr = lappend(lowerIndexpr, subexpr);
+ continue;
+ }
+
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ INT4OID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("array subscript must have type integer"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ lowerIndexpr = lappend(lowerIndexpr, subexpr);
+ }
+
+ sbsref->reflowerindexpr = lowerIndexpr;
+
+ if (sbsref->refassgnexpr != NULL)
+ {
+ new_from = coerce_to_target_type(pstate,
+ (Node *)sbsref->refassgnexpr, typesource,
+ typeneeded, sbsref->reftypmod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (new_from == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("array assignment requires type %s"
+ " but expression is of type %s",
+ format_type_be(typeneeded),
+ format_type_be(typesource)),
+ errhint("You will need to rewrite or cast the expression."),
+ parser_errposition(pstate, exprLocation((Node *)sbsref->refassgnexpr))));
+ sbsref->refassgnexpr = (Expr *)new_from;
+
+ if (array_type != sbsref->refcontainertype)
+ {
+ typesource = exprType((Node *)sbsref->refassgnexpr);
+ typesource = is_slice ? sbsref->refcontainertype : sbsref->refelemtype;
+
+ node = coerce_to_target_type(pstate,
+ node, array_type,
+ sbsref->refcontainertype, sbsref->reftypmod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+
+ /* can fail if we had int2vector/oidvector, but not for true domains */
+ if (node == NULL && node->type != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_CANNOT_COERCE),
+ errmsg("cannot cast type %s to %s",
+ format_type_be(array_type),
+ format_type_be(sbsref->refcontainertype)),
+ parser_errposition(pstate, 0)));
+
+ PG_RETURN_POINTER(node);
+ }
+
+ }
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+/*
+ * Handle array-type subscription logic.
+ */
+Datum
+array_subscription(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1,
+ fcinfo->nargs);
+
+ if (op_type & SBS_VALIDATION)
+ return array_subscription_prepare(&target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return array_subscription_evaluate(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscription function: %d", op_type);
+}
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index 987cfd1..27a5e89 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -1145,23 +1145,34 @@ to_jsonb(PG_FUNCTION_ARGS)
{
Datum val = PG_GETARG_DATUM(0);
Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
- JsonbInState result;
- JsonbTypeCategory tcategory;
- Oid outfuncoid;
+ JsonbValue *res = to_jsonb_worker(val, val_type);
+ PG_RETURN_POINTER(JsonbValueToJsonb(res));
+}
- if (val_type == InvalidOid)
+/*
+ * Do the actual conversion to jsonb for to_jsonb function. This logic is
+ * separated because it can be useful not only in here (e.g. we use it in
+ * jsonb subscription)
+ */
+JsonbValue *
+to_jsonb_worker(Datum source, Oid source_type)
+{
+ JsonbInState result;
+ JsonbTypeCategory tcategory;
+ Oid outfuncoid;
+
+ if (source_type == InvalidOid)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("could not determine input data type")));
- jsonb_categorize_type(val_type,
+ jsonb_categorize_type(source_type,
&tcategory, &outfuncoid);
memset(&result, 0, sizeof(JsonbInState));
- datum_to_jsonb(val, false, &result, tcategory, outfuncoid, false);
-
- PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
+ datum_to_jsonb(source, false, &result, tcategory, outfuncoid, false);
+ return result.res;
}
/*
diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c
index ddc34ce..5e1b682 100644
--- a/src/backend/utils/adt/jsonb_util.c
+++ b/src/backend/utils/adt/jsonb_util.c
@@ -61,18 +61,29 @@ static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate,
JsonbIteratorToken seq,
JsonbValue *scalarVal);
+JsonbValue *
+JsonbToJsonbValue(Jsonb *jsonb)
+{
+ JsonbValue *val = (JsonbValue *) palloc(sizeof(JsonbValue));
+
+ val->type = jbvBinary;
+ val->val.binary.data = &jsonb->root;
+ val->val.binary.len = VARSIZE(jsonb) - VARHDRSZ;
+
+ return val;
+}
+
/*
* Turn an in-memory JsonbValue into a Jsonb for on-disk storage.
*
- * There isn't a JsonbToJsonbValue(), because generally we find it more
- * convenient to directly iterate through the Jsonb representation and only
- * really convert nested scalar values. JsonbIteratorNext() does this, so that
- * clients of the iteration code don't have to directly deal with the binary
- * representation (JsonbDeepContains() is a notable exception, although all
- * exceptions are internal to this module). In general, functions that accept
- * a JsonbValue argument are concerned with the manipulation of scalar values,
- * or simple containers of scalar values, where it would be inconvenient to
- * deal with a great amount of other state.
+ * Generally we find it more convenient to directly iterate through the Jsonb
+ * representation and only really convert nested scalar values.
+ * JsonbIteratorNext() does this, so that clients of the iteration code don't
+ * have to directly deal with the binary representation (JsonbDeepContains() is
+ * a notable exception, although all exceptions are internal to this module).
+ * In general, functions that accept a JsonbValue argument are concerned with
+ * the manipulation of scalar values, or simple containers of scalar values,
+ * where it would be inconvenient to deal with a great amount of other state.
*/
Jsonb *
JsonbValueToJsonb(JsonbValue *val)
@@ -520,6 +531,30 @@ pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq,
JsonbValue *res = NULL;
JsonbValue v;
JsonbIteratorToken tok;
+ int i;
+
+ if (jbval && (seq == WJB_ELEM || seq == WJB_VALUE) && jbval->type == jbvObject)
+ {
+ pushJsonbValue(pstate, WJB_BEGIN_OBJECT, NULL);
+ for (i = 0; i < jbval->val.object.nPairs; i++)
+ {
+ pushJsonbValue(pstate, WJB_KEY, &jbval->val.object.pairs[i].key);
+ pushJsonbValue(pstate, WJB_VALUE, &jbval->val.object.pairs[i].value);
+ }
+
+ return pushJsonbValue(pstate, WJB_END_OBJECT, NULL);
+ }
+
+ if (jbval && (seq == WJB_ELEM || seq == WJB_VALUE) && jbval->type == jbvArray)
+ {
+ pushJsonbValue(pstate, WJB_BEGIN_ARRAY, NULL);
+ for (i = 0; i < jbval->val.array.nElems; i++)
+ {
+ pushJsonbValue(pstate, WJB_ELEM, &jbval->val.array.elems[i]);
+ }
+
+ return pushJsonbValue(pstate, WJB_END_ARRAY, NULL);
+ }
if (!jbval || (seq != WJB_ELEM && seq != WJB_VALUE) ||
jbval->type != jbvBinary)
@@ -530,9 +565,30 @@ pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq,
/* unpack the binary and add each piece to the pstate */
it = JsonbIteratorInit(jbval->val.binary.data);
+
+ if ((jbval->val.binary.data->header & JB_FSCALAR) && *pstate)
+ {
+ tok = JsonbIteratorNext(&it, &v, true);
+ Assert(tok == WJB_BEGIN_ARRAY);
+ Assert(v.type == jbvArray && v.val.array.rawScalar);
+
+ tok = JsonbIteratorNext(&it, &v, true);
+ Assert(tok == WJB_ELEM);
+
+ res = pushJsonbValueScalar(pstate, seq, &v);
+
+ tok = JsonbIteratorNext(&it, &v, true);
+ Assert(tok == WJB_END_ARRAY);
+ Assert(it == NULL);
+
+ return res;
+ }
+
while ((tok = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
res = pushJsonbValueScalar(pstate, tok,
- tok < WJB_BEGIN_ARRAY ? &v : NULL);
+ tok < WJB_BEGIN_ARRAY ||
+ (tok == WJB_BEGIN_ARRAY &&
+ v.val.array.rawScalar) ? &v : NULL);
return res;
}
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 059d570..ec3c520 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -23,6 +23,8 @@
#include "lib/stringinfo.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_coerce.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/hsearch.h"
@@ -32,6 +34,7 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
+#include "parser/parse_node.h"
/* Operations available for setPath */
#define JB_PATH_CREATE 0x0001
@@ -136,18 +139,21 @@ static JsonbValue *findJsonbValueFromContainerLen(JsonbContainer *container,
/* functions supporting jsonb_delete, jsonb_set and jsonb_concat */
static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
JsonbParseState **state);
+static Datum jsonb_set_element(Datum datum, Datum *path, int path_len,
+ Datum sourceData, Oid source_type);
+static Datum jsonb_get_element(Jsonb *jb, Datum *path, int npath,
+ bool *isnull, bool as_text);
static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len,
- JsonbParseState **st, int level, Jsonb *newval,
- int op_type);
+ JsonbParseState **st, int level, JsonbValue *newval, int op_type);
static void setPathObject(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len, JsonbParseState **st,
- int level,
- Jsonb *newval, uint32 npairs, int op_type);
+ int level, JsonbValue *newval, uint32 npairs, int op_type);
static void setPathArray(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len, JsonbParseState **st,
- int level, Jsonb *newval, uint32 nelems, int op_type);
-static void addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb);
+ int level, JsonbValue *newval, uint32 nelems, int op_type);
+static Datum jsonb_subscription_evaluate(PG_FUNCTION_ARGS);
+static Datum jsonb_subscription_prepare(PG_FUNCTION_ARGS);
/* state for json_object_keys */
typedef struct OkeysState
@@ -1171,16 +1177,11 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
Jsonb *jb = PG_GETARG_JSONB(0);
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
- Jsonb *res;
Datum *pathtext;
bool *pathnulls;
+ bool isnull;
int npath;
- int i;
- bool have_object = false,
- have_array = false;
- JsonbValue *jbvp = NULL;
- JsonbValue tv;
- JsonbContainer *container;
+ Datum res;
/*
* If the array contains any null elements, return NULL, on the grounds
@@ -1195,9 +1196,28 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
deconstruct_array(path, TEXTOID, -1, false, 'i',
&pathtext, &pathnulls, &npath);
- /* Identify whether we have object, array, or scalar at top-level */
- container = &jb->root;
+ res = jsonb_get_element(jb, pathtext, npath, &isnull, as_text);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(res);
+}
+static Datum
+jsonb_get_element(Jsonb *jb, Datum *path, int npath, bool *isnull, bool as_text)
+{
+ Jsonb *res;
+ JsonbContainer *container = &jb->root;
+ JsonbValue *jbvp = NULL;
+ JsonbValue tv;
+ int i;
+ bool have_object = false,
+ have_array = false;
+
+ *isnull = false;
+
+ /* Identify whether we have object, array, or scalar at top-level */
if (JB_ROOT_IS_OBJECT(jb))
have_object = true;
else if (JB_ROOT_IS_ARRAY(jb) && !JB_ROOT_IS_SCALAR(jb))
@@ -1222,14 +1242,14 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
if (as_text)
{
- PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL,
+ return PointerGetDatum(cstring_to_text(JsonbToCString(NULL,
container,
VARSIZE(jb))));
}
else
{
/* not text mode - just hand back the jsonb */
- PG_RETURN_JSONB(jb);
+ return JsonbGetDatum(jb);
}
}
@@ -1239,21 +1259,24 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
jbvp = findJsonbValueFromContainerLen(container,
JB_FOBJECT,
- VARDATA_ANY(pathtext[i]),
- VARSIZE_ANY_EXHDR(pathtext[i]));
+ VARDATA_ANY(path[i]),
+ VARSIZE_ANY_EXHDR(path[i]));
}
else if (have_array)
{
long lindex;
uint32 index;
- char *indextext = TextDatumGetCString(pathtext[i]);
+ char *indextext = TextDatumGetCString(path[i]);
char *endptr;
errno = 0;
lindex = strtol(indextext, &endptr, 10);
if (endptr == indextext || *endptr != '\0' || errno != 0 ||
lindex > INT_MAX || lindex < INT_MIN)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
if (lindex >= 0)
{
@@ -1271,7 +1294,10 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
nelements = container->header & JB_CMASK;
if (-lindex > nelements)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
else
index = nelements + lindex;
}
@@ -1281,11 +1307,15 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
else
{
/* scalar, extraction yields a null */
- PG_RETURN_NULL();
+ *isnull = true;
+ return PointerGetDatum(NULL);
}
if (jbvp == NULL)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
else if (i == npath - 1)
break;
@@ -1310,27 +1340,57 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
/* special-case outputs for string and null values */
if (jbvp->type == jbvString)
- PG_RETURN_TEXT_P(cstring_to_text_with_len(jbvp->val.string.val,
- jbvp->val.string.len));
+ return PointerGetDatum(
+ cstring_to_text_with_len(jbvp->val.string.val,
+ jbvp->val.string.len));
if (jbvp->type == jbvNull)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
}
res = JsonbValueToJsonb(jbvp);
if (as_text)
{
- PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL,
+ return PointerGetDatum(cstring_to_text(JsonbToCString(NULL,
&res->root,
VARSIZE(res))));
}
else
{
/* not text mode - just hand back the jsonb */
- PG_RETURN_JSONB(res);
+ return JsonbGetDatum(res);
}
}
+Datum
+jsonb_set_element(Datum jsonbdatum, Datum *path, int path_len,
+ Datum sourceData, Oid source_type)
+{
+ Jsonb *jb = DatumGetJsonb(jsonbdatum);
+ JsonbValue *newval,
+ *res;
+ JsonbParseState *state = NULL;
+ JsonbIterator *it;
+ bool *path_nulls = palloc0(path_len * sizeof(bool));
+
+ newval = to_jsonb_worker(sourceData, source_type);
+
+ if (newval->type == jbvArray && newval->val.array.rawScalar)
+ *newval = newval->val.array.elems[0];
+
+ it = JsonbIteratorInit(&jb->root);
+
+ res = setPath(&it, path, path_nulls, path_len, &state, 0,
+ newval, JB_PATH_CREATE);
+
+ pfree(path_nulls);
+
+ PG_RETURN_JSONB(JsonbValueToJsonb(res));
+}
+
/*
* SQL function json_array_length(json) -> int
*/
@@ -3278,57 +3338,6 @@ jsonb_strip_nulls(PG_FUNCTION_ARGS)
}
/*
- * Add values from the jsonb to the parse state.
- *
- * If the parse state container is an object, the jsonb is pushed as
- * a value, not a key.
- *
- * This needs to be done using an iterator because pushJsonbValue doesn't
- * like getting jbvBinary values, so we can't just push jb as a whole.
- */
-static void
-addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb)
-{
- JsonbIterator *it;
- JsonbValue *o = &(*jbps)->contVal;
- JsonbValue v;
- JsonbIteratorToken type;
-
- it = JsonbIteratorInit(&jb->root);
-
- Assert(o->type == jbvArray || o->type == jbvObject);
-
- if (JB_ROOT_IS_SCALAR(jb))
- {
- (void) JsonbIteratorNext(&it, &v, false); /* skip array header */
- (void) JsonbIteratorNext(&it, &v, false); /* fetch scalar value */
-
- switch (o->type)
- {
- case jbvArray:
- (void) pushJsonbValue(jbps, WJB_ELEM, &v);
- break;
- case jbvObject:
- (void) pushJsonbValue(jbps, WJB_VALUE, &v);
- break;
- default:
- elog(ERROR, "unexpected parent of nested structure");
- }
- }
- else
- {
- while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
- {
- if (type == WJB_KEY || type == WJB_VALUE || type == WJB_ELEM)
- (void) pushJsonbValue(jbps, type, &v);
- else
- (void) pushJsonbValue(jbps, type, NULL);
- }
- }
-
-}
-
-/*
* SQL function jsonb_pretty (jsonb)
*
* Pretty-printed text for the jsonb
@@ -3514,7 +3523,8 @@ jsonb_set(PG_FUNCTION_ARGS)
{
Jsonb *in = PG_GETARG_JSONB(0);
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
- Jsonb *newval = PG_GETARG_JSONB(2);
+ Jsonb *newjsonb = PG_GETARG_JSONB(2);
+ JsonbValue *newval = JsonbToJsonbValue(newjsonb);
bool create = PG_GETARG_BOOL(3);
JsonbValue *res = NULL;
Datum *path_elems;
@@ -3606,7 +3616,8 @@ jsonb_insert(PG_FUNCTION_ARGS)
{
Jsonb *in = PG_GETARG_JSONB(0);
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
- Jsonb *newval = PG_GETARG_JSONB(2);
+ Jsonb *newjsonb = PG_GETARG_JSONB(2);
+ JsonbValue *newval = JsonbToJsonbValue(newjsonb);
bool after = PG_GETARG_BOOL(3);
JsonbValue *res = NULL;
Datum *path_elems;
@@ -3769,7 +3780,7 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
static JsonbValue *
setPath(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len,
- JsonbParseState **st, int level, Jsonb *newval, int op_type)
+ JsonbParseState **st, int level, JsonbValue *newval, int op_type)
{
JsonbValue v;
JsonbIteratorToken r;
@@ -3822,11 +3833,11 @@ setPath(JsonbIterator **it, Datum *path_elems,
static void
setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
int path_len, JsonbParseState **st, int level,
- Jsonb *newval, uint32 npairs, int op_type)
+ JsonbValue *newval, uint32 npairs, int op_type)
{
- JsonbValue v;
int i;
- JsonbValue k;
+ JsonbValue k,
+ v;
bool done = false;
if (level >= path_len || path_nulls[level])
@@ -3843,7 +3854,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
newkey.val.string.val = VARDATA_ANY(path_elems[level]);
(void) pushJsonbValue(st, WJB_KEY, &newkey);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_VALUE, newval);
}
for (i = 0; i < npairs; i++)
@@ -3874,7 +3885,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
if (!(op_type & JB_PATH_DELETE))
{
(void) pushJsonbValue(st, WJB_KEY, &k);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_VALUE, newval);
}
done = true;
}
@@ -3897,7 +3908,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
newkey.val.string.val = VARDATA_ANY(path_elems[level]);
(void) pushJsonbValue(st, WJB_KEY, &newkey);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_VALUE, newval);
}
(void) pushJsonbValue(st, r, &k);
@@ -3929,7 +3940,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
static void
setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
int path_len, JsonbParseState **st, int level,
- Jsonb *newval, uint32 nelems, int op_type)
+ JsonbValue *newval, uint32 nelems, int op_type)
{
JsonbValue v;
int idx,
@@ -3977,7 +3988,7 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
(op_type & JB_PATH_CREATE_OR_INSERT))
{
Assert(newval != NULL);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, newval);
done = true;
}
@@ -3993,7 +4004,7 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
r = JsonbIteratorNext(it, &v, true); /* skip */
if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_CREATE))
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, newval);
/*
* We should keep current value only in case of
@@ -4004,7 +4015,7 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
(void) pushJsonbValue(st, r, &v);
if (op_type & (JB_PATH_INSERT_AFTER | JB_PATH_REPLACE))
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, newval);
done = true;
}
@@ -4038,8 +4049,169 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
if ((op_type & JB_PATH_CREATE_OR_INSERT) && !done &&
level == path_len - 1 && i == nelems - 1)
{
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, newval);
}
}
}
}
+
+/*
+ * Perform an actual data extraction or modification for the jsonb
+ * subscription. As a result the extracted Datum or the modified containers
+ * value will be returned.
+ */
+Datum
+jsonb_subscription_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptingRefExprState *sbstate = (SubscriptingRefExprState *) PG_GETARG_POINTER(0);
+ SubscriptingExecData *sbsdata = (SubscriptingExecData *) PG_GETARG_POINTER(1);
+ SubscriptingRef *jsonb_ref = (SubscriptingRef *) sbstate->xprstate.expr;
+ bool *is_null = sbsdata->isNull;
+ bool is_assignment = (jsonb_ref->refassgnexpr != NULL);
+
+ if (is_assignment)
+ {
+ ExprContext *econtext = sbsdata->xprcontext;
+ Datum sourceData;
+ Datum save_datum;
+ bool save_isNull;
+ bool eisnull;
+
+ /*
+ * We might have a nested-assignment situation, in which the
+ * refassgnexpr is itself a FieldStore or SubscriptingRef that needs to
+ * obtain and modify the previous value of the array element or slice
+ * being replaced. If so, we have to extract that value from the
+ * array and pass it down via the econtext's caseValue. It's safe to
+ * reuse the CASE mechanism because there cannot be a CASE between
+ * here and where the value would be needed, and an array assignment
+ * can't be within a CASE either. (So saving and restoring the
+ * caseValue is just paranoia, but let's do it anyway.)
+ *
+ * Since fetching the old element might be a nontrivial expense, do it
+ * only if the argument appears to actually need it.
+ */
+ save_datum = econtext->caseValue_datum;
+ save_isNull = econtext->caseValue_isNull;
+
+ /*
+ * Evaluate the value to be assigned into the array.
+ */
+ sourceData = ExecEvalExpr(sbstate->refassgnexpr,
+ econtext,
+ &eisnull,
+ NULL);
+
+ econtext->caseValue_datum = save_datum;
+ econtext->caseValue_isNull = save_isNull;
+
+ /*
+ * For an assignment to a fixed-length array type, both the original
+ * array and the value to be assigned into it must be non-NULL, else
+ * we punt and return the original array.
+ */
+ if (sbstate->refattrlength > 0) /* fixed-length array? */
+ if (eisnull || *is_null)
+ return sbsdata->containerSource;
+
+ /*
+ * For assignment to varlena arrays, we handle a NULL original array
+ * by substituting an empty (zero-dimensional) array; insertion of the
+ * new element will result in a singleton array value. It does not
+ * matter whether the new element is NULL.
+ */
+ if (*is_null)
+ {
+ sbsdata->containerSource =
+ PointerGetDatum(construct_empty_array(jsonb_ref->refelemtype));
+ *is_null = false;
+ }
+
+ return jsonb_set_element(sbsdata->containerSource,
+ sbsdata->upper,
+ sbsdata->indexprNumber,
+ sourceData,
+ jsonb_ref->refelemtype);
+ }
+ else
+ return jsonb_get_element(DatumGetJsonb(sbsdata->containerSource),
+ sbsdata->upper,
+ sbsdata->indexprNumber,
+ is_null,
+ false);
+}
+
+/*
+ * Perform preparation for the jsonb subscription. Since there are not any
+ * particular restrictions for this kind of subscription, we will verify that
+ * it is not a slice operation. This function produces an expression that
+ * represents the result of extracting a single container element or the new
+ * container value with the source data inserted into the right part of the
+ * container.
+ */
+Datum
+jsonb_subscription_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptingRef *sbsref = (SubscriptingRef *) PG_GETARG_POINTER(0);
+ ParseState *pstate = (ParseState *) PG_GETARG_POINTER(1);
+ List *upperIndexpr = NIL;
+ ListCell *l;
+
+ if (sbsref->reflowerindexpr != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("jsonb subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *)lfirst(sbsref->reflowerindexpr->head))))));
+
+ foreach(l, sbsref->refupperindexpr)
+ {
+ Node *subexpr = (Node *) lfirst(l);
+
+ Assert(subexpr != NULL);
+
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("jsonb subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *) lfirst(sbsref->refupperindexpr->head))))));
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ TEXTOID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("jsonb subscript must have text type"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ }
+
+ sbsref->refupperindexpr = upperIndexpr;
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+/*
+ * Handle jsonb-type subscription logic.
+ */
+Datum
+jsonb_subscription(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1,
+ fcinfo->nargs);
+
+ if (op_type & SBS_VALIDATION)
+ return jsonb_subscription_prepare(&target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return jsonb_subscription_evaluate(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscription function: %d", op_type);
+}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 8a81d7a..fd78c5a 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -439,7 +439,7 @@ static void get_tablesample_def(TableSampleClause *tablesample,
static void get_opclass_name(Oid opclass, Oid actual_datatype,
StringInfo buf);
static Node *processIndirection(Node *node, deparse_context *context);
-static void printSubscripts(ArrayRef *aref, deparse_context *context);
+static void printSubscripts(SubscriptingRef *aref, deparse_context *context);
static char *get_relation_name(Oid relid);
static char *generate_relation_name(Oid relid, List *namespaces);
static char *generate_qualified_relation_name(Oid relid);
@@ -5828,7 +5828,7 @@ get_update_query_targetlist_def(Query *query, List *targetList,
{
/*
* We must dig down into the expr to see if it's a PARAM_MULTIEXPR
- * Param. That could be buried under FieldStores and ArrayRefs
+ * Param. That could be buried under FieldStores and SubscriptingRefs
* (cf processIndirection()), and underneath those there could be
* an implicit type coercion.
*/
@@ -5841,13 +5841,13 @@ get_update_query_targetlist_def(Query *query, List *targetList,
expr = (Node *) linitial(fstore->newvals);
}
- else if (IsA(expr, ArrayRef))
+ else if (IsA(expr, SubscriptingRef))
{
- ArrayRef *aref = (ArrayRef *) expr;
+ SubscriptingRef *sbsref = (SubscriptingRef *) expr;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
break;
- expr = (Node *) aref->refassgnexpr;
+ expr = (Node *) sbsref->refassgnexpr;
}
else
break;
@@ -6879,7 +6879,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
/* single words: always simple */
return true;
- case T_ArrayRef:
+ case T_SubscriptingRef:
case T_ArrayExpr:
case T_RowExpr:
case T_CoalesceExpr:
@@ -6996,7 +6996,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
return true; /* own parentheses */
}
case T_BoolExpr: /* lower precedence */
- case T_ArrayRef: /* other separators */
+ case T_SubscriptingRef: /* other separators */
case T_ArrayExpr: /* other separators */
case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */
@@ -7046,7 +7046,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
return false;
return true; /* own parentheses */
}
- case T_ArrayRef: /* other separators */
+ case T_SubscriptingRef: /* other separators */
case T_ArrayExpr: /* other separators */
case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */
@@ -7232,9 +7232,9 @@ get_rule_expr(Node *node, deparse_context *context,
get_windowfunc_expr((WindowFunc *) node, context);
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
bool need_parens;
/*
@@ -7245,24 +7245,24 @@ get_rule_expr(Node *node, deparse_context *context,
* here too, and display only the assignment source
* expression.
*/
- if (IsA(aref->refexpr, CaseTestExpr))
+ if (IsA(sbsref->refexpr, CaseTestExpr))
{
- Assert(aref->refassgnexpr);
- get_rule_expr((Node *) aref->refassgnexpr,
+ Assert(sbsref->refassgnexpr);
+ get_rule_expr((Node *) sbsref->refassgnexpr,
context, showimplicit);
break;
}
/*
* Parenthesize the argument unless it's a simple Var or a
- * FieldSelect. (In particular, if it's another ArrayRef, we
+ * FieldSelect. (In particular, if it's another SubscriptingRef, we
* *must* parenthesize to avoid confusion.)
*/
- need_parens = !IsA(aref->refexpr, Var) &&
- !IsA(aref->refexpr, FieldSelect);
+ need_parens = !IsA(sbsref->refexpr, Var) &&
+ !IsA(sbsref->refexpr, FieldSelect);
if (need_parens)
appendStringInfoChar(buf, '(');
- get_rule_expr((Node *) aref->refexpr, context, showimplicit);
+ get_rule_expr((Node *) sbsref->refexpr, context, showimplicit);
if (need_parens)
appendStringInfoChar(buf, ')');
@@ -7275,7 +7275,7 @@ get_rule_expr(Node *node, deparse_context *context,
* EXPLAIN tries to print the targetlist of a plan resulting
* from such a statement.
*/
- if (aref->refassgnexpr)
+ if (sbsref->refassgnexpr)
{
Node *refassgnexpr;
@@ -7292,7 +7292,7 @@ get_rule_expr(Node *node, deparse_context *context,
else
{
/* Just an ordinary array fetch, so print subscripts */
- printSubscripts(aref, context);
+ printSubscripts(sbsref, context);
}
}
break;
@@ -7491,12 +7491,12 @@ get_rule_expr(Node *node, deparse_context *context,
bool need_parens;
/*
- * Parenthesize the argument unless it's an ArrayRef or
+ * Parenthesize the argument unless it's an SubscriptingRef or
* another FieldSelect. Note in particular that it would be
* WRONG to not parenthesize a Var argument; simplicity is not
* the issue here, having the right number of names is.
*/
- need_parens = !IsA(arg, ArrayRef) &&!IsA(arg, FieldSelect);
+ need_parens = !IsA(arg, SubscriptingRef) && !IsA(arg, FieldSelect);
if (need_parens)
appendStringInfoChar(buf, '(');
get_rule_expr(arg, context, true);
@@ -9610,7 +9610,7 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
/*
* processIndirection - take care of array and subfield assignment
*
- * We strip any top-level FieldStore or assignment ArrayRef nodes that
+ * We strip any top-level FieldStore or assignment SubscriptingRef nodes that
* appear in the input, printing them as decoration for the base column
* name (which we assume the caller just printed). Return the subexpression
* that's to be assigned.
@@ -9652,19 +9652,19 @@ processIndirection(Node *node, deparse_context *context)
*/
node = (Node *) linitial(fstore->newvals);
}
- else if (IsA(node, ArrayRef))
+ else if (IsA(node, SubscriptingRef))
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
break;
- printSubscripts(aref, context);
+ printSubscripts(sbsref, context);
/*
* We ignore refexpr since it should be an uninteresting reference
* to the target column or subcolumn.
*/
- node = (Node *) aref->refassgnexpr;
+ node = (Node *) sbsref->refassgnexpr;
}
else
break;
@@ -9674,14 +9674,14 @@ processIndirection(Node *node, deparse_context *context)
}
static void
-printSubscripts(ArrayRef *aref, deparse_context *context)
+printSubscripts(SubscriptingRef *sbsref, deparse_context *context)
{
StringInfo buf = context->buf;
ListCell *lowlist_item;
ListCell *uplist_item;
- lowlist_item = list_head(aref->reflowerindexpr); /* could be NULL */
- foreach(uplist_item, aref->refupperindexpr)
+ lowlist_item = list_head(sbsref->reflowerindexpr); /* could be NULL */
+ foreach(uplist_item, sbsref->refupperindexpr)
{
appendStringInfoChar(buf, '[');
if (lowlist_item)
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 13ae6ad..ad8f334 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -3061,3 +3061,23 @@ get_range_subtype(Oid rangeOid)
else
return InvalidOid;
}
+
+/*
+ * get_typsubscription
+ *
+ * Given the type OID, return the type's typsubscription procedure, if any.
+ */
+RegProcedure
+get_typsubscription(Oid typid)
+{
+ HeapTuple tp;
+ RegProcedure result = InvalidOid;
+
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (HeapTupleIsValid(tp))
+ {
+ result = ((Form_pg_type) GETSTRUCT(tp))->typsubscription;
+ ReleaseSysCache(tp);
+ }
+ return result;
+}
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 5d49fe5..837f033 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -1396,3 +1396,22 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
return tupdesc;
}
+
+FunctionCallInfoData
+get_slice_arguments(FunctionCallInfo fcinfo, int begin, int end)
+{
+ FunctionCallInfoData sliced_fcinfo;
+ int i;
+
+ InitFunctionCallInfoData(sliced_fcinfo, fcinfo->flinfo,
+ fcinfo->nargs - 1, fcinfo->fncollation,
+ NULL, NULL);
+
+ for(i = begin; i < end; i++)
+ {
+ sliced_fcinfo.arg[i - begin] = fcinfo->arg[i];
+ sliced_fcinfo.argnull[i - begin] = false;
+ }
+
+ return sliced_fcinfo;
+}
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index e57b81c..23bf824 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -143,7 +143,7 @@ typedef FormData_pg_class *Form_pg_class;
* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId;
* similarly, "1" in relminmxid stands for FirstMultiXactId
*/
-DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f f f t n 3 1 _null_ _null_ ));
+DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 31 0 t f f f f f f t n 3 1 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 21 0 f f f f f f f t n 3 1 _null_ _null_ ));
DESCR("");
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 17ec71d..7f7bd2e 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -5341,6 +5341,12 @@ DESCR("pg_controldata recovery state information as a function");
DATA(insert OID = 3444 ( pg_control_init PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,23,23,23,23,23,23,23,16,16,16,23}" "{o,o,o,o,o,o,o,o,o,o,o,o,o}" "{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,bigint_timestamps,float4_pass_by_value,float8_pass_by_value,data_page_checksum_version}" _null_ _null_ pg_control_init _null_ _null_ _null_ ));
DESCR("pg_controldata init state information as a function");
+/* type subscription support */
+DATA(insert OID = 3343 ( jsonb_subscription PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 "2281" "2281" _null_ _null_ _null_ _null_ _null_ jsonb_subscription _null_ _null_ _null_ ));
+DESCR("Jsonb subscription logic");
+DATA(insert OID = 3344 ( array_subscription PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 "2281" "2281" _null_ _null_ _null_ _null_ _null_ array_subscription _null_ _null_ _null_ ));
+DESCR("Array subscription logic");
+
/*
* Symbolic values for provolatile column: these indicate whether the result
* of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 162239c..b54d136 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -199,6 +199,12 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO
*/
Oid typcollation;
+ /*
+ * Type specific subscription logic. If typsubscription is none, it means
+ * that this type doesn't support subscription.
+ */
+ regproc typsubscription;
+
#ifdef CATALOG_VARLEN /* variable-length fields start here */
/*
@@ -236,7 +242,7 @@ typedef FormData_pg_type *Form_pg_type;
* compiler constants for pg_type
* ----------------
*/
-#define Natts_pg_type 30
+#define Natts_pg_type 31
#define Anum_pg_type_typname 1
#define Anum_pg_type_typnamespace 2
#define Anum_pg_type_typowner 3
@@ -264,10 +270,10 @@ typedef FormData_pg_type *Form_pg_type;
#define Anum_pg_type_typtypmod 25
#define Anum_pg_type_typndims 26
#define Anum_pg_type_typcollation 27
-#define Anum_pg_type_typdefaultbin 28
-#define Anum_pg_type_typdefault 29
-#define Anum_pg_type_typacl 30
-
+#define Anum_pg_type_typsubscription 28
+#define Anum_pg_type_typdefaultbin 29
+#define Anum_pg_type_typdefault 30
+#define Anum_pg_type_typacl 31
/* ----------------
* initial contents of pg_type
@@ -283,94 +289,94 @@ typedef FormData_pg_type *Form_pg_type;
*/
/* OIDS 1 - 99 */
-DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("boolean, 'true'/'false'");
#define BOOLOID 16
-DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("variable-length string, binary values escaped");
#define BYTEAOID 17
-DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("single character");
#define CHAROID 18
-DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("63-byte type for storing system identifiers");
#define NAMEOID 19
-DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("~18 digit integer, 8-byte storage");
#define INT8OID 20
-DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("-32 thousand to 32 thousand, 2-byte storage");
#define INT2OID 21
-DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("array of int2, used in system tables");
#define INT2VECTOROID 22
-DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("-2 billion to 2 billion integer, 4-byte storage");
#define INT4OID 23
-DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered procedure");
#define REGPROCOID 24
-DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("variable-length string, no limit specified");
#define TEXTOID 25
-DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("object identifier(oid), maximum 4 billion");
#define OIDOID 26
-DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("(block, offset), physical location of tuple");
#define TIDOID 27
-DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("transaction id");
#define XIDOID 28
-DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("command identifier type, sequence in transaction id");
#define CIDOID 29
-DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("array of oids, used in system tables");
#define OIDVECTOROID 30
/* hand-built rowtype entries for bootstrapped catalogs */
/* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */
-DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
+DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
+DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
+DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
/* OIDS 100 - 199 */
-DATA(insert OID = 114 ( json PGNSP PGUID -1 f b U f t \054 0 0 199 json_in json_out json_recv json_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 114 ( json PGNSP PGUID -1 f b U f t \054 0 0 199 json_in json_out json_recv json_send - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define JSONOID 114
-DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("XML content");
#define XMLOID 142
-DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 199 ( _json PGNSP PGUID -1 f b A f t \054 0 114 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 199 ( _json PGNSP PGUID -1 f b A f t \054 0 114 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
-DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("string representing an internal node tree");
#define PGNODETREEOID 194
-DATA(insert OID = 32 ( pg_ddl_command PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 pg_ddl_command_in pg_ddl_command_out pg_ddl_command_recv pg_ddl_command_send - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 32 ( pg_ddl_command PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 pg_ddl_command_in pg_ddl_command_out pg_ddl_command_recv pg_ddl_command_send - - - ALIGNOF_POINTER p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("internal type for passing CollectedCommand");
#define PGDDLCOMMANDOID 32
/* OIDS 200 - 299 */
-DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("storage manager");
/* OIDS 300 - 399 */
@@ -380,276 +386,276 @@ DESCR("storage manager");
/* OIDS 500 - 599 */
/* OIDS 600 - 699 */
-DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("geometric point '(x, y)'");
#define POINTOID 600
-DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("geometric line segment '(pt1,pt2)'");
#define LSEGOID 601
-DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("geometric path '(pt1,...)'");
#define PATHOID 602
-DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("geometric box '(lower left,upper right)'");
#define BOXOID 603
-DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("geometric polygon '(pt1,...)'");
#define POLYGONOID 604
-DATA(insert OID = 628 ( line PGNSP PGUID 24 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 628 ( line PGNSP PGUID 24 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
DESCR("geometric line");
#define LINEOID 628
-DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* OIDS 700 - 799 */
-DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("single-precision floating point number, 4-byte storage");
#define FLOAT4OID 700
-DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("double-precision floating point number, 8-byte storage");
#define FLOAT8OID 701
-DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("absolute, limited-range date and time (Unix system time)");
#define ABSTIMEOID 702
-DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("relative, limited-range time interval (Unix delta time)");
#define RELTIMEOID 703
-DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("(abstime,abstime), time interval");
#define TINTERVALOID 704
-DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("");
#define UNKNOWNOID 705
-DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("geometric circle '(center,radius)'");
#define CIRCLEOID 718
-DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("monetary amounts, $d,ddd.cc");
#define CASHOID 790
-DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* OIDS 800 - 899 */
-DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDROID 829
-DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("IP address/netmask, host address, netmask optional");
#define INETOID 869
-DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("network IP address/netmask, network address");
#define CIDROID 650
/* OIDS 900 - 999 */
/* OIDS 1000 - 1099 */
-DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
#define INT2ARRAYOID 1005
-DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
#define INT4ARRAYOID 1007
-DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 100 array_subscription _null_ _null_ _null_ ));
#define TEXTARRAYOID 1009
-DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
#define OIDARRAYOID 1028
-DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
-DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
-DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout array_typanalyze i x f 0 -1 0 100 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout array_typanalyze i x f 0 -1 0 100 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
#define FLOAT4ARRAYOID 1021
-DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("access control list");
#define ACLITEMOID 1033
-DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
#define CSTRINGARRAYOID 1263
-DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("char(length), blank-padded string, fixed storage length");
#define BPCHAROID 1042
-DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("varchar(length), non-blank-padded string, variable storage length");
#define VARCHAROID 1043
-DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("date");
#define DATEOID 1082
-DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("time of day");
#define TIMEOID 1083
/* OIDS 1100 - 1199 */
-DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("date and time");
#define TIMESTAMPOID 1114
-DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("date and time with time zone");
#define TIMESTAMPTZOID 1184
-DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("@ <number> <units>, time interval");
#define INTERVALOID 1186
-DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* OIDS 1200 - 1299 */
-DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("time of day with time zone");
#define TIMETZOID 1266
-DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* OIDS 1500 - 1599 */
-DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("fixed-length bit string");
#define BITOID 1560
-DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("variable-length bit string");
#define VARBITOID 1562
-DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* OIDS 1600 - 1699 */
/* OIDS 1700 - 1799 */
-DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("numeric(precision, decimal), arbitrary precision number");
#define NUMERICOID 1700
-DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("reference to cursor (portal name)");
#define REFCURSOROID 1790
/* OIDS 2200 - 2299 */
-DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
-DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered procedure (with args)");
#define REGPROCEDUREOID 2202
-DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered operator");
#define REGOPEROID 2203
-DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered operator (with args)");
#define REGOPERATOROID 2204
-DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered class");
#define REGCLASSOID 2205
-DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered type");
#define REGTYPEOID 2206
-DATA(insert OID = 4096 ( regrole PGNSP PGUID 4 t b N f t \054 0 0 4097 regrolein regroleout regrolerecv regrolesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4096 ( regrole PGNSP PGUID 4 t b N f t \054 0 0 4097 regrolein regroleout regrolerecv regrolesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered role");
#define REGROLEOID 4096
-DATA(insert OID = 4089 ( regnamespace PGNSP PGUID 4 t b N f t \054 0 0 4090 regnamespacein regnamespaceout regnamespacerecv regnamespacesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4089 ( regnamespace PGNSP PGUID 4 t b N f t \054 0 0 4090 regnamespacein regnamespaceout regnamespacerecv regnamespacesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered namespace");
#define REGNAMESPACEOID 4089
-DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
#define REGTYPEARRAYOID 2211
-DATA(insert OID = 4097 ( _regrole PGNSP PGUID -1 f b A f t \054 0 4096 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 4090 ( _regnamespace PGNSP PGUID -1 f b A f t \054 0 4089 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4097 ( _regrole PGNSP PGUID -1 f b A f t \054 0 4096 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 4090 ( _regnamespace PGNSP PGUID -1 f b A f t \054 0 4089 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* uuid */
-DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("UUID datatype");
#define UUIDOID 2950
-DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* pg_lsn */
-DATA(insert OID = 3220 ( pg_lsn PGNSP PGUID 8 FLOAT8PASSBYVAL b U f t \054 0 0 3221 pg_lsn_in pg_lsn_out pg_lsn_recv pg_lsn_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3220 ( pg_lsn PGNSP PGUID 8 FLOAT8PASSBYVAL b U f t \054 0 0 3221 pg_lsn_in pg_lsn_out pg_lsn_recv pg_lsn_send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("PostgreSQL LSN datatype");
#define LSNOID 3220
-DATA(insert OID = 3221 ( _pg_lsn PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3221 ( _pg_lsn PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* text search */
-DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("text representation for text search");
#define TSVECTOROID 3614
-DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("GiST index internal text representation for text search");
#define GTSVECTOROID 3642
-DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("query representation for text search");
#define TSQUERYOID 3615
-DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered text search configuration");
#define REGCONFIGOID 3734
-DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered text search dictionary");
#define REGDICTIONARYOID 3769
-DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* jsonb */
-DATA(insert OID = 3802 ( jsonb PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3802 ( jsonb PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 jsonb_subscription _null_ _null_ _null_ ));
DESCR("Binary JSON");
#define JSONBOID 3802
-DATA(insert OID = 3807 ( _jsonb PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3807 ( _jsonb PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
-DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("txid snapshot");
-DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/* range types */
-DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of integers");
#define INT4RANGEOID 3904
-DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of numerics");
-DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of timestamps without time zone");
-DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of timestamps with time zone");
-DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of dates");
-DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
+DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of bigints");
-DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
/*
* pseudo-types
@@ -664,41 +670,41 @@ DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 arr
* but there is now support for it in records and arrays. Perhaps we should
* just treat it as a regular base type?
*/
-DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define RECORDOID 2249
-DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscription _null_ _null_ _null_ ));
#define RECORDARRAYOID 2287
-DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define CSTRINGOID 2275
-DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYOID 2276
-DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYARRAYOID 2277
-DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out void_recv void_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out void_recv void_send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define VOIDOID 2278
-DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define TRIGGEROID 2279
-DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define EVTTRIGGEROID 3838
-DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
-DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define INTERNALOID 2281
-DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define OPAQUEOID 2282
-DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYELEMENTOID 2283
-DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYNONARRAYOID 2776
-DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYENUMOID 3500
-DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define FDW_HANDLEROID 3115
-DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define INDEX_AM_HANDLEROID 325
-DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define TSM_HANDLEROID 3310
-DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYRANGEOID 3831
diff --git a/src/include/catalog/pg_type_fn.h b/src/include/catalog/pg_type_fn.h
index d7bbfdb..947cad0 100644
--- a/src/include/catalog/pg_type_fn.h
+++ b/src/include/catalog/pg_type_fn.h
@@ -52,7 +52,8 @@ extern ObjectAddress TypeCreate(Oid newTypeOid,
int32 typeMod,
int32 typNDims,
bool typeNotNull,
- Oid typeCollation);
+ Oid typeCollation,
+ Oid subscriptionProcedure);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
@@ -70,6 +71,7 @@ extern void GenerateTypeDependencies(Oid typeNamespace,
bool isImplicitArray,
Oid baseType,
Oid typeCollation,
+ Oid subscriptionProcedure,
Node *defaultExpr,
bool rebuild);
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index e73a824..760d45f 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -183,6 +183,7 @@ extern TupleDesc build_function_result_tupdesc_d(Datum proallargtypes,
Datum proargmodes,
Datum proargnames);
extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
+extern FunctionCallInfoData get_slice_arguments(FunctionCallInfo fcinfo, int begin, int end);
/*----------
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index f6f73f3..2b32ae0 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -650,25 +650,46 @@ typedef struct WindowFuncExprState
} WindowFuncExprState;
/* ----------------
- * ArrayRefExprState node
+ * SubscriptingRefExprState node
*
* Note: array types can be fixed-length (typlen > 0), but only when the
* element type is itself fixed-length. Otherwise they are varlena structures
* and have typlen = -1. In any case, an array type is never pass-by-value.
* ----------------
*/
-typedef struct ArrayRefExprState
+typedef struct SubscriptingRefExprState
{
ExprState xprstate;
List *refupperindexpr; /* states for child nodes */
List *reflowerindexpr;
ExprState *refexpr;
ExprState *refassgnexpr;
- int16 refattrlength; /* typlen of array type */
- int16 refelemlength; /* typlen of the array element type */
+ int16 refattrlength; /* typlen of container type */
+ int16 refelemlength; /* typlen of the container element type */
bool refelembyval; /* is the element type pass-by-value? */
char refelemalign; /* typalign of the element type */
-} ArrayRefExprState;
+} SubscriptingRefExprState;
+
+/* ---------------------------------
+ * Subscripting exec information
+ *
+ * It contains all information which is required to perform type-specific data
+ * extraction or modification. This information will be gathered in
+ * `ExecEvalSubscriptingRef` function and passed to `typsubscription`
+ * procedure.
+ * ---------------------------------
+ */
+typedef struct SubscriptingExecData
+{
+ ExprContext *xprcontext; /* econtext for subscription */
+ bool *isNull;
+ Datum *upper; /* upper boundary for subscription */
+ Datum *lower; /* lower boundary for subscription */
+ bool *upperProvided;
+ bool *lowerProvided;
+ Datum containerSource;
+ int indexprNumber;
+} SubscriptingExecData;
/* ----------------
* FuncExprState node
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 88297bb..5c8d0a1 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -140,7 +140,7 @@ typedef enum NodeTag
T_Aggref,
T_GroupingFunc,
T_WindowFunc,
- T_ArrayRef,
+ T_SubscriptingRef,
T_FuncExpr,
T_NamedArgExpr,
T_OpExpr,
@@ -194,7 +194,7 @@ typedef enum NodeTag
T_AggrefExprState,
T_GroupingFuncExprState,
T_WindowFuncExprState,
- T_ArrayRefExprState,
+ T_SubscriptingRefExprState,
T_FuncExprState,
T_ScalarArrayOpExprState,
T_BoolExprState,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 65510b0..5f73a60 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -350,18 +350,18 @@ typedef struct WindowFunc
} WindowFunc;
/* ----------------
- * ArrayRef: describes an array subscripting operation
- *
- * An ArrayRef can describe fetching a single element from an array,
- * fetching a subarray (array slice), storing a single element into
- * an array, or storing a slice. The "store" cases work with an
- * initial array value and a source value that is inserted into the
- * appropriate part of the array; the result of the operation is an
- * entire new modified array value.
- *
- * If reflowerindexpr = NIL, then we are fetching or storing a single array
- * element at the subscripts given by refupperindexpr. Otherwise we are
- * fetching or storing an array slice, that is a rectangular subarray
+ * SubscriptingRef: describes a subscripting operation over a container
+ *
+ * An SubscriptingRef can describe fetching a single element from a container,
+ * fetching a part of container (e.g. array slice), storing a single element into
+ * a container, or storing a slice. The "store" cases work with an
+ * initial container value and a source value that is inserted into the
+ * appropriate part of the container; the result of the operation is an
+ * entire new modified container value.
+ *
+ * If reflowerindexpr = NIL, then we are fetching or storing a single container
+ * element at the subscripts given by refupperindexpr. Otherwise we are
+ * fetching or storing a container slice, that is a rectangular subcontainer
* with lower and upper bounds given by the index expressions.
* reflowerindexpr must be the same length as refupperindexpr when it
* is not NIL.
@@ -373,27 +373,27 @@ typedef struct WindowFunc
* element; but it is the array type when doing subarray fetch or either
* type of store.
*
- * Note: for the cases where an array is returned, if refexpr yields a R/W
- * expanded array, then the implementation is allowed to modify that object
+ * Note: for the cases where a container is returned, if refexpr yields a R/W
+ * expanded container, then the implementation is allowed to modify that object
* in-place and return the same object.)
* ----------------
*/
-typedef struct ArrayRef
+typedef struct SubscriptingRef
{
Expr xpr;
- Oid refarraytype; /* type of the array proper */
- Oid refelemtype; /* type of the array elements */
- int32 reftypmod; /* typmod of the array (and elements too) */
- Oid refcollid; /* OID of collation, or InvalidOid if none */
- List *refupperindexpr;/* expressions that evaluate to upper array
- * indexes */
- List *reflowerindexpr;/* expressions that evaluate to lower array
- * indexes, or NIL for single array element */
- Expr *refexpr; /* the expression that evaluates to an array
- * value */
- Expr *refassgnexpr; /* expression for the source value, or NULL if
- * fetch */
-} ArrayRef;
+ Oid refcontainertype; /* type of the container proper */
+ Oid refelemtype; /* type of the container elements */
+ int32 reftypmod; /* typmod of the container (and elements too) */
+ Oid refcollid; /* OID of collation, or InvalidOid if none */
+ List *refupperindexpr; /* expressions that evaluate to upper container
+ * indexes */
+ List *reflowerindexpr; /* expressions that evaluate to lower container
+ * indexes, or NIL for single container element */
+ Expr *refexpr; /* the expression that evaluates to an container
+ * value */
+ Expr *refassgnexpr; /* expression for the source value, or NULL if
+ * fetch */
+} SubscriptingRef;
/*
* CoercionContext - distinguishes the allowed set of type casts
@@ -734,7 +734,7 @@ typedef struct FieldSelect
*
* FieldStore represents the operation of modifying one field in a tuple
* value, yielding a new tuple value (the input is not touched!). Like
- * the assign case of ArrayRef, this is used to implement UPDATE of a
+ * the assign case of SubscriptingRef, this is used to implement UPDATE of a
* portion of a column.
*
* A single FieldStore can actually represent updates of several different
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 6633586..36632b7 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -224,12 +224,17 @@ extern void cancel_parser_errposition_callback(ParseCallbackState *pcbstate);
extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno,
int location);
-extern Oid transformArrayType(Oid *arrayType, int32 *arrayTypmod);
-extern ArrayRef *transformArraySubscripts(ParseState *pstate,
- Node *arrayBase,
- Oid arrayType,
+extern Oid transformArrayType(Oid *containerType, int32 *containerTypmod);
+
+/* Type of a stage in case of type-specific subscription procedure */
+#define SBS_VALIDATION 0x0001
+#define SBS_EXEC 0x0002
+
+extern SubscriptingRef *transformContainerSubscripts(ParseState *pstate,
+ Node *containerBase,
+ Oid containerType,
Oid elementType,
- int32 arrayTypMod,
+ int32 containerTypMod,
List *indirection,
Node *assignFrom);
extern Const *make_const(ParseState *pstate, Value *value, int location);
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index 6164f11..64dd619 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -502,4 +502,9 @@ extern Datum array_positions(PG_FUNCTION_ARGS);
*/
extern Datum array_typanalyze(PG_FUNCTION_ARGS);
+/*
+ * prototypes for functions with array subscription logic
+ */
+extern Datum array_subscription(PG_FUNCTION_ARGS);
+
#endif /* ARRAY_H */
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index 470d5b1..290cf3b 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -354,6 +354,7 @@ extern Datum jsonb_typeof(PG_FUNCTION_ARGS);
/* generator routines */
extern Datum to_jsonb(PG_FUNCTION_ARGS);
+extern JsonbValue *to_jsonb_worker(Datum source, Oid source_type);
extern Datum jsonb_build_object(PG_FUNCTION_ARGS);
extern Datum jsonb_build_object_noargs(PG_FUNCTION_ARGS);
@@ -427,6 +428,7 @@ extern JsonbValue *pushJsonbValue(JsonbParseState **pstate,
extern JsonbIterator *JsonbIteratorInit(JsonbContainer *container);
extern JsonbIteratorToken JsonbIteratorNext(JsonbIterator **it, JsonbValue *val,
bool skipNested);
+extern JsonbValue *JsonbToJsonbValue(Jsonb *jsonb);
extern Jsonb *JsonbValueToJsonb(JsonbValue *val);
extern bool JsonbDeepContains(JsonbIterator **val,
JsonbIterator **mContained);
@@ -439,4 +441,7 @@ extern char *JsonbToCStringIndent(StringInfo out, JsonbContainer *in,
int estimated_len);
+/* Jsonb subscription logic */
+extern Datum jsonb_subscription(PG_FUNCTION_ARGS);
+
#endif /* __JSONB_H__ */
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index dcb8980..474957d 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -159,6 +159,7 @@ extern void free_attstatsslot(Oid atttype,
extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
+extern RegProcedure get_typsubscription(Oid typid);
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
/* type_is_array_domain accepts both plain arrays and domains over arrays */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 470cf93..bc92a66 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -4742,7 +4742,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
/*
* Evaluate the subscripts, switch into left-to-right order.
- * Like ExecEvalArrayRef(), complain if any subscript is null.
+ * Like ExecEvalSubscriptingRef(), complain if any subscript is null.
*/
for (i = 0; i < nsubscripts; i++)
{
@@ -4790,7 +4790,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
* fixed-length array types we skip the assignment. We can't
* support assignment of a null entry into a fixed-length
* array, either, so that's a no-op too. This is all ugly but
- * corresponds to the current behavior of ExecEvalArrayRef().
+ * corresponds to the current behavior of ExecEvalSubscriptingRef().
*/
if (arrayelem->arraytyplen > 0 && /* fixed-length array? */
(oldarrayisnull || isNull))
@@ -6498,9 +6498,9 @@ exec_simple_check_node(Node *node)
case T_Param:
return TRUE;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *expr = (ArrayRef *) node;
+ SubscriptingRef *expr = (SubscriptingRef *) node;
if (!exec_simple_check_node((Node *) expr->refupperindexpr))
return FALSE;
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
index e2cb08a..dcacf20 100644
--- a/src/test/regress/expected/jsonb.out
+++ b/src/test/regress/expected/jsonb.out
@@ -3447,3 +3447,211 @@ HINT: Try using the function jsonb_set to replace key value.
select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"', true);
ERROR: cannot replace existing key
HINT: Try using the function jsonb_set to replace key value.
+-- jsonb subscript
+select ('123'::jsonb)['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('123'::jsonb)[0];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": 1}'::jsonb)['a'];
+ jsonb
+-------
+ 1
+(1 row)
+
+select ('{"a": 1}'::jsonb)[0];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": 1}'::jsonb)['not_exist'];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[0];
+ jsonb
+-------
+ 1
+(1 row)
+
+select ('[1, "2", null]'::jsonb)['1'];
+ jsonb
+-------
+ "2"
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[1.0];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[2];
+ jsonb
+-------
+ null
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[3];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[-2];
+ jsonb
+-------
+ "2"
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[1]['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[1][0];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['b'];
+ jsonb
+-------
+ "c"
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'];
+ jsonb
+-----------
+ [1, 2, 3]
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'][1];
+ jsonb
+-------
+ 2
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d']['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1'];
+ jsonb
+---------------
+ {"a2": "aaa"}
+(1 row)
+
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2'];
+ jsonb
+-------
+ "aaa"
+(1 row)
+
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']['a3'];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'];
+ jsonb
+-----------------------
+ ["aaa", "bbb", "ccc"]
+(1 row)
+
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'][2];
+ jsonb
+-------
+ "ccc"
+(1 row)
+
+create TEMP TABLE test_jsonb_subscript (
+ id int,
+ test_json jsonb
+);
+insert into test_jsonb_subscript values
+(1, '{}'), -- empty jsonb
+(2, '{"key": "value"}'); -- jsonb with data
+-- update empty jsonb
+update test_jsonb_subscript set test_json['a'] = 1 where id = 1;
+select * from test_jsonb_subscript;
+ id | test_json
+----+------------------
+ 2 | {"key": "value"}
+ 1 | {"a": 1}
+(2 rows)
+
+-- update jsonb with some data
+update test_jsonb_subscript set test_json['a'] = 1 where id = 2;
+select * from test_jsonb_subscript;
+ id | test_json
+----+--------------------------
+ 1 | {"a": 1}
+ 2 | {"a": 1, "key": "value"}
+(2 rows)
+
+-- replace jsonb
+update test_jsonb_subscript set test_json['a'] = 'test';
+select * from test_jsonb_subscript;
+ id | test_json
+----+-------------------------------
+ 1 | {"a": "test"}
+ 2 | {"a": "test", "key": "value"}
+(2 rows)
+
+-- replace by object
+update test_jsonb_subscript set test_json['a'] = '{"b": 1}'::jsonb;
+select * from test_jsonb_subscript;
+ id | test_json
+----+---------------------------------
+ 1 | {"a": {"b": 1}}
+ 2 | {"a": {"b": 1}, "key": "value"}
+(2 rows)
+
+-- replace by array
+update test_jsonb_subscript set test_json['a'] = '[1, 2, 3]'::jsonb;
+select * from test_jsonb_subscript;
+ id | test_json
+----+----------------------------------
+ 1 | {"a": [1, 2, 3]}
+ 2 | {"a": [1, 2, 3], "key": "value"}
+(2 rows)
+
+-- use jsonb subscription in where clause
+select * from test_jsonb_subscript where test_json['key'] = '"value"';
+ id | test_json
+----+----------------------------------
+ 2 | {"a": [1, 2, 3], "key": "value"}
+(1 row)
+
+select * from test_jsonb_subscript where test_json['key_doesnt_exists'] = '"value"';
+ id | test_json
+----+-----------
+(0 rows)
+
+select * from test_jsonb_subscript where test_json['key'] = '"wrong_value"';
+ id | test_json
+----+-----------
+(0 rows)
+
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
index 6b4c796..e277e0d 100644
--- a/src/test/regress/sql/jsonb.sql
+++ b/src/test/regress/sql/jsonb.sql
@@ -868,3 +868,62 @@ select jsonb_insert('{"a": {"b": "value"}}', '{a, c}', '"new_value"', true);
select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"');
select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"', true);
+
+-- jsonb subscript
+select ('123'::jsonb)['a'];
+select ('123'::jsonb)[0];
+select ('{"a": 1}'::jsonb)['a'];
+select ('{"a": 1}'::jsonb)[0];
+select ('{"a": 1}'::jsonb)['not_exist'];
+select ('[1, "2", null]'::jsonb)['a'];
+select ('[1, "2", null]'::jsonb)[0];
+select ('[1, "2", null]'::jsonb)['1'];
+select ('[1, "2", null]'::jsonb)[1.0];
+select ('[1, "2", null]'::jsonb)[2];
+select ('[1, "2", null]'::jsonb)[3];
+select ('[1, "2", null]'::jsonb)[-2];
+select ('[1, "2", null]'::jsonb)[1]['a'];
+select ('[1, "2", null]'::jsonb)[1][0];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['b'];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'][1];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d']['a'];
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1'];
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2'];
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']['a3'];
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'];
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'][2];
+
+create TEMP TABLE test_jsonb_subscript (
+ id int,
+ test_json jsonb
+);
+
+insert into test_jsonb_subscript values
+(1, '{}'), -- empty jsonb
+(2, '{"key": "value"}'); -- jsonb with data
+
+-- update empty jsonb
+update test_jsonb_subscript set test_json['a'] = 1 where id = 1;
+select * from test_jsonb_subscript;
+
+-- update jsonb with some data
+update test_jsonb_subscript set test_json['a'] = 1 where id = 2;
+select * from test_jsonb_subscript;
+
+-- replace jsonb
+update test_jsonb_subscript set test_json['a'] = 'test';
+select * from test_jsonb_subscript;
+
+-- replace by object
+update test_jsonb_subscript set test_json['a'] = '{"b": 1}'::jsonb;
+select * from test_jsonb_subscript;
+
+-- replace by array
+update test_jsonb_subscript set test_json['a'] = '[1, 2, 3]'::jsonb;
+select * from test_jsonb_subscript;
+
+-- use jsonb subscription in where clause
+select * from test_jsonb_subscript where test_json['key'] = '"value"';
+select * from test_jsonb_subscript where test_json['key_doesnt_exists'] = '"value"';
+select * from test_jsonb_subscript where test_json['key'] = '"wrong_value"';
diff --git a/src/tutorial/Makefile b/src/tutorial/Makefile
index 16dc390..cf8082a 100644
--- a/src/tutorial/Makefile
+++ b/src/tutorial/Makefile
@@ -13,8 +13,8 @@
#
#-------------------------------------------------------------------------
-MODULES = complex funcs
-DATA_built = advanced.sql basics.sql complex.sql funcs.sql syscat.sql
+MODULES = complex funcs subscription
+DATA_built = advanced.sql basics.sql complex.sql funcs.sql syscat.sql subscription.sql
ifdef NO_PGXS
subdir = src/tutorial
diff --git a/src/tutorial/subscription.c b/src/tutorial/subscription.c
new file mode 100644
index 0000000..2e11e65
--- /dev/null
+++ b/src/tutorial/subscription.c
@@ -0,0 +1,230 @@
+/*
+ * src/tutorial/subscription.c
+ *
+ ******************************************************************************
+ This file contains routines that can be bound to a Postgres backend and
+ called by the backend in the process of processing queries. The calling
+ format for these routines is dictated by Postgres architecture.
+******************************************************************************/
+
+#include "postgres.h"
+
+#include "catalog/pg_type.h"
+#include "executor/executor.h"
+#include "nodes/execnodes.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_node.h"
+#include "utils/array.h"
+#include "fmgr.h"
+#include "funcapi.h"
+
+PG_MODULE_MAGIC;
+
+typedef struct Custom
+{
+ int first;
+ int second;
+} Custom;
+
+
+/*****************************************************************************
+ * Input/Output functions
+ *****************************************************************************/
+
+PG_FUNCTION_INFO_V1(custom_in);
+
+Datum
+custom_in(PG_FUNCTION_ARGS)
+{
+ char *str = PG_GETARG_CSTRING(0);
+ int firstValue,
+ secondValue;
+ Custom *result;
+
+ if (sscanf(str, " ( %d , %d )", &firstValue, &secondValue) != 2)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid input syntax for complex: \"%s\"",
+ str)));
+
+
+ result = (Custom *) palloc(sizeof(Custom));
+ result->first = firstValue;
+ result->second = secondValue;
+ PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(custom_out);
+
+Datum
+custom_out(PG_FUNCTION_ARGS)
+{
+ Custom *custom = (Custom *) PG_GETARG_POINTER(0);
+ char *result;
+
+ result = psprintf("(%d, %d)", custom->first, custom->second);
+ PG_RETURN_CSTRING(result);
+}
+
+/*****************************************************************************
+ * Custom subscription logic functions
+ *****************************************************************************/
+
+Datum
+custom_subscription_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptingRefExprState *sbstate = (SubscriptingRefExprState *) PG_GETARG_POINTER(0);
+ SubscriptingExecData *sbsdata = (SubscriptingExecData *) PG_GETARG_POINTER(1);
+ SubscriptingRef *custom_ref = (SubscriptingRef *) sbstate->xprstate.expr;
+ Custom *result = (Custom *) sbsdata->containerSource;
+ bool *is_null = sbsdata->isNull;
+ bool is_assignment = (custom_ref->refassgnexpr != NULL);
+
+ int index;
+
+ if (sbsdata->indexprNumber != 1)
+ ereport(ERROR, (errmsg("custom does not support nested subscription")));
+
+ index = DatumGetInt32(sbsdata->upper[0]);
+
+ if (is_assignment)
+ {
+ ExprContext *econtext = sbsdata->xprcontext;
+ Datum sourceData;
+ Datum save_datum;
+ bool save_isNull;
+ bool eisnull;
+
+ /*
+ * We might have a nested-assignment situation, in which the
+ * refassgnexpr is itself a FieldStore or SubscriptingRef that needs to
+ * obtain and modify the previous value of the array element or slice
+ * being replaced. If so, we have to extract that value from the
+ * array and pass it down via the econtext's caseValue. It's safe to
+ * reuse the CASE mechanism because there cannot be a CASE between
+ * here and where the value would be needed, and an array assignment
+ * can't be within a CASE either. (So saving and restoring the
+ * caseValue is just paranoia, but let's do it anyway.)
+ *
+ * Since fetching the old element might be a nontrivial expense, do it
+ * only if the argument appears to actually need it.
+ */
+ save_datum = econtext->caseValue_datum;
+ save_isNull = econtext->caseValue_isNull;
+
+ /*
+ * Evaluate the value to be assigned into the container.
+ */
+ sourceData = ExecEvalExpr(sbstate->refassgnexpr,
+ econtext,
+ &eisnull,
+ NULL);
+
+ econtext->caseValue_datum = save_datum;
+ econtext->caseValue_isNull = save_isNull;
+
+ /*
+ * For an assignment to a fixed-length array type, both the original
+ * array and the value to be assigned into it must be non-NULL, else
+ * we punt and return the original array.
+ */
+ if (sbstate->refattrlength > 0) /* fixed-length container? */
+ if (eisnull || *is_null)
+ return sbsdata->containerSource;
+
+ /*
+ * For assignment to varlena container, we handle a NULL original array
+ * by substituting an empty (zero-dimensional) array; insertion of the
+ * new element will result in a singleton array value. It does not
+ * matter whether the new element is NULL.
+ */
+ if (*is_null)
+ {
+ sbsdata->containerSource =
+ PointerGetDatum(construct_empty_array(custom_ref->refelemtype));
+ *is_null = false;
+ }
+
+ if (index == 1)
+ result->first = DatumGetInt32(sourceData);
+ else
+ result->second = DatumGetInt32(sourceData);
+
+ PG_RETURN_POINTER(result);
+ }
+ else
+ {
+ if (index == 1)
+ PG_RETURN_INT32(result->first);
+ else
+ PG_RETURN_INT32(result->second);
+ }
+}
+
+Datum
+custom_subscription_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptingRef *sbsref = (SubscriptingRef *) PG_GETARG_POINTER(0);
+ ParseState *pstate = (ParseState *) PG_GETARG_POINTER(1);
+ List *upperIndexpr = NIL;
+ ListCell *l;
+
+ if (sbsref->reflowerindexpr != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("custom subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *)lfirst(sbsref->reflowerindexpr->head))))));
+
+ foreach(l, sbsref->refupperindexpr)
+ {
+ Node *subexpr = (Node *) lfirst(l);
+
+ Assert(subexpr != NULL);
+
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("custom subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *) lfirst(sbsref->refupperindexpr->head))))));
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ INT4OID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("custom subscript must have int type"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ }
+
+ sbsref->refupperindexpr = upperIndexpr;
+ sbsref->refelemtype = INT4OID;
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+PG_FUNCTION_INFO_V1(custom_subscription);
+
+Datum
+custom_subscription(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1,
+ fcinfo->nargs);
+
+ if (op_type & SBS_VALIDATION)
+ return custom_subscription_prepare(&target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return custom_subscription_evaluate(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscription function: %d", op_type);
+}
diff --git a/src/tutorial/subscription.source b/src/tutorial/subscription.source
new file mode 100644
index 0000000..7479fa7
--- /dev/null
+++ b/src/tutorial/subscription.source
@@ -0,0 +1,71 @@
+---------------------------------------------------------------------------
+--
+-- subscription.sql-
+-- This file shows how to create a new subscription procedure for
+-- user-defined type.
+--
+--
+-- Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+-- Portions Copyright (c) 1994, Regents of the University of California
+--
+-- src/tutorial/subscription.source
+--
+---------------------------------------------------------------------------
+
+-----------------------------
+-- Creating a new type:
+-- We are going to create a new type called 'complex' which represents
+-- complex numbers.
+-- A user-defined type must have an input and an output function, and
+-- optionally can have binary input and output functions. All of these
+-- are usually user-defined C functions.
+-----------------------------
+
+-- Assume the user defined functions are in /home/erthalion/programms/postgresql-master/src/tutorial/complex$DLSUFFIX
+-- (we do not want to assume this is in the dynamic loader search path).
+-- Look at $PWD/complex.c for the source. Note that we declare all of
+-- them as STRICT, so we do not need to cope with NULL inputs in the
+-- C code. We also mark them IMMUTABLE, since they always return the
+-- same outputs given the same inputs.
+
+-- the input function 'complex_in' takes a null-terminated string (the
+-- textual representation of the type) and turns it into the internal
+-- (in memory) representation. You will get a message telling you 'complex'
+-- does not exist yet but that's okay.
+
+CREATE FUNCTION custom_in(cstring)
+ RETURNS custom
+ AS '_OBJWD_/subscription'
+ LANGUAGE C IMMUTABLE STRICT;
+
+-- the output function 'complex_out' takes the internal representation and
+-- converts it into the textual representation.
+
+CREATE FUNCTION custom_out(custom)
+ RETURNS cstring
+ AS '_OBJWD_/subscription'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION custom_subscription(internal)
+ RETURNS internal
+ AS '_OBJWD_/subscription'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE custom (
+ internallength = 8,
+ input = custom_in,
+ output = custom_out,
+ subscription = custom_subscription
+);
+
+-- we can use it in a table
+
+CREATE TABLE test_subscription (
+ data custom,
+);
+
+INSERT INTO test_subscription VALUES ('(1, 2)');
+
+SELECT data[0] from test_subscription;
+
+UPDATE test_subscription SET data[1] = 3;
Hello,
Do you have an updated version of the patch?
2016-10-18 20:41 GMT+03:00 Dmitry Dolgov <9erthalion6@gmail.com>:
The term "subscription" is confusing me
Yes, you're right. "container" is too general I think, so I renamed
everything
to "subscripting".
There is another occurrences of "subscription" including file names. Please
fix them.
Also I've sent a personal email, that we have performance degradation of
SELECT queries:
create table test (
a int2[],
b int4[][][]);
With patch:
=> insert into test (a[1:5], b[1:1][1:2][1:2])
select '{1,2,3,4,5}', '{{{0,0},{1,2}}}'
from generate_series(1,100000);
Time: 936,285 ms
=> UPDATE test SET a[0] = '2';
Time: 1605,406 ms (00:01,605)
=> UPDATE test SET b[1:1][1:1][1:2] = '{113, 117}';
Time: 1603,076 ms (00:01,603)
=> explain analyze select a[1], b[1][1][1] from test;
QUERY PLAN
------------------------------------------------------------
-------------------------------------------------
Seq Scan on test (cost=0.00..3858.00 rows=100000 width=6) (actual
time=0.035..241.280 rows=100000 loops=1)
Planning time: 0.087 ms
Execution time: 246.916 ms
(3 rows)
And without patch:
=> insert into test (a[1:5], b[1:1][1:2][1:2])
select '{1,2,3,4,5}', '{{{0,0},{1,2}}}'
from generate_series(1,100000);
Time: 971,677 ms
=> UPDATE test SET a[0] = '2';
Time: 1508,262 ms (00:01,508)
=> UPDATE test SET b[1:1][1:1][1:2] = '{113, 117}';
Time: 1473,459 ms (00:01,473)
=> explain analyze select a[1], b[1][1][1] from test;
QUERY PLAN
------------------------------------------------------------
------------------------------------------------
Seq Scan on test (cost=0.00..5286.00 rows=100000 width=6) (actual
time=0.024..98.475 rows=100000 loops=1)
Planning time: 0.081 ms
Execution time: 105.055 ms
(3 rows)
--
Artur Zakirov
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
Hi
Sorry for late reply and thank you for the feedback (especially about the
problem with performance).
There is another occurrences of "subscription" including file names
Fixed.
we have performance degradation of SELECT queries
Yes, I figured it out. The main problem was about type info lookups, some of
them were made for each data extraction operation, which is unnecessary.
Here are results on my machine:
with the fixed version of patch
=# explain analyze select a[1], b[1][1][1] from test;
QUERY PLAN
------------------------------------------------------------------------------------------------------------
Seq Scan on test (cost=0.00..5286.00 rows=100000 width=6) (actual
time=0.950..48.370 rows=100000 loops=1)
Planning time: 0.054 ms
Execution time: 51.859 ms
(3 rows)
and without the patch
=# explain analyze select a[1], b[1][1][1] from test;
QUERY PLAN
------------------------------------------------------------------------------------------------------------
Seq Scan on test (cost=0.00..5286.00 rows=100000 width=6) (actual
time=3.443..43.452 rows=100000 loops=1)
Planning time: 0.117 ms
Execution time: 46.875 ms
(3 rows)
It's still slightly slower because of all new logic aroung subscripting
operation, but not that much.
Attachments:
generic_type_subscription_v4.patchtext/x-patch; charset=US-ASCII; name=generic_type_subscription_v4.patchDownload
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 8ce24e0..d9c9d64 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -2464,14 +2464,14 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
JumbleExpr(jstate, (Node *) expr->aggfilter);
}
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
- JumbleExpr(jstate, (Node *) aref->refupperindexpr);
- JumbleExpr(jstate, (Node *) aref->reflowerindexpr);
- JumbleExpr(jstate, (Node *) aref->refexpr);
- JumbleExpr(jstate, (Node *) aref->refassgnexpr);
+ JumbleExpr(jstate, (Node *) sbsref->refupperindexpr);
+ JumbleExpr(jstate, (Node *) sbsref->reflowerindexpr);
+ JumbleExpr(jstate, (Node *) sbsref->refexpr);
+ JumbleExpr(jstate, (Node *) sbsref->refassgnexpr);
}
break;
case T_FuncExpr:
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 691658f..7c43585 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -137,7 +137,7 @@ static void deparseExpr(Expr *expr, deparse_expr_cxt *context);
static void deparseVar(Var *node, deparse_expr_cxt *context);
static void deparseConst(Const *node, deparse_expr_cxt *context);
static void deparseParam(Param *node, deparse_expr_cxt *context);
-static void deparseArrayRef(ArrayRef *node, deparse_expr_cxt *context);
+static void deparseSubscriptingRef(SubscriptingRef *node, deparse_expr_cxt *context);
static void deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context);
static void deparseOpExpr(OpExpr *node, deparse_expr_cxt *context);
static void deparseOperatorName(StringInfo buf, Form_pg_operator opform);
@@ -358,9 +358,9 @@ foreign_expr_walker(Node *node,
state = FDW_COLLATE_UNSAFE;
}
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *ar = (ArrayRef *) node;
+ SubscriptingRef *ar = (SubscriptingRef *) node;
/* Assignment should not be in restrictions. */
if (ar->refassgnexpr != NULL)
@@ -1822,8 +1822,8 @@ deparseExpr(Expr *node, deparse_expr_cxt *context)
case T_Param:
deparseParam((Param *) node, context);
break;
- case T_ArrayRef:
- deparseArrayRef((ArrayRef *) node, context);
+ case T_SubscriptingRef:
+ deparseSubscriptingRef((SubscriptingRef *) node, context);
break;
case T_FuncExpr:
deparseFuncExpr((FuncExpr *) node, context);
@@ -2043,7 +2043,7 @@ deparseParam(Param *node, deparse_expr_cxt *context)
* Deparse an array subscript expression.
*/
static void
-deparseArrayRef(ArrayRef *node, deparse_expr_cxt *context)
+deparseSubscriptingRef(SubscriptingRef *node, deparse_expr_cxt *context)
{
StringInfo buf = context->buf;
ListCell *lowlist_item;
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 29738b0..565e493 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -7056,6 +7056,13 @@
</row>
<row>
+ <entry><structfield>typsubscripting</structfield></entry>
+ <entry><type>regproc</type></entry>
+ <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+ <entry>Custom subscripting function with type-specific logic, or 0 if this type doesn't support subscripting.</entry>
+ </row>
+
+ <row>
<entry><structfield>typdefaultbin</structfield></entry>
<entry><type>pg_node_tree</type></entry>
<entry></entry>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 69649a7..38433ef 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -71,6 +71,7 @@
<!ENTITY xplang SYSTEM "xplang.sgml">
<!ENTITY xoper SYSTEM "xoper.sgml">
<!ENTITY xtypes SYSTEM "xtypes.sgml">
+<!ENTITY xsubscripting SYSTEM "xsubscripting.sgml">
<!ENTITY plperl SYSTEM "plperl.sgml">
<!ENTITY plpython SYSTEM "plpython.sgml">
<!ENTITY plsql SYSTEM "plpgsql.sgml">
diff --git a/doc/src/sgml/json.sgml b/doc/src/sgml/json.sgml
index 3cf78d6..efffd5c 100644
--- a/doc/src/sgml/json.sgml
+++ b/doc/src/sgml/json.sgml
@@ -569,4 +569,29 @@ SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"tags": ["qu
compared using the default database collation.
</para>
</sect2>
+
+ <sect2 id="json-subscripting">
+ <title>JSON subscripting</title>
+ <para>
+ JSONB data type support array-style subscripting expressions to extract or update particular element. An example of subscripting syntax:
+<programlisting>
+-- Extract value by key
+SELECT ('{"a": 1}'::jsonb)['a'];
+
+-- Extract nested value by key path
+SELECT ('{"a": {"b": {"c": 1}}}'::jsonb)['a']['b']['c'];
+
+-- Extract element by index
+SELECT ('[1, "2", null]'::jsonb)['1'];
+
+-- Update value by key
+UPDATE table_name set jsonb_field['key'] = 1;
+
+-- Select records using where clause with subscripting
+SELECT * from table_name where jsonb_field['key'] = '"value"';
+</programlisting>
+ </para>
+ </sect2>
+
+
</sect1>
diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index 5a09f19..5448b1b 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -54,6 +54,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
[ , ELEMENT = <replaceable class="parameter">element</replaceable> ]
[ , DELIMITER = <replaceable class="parameter">delimiter</replaceable> ]
[ , COLLATABLE = <replaceable class="parameter">collatable</replaceable> ]
+ [ , SUBSCRIPTING = <replaceable class="parameter">subscripting_function</replaceable> ]
)
CREATE TYPE <replaceable class="parameter">name</replaceable>
@@ -194,7 +195,8 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
<replaceable class="parameter">send_function</replaceable>,
<replaceable class="parameter">type_modifier_input_function</replaceable>,
<replaceable class="parameter">type_modifier_output_function</replaceable> and
- <replaceable class="parameter">analyze_function</replaceable>
+ <replaceable class="parameter">analyze_function</replaceable>,
+ <replaceable class="parameter">subscripting_function</replaceable>
are optional. Generally these functions have to be coded in C
or another low-level language.
</para>
@@ -451,6 +453,22 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
make use of the collation information; this does not happen
automatically merely by marking the type collatable.
</para>
+
+ <para>
+ The optional
+ <replaceable class="parameter">subscripting_function</replaceable>
+ contains type-specific logic for subscripting of the data type.
+ By default, there is no such function, which means that the data
+ type doesn't support subscripting. The subscripting function must be
+ declared to take a single argument of type <type>internal</>, and return
+ a <type>internal</> result. There are two examples of implementation for
+ subscripting function in case of array
+ (<replaceable class="parameter">array_subscripting</replaceable>)
+ and jsonb
+ (<replaceable class="parameter">jsonb_subscripting</replaceable>)
+ types in <filename>src/backend/utils/adt/arrayfuncs.c</> and
+ <filename>src/backend/utils/adt/jsonfuncs.c</> corresponding.
+ </para>
</refsect2>
<refsect2>
@@ -766,6 +784,16 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">subscripting_function</replaceable></term>
+ <listitem>
+ <para>
+ The name of a function that contains type-specific subscripting logic for
+ the data type.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/doc/src/sgml/xsubscripting.sgml b/doc/src/sgml/xsubscripting.sgml
new file mode 100644
index 0000000..37ab6ea
--- /dev/null
+++ b/doc/src/sgml/xsubscripting.sgml
@@ -0,0 +1,100 @@
+<!-- doc/src/sgml/xsubscripting.sgml -->
+
+ <sect1 id="xsubscripting">
+ <title>User-defined subscripting procedure</title>
+
+ <indexterm zone="xsubscripting">
+ <primary>custom subscripting</primary>
+ </indexterm>
+ <para>
+ When you define a new base type, you can also specify a custom procedure
+ to handle subscripting expressions. It should contains logic for verification
+ and for extraction or update your data. For instance:
+</para>
+<programlisting><![CDATA[
+typedef struct Custom
+{
+ int first;
+ int second;
+} Custom;
+
+Datum
+custom_subscripting_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptingExecData *sbsdata = (SubscriptingExecData *) PG_GETARG_POINTER(1);
+ Custom *result = (Custom *) sbsdata->containerSource;
+
+ // Some extraction or update logic based on sbsdata
+}
+
+Datum
+custom_subscripting_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptingRef *sbsref = (SubscriptingRef *) PG_GETARG_POINTER(0);
+
+ // Some verifications or type coersion
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+PG_FUNCTION_INFO_V1(custom_subscripting);
+
+Datum
+custom_subscripting(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1,
+ fcinfo->nargs);
+
+ if (op_type & SBS_VALIDATION)
+ return custom_subscripting_prepare(&target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return custom_subscripting_evaluate(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscripting function: %d", op_type);
+}]]>
+</programlisting>
+
+<para>
+ Then you can define a subscripting procedure and a custom data type:
+</para>
+<programlisting>
+CREATE FUNCTION custom_subscripting(internal)
+ RETURNS internal
+ AS '<replaceable>filename</replaceable>'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE custom (
+ internallength = 4,
+ input = custom_in,
+ output = custom_out,
+ subscripting = custom_subscripting
+);
+</programlisting>
+
+<para>
+ and use it as usual:
+</para>
+<programlisting>
+CREATE TABLE test_subscripting (
+ data custom,
+);
+
+INSERT INTO test_subscripting VALUES ('(1, 2)');
+
+SELECT data[0] from test_subscripting;
+
+UPDATE test_subscripting SET data[1] = 3;
+</programlisting>
+
+
+ <para>
+ The examples of custom subscripting implementation can be found in
+ <filename>subscripting.sql</filename> and <filename>subscripting.c</filename>
+ in the <filename>src/tutorial</> directory of the source distribution.
+ See the <filename>README</> file in that directory for instructions
+ about running the examples.
+ </para>
+
+</sect1>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index 0cf7b9e..81ba2ea 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -978,7 +978,8 @@ AddNewRelationType(const char *typeName,
-1, /* typmod */
0, /* array dimensions for typBaseType */
false, /* Type NOT NULL */
- InvalidOid); /* rowtypes never have a collation */
+ InvalidOid, /* rowtypes never have a collation */
+ InvalidOid); /* typsubscripting - none */
}
/* --------------------------------
@@ -1245,7 +1246,8 @@ heap_create_with_catalog(const char *relname,
-1, /* typmod */
0, /* array dimensions for typBaseType */
false, /* Type NOT NULL */
- InvalidOid); /* rowtypes never have a collation */
+ InvalidOid, /* rowtypes never have a collation */
+ F_ARRAY_SUBSCRIPTING); /* array implementation */
pfree(relarrayname);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 4b2d281..7ceee0f 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -118,6 +118,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(-1);
values[Anum_pg_type_typndims - 1] = Int32GetDatum(0);
values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(InvalidOid);
+ values[Anum_pg_type_typsubscripting - 1] = ObjectIdGetDatum(InvalidOid);
nulls[Anum_pg_type_typdefaultbin - 1] = true;
nulls[Anum_pg_type_typdefault - 1] = true;
nulls[Anum_pg_type_typacl - 1] = true;
@@ -166,6 +167,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
false,
InvalidOid,
InvalidOid,
+ InvalidOid,
NULL,
false);
@@ -224,7 +226,8 @@ TypeCreate(Oid newTypeOid,
int32 typeMod,
int32 typNDims, /* Array dimensions for baseType */
bool typeNotNull,
- Oid typeCollation)
+ Oid typeCollation,
+ Oid subscriptingProcedure)
{
Relation pg_type_desc;
Oid typeObjectId;
@@ -364,6 +367,7 @@ TypeCreate(Oid newTypeOid,
values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(typeMod);
values[Anum_pg_type_typndims - 1] = Int32GetDatum(typNDims);
values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(typeCollation);
+ values[Anum_pg_type_typsubscripting - 1] = ObjectIdGetDatum(subscriptingProcedure);
/*
* initialize the default binary value for this type. Check for nulls of
@@ -484,6 +488,7 @@ TypeCreate(Oid newTypeOid,
isImplicitArray,
baseType,
typeCollation,
+ subscriptingProcedure,
(defaultTypeBin ?
stringToNode(defaultTypeBin) :
NULL),
@@ -530,6 +535,7 @@ GenerateTypeDependencies(Oid typeNamespace,
bool isImplicitArray,
Oid baseType,
Oid typeCollation,
+ Oid subscriptingProcedure,
Node *defaultExpr,
bool rebuild)
{
@@ -682,6 +688,14 @@ GenerateTypeDependencies(Oid typeNamespace,
/* Normal dependency on the default expression. */
if (defaultExpr)
recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL);
+
+ if (OidIsValid(subscriptingProcedure))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = subscriptingProcedure;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
}
/*
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 056933a..5c6284e 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -94,6 +94,7 @@ static Oid findTypeSendFunction(List *procname, Oid typeOid);
static Oid findTypeTypmodinFunction(List *procname);
static Oid findTypeTypmodoutFunction(List *procname);
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
+static Oid findTypeSubscriptingFunction(List *procname);
static Oid findRangeSubOpclass(List *opcname, Oid subtype);
static Oid findRangeCanonicalFunction(List *procname, Oid typeOid);
static Oid findRangeSubtypeDiffFunction(List *procname, Oid subtype);
@@ -123,6 +124,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
List *typmodinName = NIL;
List *typmodoutName = NIL;
List *analyzeName = NIL;
+ List *subscriptingName = NIL;
char category = TYPCATEGORY_USER;
bool preferred = false;
char delimiter = DEFAULT_TYPDELIM;
@@ -141,6 +143,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
DefElem *typmodinNameEl = NULL;
DefElem *typmodoutNameEl = NULL;
DefElem *analyzeNameEl = NULL;
+ DefElem *subscriptingNameEl = NULL;
DefElem *categoryEl = NULL;
DefElem *preferredEl = NULL;
DefElem *delimiterEl = NULL;
@@ -163,6 +166,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
Oid resulttype;
ListCell *pl;
ObjectAddress address;
+ Oid subscriptingOid = InvalidOid;
/*
* As of Postgres 8.4, we require superuser privilege to create a base
@@ -262,6 +266,9 @@ DefineType(ParseState *pstate, List *names, List *parameters)
else if (pg_strcasecmp(defel->defname, "analyze") == 0 ||
pg_strcasecmp(defel->defname, "analyse") == 0)
defelp = &analyzeNameEl;
+ else if (pg_strcasecmp(defel->defname, "subscripting") == 0 ||
+ pg_strcasecmp(defel->defname, "subscripting") == 0)
+ defelp = &subscriptingNameEl;
else if (pg_strcasecmp(defel->defname, "category") == 0)
defelp = &categoryEl;
else if (pg_strcasecmp(defel->defname, "preferred") == 0)
@@ -332,6 +339,8 @@ DefineType(ParseState *pstate, List *names, List *parameters)
typmodoutName = defGetQualifiedName(typmodoutNameEl);
if (analyzeNameEl)
analyzeName = defGetQualifiedName(analyzeNameEl);
+ if (subscriptingNameEl)
+ subscriptingName = defGetQualifiedName(subscriptingNameEl);
if (categoryEl)
{
char *p = defGetString(categoryEl);
@@ -513,6 +522,9 @@ DefineType(ParseState *pstate, List *names, List *parameters)
if (analyzeName)
analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid);
+ if (subscriptingName)
+ subscriptingOid = findTypeSubscriptingFunction(subscriptingName);
+
/*
* Check permissions on functions. We choose to require the creator/owner
* of a type to also own the underlying functions. Since creating a type
@@ -632,7 +644,8 @@ DefineType(ParseState *pstate, List *names, List *parameters)
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
false, /* Type NOT NULL */
- collation); /* type's collation */
+ collation, /* type's collation */
+ subscriptingOid); /* subscripting procedure */
Assert(typoid == address.objectId);
/*
@@ -673,7 +686,8 @@ DefineType(ParseState *pstate, List *names, List *parameters)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- collation); /* type's collation */
+ collation, /* type's collation */
+ F_ARRAY_SUBSCRIPTING);
pfree(array_type);
@@ -735,6 +749,7 @@ DefineDomain(CreateDomainStmt *stmt)
Oid receiveProcedure;
Oid sendProcedure;
Oid analyzeProcedure;
+ Oid subscriptingProcedure;
bool byValue;
char category;
char delimiter;
@@ -858,6 +873,9 @@ DefineDomain(CreateDomainStmt *stmt)
/* Analysis function */
analyzeProcedure = baseType->typanalyze;
+ /* Subscripting function */
+ subscriptingProcedure = baseType->typsubscripting;
+
/* Inherited default value */
datum = SysCacheGetAttr(TYPEOID, typeTup,
Anum_pg_type_typdefault, &isnull);
@@ -1059,7 +1077,8 @@ DefineDomain(CreateDomainStmt *stmt)
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
typNotNull, /* Type NOT NULL */
- domaincoll); /* type's collation */
+ domaincoll, /* type's collation */
+ subscriptingProcedure); /* subscripting procedure */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
@@ -1171,7 +1190,8 @@ DefineEnum(CreateEnumStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* type's collation */
+ InvalidOid, /* type's collation */
+ InvalidOid); /* typsubscripting - none */
/* Enter the enum's values into pg_enum */
EnumValuesCreate(enumTypeAddr.objectId, stmt->vals);
@@ -1211,7 +1231,8 @@ DefineEnum(CreateEnumStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* type's collation */
+ InvalidOid, /* type's collation */
+ F_ARRAY_SUBSCRIPTING); /* array subscripting implementation */
pfree(enumArrayName);
@@ -1499,7 +1520,8 @@ DefineRange(CreateRangeStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* type's collation (ranges never have one) */
+ InvalidOid, /* type's collation (ranges never have one) */
+ InvalidOid); /* typsubscripting - none */
Assert(typoid == address.objectId);
/* Create the entry in pg_range */
@@ -1541,7 +1563,8 @@ DefineRange(CreateRangeStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* typcollation */
+ InvalidOid, /* typcollation */
+ F_ARRAY_SUBSCRIPTING); /* array subscripting implementation */
pfree(rangeArrayName);
@@ -1885,6 +1908,33 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
return procOid;
}
+static Oid
+findTypeSubscriptingFunction(List *procname)
+{
+ Oid argList[1];
+ Oid procOid;
+
+ /*
+ * Subscripting functions always take one INTERNAL argument and return INTERNAL.
+ */
+ argList[0] = INTERNALOID;
+
+ procOid = LookupFuncName(procname, 1, argList, true);
+ if (!OidIsValid(procOid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("function %s does not exist",
+ func_signature_string(procname, 1, NIL, argList))));
+
+ if (get_func_rettype(procOid) != INTERNALOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("type subscripting function %s must return type %s",
+ NameListToString(procname), "internal")));
+
+ return procOid;
+}
+
/*
* Find suitable support functions and opclasses for a range type.
*/
@@ -2239,6 +2289,7 @@ AlterDomainDefault(List *names, Node *defaultRaw)
false, /* a domain isn't an implicit array */
typTup->typbasetype,
typTup->typcollation,
+ typTup->typsubscripting,
defaultExpr,
true); /* Rebuild is true */
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index 743e7d6..b0e6070 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -55,6 +55,7 @@
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/lsyscache.h"
+#include "utils/syscache.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
#include "utils/typcache.h"
@@ -62,10 +63,9 @@
/* static function decls */
-static Datum ExecEvalArrayRef(ArrayRefExprState *astate,
+static Datum ExecEvalSubscriptingRef(SubscriptingRefExprState *astate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
-static bool isAssignmentIndirectionExpr(ExprState *exprstate);
static Datum ExecEvalAggref(AggrefExprState *aggref,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
@@ -251,39 +251,48 @@ static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
/*----------
- * ExecEvalArrayRef
+ * ExecEvalSubscriptingRef
*
- * This function takes an ArrayRef and returns the extracted Datum
- * if it's a simple reference, or the modified array value if it's
- * an array assignment (i.e., array element or slice insertion).
+ * This function takes a SubscriptingRef, extracts all information required
+ * for subscripting and pass it to a particular subscripting procedure,
+ * specified for this data type. As a result the extracted Datum will be
+ * returned if it's a simple reference, or the modified containers value if
+ * it's an containers assignment (i.e., containers element or slice
+ * insertion).
*
* NOTE: if we get a NULL result from a subscript expression, we return NULL
- * when it's an array reference, or raise an error when it's an assignment.
+ * when it's an containers reference, or raise an error when it's an assignment.
*----------
*/
+
static Datum
-ExecEvalArrayRef(ArrayRefExprState *astate,
- ExprContext *econtext,
- bool *isNull,
- ExprDoneCond *isDone)
+ExecEvalSubscriptingRef(SubscriptingRefExprState *sbstate, ExprContext *econtext,
+ bool *isNull, ExprDoneCond *isDone)
{
- ArrayRef *arrayRef = (ArrayRef *) astate->xprstate.expr;
- Datum array_source;
- bool isAssignment = (arrayRef->refassgnexpr != NULL);
- bool eisnull;
- ListCell *l;
- int i = 0,
- j = 0;
- IntArray upper,
- lower;
- bool upperProvided[MAXDIM],
- lowerProvided[MAXDIM];
- int *lIndex;
-
- array_source = ExecEvalExpr(astate->refexpr,
- econtext,
- isNull,
- isDone);
+ SubscriptingRef *sbsRef = (SubscriptingRef *) sbstate->xprstate.expr;
+ Oid containerType;
+ RegProcedure typsubscripting;
+ bool isAssignment = (sbsRef->refassgnexpr != NULL);
+ bool eisnull;
+ Datum *upper = NULL,
+ *lower = NULL;
+ ListCell *l;
+ int i = 0,
+ j = 0;
+ SubscriptingExecData sbsdata;
+ bool upperProvided[MAXDIM],
+ lowerProvided[MAXDIM];
+
+ if (sbstate->refupperindexpr != NULL)
+ upper = (Datum *) palloc(sbstate->refupperindexpr->length * sizeof(Datum *));
+
+ if (sbstate->reflowerindexpr != NULL)
+ lower = (Datum *) palloc(sbstate->reflowerindexpr->length * sizeof(Datum *));
+
+ sbsdata.xprcontext = econtext;
+ sbsdata.isNull = isNull;
+ sbsdata.containerSource = ExecEvalExpr(sbstate->refexpr, econtext,
+ isNull, isDone);
/*
* If refexpr yields NULL, and it's a fetch, then result is NULL. In the
@@ -297,51 +306,49 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
return (Datum) NULL;
}
- foreach(l, astate->refupperindexpr)
+ foreach(l, sbstate->refupperindexpr)
{
ExprState *eltstate = (ExprState *) lfirst(l);
if (i >= MAXDIM)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ errmsg("number of container dimensions (%d) exceeds the maximum allowed (%d)",
i + 1, MAXDIM)));
if (eltstate == NULL)
{
- /* Slice bound is omitted, so use array's upper bound */
- Assert(astate->reflowerindexpr != NIL);
+ /* Slice bound is omitted, so use containers's upper bound */
+ Assert(sbstate->reflowerindexpr != NIL);
upperProvided[i++] = false;
continue;
}
+
upperProvided[i] = true;
+ upper[i++] = ExecEvalExpr(eltstate, econtext, &eisnull, NULL);
- upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate,
- econtext,
- &eisnull,
- NULL));
/* If any index expr yields NULL, result is NULL or error */
if (eisnull)
{
if (isAssignment)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
- errmsg("array subscript in assignment must not be null")));
+ errmsg("container subscript in assignment must not be null")));
*isNull = true;
return (Datum) NULL;
}
}
- if (astate->reflowerindexpr != NIL)
+ if (sbstate->reflowerindexpr != NIL)
{
- foreach(l, astate->reflowerindexpr)
+ foreach(l, sbstate->reflowerindexpr)
{
ExprState *eltstate = (ExprState *) lfirst(l);
if (j >= MAXDIM)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ errmsg("number of container dimensions (%d) exceeds the maximum allowed (%d)",
j + 1, MAXDIM)));
if (eltstate == NULL)
@@ -350,191 +357,54 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
lowerProvided[j++] = false;
continue;
}
+
lowerProvided[j] = true;
+ lower[j++] = ExecEvalExpr(eltstate, econtext, &eisnull, NULL);
- lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate,
- econtext,
- &eisnull,
- NULL));
/* If any index expr yields NULL, result is NULL or error */
if (eisnull)
{
if (isAssignment)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
- errmsg("array subscript in assignment must not be null")));
+ errmsg("container subscript in assignment must not be null")));
*isNull = true;
return (Datum) NULL;
}
}
+
/* this can't happen unless parser messed up */
if (i != j)
elog(ERROR, "upper and lower index lists are not same length");
- lIndex = lower.indx;
}
- else
- lIndex = NULL;
-
- if (isAssignment)
- {
- Datum sourceData;
- Datum save_datum;
- bool save_isNull;
-
- /*
- * We might have a nested-assignment situation, in which the
- * refassgnexpr is itself a FieldStore or ArrayRef that needs to
- * obtain and modify the previous value of the array element or slice
- * being replaced. If so, we have to extract that value from the
- * array and pass it down via the econtext's caseValue. It's safe to
- * reuse the CASE mechanism because there cannot be a CASE between
- * here and where the value would be needed, and an array assignment
- * can't be within a CASE either. (So saving and restoring the
- * caseValue is just paranoia, but let's do it anyway.)
- *
- * Since fetching the old element might be a nontrivial expense, do it
- * only if the argument appears to actually need it.
- */
- save_datum = econtext->caseValue_datum;
- save_isNull = econtext->caseValue_isNull;
-
- if (isAssignmentIndirectionExpr(astate->refassgnexpr))
- {
- if (*isNull)
- {
- /* whole array is null, so any element or slice is too */
- econtext->caseValue_datum = (Datum) 0;
- econtext->caseValue_isNull = true;
- }
- else if (lIndex == NULL)
- {
- econtext->caseValue_datum =
- array_get_element(array_source, i,
- upper.indx,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign,
- &econtext->caseValue_isNull);
- }
- else
- {
- econtext->caseValue_datum =
- array_get_slice(array_source, i,
- upper.indx, lower.indx,
- upperProvided, lowerProvided,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
- econtext->caseValue_isNull = false;
- }
- }
- else
- {
- /* argument shouldn't need caseValue, but for safety set it null */
- econtext->caseValue_datum = (Datum) 0;
- econtext->caseValue_isNull = true;
- }
-
- /*
- * Evaluate the value to be assigned into the array.
- */
- sourceData = ExecEvalExpr(astate->refassgnexpr,
- econtext,
- &eisnull,
- NULL);
-
- econtext->caseValue_datum = save_datum;
- econtext->caseValue_isNull = save_isNull;
- /*
- * For an assignment to a fixed-length array type, both the original
- * array and the value to be assigned into it must be non-NULL, else
- * we punt and return the original array.
- */
- if (astate->refattrlength > 0) /* fixed-length array? */
- if (eisnull || *isNull)
- return array_source;
-
- /*
- * For assignment to varlena arrays, we handle a NULL original array
- * by substituting an empty (zero-dimensional) array; insertion of the
- * new element will result in a singleton array value. It does not
- * matter whether the new element is NULL.
- */
- if (*isNull)
- {
- array_source = PointerGetDatum(construct_empty_array(arrayRef->refelemtype));
- *isNull = false;
- }
+ sbsdata.upper = upper;
+ sbsdata.upperProvided = upperProvided;
+ sbsdata.lower = lower;
+ sbsdata.lowerProvided = lowerProvided;
+ sbsdata.indexprNumber = i;
- if (lIndex == NULL)
- return array_set_element(array_source, i,
- upper.indx,
- sourceData,
- eisnull,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
- else
- return array_set_slice(array_source, i,
- upper.indx, lower.indx,
- upperProvided, lowerProvided,
- sourceData,
- eisnull,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
- }
+ if (!OidIsValid(sbstate->fcache.fn_oid))
+ {
+ FmgrInfo flinfo;
- if (lIndex == NULL)
- return array_get_element(array_source, i,
- upper.indx,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign,
- isNull);
- else
- return array_get_slice(array_source, i,
- upper.indx, lower.indx,
- upperProvided, lowerProvided,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
-}
+ containerType = getBaseTypeAndTypmod(sbsRef->refcontainertype,
+ &sbsRef->reftypmod);
+ typsubscripting = get_typsubscripting(containerType);
-/*
- * Helper for ExecEvalArrayRef: is expr a nested FieldStore or ArrayRef
- * that might need the old element value passed down?
- *
- * (We could use this in ExecEvalFieldStore too, but in that case passing
- * the old value is so cheap there's no need.)
- */
-static bool
-isAssignmentIndirectionExpr(ExprState *exprstate)
-{
- if (exprstate == NULL)
- return false; /* just paranoia */
- if (IsA(exprstate, FieldStoreState))
- {
- FieldStore *fstore = (FieldStore *) exprstate->expr;
+ if (!OidIsValid(typsubscripting))
+ /* this can't happen */
+ elog(ERROR, "can not find subscripting procedure for type %s",
+ format_type_be(containerType));
- if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
- return true;
+ fmgr_info(typsubscripting, &flinfo);
+ sbstate->fcache = flinfo;
}
- else if (IsA(exprstate, ArrayRefExprState))
- {
- ArrayRef *arrayRef = (ArrayRef *) exprstate->expr;
- if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr))
- return true;
- }
- return false;
+ return FunctionCall3(&sbstate->fcache,
+ Int32GetDatum(SBS_EXEC),
+ PointerGetDatum(sbstate),
+ PointerGetDatum(&sbsdata));
}
/* ----------------------------------------------------------------
@@ -4332,7 +4202,7 @@ ExecEvalFieldStore(FieldStoreState *fstate,
/*
* Use the CaseTestExpr mechanism to pass down the old value of the
* field being replaced; this is needed in case the newval is itself a
- * FieldStore or ArrayRef that has to obtain and modify the old value.
+ * FieldStore or SubscriptingRef that has to obtain and modify the old value.
* It's safe to reuse the CASE mechanism because there cannot be a
* CASE between here and where the value would be needed, and a field
* assignment can't be within a CASE either. (So saving and restoring
@@ -4695,25 +4565,21 @@ ExecInitExpr(Expr *node, PlanState *parent)
state = (ExprState *) wfstate;
}
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *aref = (ArrayRef *) node;
- ArrayRefExprState *astate = makeNode(ArrayRefExprState);
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
+ SubscriptingRefExprState *astate = makeNode(SubscriptingRefExprState);
- astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayRef;
+ astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalSubscriptingRef;
astate->refupperindexpr = (List *)
- ExecInitExpr((Expr *) aref->refupperindexpr, parent);
+ ExecInitExpr((Expr *) sbsref->refupperindexpr, parent);
astate->reflowerindexpr = (List *)
- ExecInitExpr((Expr *) aref->reflowerindexpr, parent);
- astate->refexpr = ExecInitExpr(aref->refexpr, parent);
- astate->refassgnexpr = ExecInitExpr(aref->refassgnexpr,
+ ExecInitExpr((Expr *) sbsref->reflowerindexpr, parent);
+ astate->refexpr = ExecInitExpr(sbsref->refexpr, parent);
+ astate->refassgnexpr = ExecInitExpr(sbsref->refassgnexpr,
parent);
/* do one-time catalog lookups for type info */
- astate->refattrlength = get_typlen(aref->refarraytype);
- get_typlenbyvalalign(aref->refelemtype,
- &astate->refelemlength,
- &astate->refelembyval,
- &astate->refelemalign);
+ astate->refattrlength = get_typlen(sbsref->refcontainertype);
state = (ExprState *) astate;
}
break;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 71714bc..01f3777 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1292,14 +1292,14 @@ _copyWindowFunc(const WindowFunc *from)
}
/*
- * _copyArrayRef
+ * _copySubscriptingRef
*/
-static ArrayRef *
-_copyArrayRef(const ArrayRef *from)
+static SubscriptingRef *
+_copySubscriptingRef(const SubscriptingRef *from)
{
- ArrayRef *newnode = makeNode(ArrayRef);
+ SubscriptingRef *newnode = makeNode(SubscriptingRef);
- COPY_SCALAR_FIELD(refarraytype);
+ COPY_SCALAR_FIELD(refcontainertype);
COPY_SCALAR_FIELD(refelemtype);
COPY_SCALAR_FIELD(reftypmod);
COPY_SCALAR_FIELD(refcollid);
@@ -4467,8 +4467,8 @@ copyObject(const void *from)
case T_WindowFunc:
retval = _copyWindowFunc(from);
break;
- case T_ArrayRef:
- retval = _copyArrayRef(from);
+ case T_SubscriptingRef:
+ retval = _copySubscriptingRef(from);
break;
case T_FuncExpr:
retval = _copyFuncExpr(from);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 29a090f..4f359b5 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -244,9 +244,9 @@ _equalWindowFunc(const WindowFunc *a, const WindowFunc *b)
}
static bool
-_equalArrayRef(const ArrayRef *a, const ArrayRef *b)
+_equalSubscriptingRef(const SubscriptingRef *a, const SubscriptingRef *b)
{
- COMPARE_SCALAR_FIELD(refarraytype);
+ COMPARE_SCALAR_FIELD(refcontainertype);
COMPARE_SCALAR_FIELD(refelemtype);
COMPARE_SCALAR_FIELD(reftypmod);
COMPARE_SCALAR_FIELD(refcollid);
@@ -2779,8 +2779,8 @@ equal(const void *a, const void *b)
case T_WindowFunc:
retval = _equalWindowFunc(a, b);
break;
- case T_ArrayRef:
- retval = _equalArrayRef(a, b);
+ case T_SubscriptingRef:
+ retval = _equalSubscriptingRef(a, b);
break;
case T_FuncExpr:
retval = _equalFuncExpr(a, b);
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 3997441..28b7cb7 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -66,13 +66,13 @@ exprType(const Node *expr)
case T_WindowFunc:
type = ((const WindowFunc *) expr)->wintype;
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- const ArrayRef *arrayref = (const ArrayRef *) expr;
+ const SubscriptingRef *arrayref = (const SubscriptingRef *) expr;
/* slice and/or store operations yield the array type */
if (arrayref->reflowerindexpr || arrayref->refassgnexpr)
- type = arrayref->refarraytype;
+ type = arrayref->refcontainertype;
else
type = arrayref->refelemtype;
}
@@ -284,9 +284,9 @@ exprTypmod(const Node *expr)
return ((const Const *) expr)->consttypmod;
case T_Param:
return ((const Param *) expr)->paramtypmod;
- case T_ArrayRef:
+ case T_SubscriptingRef:
/* typmod is the same for array or element */
- return ((const ArrayRef *) expr)->reftypmod;
+ return ((const SubscriptingRef *) expr)->reftypmod;
case T_FuncExpr:
{
int32 coercedTypmod;
@@ -772,8 +772,8 @@ exprCollation(const Node *expr)
case T_WindowFunc:
coll = ((const WindowFunc *) expr)->wincollid;
break;
- case T_ArrayRef:
- coll = ((const ArrayRef *) expr)->refcollid;
+ case T_SubscriptingRef:
+ coll = ((const SubscriptingRef *) expr)->refcollid;
break;
case T_FuncExpr:
coll = ((const FuncExpr *) expr)->funccollid;
@@ -1014,8 +1014,8 @@ exprSetCollation(Node *expr, Oid collation)
case T_WindowFunc:
((WindowFunc *) expr)->wincollid = collation;
break;
- case T_ArrayRef:
- ((ArrayRef *) expr)->refcollid = collation;
+ case T_SubscriptingRef:
+ ((SubscriptingRef *) expr)->refcollid = collation;
break;
case T_FuncExpr:
((FuncExpr *) expr)->funccollid = collation;
@@ -1237,9 +1237,9 @@ exprLocation(const Node *expr)
/* function name should always be the first thing */
loc = ((const WindowFunc *) expr)->location;
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
/* just use array argument's location */
- loc = exprLocation((Node *) ((const ArrayRef *) expr)->refexpr);
+ loc = exprLocation((Node *) ((const SubscriptingRef *) expr)->refexpr);
break;
case T_FuncExpr:
{
@@ -1926,21 +1926,21 @@ expression_tree_walker(Node *node,
return true;
}
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
/* recurse directly for upper/lower array index lists */
- if (expression_tree_walker((Node *) aref->refupperindexpr,
+ if (expression_tree_walker((Node *) sbsref->refupperindexpr,
walker, context))
return true;
- if (expression_tree_walker((Node *) aref->reflowerindexpr,
+ if (expression_tree_walker((Node *) sbsref->reflowerindexpr,
walker, context))
return true;
/* walker must see the refexpr and refassgnexpr, however */
- if (walker(aref->refexpr, context))
+ if (walker(sbsref->refexpr, context))
return true;
- if (walker(aref->refassgnexpr, context))
+ if (walker(sbsref->refassgnexpr, context))
return true;
}
break;
@@ -2515,12 +2515,12 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *arrayref = (ArrayRef *) node;
- ArrayRef *newnode;
+ SubscriptingRef *arrayref = (SubscriptingRef *) node;
+ SubscriptingRef *newnode;
- FLATCOPY(newnode, arrayref, ArrayRef);
+ FLATCOPY(newnode, arrayref, SubscriptingRef);
MUTATE(newnode->refupperindexpr, arrayref->refupperindexpr,
List *);
MUTATE(newnode->reflowerindexpr, arrayref->reflowerindexpr,
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index ae86954..48f1265 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1062,11 +1062,11 @@ _outWindowFunc(StringInfo str, const WindowFunc *node)
}
static void
-_outArrayRef(StringInfo str, const ArrayRef *node)
+_outSubscriptingRef(StringInfo str, const SubscriptingRef *node)
{
- WRITE_NODE_TYPE("ARRAYREF");
+ WRITE_NODE_TYPE("SUBSCRIPTINGREF");
- WRITE_OID_FIELD(refarraytype);
+ WRITE_OID_FIELD(refcontainertype);
WRITE_OID_FIELD(refelemtype);
WRITE_INT_FIELD(reftypmod);
WRITE_OID_FIELD(refcollid);
@@ -3445,8 +3445,8 @@ outNode(StringInfo str, const void *obj)
case T_WindowFunc:
_outWindowFunc(str, obj);
break;
- case T_ArrayRef:
- _outArrayRef(str, obj);
+ case T_SubscriptingRef:
+ _outSubscriptingRef(str, obj);
break;
case T_FuncExpr:
_outFuncExpr(str, obj);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 917e6c8..4fae35c 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -606,14 +606,14 @@ _readWindowFunc(void)
}
/*
- * _readArrayRef
+ * _readSubscriptingRef
*/
-static ArrayRef *
-_readArrayRef(void)
+static SubscriptingRef *
+_readSubscriptingRef(void)
{
- READ_LOCALS(ArrayRef);
+ READ_LOCALS(SubscriptingRef);
- READ_OID_FIELD(refarraytype);
+ READ_OID_FIELD(refcontainertype);
READ_OID_FIELD(refelemtype);
READ_INT_FIELD(reftypmod);
READ_OID_FIELD(refcollid);
@@ -2319,8 +2319,8 @@ parseNodeString(void)
return_value = _readGroupingFunc();
else if (MATCH("WINDOWFUNC", 10))
return_value = _readWindowFunc();
- else if (MATCH("ARRAYREF", 8))
- return_value = _readArrayRef();
+ else if (MATCH("SUBSCRIPTINGREF", 15))
+ return_value = _readSubscriptingRef();
else if (MATCH("FUNCEXPR", 8))
return_value = _readFuncExpr();
else if (MATCH("NAMEDARGEXPR", 12))
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 663ffe0..6e041e2 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -1329,10 +1329,10 @@ contain_nonstrict_functions_walker(Node *node, void *context)
/* a window function could return non-null with null input */
return true;
}
- if (IsA(node, ArrayRef))
+ if (IsA(node, SubscriptingRef))
{
/* array assignment is nonstrict, but subscripting is strict */
- if (((ArrayRef *) node)->refassgnexpr != NULL)
+ if (((SubscriptingRef *) node)->refassgnexpr != NULL)
return true;
/* else fall through to check args */
}
@@ -1512,7 +1512,7 @@ contain_leaked_vars_walker(Node *node, void *context)
case T_Var:
case T_Const:
case T_Param:
- case T_ArrayRef:
+ case T_SubscriptingRef:
case T_ArrayExpr:
case T_FieldSelect:
case T_FieldStore:
@@ -3573,7 +3573,7 @@ eval_const_expressions_mutator(Node *node,
* For any node type not handled above, we recurse using
* expression_tree_mutator, which will copy the node unchanged but try to
* simplify its arguments (if any) using this routine. For example: we
- * cannot eliminate an ArrayRef node, but we might be able to simplify
+ * cannot eliminate an SubscriptingRef node, but we might be able to simplify
* constant expressions in its subscripts.
*/
return expression_tree_mutator(node, eval_const_expressions_mutator,
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index 6901e08..37bf24b 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -917,13 +917,13 @@ transformInsertRow(ParseState *pstate, List *exprlist,
expr = (Expr *) linitial(fstore->newvals);
}
- else if (IsA(expr, ArrayRef))
+ else if (IsA(expr, SubscriptingRef))
{
- ArrayRef *aref = (ArrayRef *) expr;
+ SubscriptingRef *sbsref = (SubscriptingRef *) expr;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
break;
- expr = aref->refassgnexpr;
+ expr = sbsref->refassgnexpr;
}
else
break;
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 63f7965..3048a59 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -461,13 +461,13 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
/* process subscripts before this field selection */
if (subscripts)
- result = (Node *) transformArraySubscripts(pstate,
- result,
- exprType(result),
- InvalidOid,
- exprTypmod(result),
- subscripts,
- NULL);
+ result = (Node *) transformContainerSubscripts(pstate,
+ result,
+ exprType(result),
+ InvalidOid,
+ exprTypmod(result),
+ subscripts,
+ NULL);
subscripts = NIL;
newresult = ParseFuncOrColumn(pstate,
@@ -482,13 +482,13 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
}
/* process trailing subscripts, if any */
if (subscripts)
- result = (Node *) transformArraySubscripts(pstate,
- result,
- exprType(result),
- InvalidOid,
- exprTypmod(result),
- subscripts,
- NULL);
+ result = (Node *) transformContainerSubscripts(pstate,
+ result,
+ exprType(result),
+ InvalidOid,
+ exprTypmod(result),
+ subscripts,
+ NULL);
return result;
}
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 62d2f71..d213a1c 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -201,18 +201,22 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location)
/*
* transformArrayType()
- * Identify the types involved in a subscripting operation
+ * Identify the types involved in a subscripting operation for array
*
* On entry, arrayType/arrayTypmod identify the type of the input value
* to be subscripted (which could be a domain type). These are modified
* if necessary to identify the actual array type and typmod, and the
* array's element type is returned. An error is thrown if the input isn't
* an array type.
+ *
+ * NOTE: This part of type-specific code is not separated into type-specific
+ * subscripting procedure for now, but it does not affect on the whole logic,
+ * since InvalidOid will be return in case of other types not an error.
+ * An error will appears only if a subscripting procedure is not defined.
*/
Oid
-transformArrayType(Oid *arrayType, int32 *arrayTypmod)
+transformArrayType(Oid *containerType, int32 *containerTypmod)
{
- Oid origArrayType = *arrayType;
Oid elementType;
HeapTuple type_tuple_array;
Form_pg_type type_struct_array;
@@ -224,7 +228,7 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
* itself. (Note that we provide no method whereby the creator of a
* domain over an array type could hide its ability to be subscripted.)
*/
- *arrayType = getBaseTypeAndTypmod(*arrayType, arrayTypmod);
+ *containerType = getBaseTypeAndTypmod(*containerType, containerTypmod);
/*
* We treat int2vector and oidvector as though they were domains over
@@ -233,25 +237,20 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
* xxxvector type; so we want the result of a slice operation to be
* considered to be of the more general type.
*/
- if (*arrayType == INT2VECTOROID)
- *arrayType = INT2ARRAYOID;
- else if (*arrayType == OIDVECTOROID)
- *arrayType = OIDARRAYOID;
+ if (*containerType == INT2VECTOROID)
+ *containerType = INT2ARRAYOID;
+ else if (*containerType == OIDVECTOROID)
+ *containerType = OIDARRAYOID;
/* Get the type tuple for the array */
- type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*arrayType));
+ type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*containerType));
if (!HeapTupleIsValid(type_tuple_array))
- elog(ERROR, "cache lookup failed for type %u", *arrayType);
+ elog(ERROR, "cache lookup failed for type %u", *containerType);
type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array);
/* needn't check typisdefined since this will fail anyway */
elementType = type_struct_array->typelem;
- if (elementType == InvalidOid)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("cannot subscript type %s because it is not an array",
- format_type_be(origArrayType))));
ReleaseSysCache(type_tuple_array);
@@ -259,61 +258,80 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
}
/*
- * transformArraySubscripts()
- * Transform array subscripting. This is used for both
- * array fetch and array assignment.
+ * transformContainerSubscripts()
+ * Transform container subscripting. This is used for both
+ * container fetch and container assignment.
*
- * In an array fetch, we are given a source array value and we produce an
- * expression that represents the result of extracting a single array element
- * or an array slice.
+ * In a container fetch, we are given a source container value and we produce
+ * an expression that represents the result of extracting a single container
+ * element or a container slice.
*
- * In an array assignment, we are given a destination array value plus a
- * source value that is to be assigned to a single element or a slice of
- * that array. We produce an expression that represents the new array value
- * with the source data inserted into the right part of the array.
+ * In a container assignment, we are given a destination container value plus a
+ * source value that is to be assigned to a single element or a slice of that
+ * container. We produce an expression that represents the new container value
+ * with the source data inserted into the right part of the container.
*
- * For both cases, if the source array is of a domain-over-array type,
- * the result is of the base array type or its element type; essentially,
- * we must fold a domain to its base type before applying subscripting.
- * (Note that int2vector and oidvector are treated as domains here.)
+ * For both cases, this function contains only general subscripting logic while
+ * type-specific logic (e.g. type verifications and coersion) is placend in
+ * separate procedure indicated by typsubscripting. There is only one exception
+ * for now about domain-over-container, if the source container is of a
+ * domain-over-container type, the result is of the base container type or its
+ * element type; essentially, we must fold a domain to its base type before
+ * applying subscripting. (Note that int2vector and oidvector are treated as
+ * domains here.) If domain verification failed we assume, that element type
+ * must be the same as container type (e.g. in case of jsonb).
+ * An error will appear in case if current container type doesn't have a
+ * subscripting procedure.
*
- * pstate Parse state
- * arrayBase Already-transformed expression for the array as a whole
- * arrayType OID of array's datatype (should match type of arrayBase,
- * or be the base type of arrayBase's domain type)
- * elementType OID of array's element type (fetch with transformArrayType,
- * or pass InvalidOid to do it here)
- * arrayTypMod typmod for the array (which is also typmod for the elements)
- * indirection Untransformed list of subscripts (must not be NIL)
- * assignFrom NULL for array fetch, else transformed expression for source.
+ * pstate Parse state
+ * containerBase Already-transformed expression for the container as a whole
+ * containerType OID of container's datatype (should match type of containerBase,
+ * or be the base type of containerBase's domain type)
+ * elementType OID of container's element type (fetch with transformArrayType,
+ * or pass InvalidOid to do it here)
+ * containerTypMod typmod for the container (which is also typmod for the elements)
+ * indirection Untransformed list of subscripts (must not be NIL)
+ * assignFrom NULL for container fetch, else transformed expression for source.
*/
-ArrayRef *
-transformArraySubscripts(ParseState *pstate,
- Node *arrayBase,
- Oid arrayType,
- Oid elementType,
- int32 arrayTypMod,
- List *indirection,
- Node *assignFrom)
+
+SubscriptingRef *
+transformContainerSubscripts(ParseState *pstate,
+ Node *containerBase,
+ Oid containerType,
+ Oid elementType,
+ int32 containerTypMod,
+ List *indirection,
+ Node *assignFrom)
{
- bool isSlice = false;
- List *upperIndexpr = NIL;
- List *lowerIndexpr = NIL;
- ListCell *idx;
- ArrayRef *aref;
+ bool isSlice = false;
+ List *upperIndexpr = NIL;
+ List *lowerIndexpr = NIL;
+ ListCell *idx;
+ SubscriptingRef *sbsref,
+ *prepared_sbsref;
+ RegProcedure typsubscripting = get_typsubscripting(containerType);
+
+ if (!OidIsValid(typsubscripting))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("cannot subscript type %s because it does not support subscripting",
+ format_type_be(containerType))));
/*
* Caller may or may not have bothered to determine elementType. Note
- * that if the caller did do so, arrayType/arrayTypMod must be as modified
+ * that if the caller did do so, containerType/containerTypMod must be as modified
* by transformArrayType, ie, smash domain to base type.
*/
if (!OidIsValid(elementType))
- elementType = transformArrayType(&arrayType, &arrayTypMod);
+ elementType = transformArrayType(&containerType, &containerTypMod);
+
+ if (!OidIsValid(elementType))
+ elementType = containerType;
/*
- * A list containing only simple subscripts refers to a single array
+ * A list containing only simple subscripts refers to a single container
* element. If any of the items are slice specifiers (lower:upper), then
- * the subscript expression means an array slice operation. In this case,
+ * the subscript expression means an container slice operation. In this case,
* we convert any non-slice items to slices by treating the single
* subscript as the upper bound and supplying an assumed lower bound of 1.
* We have to prescan the list to see if there are any slice items.
@@ -343,107 +361,37 @@ transformArraySubscripts(ParseState *pstate,
if (ai->lidx)
{
subexpr = transformExpr(pstate, ai->lidx, pstate->p_expr_kind);
- /* If it's not int4 already, try to coerce */
- subexpr = coerce_to_target_type(pstate,
- subexpr, exprType(subexpr),
- INT4OID, -1,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (subexpr == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array subscript must have type integer"),
- parser_errposition(pstate, exprLocation(ai->lidx))));
- }
- else if (!ai->is_slice)
- {
- /* Make a constant 1 */
- subexpr = (Node *) makeConst(INT4OID,
- -1,
- InvalidOid,
- sizeof(int32),
- Int32GetDatum(1),
- false,
- true); /* pass by value */
}
else
{
/* Slice with omitted lower bound, put NULL into the list */
subexpr = NULL;
}
- lowerIndexpr = lappend(lowerIndexpr, subexpr);
- }
- else
- Assert(ai->lidx == NULL && !ai->is_slice);
-
- if (ai->uidx)
- {
- subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
- /* If it's not int4 already, try to coerce */
- subexpr = coerce_to_target_type(pstate,
- subexpr, exprType(subexpr),
- INT4OID, -1,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (subexpr == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array subscript must have type integer"),
- parser_errposition(pstate, exprLocation(ai->uidx))));
- }
- else
- {
- /* Slice with omitted upper bound, put NULL into the list */
- Assert(isSlice && ai->is_slice);
- subexpr = NULL;
+ lowerIndexpr = lappend(lowerIndexpr, list_make2(subexpr, ai));
}
+ subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
upperIndexpr = lappend(upperIndexpr, subexpr);
}
/*
- * If doing an array store, coerce the source value to the right type.
- * (This should agree with the coercion done by transformAssignedExpr.)
- */
- if (assignFrom != NULL)
- {
- Oid typesource = exprType(assignFrom);
- Oid typeneeded = isSlice ? arrayType : elementType;
- Node *newFrom;
-
- newFrom = coerce_to_target_type(pstate,
- assignFrom, typesource,
- typeneeded, arrayTypMod,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (newFrom == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array assignment requires type %s"
- " but expression is of type %s",
- format_type_be(typeneeded),
- format_type_be(typesource)),
- errhint("You will need to rewrite or cast the expression."),
- parser_errposition(pstate, exprLocation(assignFrom))));
- assignFrom = newFrom;
- }
-
- /*
- * Ready to build the ArrayRef node.
+ * Ready to build the SubscriptingRef node.
*/
- aref = makeNode(ArrayRef);
- aref->refarraytype = arrayType;
- aref->refelemtype = elementType;
- aref->reftypmod = arrayTypMod;
+ sbsref = makeNode(SubscriptingRef);
+ sbsref->refcontainertype = containerType;
+ sbsref->refelemtype = elementType;
+ sbsref->reftypmod = containerTypMod;
/* refcollid will be set by parse_collate.c */
- aref->refupperindexpr = upperIndexpr;
- aref->reflowerindexpr = lowerIndexpr;
- aref->refexpr = (Expr *) arrayBase;
- aref->refassgnexpr = (Expr *) assignFrom;
+ sbsref->refupperindexpr = upperIndexpr;
+ sbsref->reflowerindexpr = lowerIndexpr;
+ sbsref->refexpr = (Expr *) containerBase;
+ sbsref->refassgnexpr = (Expr *) assignFrom;
+
+ prepared_sbsref = (SubscriptingRef *) OidFunctionCall3(typsubscripting,
+ Int32GetDatum(SBS_VALIDATION),
+ PointerGetDatum(sbsref),
+ PointerGetDatum(pstate));
- return aref;
+ return prepared_sbsref;
}
/*
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index b7b82bf..e2b0741 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -759,41 +759,24 @@ transformAssignmentIndirection(ParseState *pstate,
/* base case: just coerce RHS to match target type ID */
- result = coerce_to_target_type(pstate,
- rhs, exprType(rhs),
- targetTypeId, targetTypMod,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
+ if (targetTypeId != InvalidOid)
+ result = coerce_to_target_type(pstate,
+ rhs, exprType(rhs),
+ targetTypeId, targetTypMod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ else
+ result = rhs;
+
if (result == NULL)
- {
- if (targetIsArray)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array assignment to \"%s\" requires type %s"
- " but expression is of type %s",
- targetName,
- format_type_be(targetTypeId),
- format_type_be(exprType(rhs))),
- errhint("You will need to rewrite or cast the expression."),
- parser_errposition(pstate, location)));
- else
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("subfield \"%s\" is of type %s"
- " but expression is of type %s",
- targetName,
- format_type_be(targetTypeId),
- format_type_be(exprType(rhs))),
- errhint("You will need to rewrite or cast the expression."),
- parser_errposition(pstate, location)));
- }
+ result = rhs;
return result;
}
/*
- * helper for transformAssignmentIndirection: process array assignment
+ * helper for transformAssignmentIndirection: process container assignment
*/
static Node *
transformAssignmentSubscripts(ParseState *pstate,
@@ -809,55 +792,55 @@ transformAssignmentSubscripts(ParseState *pstate,
int location)
{
Node *result;
- Oid arrayType;
- int32 arrayTypMod;
+ Oid containerType;
+ int32 containerTypMod;
Oid elementTypeId;
Oid typeNeeded;
Oid collationNeeded;
Assert(subscripts != NIL);
- /* Identify the actual array type and element type involved */
- arrayType = targetTypeId;
- arrayTypMod = targetTypMod;
- elementTypeId = transformArrayType(&arrayType, &arrayTypMod);
+ /* Identify the actual container type and element type involved */
+ containerType = targetTypeId;
+ containerTypMod = targetTypMod;
+ elementTypeId = transformArrayType(&containerType, &containerTypMod);
/* Identify type that RHS must provide */
- typeNeeded = isSlice ? arrayType : elementTypeId;
+ typeNeeded = isSlice ? containerType : elementTypeId;
/*
- * Array normally has same collation as elements, but there's an
- * exception: we might be subscripting a domain over an array type. In
+ * container normally has same collation as elements, but there's an
+ * exception: we might be subscripting a domain over an container type. In
* that case use collation of the base type.
*/
- if (arrayType == targetTypeId)
+ if (containerType == targetTypeId)
collationNeeded = targetCollation;
else
- collationNeeded = get_typcollation(arrayType);
+ collationNeeded = get_typcollation(containerType);
- /* recurse to create appropriate RHS for array assign */
+ /* recurse to create appropriate RHS for container assign */
rhs = transformAssignmentIndirection(pstate,
NULL,
targetName,
true,
typeNeeded,
- arrayTypMod,
+ containerTypMod,
collationNeeded,
next_indirection,
rhs,
location);
/* process subscripts */
- result = (Node *) transformArraySubscripts(pstate,
- basenode,
- arrayType,
- elementTypeId,
- arrayTypMod,
- subscripts,
- rhs);
-
- /* If target was a domain over array, need to coerce up to the domain */
- if (arrayType != targetTypeId)
+ result = (Node *) transformContainerSubscripts(pstate,
+ basenode,
+ containerType,
+ exprType(rhs),
+ containerTypMod,
+ subscripts,
+ rhs);
+
+ /* If target was a domain over container, need to coerce up to the domain */
+ if (containerType != targetTypeId)
{
Oid resulttype = exprType(result);
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index b828e3c..c09ac03 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -895,7 +895,7 @@ process_matched_tle(TargetEntry *src_tle,
/*----------
* Multiple assignments to same attribute. Allow only if all are
- * FieldStore or ArrayRef assignment operations. This is a bit
+ * FieldStore or SubscriptingRef assignment operations. This is a bit
* tricky because what we may actually be looking at is a nest of
* such nodes; consider
* UPDATE tab SET col.fld1.subfld1 = x, col.fld2.subfld2 = y
@@ -903,7 +903,7 @@ process_matched_tle(TargetEntry *src_tle,
* FieldStore(col, fld1, FieldStore(placeholder, subfld1, x))
* FieldStore(col, fld2, FieldStore(placeholder, subfld2, y))
* However, we can ignore the substructure and just consider the top
- * FieldStore or ArrayRef from each assignment, because it works to
+ * FieldStore or SubscriptingRef from each assignment, because it works to
* combine these as
* FieldStore(FieldStore(col, fld1,
* FieldStore(placeholder, subfld1, x)),
@@ -913,7 +913,7 @@ process_matched_tle(TargetEntry *src_tle,
*
* For FieldStore, instead of nesting we can generate a single
* FieldStore with multiple target fields. We must nest when
- * ArrayRefs are involved though.
+ * SubscriptingRefs are involved though.
*----------
*/
src_expr = (Node *) src_tle->expr;
@@ -972,13 +972,13 @@ process_matched_tle(TargetEntry *src_tle,
}
newexpr = (Node *) fstore;
}
- else if (IsA(src_expr, ArrayRef))
+ else if (IsA(src_expr, SubscriptingRef))
{
- ArrayRef *aref = makeNode(ArrayRef);
+ SubscriptingRef *sbsref = makeNode(SubscriptingRef);
- memcpy(aref, src_expr, sizeof(ArrayRef));
- aref->refexpr = (Expr *) prior_expr;
- newexpr = (Node *) aref;
+ memcpy(sbsref, src_expr, sizeof(SubscriptingRef));
+ sbsref->refexpr = (Expr *) prior_expr;
+ newexpr = (Node *) sbsref;
}
else
{
@@ -1005,14 +1005,15 @@ get_assignment_input(Node *node)
return (Node *) fstore->arg;
}
- else if (IsA(node, ArrayRef))
+ else if (IsA(node, SubscriptingRef))
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
return NULL;
- return (Node *) aref->refexpr;
+ return (Node *) sbsref->refexpr;
}
+
return NULL;
}
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 1db7bf0..b5b6811 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -24,6 +24,8 @@
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "libpq/pqformat.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
#include "utils/array.h"
#include "utils/arrayaccess.h"
#include "utils/builtins.h"
@@ -31,6 +33,8 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
+#include "parser/parse_node.h"
+#include "parser/parse_coerce.h"
/*
@@ -88,6 +92,7 @@ typedef struct ArrayIteratorData
static bool array_isspace(char ch);
static int ArrayCount(const char *str, int *dim, char typdelim);
+static bool isAssignmentIndirectionExpr(ExprState *exprstate);
static void ReadArrayStr(char *arrayStr, const char *origStr,
int nitems, int ndim, int *dim,
FmgrInfo *inputproc, Oid typioparam, int32 typmod,
@@ -157,7 +162,8 @@ static int width_bucket_array_variable(Datum operand,
ArrayType *thresholds,
Oid collation,
TypeCacheEntry *typentry);
-
+static Datum array_subscripting_prepare(PG_FUNCTION_ARGS);
+static Datum array_subscripting_evaluate(PG_FUNCTION_ARGS);
/*
* array_in :
@@ -6520,3 +6526,367 @@ width_bucket_array_variable(Datum operand,
return left;
}
+
+/*
+ * Helper for ExecEvalSubscriptingRef: is expr a nested FieldStore or SubscriptingRef
+ * that might need the old element value passed down?
+ *
+ * (We could use this in ExecEvalFieldStore too, but in that case passing
+ * the old value is so cheap there's no need.)
+ */
+static bool
+isAssignmentIndirectionExpr(ExprState *exprstate)
+{
+ if (exprstate == NULL)
+ return false; /* just paranoia */
+ if (IsA(exprstate, FieldStoreState))
+ {
+ FieldStore *fstore = (FieldStore *) exprstate->expr;
+
+ if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
+ return true;
+ }
+ else if (IsA(exprstate, SubscriptingRefExprState))
+ {
+ SubscriptingRef *array_ref = (SubscriptingRef *) exprstate->expr;
+
+ if (array_ref->refexpr && IsA(array_ref->refexpr, CaseTestExpr))
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Perform an actual data extraction or modification for the array
+ * subscripting. As a result the extracted Datum or the modified containers
+ * value will be returned.
+ */
+Datum
+array_subscripting_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptingRefExprState *sbstate = (SubscriptingRefExprState *) PG_GETARG_POINTER(0);
+ SubscriptingExecData *sbsdata = (SubscriptingExecData *) PG_GETARG_POINTER(1);
+ ExprContext *econtext = sbsdata->xprcontext;
+ bool *is_null = sbsdata->isNull;
+ SubscriptingRef *array_ref = (SubscriptingRef *) sbstate->xprstate.expr;
+ bool is_assignment = (array_ref->refassgnexpr != NULL);
+ bool is_slice = (array_ref->reflowerindexpr != NIL);
+ IntArray u_index, l_index;
+ bool eisnull;
+ int i = 0;
+
+ if (sbstate->refelemlength == 0)
+ {
+ /* do one-time catalog lookups for type info */
+ get_typlenbyvalalign(array_ref->refelemtype,
+ &sbstate->refelemlength,
+ &sbstate->refelembyval,
+ &sbstate->refelemalign);
+ }
+
+ for(i = 0; i < sbsdata->indexprNumber; i++)
+ u_index.indx[i] = DatumGetInt32(sbsdata->upper[i]);
+
+ if (is_slice)
+ {
+ for(i = 0; i < sbsdata->indexprNumber; i++)
+ l_index.indx[i] = DatumGetInt32(sbsdata->lower[i]);
+ }
+
+ if (is_assignment)
+ {
+ Datum sourceData;
+ Datum save_datum;
+ bool save_isNull;
+
+ /*
+ * We might have a nested-assignment situation, in which the
+ * refassgnexpr is itself a FieldStore or SubscriptingRef that needs to
+ * obtain and modify the previous value of the array element or slice
+ * being replaced. If so, we have to extract that value from the
+ * array and pass it down via the econtext's caseValue. It's safe to
+ * reuse the CASE mechanism because there cannot be a CASE between
+ * here and where the value would be needed, and an array assignment
+ * can't be within a CASE either. (So saving and restoring the
+ * caseValue is just paranoia, but let's do it anyway.)
+ *
+ * Since fetching the old element might be a nontrivial expense, do it
+ * only if the argument appears to actually need it.
+ */
+ save_datum = econtext->caseValue_datum;
+ save_isNull = econtext->caseValue_isNull;
+
+ if (isAssignmentIndirectionExpr(sbstate->refassgnexpr))
+ {
+ if (*is_null)
+ {
+ /* whole array is null, so any element or slice is too */
+ econtext->caseValue_datum = (Datum) 0;
+ econtext->caseValue_isNull = true;
+ }
+ else if (!is_slice)
+ {
+ econtext->caseValue_datum =
+ array_get_element(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign,
+ &econtext->caseValue_isNull);
+ }
+ else
+ {
+ econtext->caseValue_datum =
+ array_get_slice(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, l_index.indx,
+ sbsdata->upperProvided,
+ sbsdata->lowerProvided,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+ econtext->caseValue_isNull = false;
+ }
+ }
+ else
+ {
+ /* argument shouldn't need caseValue, but for safety set it null */
+ econtext->caseValue_datum = (Datum) 0;
+ econtext->caseValue_isNull = true;
+ }
+
+ /*
+ * Evaluate the value to be assigned into the array.
+ */
+ sourceData = ExecEvalExpr(sbstate->refassgnexpr,
+ econtext,
+ &eisnull,
+ NULL);
+
+ econtext->caseValue_datum = save_datum;
+ econtext->caseValue_isNull = save_isNull;
+
+ /*
+ * For an assignment to a fixed-length array type, both the original
+ * array and the value to be assigned into it must be non-NULL, else
+ * we punt and return the original array.
+ */
+ if (sbstate->refattrlength > 0) /* fixed-length array? */
+ if (eisnull || *is_null)
+ return sbsdata->containerSource;
+
+ /*
+ * For assignment to varlena arrays, we handle a NULL original array
+ * by substituting an empty (zero-dimensional) array; insertion of the
+ * new element will result in a singleton array value. It does not
+ * matter whether the new element is NULL.
+ */
+ if (*is_null)
+ {
+ sbsdata->containerSource = PointerGetDatum(construct_empty_array(array_ref->refelemtype));
+ *is_null = false;
+ }
+
+ if (!is_slice)
+ return array_set_element(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, sourceData, eisnull,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+ else
+ return array_set_slice(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, l_index.indx,
+ sbsdata->upperProvided,
+ sbsdata->lowerProvided,
+ sourceData, eisnull,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+ }
+
+ if (!is_slice)
+ return array_get_element(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign,
+ is_null);
+ else
+ return array_get_slice(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, l_index.indx,
+ sbsdata->upperProvided,
+ sbsdata->lowerProvided,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+}
+
+/*
+ * Perform preparation for the array subscripting, mostly type verification
+ * and coersion. This function produces an expression that represents the
+ * result of extracting a single container element/container slice or the new
+ * container value with the source data inserted into the right part of the
+ * container.
+ */
+Datum
+array_subscripting_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptingRef *sbsref = (SubscriptingRef *) PG_GETARG_POINTER(0);
+ ParseState *pstate = (ParseState *) PG_GETARG_POINTER(1);
+ Node *node = (Node *)sbsref;
+ Oid array_type = sbsref->refcontainertype;
+ int32 array_typ_mode = (int32) sbsref->reftypmod;
+ bool is_slice = sbsref->reflowerindexpr != NIL;
+ Oid typeneeded = InvalidOid,
+ typesource = InvalidOid;
+ Node *new_from;
+ Oid element_type_id;
+ Node *subexpr;
+ List *upperIndexpr = NIL;
+ List *lowerIndexpr = NIL;
+ ListCell *l;
+
+ element_type_id = transformArrayType(&array_type, &array_typ_mode);
+ sbsref->refelemtype = element_type_id;
+
+ foreach(l, sbsref->refupperindexpr)
+ {
+ subexpr = (Node *) lfirst(l);
+
+ if (subexpr == NULL)
+ {
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ continue;
+ }
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ INT4OID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("array subscript must have type integer"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ }
+
+ sbsref->refupperindexpr = upperIndexpr;
+
+ foreach(l, sbsref->reflowerindexpr)
+ {
+ List *expr_ai = (List *) lfirst(l);
+ A_Indices *ai = (A_Indices *) lfirst(list_tail(expr_ai));
+
+ subexpr = (Node *) lfirst(list_head(expr_ai));
+ if (subexpr == NULL && !ai->is_slice)
+ {
+ /* Make a constant 1 */
+ subexpr = (Node *) makeConst(INT4OID,
+ -1,
+ InvalidOid,
+ sizeof(int32),
+ Int32GetDatum(1),
+ false,
+ true); /* pass by value */
+ }
+
+ if (subexpr == NULL)
+ {
+ lowerIndexpr = lappend(lowerIndexpr, subexpr);
+ continue;
+ }
+
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ INT4OID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("array subscript must have type integer"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ lowerIndexpr = lappend(lowerIndexpr, subexpr);
+ }
+
+ sbsref->reflowerindexpr = lowerIndexpr;
+
+ if (sbsref->refassgnexpr != NULL)
+ {
+ new_from = coerce_to_target_type(pstate,
+ (Node *)sbsref->refassgnexpr, typesource,
+ typeneeded, sbsref->reftypmod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (new_from == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("array assignment requires type %s"
+ " but expression is of type %s",
+ format_type_be(typeneeded),
+ format_type_be(typesource)),
+ errhint("You will need to rewrite or cast the expression."),
+ parser_errposition(pstate, exprLocation((Node *)sbsref->refassgnexpr))));
+ sbsref->refassgnexpr = (Expr *)new_from;
+
+ if (array_type != sbsref->refcontainertype)
+ {
+ typesource = exprType((Node *)sbsref->refassgnexpr);
+ typesource = is_slice ? sbsref->refcontainertype : sbsref->refelemtype;
+
+ node = coerce_to_target_type(pstate,
+ node, array_type,
+ sbsref->refcontainertype, sbsref->reftypmod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+
+ /* can fail if we had int2vector/oidvector, but not for true domains */
+ if (node == NULL && node->type != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_CANNOT_COERCE),
+ errmsg("cannot cast type %s to %s",
+ format_type_be(array_type),
+ format_type_be(sbsref->refcontainertype)),
+ parser_errposition(pstate, 0)));
+
+ PG_RETURN_POINTER(node);
+ }
+
+ }
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+/*
+ * Handle array-type subscripting logic.
+ */
+Datum
+array_subscripting(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo;
+ get_slice_arguments(fcinfo, 1, fcinfo->nargs, &target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return array_subscripting_evaluate(&target_fcinfo);
+
+ if (op_type & SBS_VALIDATION)
+ return array_subscripting_prepare(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscripting function: %d", op_type);
+}
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index 987cfd1..2e64c53 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -1145,23 +1145,34 @@ to_jsonb(PG_FUNCTION_ARGS)
{
Datum val = PG_GETARG_DATUM(0);
Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
- JsonbInState result;
- JsonbTypeCategory tcategory;
- Oid outfuncoid;
+ JsonbValue *res = to_jsonb_worker(val, val_type);
+ PG_RETURN_POINTER(JsonbValueToJsonb(res));
+}
- if (val_type == InvalidOid)
+/*
+ * Do the actual conversion to jsonb for to_jsonb function. This logic is
+ * separated because it can be useful not only in here (e.g. we use it in
+ * jsonb subscripting)
+ */
+JsonbValue *
+to_jsonb_worker(Datum source, Oid source_type)
+{
+ JsonbInState result;
+ JsonbTypeCategory tcategory;
+ Oid outfuncoid;
+
+ if (source_type == InvalidOid)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("could not determine input data type")));
- jsonb_categorize_type(val_type,
+ jsonb_categorize_type(source_type,
&tcategory, &outfuncoid);
memset(&result, 0, sizeof(JsonbInState));
- datum_to_jsonb(val, false, &result, tcategory, outfuncoid, false);
-
- PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
+ datum_to_jsonb(source, false, &result, tcategory, outfuncoid, false);
+ return result.res;
}
/*
diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c
index ddc34ce..5e1b682 100644
--- a/src/backend/utils/adt/jsonb_util.c
+++ b/src/backend/utils/adt/jsonb_util.c
@@ -61,18 +61,29 @@ static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate,
JsonbIteratorToken seq,
JsonbValue *scalarVal);
+JsonbValue *
+JsonbToJsonbValue(Jsonb *jsonb)
+{
+ JsonbValue *val = (JsonbValue *) palloc(sizeof(JsonbValue));
+
+ val->type = jbvBinary;
+ val->val.binary.data = &jsonb->root;
+ val->val.binary.len = VARSIZE(jsonb) - VARHDRSZ;
+
+ return val;
+}
+
/*
* Turn an in-memory JsonbValue into a Jsonb for on-disk storage.
*
- * There isn't a JsonbToJsonbValue(), because generally we find it more
- * convenient to directly iterate through the Jsonb representation and only
- * really convert nested scalar values. JsonbIteratorNext() does this, so that
- * clients of the iteration code don't have to directly deal with the binary
- * representation (JsonbDeepContains() is a notable exception, although all
- * exceptions are internal to this module). In general, functions that accept
- * a JsonbValue argument are concerned with the manipulation of scalar values,
- * or simple containers of scalar values, where it would be inconvenient to
- * deal with a great amount of other state.
+ * Generally we find it more convenient to directly iterate through the Jsonb
+ * representation and only really convert nested scalar values.
+ * JsonbIteratorNext() does this, so that clients of the iteration code don't
+ * have to directly deal with the binary representation (JsonbDeepContains() is
+ * a notable exception, although all exceptions are internal to this module).
+ * In general, functions that accept a JsonbValue argument are concerned with
+ * the manipulation of scalar values, or simple containers of scalar values,
+ * where it would be inconvenient to deal with a great amount of other state.
*/
Jsonb *
JsonbValueToJsonb(JsonbValue *val)
@@ -520,6 +531,30 @@ pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq,
JsonbValue *res = NULL;
JsonbValue v;
JsonbIteratorToken tok;
+ int i;
+
+ if (jbval && (seq == WJB_ELEM || seq == WJB_VALUE) && jbval->type == jbvObject)
+ {
+ pushJsonbValue(pstate, WJB_BEGIN_OBJECT, NULL);
+ for (i = 0; i < jbval->val.object.nPairs; i++)
+ {
+ pushJsonbValue(pstate, WJB_KEY, &jbval->val.object.pairs[i].key);
+ pushJsonbValue(pstate, WJB_VALUE, &jbval->val.object.pairs[i].value);
+ }
+
+ return pushJsonbValue(pstate, WJB_END_OBJECT, NULL);
+ }
+
+ if (jbval && (seq == WJB_ELEM || seq == WJB_VALUE) && jbval->type == jbvArray)
+ {
+ pushJsonbValue(pstate, WJB_BEGIN_ARRAY, NULL);
+ for (i = 0; i < jbval->val.array.nElems; i++)
+ {
+ pushJsonbValue(pstate, WJB_ELEM, &jbval->val.array.elems[i]);
+ }
+
+ return pushJsonbValue(pstate, WJB_END_ARRAY, NULL);
+ }
if (!jbval || (seq != WJB_ELEM && seq != WJB_VALUE) ||
jbval->type != jbvBinary)
@@ -530,9 +565,30 @@ pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq,
/* unpack the binary and add each piece to the pstate */
it = JsonbIteratorInit(jbval->val.binary.data);
+
+ if ((jbval->val.binary.data->header & JB_FSCALAR) && *pstate)
+ {
+ tok = JsonbIteratorNext(&it, &v, true);
+ Assert(tok == WJB_BEGIN_ARRAY);
+ Assert(v.type == jbvArray && v.val.array.rawScalar);
+
+ tok = JsonbIteratorNext(&it, &v, true);
+ Assert(tok == WJB_ELEM);
+
+ res = pushJsonbValueScalar(pstate, seq, &v);
+
+ tok = JsonbIteratorNext(&it, &v, true);
+ Assert(tok == WJB_END_ARRAY);
+ Assert(it == NULL);
+
+ return res;
+ }
+
while ((tok = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
res = pushJsonbValueScalar(pstate, tok,
- tok < WJB_BEGIN_ARRAY ? &v : NULL);
+ tok < WJB_BEGIN_ARRAY ||
+ (tok == WJB_BEGIN_ARRAY &&
+ v.val.array.rawScalar) ? &v : NULL);
return res;
}
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 059d570..bf5e382 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -23,6 +23,8 @@
#include "lib/stringinfo.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_coerce.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/hsearch.h"
@@ -32,6 +34,7 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
+#include "parser/parse_node.h"
/* Operations available for setPath */
#define JB_PATH_CREATE 0x0001
@@ -136,18 +139,21 @@ static JsonbValue *findJsonbValueFromContainerLen(JsonbContainer *container,
/* functions supporting jsonb_delete, jsonb_set and jsonb_concat */
static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
JsonbParseState **state);
+static Datum jsonb_set_element(Datum datum, Datum *path, int path_len,
+ Datum sourceData, Oid source_type);
+static Datum jsonb_get_element(Jsonb *jb, Datum *path, int npath,
+ bool *isnull, bool as_text);
static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len,
- JsonbParseState **st, int level, Jsonb *newval,
- int op_type);
+ JsonbParseState **st, int level, JsonbValue *newval, int op_type);
static void setPathObject(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len, JsonbParseState **st,
- int level,
- Jsonb *newval, uint32 npairs, int op_type);
+ int level, JsonbValue *newval, uint32 npairs, int op_type);
static void setPathArray(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len, JsonbParseState **st,
- int level, Jsonb *newval, uint32 nelems, int op_type);
-static void addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb);
+ int level, JsonbValue *newval, uint32 nelems, int op_type);
+static Datum jsonb_subscripting_evaluate(PG_FUNCTION_ARGS);
+static Datum jsonb_subscripting_prepare(PG_FUNCTION_ARGS);
/* state for json_object_keys */
typedef struct OkeysState
@@ -1171,16 +1177,11 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
Jsonb *jb = PG_GETARG_JSONB(0);
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
- Jsonb *res;
Datum *pathtext;
bool *pathnulls;
+ bool isnull;
int npath;
- int i;
- bool have_object = false,
- have_array = false;
- JsonbValue *jbvp = NULL;
- JsonbValue tv;
- JsonbContainer *container;
+ Datum res;
/*
* If the array contains any null elements, return NULL, on the grounds
@@ -1195,9 +1196,28 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
deconstruct_array(path, TEXTOID, -1, false, 'i',
&pathtext, &pathnulls, &npath);
- /* Identify whether we have object, array, or scalar at top-level */
- container = &jb->root;
+ res = jsonb_get_element(jb, pathtext, npath, &isnull, as_text);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(res);
+}
+static Datum
+jsonb_get_element(Jsonb *jb, Datum *path, int npath, bool *isnull, bool as_text)
+{
+ Jsonb *res;
+ JsonbContainer *container = &jb->root;
+ JsonbValue *jbvp = NULL;
+ JsonbValue tv;
+ int i;
+ bool have_object = false,
+ have_array = false;
+
+ *isnull = false;
+
+ /* Identify whether we have object, array, or scalar at top-level */
if (JB_ROOT_IS_OBJECT(jb))
have_object = true;
else if (JB_ROOT_IS_ARRAY(jb) && !JB_ROOT_IS_SCALAR(jb))
@@ -1222,14 +1242,14 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
if (as_text)
{
- PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL,
+ return PointerGetDatum(cstring_to_text(JsonbToCString(NULL,
container,
VARSIZE(jb))));
}
else
{
/* not text mode - just hand back the jsonb */
- PG_RETURN_JSONB(jb);
+ return JsonbGetDatum(jb);
}
}
@@ -1239,21 +1259,24 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
jbvp = findJsonbValueFromContainerLen(container,
JB_FOBJECT,
- VARDATA_ANY(pathtext[i]),
- VARSIZE_ANY_EXHDR(pathtext[i]));
+ VARDATA_ANY(path[i]),
+ VARSIZE_ANY_EXHDR(path[i]));
}
else if (have_array)
{
long lindex;
uint32 index;
- char *indextext = TextDatumGetCString(pathtext[i]);
+ char *indextext = TextDatumGetCString(path[i]);
char *endptr;
errno = 0;
lindex = strtol(indextext, &endptr, 10);
if (endptr == indextext || *endptr != '\0' || errno != 0 ||
lindex > INT_MAX || lindex < INT_MIN)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
if (lindex >= 0)
{
@@ -1271,7 +1294,10 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
nelements = container->header & JB_CMASK;
if (-lindex > nelements)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
else
index = nelements + lindex;
}
@@ -1281,11 +1307,15 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
else
{
/* scalar, extraction yields a null */
- PG_RETURN_NULL();
+ *isnull = true;
+ return PointerGetDatum(NULL);
}
if (jbvp == NULL)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
else if (i == npath - 1)
break;
@@ -1310,27 +1340,57 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
/* special-case outputs for string and null values */
if (jbvp->type == jbvString)
- PG_RETURN_TEXT_P(cstring_to_text_with_len(jbvp->val.string.val,
- jbvp->val.string.len));
+ return PointerGetDatum(
+ cstring_to_text_with_len(jbvp->val.string.val,
+ jbvp->val.string.len));
if (jbvp->type == jbvNull)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
}
res = JsonbValueToJsonb(jbvp);
if (as_text)
{
- PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL,
+ return PointerGetDatum(cstring_to_text(JsonbToCString(NULL,
&res->root,
VARSIZE(res))));
}
else
{
/* not text mode - just hand back the jsonb */
- PG_RETURN_JSONB(res);
+ return JsonbGetDatum(res);
}
}
+Datum
+jsonb_set_element(Datum jsonbdatum, Datum *path, int path_len,
+ Datum sourceData, Oid source_type)
+{
+ Jsonb *jb = DatumGetJsonb(jsonbdatum);
+ JsonbValue *newval,
+ *res;
+ JsonbParseState *state = NULL;
+ JsonbIterator *it;
+ bool *path_nulls = palloc0(path_len * sizeof(bool));
+
+ newval = to_jsonb_worker(sourceData, source_type);
+
+ if (newval->type == jbvArray && newval->val.array.rawScalar)
+ *newval = newval->val.array.elems[0];
+
+ it = JsonbIteratorInit(&jb->root);
+
+ res = setPath(&it, path, path_nulls, path_len, &state, 0,
+ newval, JB_PATH_CREATE);
+
+ pfree(path_nulls);
+
+ PG_RETURN_JSONB(JsonbValueToJsonb(res));
+}
+
/*
* SQL function json_array_length(json) -> int
*/
@@ -3278,57 +3338,6 @@ jsonb_strip_nulls(PG_FUNCTION_ARGS)
}
/*
- * Add values from the jsonb to the parse state.
- *
- * If the parse state container is an object, the jsonb is pushed as
- * a value, not a key.
- *
- * This needs to be done using an iterator because pushJsonbValue doesn't
- * like getting jbvBinary values, so we can't just push jb as a whole.
- */
-static void
-addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb)
-{
- JsonbIterator *it;
- JsonbValue *o = &(*jbps)->contVal;
- JsonbValue v;
- JsonbIteratorToken type;
-
- it = JsonbIteratorInit(&jb->root);
-
- Assert(o->type == jbvArray || o->type == jbvObject);
-
- if (JB_ROOT_IS_SCALAR(jb))
- {
- (void) JsonbIteratorNext(&it, &v, false); /* skip array header */
- (void) JsonbIteratorNext(&it, &v, false); /* fetch scalar value */
-
- switch (o->type)
- {
- case jbvArray:
- (void) pushJsonbValue(jbps, WJB_ELEM, &v);
- break;
- case jbvObject:
- (void) pushJsonbValue(jbps, WJB_VALUE, &v);
- break;
- default:
- elog(ERROR, "unexpected parent of nested structure");
- }
- }
- else
- {
- while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
- {
- if (type == WJB_KEY || type == WJB_VALUE || type == WJB_ELEM)
- (void) pushJsonbValue(jbps, type, &v);
- else
- (void) pushJsonbValue(jbps, type, NULL);
- }
- }
-
-}
-
-/*
* SQL function jsonb_pretty (jsonb)
*
* Pretty-printed text for the jsonb
@@ -3514,7 +3523,8 @@ jsonb_set(PG_FUNCTION_ARGS)
{
Jsonb *in = PG_GETARG_JSONB(0);
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
- Jsonb *newval = PG_GETARG_JSONB(2);
+ Jsonb *newjsonb = PG_GETARG_JSONB(2);
+ JsonbValue *newval = JsonbToJsonbValue(newjsonb);
bool create = PG_GETARG_BOOL(3);
JsonbValue *res = NULL;
Datum *path_elems;
@@ -3606,7 +3616,8 @@ jsonb_insert(PG_FUNCTION_ARGS)
{
Jsonb *in = PG_GETARG_JSONB(0);
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
- Jsonb *newval = PG_GETARG_JSONB(2);
+ Jsonb *newjsonb = PG_GETARG_JSONB(2);
+ JsonbValue *newval = JsonbToJsonbValue(newjsonb);
bool after = PG_GETARG_BOOL(3);
JsonbValue *res = NULL;
Datum *path_elems;
@@ -3769,7 +3780,7 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
static JsonbValue *
setPath(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len,
- JsonbParseState **st, int level, Jsonb *newval, int op_type)
+ JsonbParseState **st, int level, JsonbValue *newval, int op_type)
{
JsonbValue v;
JsonbIteratorToken r;
@@ -3822,11 +3833,11 @@ setPath(JsonbIterator **it, Datum *path_elems,
static void
setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
int path_len, JsonbParseState **st, int level,
- Jsonb *newval, uint32 npairs, int op_type)
+ JsonbValue *newval, uint32 npairs, int op_type)
{
- JsonbValue v;
int i;
- JsonbValue k;
+ JsonbValue k,
+ v;
bool done = false;
if (level >= path_len || path_nulls[level])
@@ -3843,7 +3854,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
newkey.val.string.val = VARDATA_ANY(path_elems[level]);
(void) pushJsonbValue(st, WJB_KEY, &newkey);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_VALUE, newval);
}
for (i = 0; i < npairs; i++)
@@ -3874,7 +3885,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
if (!(op_type & JB_PATH_DELETE))
{
(void) pushJsonbValue(st, WJB_KEY, &k);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_VALUE, newval);
}
done = true;
}
@@ -3897,7 +3908,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
newkey.val.string.val = VARDATA_ANY(path_elems[level]);
(void) pushJsonbValue(st, WJB_KEY, &newkey);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_VALUE, newval);
}
(void) pushJsonbValue(st, r, &k);
@@ -3929,7 +3940,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
static void
setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
int path_len, JsonbParseState **st, int level,
- Jsonb *newval, uint32 nelems, int op_type)
+ JsonbValue *newval, uint32 nelems, int op_type)
{
JsonbValue v;
int idx,
@@ -3977,7 +3988,7 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
(op_type & JB_PATH_CREATE_OR_INSERT))
{
Assert(newval != NULL);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, newval);
done = true;
}
@@ -3993,7 +4004,7 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
r = JsonbIteratorNext(it, &v, true); /* skip */
if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_CREATE))
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, newval);
/*
* We should keep current value only in case of
@@ -4004,7 +4015,7 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
(void) pushJsonbValue(st, r, &v);
if (op_type & (JB_PATH_INSERT_AFTER | JB_PATH_REPLACE))
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, newval);
done = true;
}
@@ -4038,8 +4049,169 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
if ((op_type & JB_PATH_CREATE_OR_INSERT) && !done &&
level == path_len - 1 && i == nelems - 1)
{
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, newval);
}
}
}
}
+
+/*
+ * Perform an actual data extraction or modification for the jsonb
+ * subscripting. As a result the extracted Datum or the modified containers
+ * value will be returned.
+ */
+Datum
+jsonb_subscripting_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptingRefExprState *sbstate = (SubscriptingRefExprState *) PG_GETARG_POINTER(0);
+ SubscriptingExecData *sbsdata = (SubscriptingExecData *) PG_GETARG_POINTER(1);
+ SubscriptingRef *jsonb_ref = (SubscriptingRef *) sbstate->xprstate.expr;
+ bool *is_null = sbsdata->isNull;
+ bool is_assignment = (jsonb_ref->refassgnexpr != NULL);
+
+ if (is_assignment)
+ {
+ ExprContext *econtext = sbsdata->xprcontext;
+ Datum sourceData;
+ Datum save_datum;
+ bool save_isNull;
+ bool eisnull;
+
+ /*
+ * We might have a nested-assignment situation, in which the
+ * refassgnexpr is itself a FieldStore or SubscriptingRef that needs to
+ * obtain and modify the previous value of the array element or slice
+ * being replaced. If so, we have to extract that value from the
+ * array and pass it down via the econtext's caseValue. It's safe to
+ * reuse the CASE mechanism because there cannot be a CASE between
+ * here and where the value would be needed, and an array assignment
+ * can't be within a CASE either. (So saving and restoring the
+ * caseValue is just paranoia, but let's do it anyway.)
+ *
+ * Since fetching the old element might be a nontrivial expense, do it
+ * only if the argument appears to actually need it.
+ */
+ save_datum = econtext->caseValue_datum;
+ save_isNull = econtext->caseValue_isNull;
+
+ /*
+ * Evaluate the value to be assigned into the array.
+ */
+ sourceData = ExecEvalExpr(sbstate->refassgnexpr,
+ econtext,
+ &eisnull,
+ NULL);
+
+ econtext->caseValue_datum = save_datum;
+ econtext->caseValue_isNull = save_isNull;
+
+ /*
+ * For an assignment to a fixed-length array type, both the original
+ * array and the value to be assigned into it must be non-NULL, else
+ * we punt and return the original array.
+ */
+ if (sbstate->refattrlength > 0) /* fixed-length array? */
+ if (eisnull || *is_null)
+ return sbsdata->containerSource;
+
+ /*
+ * For assignment to varlena arrays, we handle a NULL original array
+ * by substituting an empty (zero-dimensional) array; insertion of the
+ * new element will result in a singleton array value. It does not
+ * matter whether the new element is NULL.
+ */
+ if (*is_null)
+ {
+ sbsdata->containerSource =
+ PointerGetDatum(construct_empty_array(jsonb_ref->refelemtype));
+ *is_null = false;
+ }
+
+ return jsonb_set_element(sbsdata->containerSource,
+ sbsdata->upper,
+ sbsdata->indexprNumber,
+ sourceData,
+ jsonb_ref->refelemtype);
+ }
+ else
+ return jsonb_get_element(DatumGetJsonb(sbsdata->containerSource),
+ sbsdata->upper,
+ sbsdata->indexprNumber,
+ is_null,
+ false);
+}
+
+/*
+ * Perform preparation for the jsonb subscripting. Since there are not any
+ * particular restrictions for this kind of subscripting, we will verify that
+ * it is not a slice operation. This function produces an expression that
+ * represents the result of extracting a single container element or the new
+ * container value with the source data inserted into the right part of the
+ * container.
+ */
+Datum
+jsonb_subscripting_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptingRef *sbsref = (SubscriptingRef *) PG_GETARG_POINTER(0);
+ ParseState *pstate = (ParseState *) PG_GETARG_POINTER(1);
+ List *upperIndexpr = NIL;
+ ListCell *l;
+
+ if (sbsref->reflowerindexpr != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("jsonb subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *)lfirst(sbsref->reflowerindexpr->head))))));
+
+ foreach(l, sbsref->refupperindexpr)
+ {
+ Node *subexpr = (Node *) lfirst(l);
+
+ Assert(subexpr != NULL);
+
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("jsonb subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *) lfirst(sbsref->refupperindexpr->head))))));
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ TEXTOID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("jsonb subscript must have text type"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ }
+
+ sbsref->refupperindexpr = upperIndexpr;
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+/*
+ * Handle jsonb-type subscripting logic.
+ */
+Datum
+jsonb_subscripting(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo;
+ get_slice_arguments(fcinfo, 1, fcinfo->nargs, &target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return jsonb_subscripting_evaluate(&target_fcinfo);
+
+ if (op_type & SBS_VALIDATION)
+ return jsonb_subscripting_prepare(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscripting function: %d", op_type);
+}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 8a81d7a..fd78c5a 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -439,7 +439,7 @@ static void get_tablesample_def(TableSampleClause *tablesample,
static void get_opclass_name(Oid opclass, Oid actual_datatype,
StringInfo buf);
static Node *processIndirection(Node *node, deparse_context *context);
-static void printSubscripts(ArrayRef *aref, deparse_context *context);
+static void printSubscripts(SubscriptingRef *aref, deparse_context *context);
static char *get_relation_name(Oid relid);
static char *generate_relation_name(Oid relid, List *namespaces);
static char *generate_qualified_relation_name(Oid relid);
@@ -5828,7 +5828,7 @@ get_update_query_targetlist_def(Query *query, List *targetList,
{
/*
* We must dig down into the expr to see if it's a PARAM_MULTIEXPR
- * Param. That could be buried under FieldStores and ArrayRefs
+ * Param. That could be buried under FieldStores and SubscriptingRefs
* (cf processIndirection()), and underneath those there could be
* an implicit type coercion.
*/
@@ -5841,13 +5841,13 @@ get_update_query_targetlist_def(Query *query, List *targetList,
expr = (Node *) linitial(fstore->newvals);
}
- else if (IsA(expr, ArrayRef))
+ else if (IsA(expr, SubscriptingRef))
{
- ArrayRef *aref = (ArrayRef *) expr;
+ SubscriptingRef *sbsref = (SubscriptingRef *) expr;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
break;
- expr = (Node *) aref->refassgnexpr;
+ expr = (Node *) sbsref->refassgnexpr;
}
else
break;
@@ -6879,7 +6879,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
/* single words: always simple */
return true;
- case T_ArrayRef:
+ case T_SubscriptingRef:
case T_ArrayExpr:
case T_RowExpr:
case T_CoalesceExpr:
@@ -6996,7 +6996,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
return true; /* own parentheses */
}
case T_BoolExpr: /* lower precedence */
- case T_ArrayRef: /* other separators */
+ case T_SubscriptingRef: /* other separators */
case T_ArrayExpr: /* other separators */
case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */
@@ -7046,7 +7046,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
return false;
return true; /* own parentheses */
}
- case T_ArrayRef: /* other separators */
+ case T_SubscriptingRef: /* other separators */
case T_ArrayExpr: /* other separators */
case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */
@@ -7232,9 +7232,9 @@ get_rule_expr(Node *node, deparse_context *context,
get_windowfunc_expr((WindowFunc *) node, context);
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
bool need_parens;
/*
@@ -7245,24 +7245,24 @@ get_rule_expr(Node *node, deparse_context *context,
* here too, and display only the assignment source
* expression.
*/
- if (IsA(aref->refexpr, CaseTestExpr))
+ if (IsA(sbsref->refexpr, CaseTestExpr))
{
- Assert(aref->refassgnexpr);
- get_rule_expr((Node *) aref->refassgnexpr,
+ Assert(sbsref->refassgnexpr);
+ get_rule_expr((Node *) sbsref->refassgnexpr,
context, showimplicit);
break;
}
/*
* Parenthesize the argument unless it's a simple Var or a
- * FieldSelect. (In particular, if it's another ArrayRef, we
+ * FieldSelect. (In particular, if it's another SubscriptingRef, we
* *must* parenthesize to avoid confusion.)
*/
- need_parens = !IsA(aref->refexpr, Var) &&
- !IsA(aref->refexpr, FieldSelect);
+ need_parens = !IsA(sbsref->refexpr, Var) &&
+ !IsA(sbsref->refexpr, FieldSelect);
if (need_parens)
appendStringInfoChar(buf, '(');
- get_rule_expr((Node *) aref->refexpr, context, showimplicit);
+ get_rule_expr((Node *) sbsref->refexpr, context, showimplicit);
if (need_parens)
appendStringInfoChar(buf, ')');
@@ -7275,7 +7275,7 @@ get_rule_expr(Node *node, deparse_context *context,
* EXPLAIN tries to print the targetlist of a plan resulting
* from such a statement.
*/
- if (aref->refassgnexpr)
+ if (sbsref->refassgnexpr)
{
Node *refassgnexpr;
@@ -7292,7 +7292,7 @@ get_rule_expr(Node *node, deparse_context *context,
else
{
/* Just an ordinary array fetch, so print subscripts */
- printSubscripts(aref, context);
+ printSubscripts(sbsref, context);
}
}
break;
@@ -7491,12 +7491,12 @@ get_rule_expr(Node *node, deparse_context *context,
bool need_parens;
/*
- * Parenthesize the argument unless it's an ArrayRef or
+ * Parenthesize the argument unless it's an SubscriptingRef or
* another FieldSelect. Note in particular that it would be
* WRONG to not parenthesize a Var argument; simplicity is not
* the issue here, having the right number of names is.
*/
- need_parens = !IsA(arg, ArrayRef) &&!IsA(arg, FieldSelect);
+ need_parens = !IsA(arg, SubscriptingRef) && !IsA(arg, FieldSelect);
if (need_parens)
appendStringInfoChar(buf, '(');
get_rule_expr(arg, context, true);
@@ -9610,7 +9610,7 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
/*
* processIndirection - take care of array and subfield assignment
*
- * We strip any top-level FieldStore or assignment ArrayRef nodes that
+ * We strip any top-level FieldStore or assignment SubscriptingRef nodes that
* appear in the input, printing them as decoration for the base column
* name (which we assume the caller just printed). Return the subexpression
* that's to be assigned.
@@ -9652,19 +9652,19 @@ processIndirection(Node *node, deparse_context *context)
*/
node = (Node *) linitial(fstore->newvals);
}
- else if (IsA(node, ArrayRef))
+ else if (IsA(node, SubscriptingRef))
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
break;
- printSubscripts(aref, context);
+ printSubscripts(sbsref, context);
/*
* We ignore refexpr since it should be an uninteresting reference
* to the target column or subcolumn.
*/
- node = (Node *) aref->refassgnexpr;
+ node = (Node *) sbsref->refassgnexpr;
}
else
break;
@@ -9674,14 +9674,14 @@ processIndirection(Node *node, deparse_context *context)
}
static void
-printSubscripts(ArrayRef *aref, deparse_context *context)
+printSubscripts(SubscriptingRef *sbsref, deparse_context *context)
{
StringInfo buf = context->buf;
ListCell *lowlist_item;
ListCell *uplist_item;
- lowlist_item = list_head(aref->reflowerindexpr); /* could be NULL */
- foreach(uplist_item, aref->refupperindexpr)
+ lowlist_item = list_head(sbsref->reflowerindexpr); /* could be NULL */
+ foreach(uplist_item, sbsref->refupperindexpr)
{
appendStringInfoChar(buf, '[');
if (lowlist_item)
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 13ae6ad..06a3c1a 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -3061,3 +3061,23 @@ get_range_subtype(Oid rangeOid)
else
return InvalidOid;
}
+
+/*
+ * get_typsubscripting
+ *
+ * Given the type OID, return the type's typsubscripting procedure, if any.
+ */
+RegProcedure
+get_typsubscripting(Oid typid)
+{
+ HeapTuple tp;
+ RegProcedure result = InvalidOid;
+
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (HeapTupleIsValid(tp))
+ {
+ result = ((Form_pg_type) GETSTRUCT(tp))->typsubscripting;
+ ReleaseSysCache(tp);
+ }
+ return result;
+}
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 5d49fe5..f7f2c7e 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -1396,3 +1396,19 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
return tupdesc;
}
+
+void
+get_slice_arguments(FunctionCallInfo fcinfo, int begin, int end, FunctionCallInfoData *sliced_fcinfo)
+{
+ int i;
+
+ InitFunctionCallInfoData(*sliced_fcinfo, fcinfo->flinfo,
+ fcinfo->nargs - 1, fcinfo->fncollation,
+ NULL, NULL);
+
+ for(i = begin; i < end; i++)
+ {
+ sliced_fcinfo->arg[i - begin] = fcinfo->arg[i];
+ sliced_fcinfo->argnull[i - begin] = false;
+ }
+}
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index e57b81c..23bf824 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -143,7 +143,7 @@ typedef FormData_pg_class *Form_pg_class;
* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId;
* similarly, "1" in relminmxid stands for FirstMultiXactId
*/
-DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f f f t n 3 1 _null_ _null_ ));
+DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 31 0 t f f f f f f t n 3 1 _null_ _null_ ));
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 21 0 f f f f f f f t n 3 1 _null_ _null_ ));
DESCR("");
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 17ec71d..a287021 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -5341,6 +5341,12 @@ DESCR("pg_controldata recovery state information as a function");
DATA(insert OID = 3444 ( pg_control_init PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,23,23,23,23,23,23,23,16,16,16,23}" "{o,o,o,o,o,o,o,o,o,o,o,o,o}" "{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,bigint_timestamps,float4_pass_by_value,float8_pass_by_value,data_page_checksum_version}" _null_ _null_ pg_control_init _null_ _null_ _null_ ));
DESCR("pg_controldata init state information as a function");
+/* type subscripting support */
+DATA(insert OID = 3343 ( jsonb_subscripting PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 "2281" "2281" _null_ _null_ _null_ _null_ _null_ jsonb_subscripting _null_ _null_ _null_ ));
+DESCR("Jsonb subscripting logic");
+DATA(insert OID = 3344 ( array_subscripting PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 "2281" "2281" _null_ _null_ _null_ _null_ _null_ array_subscripting _null_ _null_ _null_ ));
+DESCR("Array subscripting logic");
+
/*
* Symbolic values for provolatile column: these indicate whether the result
* of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 162239c..c6cd95e 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -199,6 +199,12 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO
*/
Oid typcollation;
+ /*
+ * Type specific subscripting logic. If typsubscripting is none, it means
+ * that this type doesn't support subscripting.
+ */
+ regproc typsubscripting;
+
#ifdef CATALOG_VARLEN /* variable-length fields start here */
/*
@@ -236,7 +242,7 @@ typedef FormData_pg_type *Form_pg_type;
* compiler constants for pg_type
* ----------------
*/
-#define Natts_pg_type 30
+#define Natts_pg_type 31
#define Anum_pg_type_typname 1
#define Anum_pg_type_typnamespace 2
#define Anum_pg_type_typowner 3
@@ -264,10 +270,10 @@ typedef FormData_pg_type *Form_pg_type;
#define Anum_pg_type_typtypmod 25
#define Anum_pg_type_typndims 26
#define Anum_pg_type_typcollation 27
-#define Anum_pg_type_typdefaultbin 28
-#define Anum_pg_type_typdefault 29
-#define Anum_pg_type_typacl 30
-
+#define Anum_pg_type_typsubscripting 28
+#define Anum_pg_type_typdefaultbin 29
+#define Anum_pg_type_typdefault 30
+#define Anum_pg_type_typacl 31
/* ----------------
* initial contents of pg_type
@@ -283,94 +289,94 @@ typedef FormData_pg_type *Form_pg_type;
*/
/* OIDS 1 - 99 */
-DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("boolean, 'true'/'false'");
#define BOOLOID 16
-DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("variable-length string, binary values escaped");
#define BYTEAOID 17
-DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("single character");
#define CHAROID 18
-DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("63-byte type for storing system identifiers");
#define NAMEOID 19
-DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("~18 digit integer, 8-byte storage");
#define INT8OID 20
-DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("-32 thousand to 32 thousand, 2-byte storage");
#define INT2OID 21
-DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("array of int2, used in system tables");
#define INT2VECTOROID 22
-DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("-2 billion to 2 billion integer, 4-byte storage");
#define INT4OID 23
-DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered procedure");
#define REGPROCOID 24
-DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("variable-length string, no limit specified");
#define TEXTOID 25
-DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("object identifier(oid), maximum 4 billion");
#define OIDOID 26
-DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("(block, offset), physical location of tuple");
#define TIDOID 27
-DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("transaction id");
#define XIDOID 28
-DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("command identifier type, sequence in transaction id");
#define CIDOID 29
-DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("array of oids, used in system tables");
#define OIDVECTOROID 30
/* hand-built rowtype entries for bootstrapped catalogs */
/* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */
-DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
+DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
+DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
+DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
/* OIDS 100 - 199 */
-DATA(insert OID = 114 ( json PGNSP PGUID -1 f b U f t \054 0 0 199 json_in json_out json_recv json_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 114 ( json PGNSP PGUID -1 f b U f t \054 0 0 199 json_in json_out json_recv json_send - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define JSONOID 114
-DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("XML content");
#define XMLOID 142
-DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 199 ( _json PGNSP PGUID -1 f b A f t \054 0 114 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 199 ( _json PGNSP PGUID -1 f b A f t \054 0 114 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
-DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("string representing an internal node tree");
#define PGNODETREEOID 194
-DATA(insert OID = 32 ( pg_ddl_command PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 pg_ddl_command_in pg_ddl_command_out pg_ddl_command_recv pg_ddl_command_send - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 32 ( pg_ddl_command PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 pg_ddl_command_in pg_ddl_command_out pg_ddl_command_recv pg_ddl_command_send - - - ALIGNOF_POINTER p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("internal type for passing CollectedCommand");
#define PGDDLCOMMANDOID 32
/* OIDS 200 - 299 */
-DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("storage manager");
/* OIDS 300 - 399 */
@@ -380,276 +386,276 @@ DESCR("storage manager");
/* OIDS 500 - 599 */
/* OIDS 600 - 699 */
-DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("geometric point '(x, y)'");
#define POINTOID 600
-DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("geometric line segment '(pt1,pt2)'");
#define LSEGOID 601
-DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("geometric path '(pt1,...)'");
#define PATHOID 602
-DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("geometric box '(lower left,upper right)'");
#define BOXOID 603
-DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("geometric polygon '(pt1,...)'");
#define POLYGONOID 604
-DATA(insert OID = 628 ( line PGNSP PGUID 24 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 628 ( line PGNSP PGUID 24 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("geometric line");
#define LINEOID 628
-DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* OIDS 700 - 799 */
-DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("single-precision floating point number, 4-byte storage");
#define FLOAT4OID 700
-DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("double-precision floating point number, 8-byte storage");
#define FLOAT8OID 701
-DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("absolute, limited-range date and time (Unix system time)");
#define ABSTIMEOID 702
-DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("relative, limited-range time interval (Unix delta time)");
#define RELTIMEOID 703
-DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("(abstime,abstime), time interval");
#define TINTERVALOID 704
-DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("");
#define UNKNOWNOID 705
-DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("geometric circle '(center,radius)'");
#define CIRCLEOID 718
-DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("monetary amounts, $d,ddd.cc");
#define CASHOID 790
-DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* OIDS 800 - 899 */
-DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDROID 829
-DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("IP address/netmask, host address, netmask optional");
#define INETOID 869
-DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("network IP address/netmask, network address");
#define CIDROID 650
/* OIDS 900 - 999 */
/* OIDS 1000 - 1099 */
-DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
#define INT2ARRAYOID 1005
-DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
#define INT4ARRAYOID 1007
-DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 100 array_subscripting _null_ _null_ _null_ ));
#define TEXTARRAYOID 1009
-DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
#define OIDARRAYOID 1028
-DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
-DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
-DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout array_typanalyze i x f 0 -1 0 100 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout array_typanalyze i x f 0 -1 0 100 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
#define FLOAT4ARRAYOID 1021
-DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("access control list");
#define ACLITEMOID 1033
-DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
#define CSTRINGARRAYOID 1263
-DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("char(length), blank-padded string, fixed storage length");
#define BPCHAROID 1042
-DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("varchar(length), non-blank-padded string, variable storage length");
#define VARCHAROID 1043
-DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("date");
#define DATEOID 1082
-DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("time of day");
#define TIMEOID 1083
/* OIDS 1100 - 1199 */
-DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("date and time");
#define TIMESTAMPOID 1114
-DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("date and time with time zone");
#define TIMESTAMPTZOID 1184
-DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("@ <number> <units>, time interval");
#define INTERVALOID 1186
-DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* OIDS 1200 - 1299 */
-DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("time of day with time zone");
#define TIMETZOID 1266
-DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* OIDS 1500 - 1599 */
-DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("fixed-length bit string");
#define BITOID 1560
-DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("variable-length bit string");
#define VARBITOID 1562
-DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* OIDS 1600 - 1699 */
/* OIDS 1700 - 1799 */
-DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("numeric(precision, decimal), arbitrary precision number");
#define NUMERICOID 1700
-DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("reference to cursor (portal name)");
#define REFCURSOROID 1790
/* OIDS 2200 - 2299 */
-DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
-DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered procedure (with args)");
#define REGPROCEDUREOID 2202
-DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered operator");
#define REGOPEROID 2203
-DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered operator (with args)");
#define REGOPERATOROID 2204
-DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered class");
#define REGCLASSOID 2205
-DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered type");
#define REGTYPEOID 2206
-DATA(insert OID = 4096 ( regrole PGNSP PGUID 4 t b N f t \054 0 0 4097 regrolein regroleout regrolerecv regrolesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4096 ( regrole PGNSP PGUID 4 t b N f t \054 0 0 4097 regrolein regroleout regrolerecv regrolesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered role");
#define REGROLEOID 4096
-DATA(insert OID = 4089 ( regnamespace PGNSP PGUID 4 t b N f t \054 0 0 4090 regnamespacein regnamespaceout regnamespacerecv regnamespacesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4089 ( regnamespace PGNSP PGUID 4 t b N f t \054 0 0 4090 regnamespacein regnamespaceout regnamespacerecv regnamespacesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered namespace");
#define REGNAMESPACEOID 4089
-DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
#define REGTYPEARRAYOID 2211
-DATA(insert OID = 4097 ( _regrole PGNSP PGUID -1 f b A f t \054 0 4096 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 4090 ( _regnamespace PGNSP PGUID -1 f b A f t \054 0 4089 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4097 ( _regrole PGNSP PGUID -1 f b A f t \054 0 4096 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 4090 ( _regnamespace PGNSP PGUID -1 f b A f t \054 0 4089 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* uuid */
-DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("UUID datatype");
#define UUIDOID 2950
-DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* pg_lsn */
-DATA(insert OID = 3220 ( pg_lsn PGNSP PGUID 8 FLOAT8PASSBYVAL b U f t \054 0 0 3221 pg_lsn_in pg_lsn_out pg_lsn_recv pg_lsn_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3220 ( pg_lsn PGNSP PGUID 8 FLOAT8PASSBYVAL b U f t \054 0 0 3221 pg_lsn_in pg_lsn_out pg_lsn_recv pg_lsn_send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("PostgreSQL LSN datatype");
#define LSNOID 3220
-DATA(insert OID = 3221 ( _pg_lsn PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3221 ( _pg_lsn PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* text search */
-DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("text representation for text search");
#define TSVECTOROID 3614
-DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("GiST index internal text representation for text search");
#define GTSVECTOROID 3642
-DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("query representation for text search");
#define TSQUERYOID 3615
-DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered text search configuration");
#define REGCONFIGOID 3734
-DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered text search dictionary");
#define REGDICTIONARYOID 3769
-DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* jsonb */
-DATA(insert OID = 3802 ( jsonb PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3802 ( jsonb PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 jsonb_subscripting _null_ _null_ _null_ ));
DESCR("Binary JSON");
#define JSONBOID 3802
-DATA(insert OID = 3807 ( _jsonb PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3807 ( _jsonb PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
-DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("txid snapshot");
-DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* range types */
-DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of integers");
#define INT4RANGEOID 3904
-DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of numerics");
-DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of timestamps without time zone");
-DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of timestamps with time zone");
-DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of dates");
-DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of bigints");
-DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/*
* pseudo-types
@@ -664,41 +670,41 @@ DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 arr
* but there is now support for it in records and arrays. Perhaps we should
* just treat it as a regular base type?
*/
-DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define RECORDOID 2249
-DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
#define RECORDARRAYOID 2287
-DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define CSTRINGOID 2275
-DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYOID 2276
-DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYARRAYOID 2277
-DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out void_recv void_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out void_recv void_send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define VOIDOID 2278
-DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define TRIGGEROID 2279
-DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define EVTTRIGGEROID 3838
-DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
-DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define INTERNALOID 2281
-DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define OPAQUEOID 2282
-DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYELEMENTOID 2283
-DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYNONARRAYOID 2776
-DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYENUMOID 3500
-DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define FDW_HANDLEROID 3115
-DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define INDEX_AM_HANDLEROID 325
-DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define TSM_HANDLEROID 3310
-DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYRANGEOID 3831
diff --git a/src/include/catalog/pg_type_fn.h b/src/include/catalog/pg_type_fn.h
index d7bbfdb..d712f6a 100644
--- a/src/include/catalog/pg_type_fn.h
+++ b/src/include/catalog/pg_type_fn.h
@@ -52,7 +52,8 @@ extern ObjectAddress TypeCreate(Oid newTypeOid,
int32 typeMod,
int32 typNDims,
bool typeNotNull,
- Oid typeCollation);
+ Oid typeCollation,
+ Oid subscriptingProcedure);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
@@ -70,6 +71,7 @@ extern void GenerateTypeDependencies(Oid typeNamespace,
bool isImplicitArray,
Oid baseType,
Oid typeCollation,
+ Oid subscriptingProcedure,
Node *defaultExpr,
bool rebuild);
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index e73a824..f53096a 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -183,6 +183,7 @@ extern TupleDesc build_function_result_tupdesc_d(Datum proallargtypes,
Datum proargmodes,
Datum proargnames);
extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
+extern void get_slice_arguments(FunctionCallInfo fcinfo, int begin, int end, FunctionCallInfoData *sliced_fcinfo);
/*----------
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index f6f73f3..924b8fd 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -650,25 +650,52 @@ typedef struct WindowFuncExprState
} WindowFuncExprState;
/* ----------------
- * ArrayRefExprState node
+ * SubscriptingRefExprState node
*
* Note: array types can be fixed-length (typlen > 0), but only when the
* element type is itself fixed-length. Otherwise they are varlena structures
* and have typlen = -1. In any case, an array type is never pass-by-value.
* ----------------
*/
-typedef struct ArrayRefExprState
+typedef struct SubscriptingRefExprState
{
ExprState xprstate;
List *refupperindexpr; /* states for child nodes */
List *reflowerindexpr;
ExprState *refexpr;
ExprState *refassgnexpr;
- int16 refattrlength; /* typlen of array type */
- int16 refelemlength; /* typlen of the array element type */
+ int16 refattrlength; /* typlen of container type */
+ int16 refelemlength; /* typlen of the container element type */
bool refelembyval; /* is the element type pass-by-value? */
char refelemalign; /* typalign of the element type */
-} ArrayRefExprState;
+
+ /*
+ * Function manager's lookup info for the target subscripting operation. If
+ * fcache.fn_oid is InvalidOid, we haven't initialized it yet.
+ */
+ FmgrInfo fcache;
+} SubscriptingRefExprState;
+
+/* ---------------------------------
+ * Subscripting exec information
+ *
+ * It contains all information which is required to perform type-specific data
+ * extraction or modification. This information will be gathered in
+ * `ExecEvalSubscriptingRef` function and passed to `typsubscripting`
+ * procedure.
+ * ---------------------------------
+ */
+typedef struct SubscriptingExecData
+{
+ ExprContext *xprcontext; /* econtext for subscripting */
+ bool *isNull;
+ Datum *upper; /* upper boundary for subscripting */
+ Datum *lower; /* lower boundary for subscripting */
+ bool *upperProvided;
+ bool *lowerProvided;
+ Datum containerSource;
+ int indexprNumber;
+} SubscriptingExecData;
/* ----------------
* FuncExprState node
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 88297bb..5c8d0a1 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -140,7 +140,7 @@ typedef enum NodeTag
T_Aggref,
T_GroupingFunc,
T_WindowFunc,
- T_ArrayRef,
+ T_SubscriptingRef,
T_FuncExpr,
T_NamedArgExpr,
T_OpExpr,
@@ -194,7 +194,7 @@ typedef enum NodeTag
T_AggrefExprState,
T_GroupingFuncExprState,
T_WindowFuncExprState,
- T_ArrayRefExprState,
+ T_SubscriptingRefExprState,
T_FuncExprState,
T_ScalarArrayOpExprState,
T_BoolExprState,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 65510b0..5f73a60 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -350,18 +350,18 @@ typedef struct WindowFunc
} WindowFunc;
/* ----------------
- * ArrayRef: describes an array subscripting operation
- *
- * An ArrayRef can describe fetching a single element from an array,
- * fetching a subarray (array slice), storing a single element into
- * an array, or storing a slice. The "store" cases work with an
- * initial array value and a source value that is inserted into the
- * appropriate part of the array; the result of the operation is an
- * entire new modified array value.
- *
- * If reflowerindexpr = NIL, then we are fetching or storing a single array
- * element at the subscripts given by refupperindexpr. Otherwise we are
- * fetching or storing an array slice, that is a rectangular subarray
+ * SubscriptingRef: describes a subscripting operation over a container
+ *
+ * An SubscriptingRef can describe fetching a single element from a container,
+ * fetching a part of container (e.g. array slice), storing a single element into
+ * a container, or storing a slice. The "store" cases work with an
+ * initial container value and a source value that is inserted into the
+ * appropriate part of the container; the result of the operation is an
+ * entire new modified container value.
+ *
+ * If reflowerindexpr = NIL, then we are fetching or storing a single container
+ * element at the subscripts given by refupperindexpr. Otherwise we are
+ * fetching or storing a container slice, that is a rectangular subcontainer
* with lower and upper bounds given by the index expressions.
* reflowerindexpr must be the same length as refupperindexpr when it
* is not NIL.
@@ -373,27 +373,27 @@ typedef struct WindowFunc
* element; but it is the array type when doing subarray fetch or either
* type of store.
*
- * Note: for the cases where an array is returned, if refexpr yields a R/W
- * expanded array, then the implementation is allowed to modify that object
+ * Note: for the cases where a container is returned, if refexpr yields a R/W
+ * expanded container, then the implementation is allowed to modify that object
* in-place and return the same object.)
* ----------------
*/
-typedef struct ArrayRef
+typedef struct SubscriptingRef
{
Expr xpr;
- Oid refarraytype; /* type of the array proper */
- Oid refelemtype; /* type of the array elements */
- int32 reftypmod; /* typmod of the array (and elements too) */
- Oid refcollid; /* OID of collation, or InvalidOid if none */
- List *refupperindexpr;/* expressions that evaluate to upper array
- * indexes */
- List *reflowerindexpr;/* expressions that evaluate to lower array
- * indexes, or NIL for single array element */
- Expr *refexpr; /* the expression that evaluates to an array
- * value */
- Expr *refassgnexpr; /* expression for the source value, or NULL if
- * fetch */
-} ArrayRef;
+ Oid refcontainertype; /* type of the container proper */
+ Oid refelemtype; /* type of the container elements */
+ int32 reftypmod; /* typmod of the container (and elements too) */
+ Oid refcollid; /* OID of collation, or InvalidOid if none */
+ List *refupperindexpr; /* expressions that evaluate to upper container
+ * indexes */
+ List *reflowerindexpr; /* expressions that evaluate to lower container
+ * indexes, or NIL for single container element */
+ Expr *refexpr; /* the expression that evaluates to an container
+ * value */
+ Expr *refassgnexpr; /* expression for the source value, or NULL if
+ * fetch */
+} SubscriptingRef;
/*
* CoercionContext - distinguishes the allowed set of type casts
@@ -734,7 +734,7 @@ typedef struct FieldSelect
*
* FieldStore represents the operation of modifying one field in a tuple
* value, yielding a new tuple value (the input is not touched!). Like
- * the assign case of ArrayRef, this is used to implement UPDATE of a
+ * the assign case of SubscriptingRef, this is used to implement UPDATE of a
* portion of a column.
*
* A single FieldStore can actually represent updates of several different
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 6633586..5e2526f 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -224,12 +224,17 @@ extern void cancel_parser_errposition_callback(ParseCallbackState *pcbstate);
extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno,
int location);
-extern Oid transformArrayType(Oid *arrayType, int32 *arrayTypmod);
-extern ArrayRef *transformArraySubscripts(ParseState *pstate,
- Node *arrayBase,
- Oid arrayType,
+extern Oid transformArrayType(Oid *containerType, int32 *containerTypmod);
+
+/* Type of a stage in case of type-specific subscripting procedure */
+#define SBS_VALIDATION 0x0001
+#define SBS_EXEC 0x0002
+
+extern SubscriptingRef *transformContainerSubscripts(ParseState *pstate,
+ Node *containerBase,
+ Oid containerType,
Oid elementType,
- int32 arrayTypMod,
+ int32 containerTypMod,
List *indirection,
Node *assignFrom);
extern Const *make_const(ParseState *pstate, Value *value, int location);
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index 6164f11..7fc1982 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -502,4 +502,9 @@ extern Datum array_positions(PG_FUNCTION_ARGS);
*/
extern Datum array_typanalyze(PG_FUNCTION_ARGS);
+/*
+ * prototypes for functions with array subscripting logic
+ */
+extern Datum array_subscripting(PG_FUNCTION_ARGS);
+
#endif /* ARRAY_H */
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index 470d5b1..6cc8053 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -354,6 +354,7 @@ extern Datum jsonb_typeof(PG_FUNCTION_ARGS);
/* generator routines */
extern Datum to_jsonb(PG_FUNCTION_ARGS);
+extern JsonbValue *to_jsonb_worker(Datum source, Oid source_type);
extern Datum jsonb_build_object(PG_FUNCTION_ARGS);
extern Datum jsonb_build_object_noargs(PG_FUNCTION_ARGS);
@@ -427,6 +428,7 @@ extern JsonbValue *pushJsonbValue(JsonbParseState **pstate,
extern JsonbIterator *JsonbIteratorInit(JsonbContainer *container);
extern JsonbIteratorToken JsonbIteratorNext(JsonbIterator **it, JsonbValue *val,
bool skipNested);
+extern JsonbValue *JsonbToJsonbValue(Jsonb *jsonb);
extern Jsonb *JsonbValueToJsonb(JsonbValue *val);
extern bool JsonbDeepContains(JsonbIterator **val,
JsonbIterator **mContained);
@@ -439,4 +441,7 @@ extern char *JsonbToCStringIndent(StringInfo out, JsonbContainer *in,
int estimated_len);
+/* Jsonb subscripting logic */
+extern Datum jsonb_subscripting(PG_FUNCTION_ARGS);
+
#endif /* __JSONB_H__ */
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index dcb8980..5c09bd0 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -159,6 +159,7 @@ extern void free_attstatsslot(Oid atttype,
extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
+extern RegProcedure get_typsubscripting(Oid typid);
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
/* type_is_array_domain accepts both plain arrays and domains over arrays */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 470cf93..bc92a66 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -4742,7 +4742,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
/*
* Evaluate the subscripts, switch into left-to-right order.
- * Like ExecEvalArrayRef(), complain if any subscript is null.
+ * Like ExecEvalSubscriptingRef(), complain if any subscript is null.
*/
for (i = 0; i < nsubscripts; i++)
{
@@ -4790,7 +4790,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
* fixed-length array types we skip the assignment. We can't
* support assignment of a null entry into a fixed-length
* array, either, so that's a no-op too. This is all ugly but
- * corresponds to the current behavior of ExecEvalArrayRef().
+ * corresponds to the current behavior of ExecEvalSubscriptingRef().
*/
if (arrayelem->arraytyplen > 0 && /* fixed-length array? */
(oldarrayisnull || isNull))
@@ -6498,9 +6498,9 @@ exec_simple_check_node(Node *node)
case T_Param:
return TRUE;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *expr = (ArrayRef *) node;
+ SubscriptingRef *expr = (SubscriptingRef *) node;
if (!exec_simple_check_node((Node *) expr->refupperindexpr))
return FALSE;
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
index e2cb08a..dcacf20 100644
--- a/src/test/regress/expected/jsonb.out
+++ b/src/test/regress/expected/jsonb.out
@@ -3447,3 +3447,211 @@ HINT: Try using the function jsonb_set to replace key value.
select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"', true);
ERROR: cannot replace existing key
HINT: Try using the function jsonb_set to replace key value.
+-- jsonb subscript
+select ('123'::jsonb)['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('123'::jsonb)[0];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": 1}'::jsonb)['a'];
+ jsonb
+-------
+ 1
+(1 row)
+
+select ('{"a": 1}'::jsonb)[0];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": 1}'::jsonb)['not_exist'];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[0];
+ jsonb
+-------
+ 1
+(1 row)
+
+select ('[1, "2", null]'::jsonb)['1'];
+ jsonb
+-------
+ "2"
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[1.0];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[2];
+ jsonb
+-------
+ null
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[3];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[-2];
+ jsonb
+-------
+ "2"
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[1]['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[1][0];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['b'];
+ jsonb
+-------
+ "c"
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'];
+ jsonb
+-----------
+ [1, 2, 3]
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'][1];
+ jsonb
+-------
+ 2
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d']['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1'];
+ jsonb
+---------------
+ {"a2": "aaa"}
+(1 row)
+
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2'];
+ jsonb
+-------
+ "aaa"
+(1 row)
+
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']['a3'];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'];
+ jsonb
+-----------------------
+ ["aaa", "bbb", "ccc"]
+(1 row)
+
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'][2];
+ jsonb
+-------
+ "ccc"
+(1 row)
+
+create TEMP TABLE test_jsonb_subscript (
+ id int,
+ test_json jsonb
+);
+insert into test_jsonb_subscript values
+(1, '{}'), -- empty jsonb
+(2, '{"key": "value"}'); -- jsonb with data
+-- update empty jsonb
+update test_jsonb_subscript set test_json['a'] = 1 where id = 1;
+select * from test_jsonb_subscript;
+ id | test_json
+----+------------------
+ 2 | {"key": "value"}
+ 1 | {"a": 1}
+(2 rows)
+
+-- update jsonb with some data
+update test_jsonb_subscript set test_json['a'] = 1 where id = 2;
+select * from test_jsonb_subscript;
+ id | test_json
+----+--------------------------
+ 1 | {"a": 1}
+ 2 | {"a": 1, "key": "value"}
+(2 rows)
+
+-- replace jsonb
+update test_jsonb_subscript set test_json['a'] = 'test';
+select * from test_jsonb_subscript;
+ id | test_json
+----+-------------------------------
+ 1 | {"a": "test"}
+ 2 | {"a": "test", "key": "value"}
+(2 rows)
+
+-- replace by object
+update test_jsonb_subscript set test_json['a'] = '{"b": 1}'::jsonb;
+select * from test_jsonb_subscript;
+ id | test_json
+----+---------------------------------
+ 1 | {"a": {"b": 1}}
+ 2 | {"a": {"b": 1}, "key": "value"}
+(2 rows)
+
+-- replace by array
+update test_jsonb_subscript set test_json['a'] = '[1, 2, 3]'::jsonb;
+select * from test_jsonb_subscript;
+ id | test_json
+----+----------------------------------
+ 1 | {"a": [1, 2, 3]}
+ 2 | {"a": [1, 2, 3], "key": "value"}
+(2 rows)
+
+-- use jsonb subscription in where clause
+select * from test_jsonb_subscript where test_json['key'] = '"value"';
+ id | test_json
+----+----------------------------------
+ 2 | {"a": [1, 2, 3], "key": "value"}
+(1 row)
+
+select * from test_jsonb_subscript where test_json['key_doesnt_exists'] = '"value"';
+ id | test_json
+----+-----------
+(0 rows)
+
+select * from test_jsonb_subscript where test_json['key'] = '"wrong_value"';
+ id | test_json
+----+-----------
+(0 rows)
+
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
index 6b4c796..e277e0d 100644
--- a/src/test/regress/sql/jsonb.sql
+++ b/src/test/regress/sql/jsonb.sql
@@ -868,3 +868,62 @@ select jsonb_insert('{"a": {"b": "value"}}', '{a, c}', '"new_value"', true);
select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"');
select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"', true);
+
+-- jsonb subscript
+select ('123'::jsonb)['a'];
+select ('123'::jsonb)[0];
+select ('{"a": 1}'::jsonb)['a'];
+select ('{"a": 1}'::jsonb)[0];
+select ('{"a": 1}'::jsonb)['not_exist'];
+select ('[1, "2", null]'::jsonb)['a'];
+select ('[1, "2", null]'::jsonb)[0];
+select ('[1, "2", null]'::jsonb)['1'];
+select ('[1, "2", null]'::jsonb)[1.0];
+select ('[1, "2", null]'::jsonb)[2];
+select ('[1, "2", null]'::jsonb)[3];
+select ('[1, "2", null]'::jsonb)[-2];
+select ('[1, "2", null]'::jsonb)[1]['a'];
+select ('[1, "2", null]'::jsonb)[1][0];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['b'];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'][1];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d']['a'];
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1'];
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2'];
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']['a3'];
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'];
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'][2];
+
+create TEMP TABLE test_jsonb_subscript (
+ id int,
+ test_json jsonb
+);
+
+insert into test_jsonb_subscript values
+(1, '{}'), -- empty jsonb
+(2, '{"key": "value"}'); -- jsonb with data
+
+-- update empty jsonb
+update test_jsonb_subscript set test_json['a'] = 1 where id = 1;
+select * from test_jsonb_subscript;
+
+-- update jsonb with some data
+update test_jsonb_subscript set test_json['a'] = 1 where id = 2;
+select * from test_jsonb_subscript;
+
+-- replace jsonb
+update test_jsonb_subscript set test_json['a'] = 'test';
+select * from test_jsonb_subscript;
+
+-- replace by object
+update test_jsonb_subscript set test_json['a'] = '{"b": 1}'::jsonb;
+select * from test_jsonb_subscript;
+
+-- replace by array
+update test_jsonb_subscript set test_json['a'] = '[1, 2, 3]'::jsonb;
+select * from test_jsonb_subscript;
+
+-- use jsonb subscription in where clause
+select * from test_jsonb_subscript where test_json['key'] = '"value"';
+select * from test_jsonb_subscript where test_json['key_doesnt_exists'] = '"value"';
+select * from test_jsonb_subscript where test_json['key'] = '"wrong_value"';
diff --git a/src/tutorial/Makefile b/src/tutorial/Makefile
index 16dc390..0ead60c 100644
--- a/src/tutorial/Makefile
+++ b/src/tutorial/Makefile
@@ -13,8 +13,8 @@
#
#-------------------------------------------------------------------------
-MODULES = complex funcs
-DATA_built = advanced.sql basics.sql complex.sql funcs.sql syscat.sql
+MODULES = complex funcs subscripting
+DATA_built = advanced.sql basics.sql complex.sql funcs.sql syscat.sql subscripting.sql
ifdef NO_PGXS
subdir = src/tutorial
diff --git a/src/tutorial/subscripting.c b/src/tutorial/subscripting.c
new file mode 100644
index 0000000..edd93f1
--- /dev/null
+++ b/src/tutorial/subscripting.c
@@ -0,0 +1,230 @@
+/*
+ * src/tutorial/subscripting.c
+ *
+ ******************************************************************************
+ This file contains routines that can be bound to a Postgres backend and
+ called by the backend in the process of processing queries. The calling
+ format for these routines is dictated by Postgres architecture.
+******************************************************************************/
+
+#include "postgres.h"
+
+#include "catalog/pg_type.h"
+#include "executor/executor.h"
+#include "nodes/execnodes.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_node.h"
+#include "utils/array.h"
+#include "fmgr.h"
+#include "funcapi.h"
+
+PG_MODULE_MAGIC;
+
+typedef struct Custom
+{
+ int first;
+ int second;
+} Custom;
+
+
+/*****************************************************************************
+ * Input/Output functions
+ *****************************************************************************/
+
+PG_FUNCTION_INFO_V1(custom_in);
+
+Datum
+custom_in(PG_FUNCTION_ARGS)
+{
+ char *str = PG_GETARG_CSTRING(0);
+ int firstValue,
+ secondValue;
+ Custom *result;
+
+ if (sscanf(str, " ( %d , %d )", &firstValue, &secondValue) != 2)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid input syntax for complex: \"%s\"",
+ str)));
+
+
+ result = (Custom *) palloc(sizeof(Custom));
+ result->first = firstValue;
+ result->second = secondValue;
+ PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(custom_out);
+
+Datum
+custom_out(PG_FUNCTION_ARGS)
+{
+ Custom *custom = (Custom *) PG_GETARG_POINTER(0);
+ char *result;
+
+ result = psprintf("(%d, %d)", custom->first, custom->second);
+ PG_RETURN_CSTRING(result);
+}
+
+/*****************************************************************************
+ * Custom subscripting logic functions
+ *****************************************************************************/
+
+Datum
+custom_subscripting_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptingRefExprState *sbstate = (SubscriptingRefExprState *) PG_GETARG_POINTER(0);
+ SubscriptingExecData *sbsdata = (SubscriptingExecData *) PG_GETARG_POINTER(1);
+ SubscriptingRef *custom_ref = (SubscriptingRef *) sbstate->xprstate.expr;
+ Custom *result = (Custom *) sbsdata->containerSource;
+ bool *is_null = sbsdata->isNull;
+ bool is_assignment = (custom_ref->refassgnexpr != NULL);
+
+ int index;
+
+ if (sbsdata->indexprNumber != 1)
+ ereport(ERROR, (errmsg("custom does not support nested subscripting")));
+
+ index = DatumGetInt32(sbsdata->upper[0]);
+
+ if (is_assignment)
+ {
+ ExprContext *econtext = sbsdata->xprcontext;
+ Datum sourceData;
+ Datum save_datum;
+ bool save_isNull;
+ bool eisnull;
+
+ /*
+ * We might have a nested-assignment situation, in which the
+ * refassgnexpr is itself a FieldStore or SubscriptingRef that needs to
+ * obtain and modify the previous value of the array element or slice
+ * being replaced. If so, we have to extract that value from the
+ * array and pass it down via the econtext's caseValue. It's safe to
+ * reuse the CASE mechanism because there cannot be a CASE between
+ * here and where the value would be needed, and an array assignment
+ * can't be within a CASE either. (So saving and restoring the
+ * caseValue is just paranoia, but let's do it anyway.)
+ *
+ * Since fetching the old element might be a nontrivial expense, do it
+ * only if the argument appears to actually need it.
+ */
+ save_datum = econtext->caseValue_datum;
+ save_isNull = econtext->caseValue_isNull;
+
+ /*
+ * Evaluate the value to be assigned into the container.
+ */
+ sourceData = ExecEvalExpr(sbstate->refassgnexpr,
+ econtext,
+ &eisnull,
+ NULL);
+
+ econtext->caseValue_datum = save_datum;
+ econtext->caseValue_isNull = save_isNull;
+
+ /*
+ * For an assignment to a fixed-length array type, both the original
+ * array and the value to be assigned into it must be non-NULL, else
+ * we punt and return the original array.
+ */
+ if (sbstate->refattrlength > 0) /* fixed-length container? */
+ if (eisnull || *is_null)
+ return sbsdata->containerSource;
+
+ /*
+ * For assignment to varlena container, we handle a NULL original array
+ * by substituting an empty (zero-dimensional) array; insertion of the
+ * new element will result in a singleton array value. It does not
+ * matter whether the new element is NULL.
+ */
+ if (*is_null)
+ {
+ sbsdata->containerSource =
+ PointerGetDatum(construct_empty_array(custom_ref->refelemtype));
+ *is_null = false;
+ }
+
+ if (index == 1)
+ result->first = DatumGetInt32(sourceData);
+ else
+ result->second = DatumGetInt32(sourceData);
+
+ PG_RETURN_POINTER(result);
+ }
+ else
+ {
+ if (index == 1)
+ PG_RETURN_INT32(result->first);
+ else
+ PG_RETURN_INT32(result->second);
+ }
+}
+
+Datum
+custom_subscripting_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptingRef *sbsref = (SubscriptingRef *) PG_GETARG_POINTER(0);
+ ParseState *pstate = (ParseState *) PG_GETARG_POINTER(1);
+ List *upperIndexpr = NIL;
+ ListCell *l;
+
+ if (sbsref->reflowerindexpr != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("custom subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *)lfirst(sbsref->reflowerindexpr->head))))));
+
+ foreach(l, sbsref->refupperindexpr)
+ {
+ Node *subexpr = (Node *) lfirst(l);
+
+ Assert(subexpr != NULL);
+
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("custom subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *) lfirst(sbsref->refupperindexpr->head))))));
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ INT4OID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("custom subscript must have int type"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ }
+
+ sbsref->refupperindexpr = upperIndexpr;
+ sbsref->refelemtype = INT4OID;
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+PG_FUNCTION_INFO_V1(custom_subscripting);
+
+Datum
+custom_subscripting(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1,
+ fcinfo->nargs);
+
+ if (op_type & SBS_VALIDATION)
+ return custom_subscripting_prepare(&target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return custom_subscripting_evaluate(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscripting function: %d", op_type);
+}
diff --git a/src/tutorial/subscripting.source b/src/tutorial/subscripting.source
new file mode 100644
index 0000000..c37d236
--- /dev/null
+++ b/src/tutorial/subscripting.source
@@ -0,0 +1,71 @@
+---------------------------------------------------------------------------
+--
+-- subscripting.sql-
+-- This file shows how to create a new subscripting procedure for
+-- user-defined type.
+--
+--
+-- Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+-- Portions Copyright (c) 1994, Regents of the University of California
+--
+-- src/tutorial/subscripting.source
+--
+---------------------------------------------------------------------------
+
+-----------------------------
+-- Creating a new type:
+-- We are going to create a new type called 'complex' which represents
+-- complex numbers.
+-- A user-defined type must have an input and an output function, and
+-- optionally can have binary input and output functions. All of these
+-- are usually user-defined C functions.
+-----------------------------
+
+-- Assume the user defined functions are in /home/erthalion/programms/postgresql-master/src/tutorial/complex$DLSUFFIX
+-- (we do not want to assume this is in the dynamic loader search path).
+-- Look at $PWD/complex.c for the source. Note that we declare all of
+-- them as STRICT, so we do not need to cope with NULL inputs in the
+-- C code. We also mark them IMMUTABLE, since they always return the
+-- same outputs given the same inputs.
+
+-- the input function 'complex_in' takes a null-terminated string (the
+-- textual representation of the type) and turns it into the internal
+-- (in memory) representation. You will get a message telling you 'complex'
+-- does not exist yet but that's okay.
+
+CREATE FUNCTION custom_in(cstring)
+ RETURNS custom
+ AS '_OBJWD_/subscripting'
+ LANGUAGE C IMMUTABLE STRICT;
+
+-- the output function 'complex_out' takes the internal representation and
+-- converts it into the textual representation.
+
+CREATE FUNCTION custom_out(custom)
+ RETURNS cstring
+ AS '_OBJWD_/subscripting'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION custom_subscripting(internal)
+ RETURNS internal
+ AS '_OBJWD_/subscripting'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE custom (
+ internallength = 8,
+ input = custom_in,
+ output = custom_out,
+ subscripting = custom_subscripting
+);
+
+-- we can use it in a table
+
+CREATE TABLE test_subscripting (
+ data custom,
+);
+
+INSERT INTO test_subscripting VALUES ('(1, 2)');
+
+SELECT data[0] from test_subscripting;
+
+UPDATE test_subscripting SET data[1] = 3;
Hello.
I took a look on the latest -v4 patch. I would like to note that this
patch breaks a backward compatibility. For instance sr_plan extension[1]https://github.com/postgrespro/sr_plan
stop to compile with errors like:
```
serialize.c:38:2: error: unknown type name ‘ArrayRef’
JsonbValue *ArrayRef_ser(const ArrayRef *node, JsonbParseState *state,
bool sub_object);
^
serialize.c:913:2: error: unknown type name ‘ArrayRef’
JsonbValue *ArrayRef_ser(const ArrayRef *node, JsonbParseState *state,
bool sub_object)
^
In file included from sr_plan.h:4:0,
from serialize.c:1:
...
```
Other extensions could be affected as well. I'm not saying that it's a
fatal drawback, but it's definitely something you should be aware of.
I personally strongly believe that we shouldn't break extensions between
major releases or at least make it trivial to properly rewrite them.
Unfortunately it's not a case currently.
[1]: https://github.com/postgrespro/sr_plan
--
Best regards,
Aleksander Alekseev
On 15 November 2016 at 15:03, Aleksander Alekseev <
a.alekseev@postgrespro.ru> wrote:
Hello.
I took a look on the latest -v4 patch. I would like to note that this
patch breaks a backward compatibility. For instance sr_plan extension[1]
stop to compile with errors
Thank you for the feedback.
Well, if we're speaking about this particular extension, if I understood
correctly, it fetches all parse tree nodes from Postgres and generates code
using this information. So to avoid compilation problems I believe you need
to
run `make USE_PGXS=1 genparser` again (it worked for me, I don't see any
mentions of `ArrayRef`).
But speaking generally, I don't see how we can provide backward
compatibility
for those extensions, who are strongly coupled with implementation details
of
parsing tree. I mean, in terms of interface it's mostly about to replace
`ArrayRef` to `SubscriptingRef`, but I think it's better to do it in the
extension code.
On Thu, Nov 17, 2016 at 10:56 PM, Dmitry Dolgov <9erthalion6@gmail.com>
wrote:
On 15 November 2016 at 15:03, Aleksander Alekseev <
a.alekseev@postgrespro.ru> wrote:
Hello.
I took a look on the latest -v4 patch. I would like to note that this
patch breaks a backward compatibility. For instance sr_plan extension[1]
stop to compile with errorsThank you for the feedback.
Well, if we're speaking about this particular extension, if I understood
correctly, it fetches all parse tree nodes from Postgres and generates code
using this information. So to avoid compilation problems I believe you
need to
run `make USE_PGXS=1 genparser` again (it worked for me, I don't see any
mentions of `ArrayRef`).But speaking generally, I don't see how we can provide backward
compatibility
for those extensions, who are strongly coupled with implementation details
of
parsing tree. I mean, in terms of interface it's mostly about to replace
`ArrayRef` to `SubscriptingRef`, but I think it's better to do it in the
extension code.
Moved to next CF with "needs review" status.
Regards,
Hari Babu
Fujitsu Australia
On 5 December 2016 at 12:03, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:
Moved to next CF with "needs review" status.
Looks like we stuck here little bit. Does anyone else have any
suggestions/improvements, or this patch is in good enough shape?
2016-12-26 18:49 GMT+03:00 Dmitry Dolgov <9erthalion6@gmail.com>:
On 5 December 2016 at 12:03, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:Moved to next CF with "needs review" status.
Looks like we stuck here little bit. Does anyone else have any
suggestions/improvements, or this patch is in good enough shape?
Would you rebase the patch, please? It seems it is necessary. It can't
be applied now.
--
Artur Zakirov
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 27 December 2016 at 04:05, Artur Zakirov <a.zakirov@postgrespro.ru>
wrote:
Would you rebase the patch, please? It seems it is necessary. It can't
be applied now.
Sure, here is a new rebased version.
Attachments:
generic_type_subscription_v5.patchtext/x-patch; charset=US-ASCII; name=generic_type_subscription_v5.patchDownload
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 8ce24e0..d9c9d64 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -2464,14 +2464,14 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
JumbleExpr(jstate, (Node *) expr->aggfilter);
}
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
- JumbleExpr(jstate, (Node *) aref->refupperindexpr);
- JumbleExpr(jstate, (Node *) aref->reflowerindexpr);
- JumbleExpr(jstate, (Node *) aref->refexpr);
- JumbleExpr(jstate, (Node *) aref->refassgnexpr);
+ JumbleExpr(jstate, (Node *) sbsref->refupperindexpr);
+ JumbleExpr(jstate, (Node *) sbsref->reflowerindexpr);
+ JumbleExpr(jstate, (Node *) sbsref->refexpr);
+ JumbleExpr(jstate, (Node *) sbsref->refassgnexpr);
}
break;
case T_FuncExpr:
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 66b059a..00ab80c 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -144,7 +144,7 @@ static void deparseExpr(Expr *expr, deparse_expr_cxt *context);
static void deparseVar(Var *node, deparse_expr_cxt *context);
static void deparseConst(Const *node, deparse_expr_cxt *context, int showtype);
static void deparseParam(Param *node, deparse_expr_cxt *context);
-static void deparseArrayRef(ArrayRef *node, deparse_expr_cxt *context);
+static void deparseSubscriptingRef(SubscriptingRef *node, deparse_expr_cxt *context);
static void deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context);
static void deparseOpExpr(OpExpr *node, deparse_expr_cxt *context);
static void deparseOperatorName(StringInfo buf, Form_pg_operator opform);
@@ -384,9 +384,9 @@ foreign_expr_walker(Node *node,
state = FDW_COLLATE_UNSAFE;
}
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *ar = (ArrayRef *) node;
+ SubscriptingRef *ar = (SubscriptingRef *) node;
/* Assignment should not be in restrictions. */
if (ar->refassgnexpr != NULL)
@@ -2008,8 +2008,8 @@ deparseExpr(Expr *node, deparse_expr_cxt *context)
case T_Param:
deparseParam((Param *) node, context);
break;
- case T_ArrayRef:
- deparseArrayRef((ArrayRef *) node, context);
+ case T_SubscriptingRef:
+ deparseSubscriptingRef((SubscriptingRef *) node, context);
break;
case T_FuncExpr:
deparseFuncExpr((FuncExpr *) node, context);
@@ -2243,7 +2243,7 @@ deparseParam(Param *node, deparse_expr_cxt *context)
* Deparse an array subscript expression.
*/
static void
-deparseArrayRef(ArrayRef *node, deparse_expr_cxt *context)
+deparseSubscriptingRef(SubscriptingRef *node, deparse_expr_cxt *context)
{
StringInfo buf = context->buf;
ListCell *lowlist_item;
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 4930506..c84c218 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -7292,6 +7292,13 @@
</row>
<row>
+ <entry><structfield>typsubscripting</structfield></entry>
+ <entry><type>regproc</type></entry>
+ <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+ <entry>Custom subscripting function with type-specific logic, or 0 if this type doesn't support subscripting.</entry>
+ </row>
+
+ <row>
<entry><structfield>typdefaultbin</structfield></entry>
<entry><type>pg_node_tree</type></entry>
<entry></entry>
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 69649a7..38433ef 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -71,6 +71,7 @@
<!ENTITY xplang SYSTEM "xplang.sgml">
<!ENTITY xoper SYSTEM "xoper.sgml">
<!ENTITY xtypes SYSTEM "xtypes.sgml">
+<!ENTITY xsubscripting SYSTEM "xsubscripting.sgml">
<!ENTITY plperl SYSTEM "plperl.sgml">
<!ENTITY plpython SYSTEM "plpython.sgml">
<!ENTITY plsql SYSTEM "plpgsql.sgml">
diff --git a/doc/src/sgml/json.sgml b/doc/src/sgml/json.sgml
index 3cf78d6..efffd5c 100644
--- a/doc/src/sgml/json.sgml
+++ b/doc/src/sgml/json.sgml
@@ -569,4 +569,29 @@ SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"tags": ["qu
compared using the default database collation.
</para>
</sect2>
+
+ <sect2 id="json-subscripting">
+ <title>JSON subscripting</title>
+ <para>
+ JSONB data type support array-style subscripting expressions to extract or update particular element. An example of subscripting syntax:
+<programlisting>
+-- Extract value by key
+SELECT ('{"a": 1}'::jsonb)['a'];
+
+-- Extract nested value by key path
+SELECT ('{"a": {"b": {"c": 1}}}'::jsonb)['a']['b']['c'];
+
+-- Extract element by index
+SELECT ('[1, "2", null]'::jsonb)['1'];
+
+-- Update value by key
+UPDATE table_name set jsonb_field['key'] = 1;
+
+-- Select records using where clause with subscripting
+SELECT * from table_name where jsonb_field['key'] = '"value"';
+</programlisting>
+ </para>
+ </sect2>
+
+
</sect1>
diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index 5a09f19..5448b1b 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -54,6 +54,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
[ , ELEMENT = <replaceable class="parameter">element</replaceable> ]
[ , DELIMITER = <replaceable class="parameter">delimiter</replaceable> ]
[ , COLLATABLE = <replaceable class="parameter">collatable</replaceable> ]
+ [ , SUBSCRIPTING = <replaceable class="parameter">subscripting_function</replaceable> ]
)
CREATE TYPE <replaceable class="parameter">name</replaceable>
@@ -194,7 +195,8 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
<replaceable class="parameter">send_function</replaceable>,
<replaceable class="parameter">type_modifier_input_function</replaceable>,
<replaceable class="parameter">type_modifier_output_function</replaceable> and
- <replaceable class="parameter">analyze_function</replaceable>
+ <replaceable class="parameter">analyze_function</replaceable>,
+ <replaceable class="parameter">subscripting_function</replaceable>
are optional. Generally these functions have to be coded in C
or another low-level language.
</para>
@@ -451,6 +453,22 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
make use of the collation information; this does not happen
automatically merely by marking the type collatable.
</para>
+
+ <para>
+ The optional
+ <replaceable class="parameter">subscripting_function</replaceable>
+ contains type-specific logic for subscripting of the data type.
+ By default, there is no such function, which means that the data
+ type doesn't support subscripting. The subscripting function must be
+ declared to take a single argument of type <type>internal</>, and return
+ a <type>internal</> result. There are two examples of implementation for
+ subscripting function in case of array
+ (<replaceable class="parameter">array_subscripting</replaceable>)
+ and jsonb
+ (<replaceable class="parameter">jsonb_subscripting</replaceable>)
+ types in <filename>src/backend/utils/adt/arrayfuncs.c</> and
+ <filename>src/backend/utils/adt/jsonfuncs.c</> corresponding.
+ </para>
</refsect2>
<refsect2>
@@ -766,6 +784,16 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">subscripting_function</replaceable></term>
+ <listitem>
+ <para>
+ The name of a function that contains type-specific subscripting logic for
+ the data type.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/doc/src/sgml/xsubscripting.sgml b/doc/src/sgml/xsubscripting.sgml
new file mode 100644
index 0000000..37ab6ea
--- /dev/null
+++ b/doc/src/sgml/xsubscripting.sgml
@@ -0,0 +1,100 @@
+<!-- doc/src/sgml/xsubscripting.sgml -->
+
+ <sect1 id="xsubscripting">
+ <title>User-defined subscripting procedure</title>
+
+ <indexterm zone="xsubscripting">
+ <primary>custom subscripting</primary>
+ </indexterm>
+ <para>
+ When you define a new base type, you can also specify a custom procedure
+ to handle subscripting expressions. It should contains logic for verification
+ and for extraction or update your data. For instance:
+</para>
+<programlisting><![CDATA[
+typedef struct Custom
+{
+ int first;
+ int second;
+} Custom;
+
+Datum
+custom_subscripting_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptingExecData *sbsdata = (SubscriptingExecData *) PG_GETARG_POINTER(1);
+ Custom *result = (Custom *) sbsdata->containerSource;
+
+ // Some extraction or update logic based on sbsdata
+}
+
+Datum
+custom_subscripting_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptingRef *sbsref = (SubscriptingRef *) PG_GETARG_POINTER(0);
+
+ // Some verifications or type coersion
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+PG_FUNCTION_INFO_V1(custom_subscripting);
+
+Datum
+custom_subscripting(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1,
+ fcinfo->nargs);
+
+ if (op_type & SBS_VALIDATION)
+ return custom_subscripting_prepare(&target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return custom_subscripting_evaluate(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscripting function: %d", op_type);
+}]]>
+</programlisting>
+
+<para>
+ Then you can define a subscripting procedure and a custom data type:
+</para>
+<programlisting>
+CREATE FUNCTION custom_subscripting(internal)
+ RETURNS internal
+ AS '<replaceable>filename</replaceable>'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE custom (
+ internallength = 4,
+ input = custom_in,
+ output = custom_out,
+ subscripting = custom_subscripting
+);
+</programlisting>
+
+<para>
+ and use it as usual:
+</para>
+<programlisting>
+CREATE TABLE test_subscripting (
+ data custom,
+);
+
+INSERT INTO test_subscripting VALUES ('(1, 2)');
+
+SELECT data[0] from test_subscripting;
+
+UPDATE test_subscripting SET data[1] = 3;
+</programlisting>
+
+
+ <para>
+ The examples of custom subscripting implementation can be found in
+ <filename>subscripting.sql</filename> and <filename>subscripting.c</filename>
+ in the <filename>src/tutorial</> directory of the source distribution.
+ See the <filename>README</> file in that directory for instructions
+ about running the examples.
+ </para>
+
+</sect1>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index e5d6aec..dff22ce 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -988,7 +988,8 @@ AddNewRelationType(const char *typeName,
-1, /* typmod */
0, /* array dimensions for typBaseType */
false, /* Type NOT NULL */
- InvalidOid); /* rowtypes never have a collation */
+ InvalidOid, /* rowtypes never have a collation */
+ InvalidOid); /* typsubscripting - none */
}
/* --------------------------------
@@ -1258,7 +1259,8 @@ heap_create_with_catalog(const char *relname,
-1, /* typmod */
0, /* array dimensions for typBaseType */
false, /* Type NOT NULL */
- InvalidOid); /* rowtypes never have a collation */
+ InvalidOid, /* rowtypes never have a collation */
+ F_ARRAY_SUBSCRIPTING); /* array implementation */
pfree(relarrayname);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 4b2d281..7ceee0f 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -118,6 +118,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(-1);
values[Anum_pg_type_typndims - 1] = Int32GetDatum(0);
values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(InvalidOid);
+ values[Anum_pg_type_typsubscripting - 1] = ObjectIdGetDatum(InvalidOid);
nulls[Anum_pg_type_typdefaultbin - 1] = true;
nulls[Anum_pg_type_typdefault - 1] = true;
nulls[Anum_pg_type_typacl - 1] = true;
@@ -166,6 +167,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
false,
InvalidOid,
InvalidOid,
+ InvalidOid,
NULL,
false);
@@ -224,7 +226,8 @@ TypeCreate(Oid newTypeOid,
int32 typeMod,
int32 typNDims, /* Array dimensions for baseType */
bool typeNotNull,
- Oid typeCollation)
+ Oid typeCollation,
+ Oid subscriptingProcedure)
{
Relation pg_type_desc;
Oid typeObjectId;
@@ -364,6 +367,7 @@ TypeCreate(Oid newTypeOid,
values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(typeMod);
values[Anum_pg_type_typndims - 1] = Int32GetDatum(typNDims);
values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(typeCollation);
+ values[Anum_pg_type_typsubscripting - 1] = ObjectIdGetDatum(subscriptingProcedure);
/*
* initialize the default binary value for this type. Check for nulls of
@@ -484,6 +488,7 @@ TypeCreate(Oid newTypeOid,
isImplicitArray,
baseType,
typeCollation,
+ subscriptingProcedure,
(defaultTypeBin ?
stringToNode(defaultTypeBin) :
NULL),
@@ -530,6 +535,7 @@ GenerateTypeDependencies(Oid typeNamespace,
bool isImplicitArray,
Oid baseType,
Oid typeCollation,
+ Oid subscriptingProcedure,
Node *defaultExpr,
bool rebuild)
{
@@ -682,6 +688,14 @@ GenerateTypeDependencies(Oid typeNamespace,
/* Normal dependency on the default expression. */
if (defaultExpr)
recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL);
+
+ if (OidIsValid(subscriptingProcedure))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = subscriptingProcedure;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
}
/*
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 5e3989a..4164b2f 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -94,6 +94,7 @@ static Oid findTypeSendFunction(List *procname, Oid typeOid);
static Oid findTypeTypmodinFunction(List *procname);
static Oid findTypeTypmodoutFunction(List *procname);
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
+static Oid findTypeSubscriptingFunction(List *procname);
static Oid findRangeSubOpclass(List *opcname, Oid subtype);
static Oid findRangeCanonicalFunction(List *procname, Oid typeOid);
static Oid findRangeSubtypeDiffFunction(List *procname, Oid subtype);
@@ -123,6 +124,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
List *typmodinName = NIL;
List *typmodoutName = NIL;
List *analyzeName = NIL;
+ List *subscriptingName = NIL;
char category = TYPCATEGORY_USER;
bool preferred = false;
char delimiter = DEFAULT_TYPDELIM;
@@ -141,6 +143,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
DefElem *typmodinNameEl = NULL;
DefElem *typmodoutNameEl = NULL;
DefElem *analyzeNameEl = NULL;
+ DefElem *subscriptingNameEl = NULL;
DefElem *categoryEl = NULL;
DefElem *preferredEl = NULL;
DefElem *delimiterEl = NULL;
@@ -163,6 +166,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
Oid resulttype;
ListCell *pl;
ObjectAddress address;
+ Oid subscriptingOid = InvalidOid;
/*
* As of Postgres 8.4, we require superuser privilege to create a base
@@ -262,6 +266,9 @@ DefineType(ParseState *pstate, List *names, List *parameters)
else if (pg_strcasecmp(defel->defname, "analyze") == 0 ||
pg_strcasecmp(defel->defname, "analyse") == 0)
defelp = &analyzeNameEl;
+ else if (pg_strcasecmp(defel->defname, "subscripting") == 0 ||
+ pg_strcasecmp(defel->defname, "subscripting") == 0)
+ defelp = &subscriptingNameEl;
else if (pg_strcasecmp(defel->defname, "category") == 0)
defelp = &categoryEl;
else if (pg_strcasecmp(defel->defname, "preferred") == 0)
@@ -332,6 +339,8 @@ DefineType(ParseState *pstate, List *names, List *parameters)
typmodoutName = defGetQualifiedName(typmodoutNameEl);
if (analyzeNameEl)
analyzeName = defGetQualifiedName(analyzeNameEl);
+ if (subscriptingNameEl)
+ subscriptingName = defGetQualifiedName(subscriptingNameEl);
if (categoryEl)
{
char *p = defGetString(categoryEl);
@@ -513,6 +522,9 @@ DefineType(ParseState *pstate, List *names, List *parameters)
if (analyzeName)
analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid);
+ if (subscriptingName)
+ subscriptingOid = findTypeSubscriptingFunction(subscriptingName);
+
/*
* Check permissions on functions. We choose to require the creator/owner
* of a type to also own the underlying functions. Since creating a type
@@ -632,7 +644,8 @@ DefineType(ParseState *pstate, List *names, List *parameters)
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
false, /* Type NOT NULL */
- collation); /* type's collation */
+ collation, /* type's collation */
+ subscriptingOid); /* subscripting procedure */
Assert(typoid == address.objectId);
/*
@@ -673,7 +686,8 @@ DefineType(ParseState *pstate, List *names, List *parameters)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- collation); /* type's collation */
+ collation, /* type's collation */
+ F_ARRAY_SUBSCRIPTING);
pfree(array_type);
@@ -735,6 +749,7 @@ DefineDomain(CreateDomainStmt *stmt)
Oid receiveProcedure;
Oid sendProcedure;
Oid analyzeProcedure;
+ Oid subscriptingProcedure;
bool byValue;
char category;
char delimiter;
@@ -858,6 +873,9 @@ DefineDomain(CreateDomainStmt *stmt)
/* Analysis function */
analyzeProcedure = baseType->typanalyze;
+ /* Subscripting function */
+ subscriptingProcedure = baseType->typsubscripting;
+
/* Inherited default value */
datum = SysCacheGetAttr(TYPEOID, typeTup,
Anum_pg_type_typdefault, &isnull);
@@ -1059,7 +1077,8 @@ DefineDomain(CreateDomainStmt *stmt)
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
typNotNull, /* Type NOT NULL */
- domaincoll); /* type's collation */
+ domaincoll, /* type's collation */
+ subscriptingProcedure); /* subscripting procedure */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
@@ -1171,7 +1190,8 @@ DefineEnum(CreateEnumStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* type's collation */
+ InvalidOid, /* type's collation */
+ InvalidOid); /* typsubscripting - none */
/* Enter the enum's values into pg_enum */
EnumValuesCreate(enumTypeAddr.objectId, stmt->vals);
@@ -1211,7 +1231,8 @@ DefineEnum(CreateEnumStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* type's collation */
+ InvalidOid, /* type's collation */
+ F_ARRAY_SUBSCRIPTING); /* array subscripting implementation */
pfree(enumArrayName);
@@ -1499,7 +1520,8 @@ DefineRange(CreateRangeStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* type's collation (ranges never have one) */
+ InvalidOid, /* type's collation (ranges never have one) */
+ InvalidOid); /* typsubscripting - none */
Assert(typoid == address.objectId);
/* Create the entry in pg_range */
@@ -1541,7 +1563,8 @@ DefineRange(CreateRangeStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* typcollation */
+ InvalidOid, /* typcollation */
+ F_ARRAY_SUBSCRIPTING); /* array subscripting implementation */
pfree(rangeArrayName);
@@ -1885,6 +1908,33 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
return procOid;
}
+static Oid
+findTypeSubscriptingFunction(List *procname)
+{
+ Oid argList[1];
+ Oid procOid;
+
+ /*
+ * Subscripting functions always take one INTERNAL argument and return INTERNAL.
+ */
+ argList[0] = INTERNALOID;
+
+ procOid = LookupFuncName(procname, 1, argList, true);
+ if (!OidIsValid(procOid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("function %s does not exist",
+ func_signature_string(procname, 1, NIL, argList))));
+
+ if (get_func_rettype(procOid) != INTERNALOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("type subscripting function %s must return type %s",
+ NameListToString(procname), "internal")));
+
+ return procOid;
+}
+
/*
* Find suitable support functions and opclasses for a range type.
*/
@@ -2240,6 +2290,7 @@ AlterDomainDefault(List *names, Node *defaultRaw)
false, /* a domain isn't an implicit array */
typTup->typbasetype,
typTup->typcollation,
+ typTup->typsubscripting,
defaultExpr,
true); /* Rebuild is true */
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index ec1ca01..29611c0 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -55,6 +55,7 @@
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/lsyscache.h"
+#include "utils/syscache.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
#include "utils/typcache.h"
@@ -62,10 +63,9 @@
/* static function decls */
-static Datum ExecEvalArrayRef(ArrayRefExprState *astate,
+static Datum ExecEvalSubscriptingRef(SubscriptingRefExprState *astate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
-static bool isAssignmentIndirectionExpr(ExprState *exprstate);
static Datum ExecEvalAggref(AggrefExprState *aggref,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
@@ -251,39 +251,48 @@ static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
/*----------
- * ExecEvalArrayRef
+ * ExecEvalSubscriptingRef
*
- * This function takes an ArrayRef and returns the extracted Datum
- * if it's a simple reference, or the modified array value if it's
- * an array assignment (i.e., array element or slice insertion).
+ * This function takes a SubscriptingRef, extracts all information required
+ * for subscripting and pass it to a particular subscripting procedure,
+ * specified for this data type. As a result the extracted Datum will be
+ * returned if it's a simple reference, or the modified containers value if
+ * it's an containers assignment (i.e., containers element or slice
+ * insertion).
*
* NOTE: if we get a NULL result from a subscript expression, we return NULL
- * when it's an array reference, or raise an error when it's an assignment.
+ * when it's an containers reference, or raise an error when it's an assignment.
*----------
*/
+
static Datum
-ExecEvalArrayRef(ArrayRefExprState *astate,
- ExprContext *econtext,
- bool *isNull,
- ExprDoneCond *isDone)
+ExecEvalSubscriptingRef(SubscriptingRefExprState *sbstate, ExprContext *econtext,
+ bool *isNull, ExprDoneCond *isDone)
{
- ArrayRef *arrayRef = (ArrayRef *) astate->xprstate.expr;
- Datum array_source;
- bool isAssignment = (arrayRef->refassgnexpr != NULL);
- bool eisnull;
- ListCell *l;
- int i = 0,
- j = 0;
- IntArray upper,
- lower;
- bool upperProvided[MAXDIM],
- lowerProvided[MAXDIM];
- int *lIndex;
-
- array_source = ExecEvalExpr(astate->refexpr,
- econtext,
- isNull,
- isDone);
+ SubscriptingRef *sbsRef = (SubscriptingRef *) sbstate->xprstate.expr;
+ Oid containerType;
+ RegProcedure typsubscripting;
+ bool isAssignment = (sbsRef->refassgnexpr != NULL);
+ bool eisnull;
+ Datum *upper = NULL,
+ *lower = NULL;
+ ListCell *l;
+ int i = 0,
+ j = 0;
+ SubscriptingExecData sbsdata;
+ bool upperProvided[MAXDIM],
+ lowerProvided[MAXDIM];
+
+ if (sbstate->refupperindexpr != NULL)
+ upper = (Datum *) palloc(sbstate->refupperindexpr->length * sizeof(Datum *));
+
+ if (sbstate->reflowerindexpr != NULL)
+ lower = (Datum *) palloc(sbstate->reflowerindexpr->length * sizeof(Datum *));
+
+ sbsdata.xprcontext = econtext;
+ sbsdata.isNull = isNull;
+ sbsdata.containerSource = ExecEvalExpr(sbstate->refexpr, econtext,
+ isNull, isDone);
/*
* If refexpr yields NULL, and it's a fetch, then result is NULL. In the
@@ -297,51 +306,49 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
return (Datum) NULL;
}
- foreach(l, astate->refupperindexpr)
+ foreach(l, sbstate->refupperindexpr)
{
ExprState *eltstate = (ExprState *) lfirst(l);
if (i >= MAXDIM)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ errmsg("number of container dimensions (%d) exceeds the maximum allowed (%d)",
i + 1, MAXDIM)));
if (eltstate == NULL)
{
- /* Slice bound is omitted, so use array's upper bound */
- Assert(astate->reflowerindexpr != NIL);
+ /* Slice bound is omitted, so use containers's upper bound */
+ Assert(sbstate->reflowerindexpr != NIL);
upperProvided[i++] = false;
continue;
}
+
upperProvided[i] = true;
+ upper[i++] = ExecEvalExpr(eltstate, econtext, &eisnull, NULL);
- upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate,
- econtext,
- &eisnull,
- NULL));
/* If any index expr yields NULL, result is NULL or error */
if (eisnull)
{
if (isAssignment)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
- errmsg("array subscript in assignment must not be null")));
+ errmsg("container subscript in assignment must not be null")));
*isNull = true;
return (Datum) NULL;
}
}
- if (astate->reflowerindexpr != NIL)
+ if (sbstate->reflowerindexpr != NIL)
{
- foreach(l, astate->reflowerindexpr)
+ foreach(l, sbstate->reflowerindexpr)
{
ExprState *eltstate = (ExprState *) lfirst(l);
if (j >= MAXDIM)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ errmsg("number of container dimensions (%d) exceeds the maximum allowed (%d)",
j + 1, MAXDIM)));
if (eltstate == NULL)
@@ -350,191 +357,54 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
lowerProvided[j++] = false;
continue;
}
+
lowerProvided[j] = true;
+ lower[j++] = ExecEvalExpr(eltstate, econtext, &eisnull, NULL);
- lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate,
- econtext,
- &eisnull,
- NULL));
/* If any index expr yields NULL, result is NULL or error */
if (eisnull)
{
if (isAssignment)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
- errmsg("array subscript in assignment must not be null")));
+ errmsg("container subscript in assignment must not be null")));
*isNull = true;
return (Datum) NULL;
}
}
+
/* this can't happen unless parser messed up */
if (i != j)
elog(ERROR, "upper and lower index lists are not same length");
- lIndex = lower.indx;
}
- else
- lIndex = NULL;
-
- if (isAssignment)
- {
- Datum sourceData;
- Datum save_datum;
- bool save_isNull;
-
- /*
- * We might have a nested-assignment situation, in which the
- * refassgnexpr is itself a FieldStore or ArrayRef that needs to
- * obtain and modify the previous value of the array element or slice
- * being replaced. If so, we have to extract that value from the
- * array and pass it down via the econtext's caseValue. It's safe to
- * reuse the CASE mechanism because there cannot be a CASE between
- * here and where the value would be needed, and an array assignment
- * can't be within a CASE either. (So saving and restoring the
- * caseValue is just paranoia, but let's do it anyway.)
- *
- * Since fetching the old element might be a nontrivial expense, do it
- * only if the argument appears to actually need it.
- */
- save_datum = econtext->caseValue_datum;
- save_isNull = econtext->caseValue_isNull;
-
- if (isAssignmentIndirectionExpr(astate->refassgnexpr))
- {
- if (*isNull)
- {
- /* whole array is null, so any element or slice is too */
- econtext->caseValue_datum = (Datum) 0;
- econtext->caseValue_isNull = true;
- }
- else if (lIndex == NULL)
- {
- econtext->caseValue_datum =
- array_get_element(array_source, i,
- upper.indx,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign,
- &econtext->caseValue_isNull);
- }
- else
- {
- econtext->caseValue_datum =
- array_get_slice(array_source, i,
- upper.indx, lower.indx,
- upperProvided, lowerProvided,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
- econtext->caseValue_isNull = false;
- }
- }
- else
- {
- /* argument shouldn't need caseValue, but for safety set it null */
- econtext->caseValue_datum = (Datum) 0;
- econtext->caseValue_isNull = true;
- }
-
- /*
- * Evaluate the value to be assigned into the array.
- */
- sourceData = ExecEvalExpr(astate->refassgnexpr,
- econtext,
- &eisnull,
- NULL);
-
- econtext->caseValue_datum = save_datum;
- econtext->caseValue_isNull = save_isNull;
- /*
- * For an assignment to a fixed-length array type, both the original
- * array and the value to be assigned into it must be non-NULL, else
- * we punt and return the original array.
- */
- if (astate->refattrlength > 0) /* fixed-length array? */
- if (eisnull || *isNull)
- return array_source;
-
- /*
- * For assignment to varlena arrays, we handle a NULL original array
- * by substituting an empty (zero-dimensional) array; insertion of the
- * new element will result in a singleton array value. It does not
- * matter whether the new element is NULL.
- */
- if (*isNull)
- {
- array_source = PointerGetDatum(construct_empty_array(arrayRef->refelemtype));
- *isNull = false;
- }
+ sbsdata.upper = upper;
+ sbsdata.upperProvided = upperProvided;
+ sbsdata.lower = lower;
+ sbsdata.lowerProvided = lowerProvided;
+ sbsdata.indexprNumber = i;
- if (lIndex == NULL)
- return array_set_element(array_source, i,
- upper.indx,
- sourceData,
- eisnull,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
- else
- return array_set_slice(array_source, i,
- upper.indx, lower.indx,
- upperProvided, lowerProvided,
- sourceData,
- eisnull,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
- }
+ if (!OidIsValid(sbstate->fcache.fn_oid))
+ {
+ FmgrInfo flinfo;
- if (lIndex == NULL)
- return array_get_element(array_source, i,
- upper.indx,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign,
- isNull);
- else
- return array_get_slice(array_source, i,
- upper.indx, lower.indx,
- upperProvided, lowerProvided,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
-}
+ containerType = getBaseTypeAndTypmod(sbsRef->refcontainertype,
+ &sbsRef->reftypmod);
+ typsubscripting = get_typsubscripting(containerType);
-/*
- * Helper for ExecEvalArrayRef: is expr a nested FieldStore or ArrayRef
- * that might need the old element value passed down?
- *
- * (We could use this in ExecEvalFieldStore too, but in that case passing
- * the old value is so cheap there's no need.)
- */
-static bool
-isAssignmentIndirectionExpr(ExprState *exprstate)
-{
- if (exprstate == NULL)
- return false; /* just paranoia */
- if (IsA(exprstate, FieldStoreState))
- {
- FieldStore *fstore = (FieldStore *) exprstate->expr;
+ if (!OidIsValid(typsubscripting))
+ /* this can't happen */
+ elog(ERROR, "can not find subscripting procedure for type %s",
+ format_type_be(containerType));
- if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
- return true;
+ fmgr_info(typsubscripting, &flinfo);
+ sbstate->fcache = flinfo;
}
- else if (IsA(exprstate, ArrayRefExprState))
- {
- ArrayRef *arrayRef = (ArrayRef *) exprstate->expr;
- if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr))
- return true;
- }
- return false;
+ return FunctionCall3(&sbstate->fcache,
+ Int32GetDatum(SBS_EXEC),
+ PointerGetDatum(sbstate),
+ PointerGetDatum(&sbsdata));
}
/* ----------------------------------------------------------------
@@ -4345,7 +4215,7 @@ ExecEvalFieldStore(FieldStoreState *fstate,
/*
* Use the CaseTestExpr mechanism to pass down the old value of the
* field being replaced; this is needed in case the newval is itself a
- * FieldStore or ArrayRef that has to obtain and modify the old value.
+ * FieldStore or SubscriptingRef that has to obtain and modify the old value.
* It's safe to reuse the CASE mechanism because there cannot be a
* CASE between here and where the value would be needed, and a field
* assignment can't be within a CASE either. (So saving and restoring
@@ -4708,25 +4578,21 @@ ExecInitExpr(Expr *node, PlanState *parent)
state = (ExprState *) wfstate;
}
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *aref = (ArrayRef *) node;
- ArrayRefExprState *astate = makeNode(ArrayRefExprState);
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
+ SubscriptingRefExprState *astate = makeNode(SubscriptingRefExprState);
- astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayRef;
+ astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalSubscriptingRef;
astate->refupperindexpr = (List *)
- ExecInitExpr((Expr *) aref->refupperindexpr, parent);
+ ExecInitExpr((Expr *) sbsref->refupperindexpr, parent);
astate->reflowerindexpr = (List *)
- ExecInitExpr((Expr *) aref->reflowerindexpr, parent);
- astate->refexpr = ExecInitExpr(aref->refexpr, parent);
- astate->refassgnexpr = ExecInitExpr(aref->refassgnexpr,
+ ExecInitExpr((Expr *) sbsref->reflowerindexpr, parent);
+ astate->refexpr = ExecInitExpr(sbsref->refexpr, parent);
+ astate->refassgnexpr = ExecInitExpr(sbsref->refassgnexpr,
parent);
/* do one-time catalog lookups for type info */
- astate->refattrlength = get_typlen(aref->refarraytype);
- get_typlenbyvalalign(aref->refelemtype,
- &astate->refelemlength,
- &astate->refelembyval,
- &astate->refelemalign);
+ astate->refattrlength = get_typlen(sbsref->refcontainertype);
state = (ExprState *) astate;
}
break;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6955298..841d6a7 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1292,14 +1292,14 @@ _copyWindowFunc(const WindowFunc *from)
}
/*
- * _copyArrayRef
+ * _copySubscriptingRef
*/
-static ArrayRef *
-_copyArrayRef(const ArrayRef *from)
+static SubscriptingRef *
+_copySubscriptingRef(const SubscriptingRef *from)
{
- ArrayRef *newnode = makeNode(ArrayRef);
+ SubscriptingRef *newnode = makeNode(SubscriptingRef);
- COPY_SCALAR_FIELD(refarraytype);
+ COPY_SCALAR_FIELD(refcontainertype);
COPY_SCALAR_FIELD(refelemtype);
COPY_SCALAR_FIELD(reftypmod);
COPY_SCALAR_FIELD(refcollid);
@@ -4545,8 +4545,8 @@ copyObject(const void *from)
case T_WindowFunc:
retval = _copyWindowFunc(from);
break;
- case T_ArrayRef:
- retval = _copyArrayRef(from);
+ case T_SubscriptingRef:
+ retval = _copySubscriptingRef(from);
break;
case T_FuncExpr:
retval = _copyFuncExpr(from);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 548a2aa..5a6d8b7 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -244,9 +244,9 @@ _equalWindowFunc(const WindowFunc *a, const WindowFunc *b)
}
static bool
-_equalArrayRef(const ArrayRef *a, const ArrayRef *b)
+_equalSubscriptingRef(const SubscriptingRef *a, const SubscriptingRef *b)
{
- COMPARE_SCALAR_FIELD(refarraytype);
+ COMPARE_SCALAR_FIELD(refcontainertype);
COMPARE_SCALAR_FIELD(refelemtype);
COMPARE_SCALAR_FIELD(reftypmod);
COMPARE_SCALAR_FIELD(refcollid);
@@ -2845,8 +2845,8 @@ equal(const void *a, const void *b)
case T_WindowFunc:
retval = _equalWindowFunc(a, b);
break;
- case T_ArrayRef:
- retval = _equalArrayRef(a, b);
+ case T_SubscriptingRef:
+ retval = _equalSubscriptingRef(a, b);
break;
case T_FuncExpr:
retval = _equalFuncExpr(a, b);
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 973fb15..c4ca3a0 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -66,13 +66,13 @@ exprType(const Node *expr)
case T_WindowFunc:
type = ((const WindowFunc *) expr)->wintype;
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- const ArrayRef *arrayref = (const ArrayRef *) expr;
+ const SubscriptingRef *arrayref = (const SubscriptingRef *) expr;
/* slice and/or store operations yield the array type */
if (arrayref->reflowerindexpr || arrayref->refassgnexpr)
- type = arrayref->refarraytype;
+ type = arrayref->refcontainertype;
else
type = arrayref->refelemtype;
}
@@ -284,9 +284,9 @@ exprTypmod(const Node *expr)
return ((const Const *) expr)->consttypmod;
case T_Param:
return ((const Param *) expr)->paramtypmod;
- case T_ArrayRef:
+ case T_SubscriptingRef:
/* typmod is the same for array or element */
- return ((const ArrayRef *) expr)->reftypmod;
+ return ((const SubscriptingRef *) expr)->reftypmod;
case T_FuncExpr:
{
int32 coercedTypmod;
@@ -772,8 +772,8 @@ exprCollation(const Node *expr)
case T_WindowFunc:
coll = ((const WindowFunc *) expr)->wincollid;
break;
- case T_ArrayRef:
- coll = ((const ArrayRef *) expr)->refcollid;
+ case T_SubscriptingRef:
+ coll = ((const SubscriptingRef *) expr)->refcollid;
break;
case T_FuncExpr:
coll = ((const FuncExpr *) expr)->funccollid;
@@ -1014,8 +1014,8 @@ exprSetCollation(Node *expr, Oid collation)
case T_WindowFunc:
((WindowFunc *) expr)->wincollid = collation;
break;
- case T_ArrayRef:
- ((ArrayRef *) expr)->refcollid = collation;
+ case T_SubscriptingRef:
+ ((SubscriptingRef *) expr)->refcollid = collation;
break;
case T_FuncExpr:
((FuncExpr *) expr)->funccollid = collation;
@@ -1237,9 +1237,9 @@ exprLocation(const Node *expr)
/* function name should always be the first thing */
loc = ((const WindowFunc *) expr)->location;
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
/* just use array argument's location */
- loc = exprLocation((Node *) ((const ArrayRef *) expr)->refexpr);
+ loc = exprLocation((Node *) ((const SubscriptingRef *) expr)->refexpr);
break;
case T_FuncExpr:
{
@@ -1932,21 +1932,21 @@ expression_tree_walker(Node *node,
return true;
}
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
/* recurse directly for upper/lower array index lists */
- if (expression_tree_walker((Node *) aref->refupperindexpr,
+ if (expression_tree_walker((Node *) sbsref->refupperindexpr,
walker, context))
return true;
- if (expression_tree_walker((Node *) aref->reflowerindexpr,
+ if (expression_tree_walker((Node *) sbsref->reflowerindexpr,
walker, context))
return true;
/* walker must see the refexpr and refassgnexpr, however */
- if (walker(aref->refexpr, context))
+ if (walker(sbsref->refexpr, context))
return true;
- if (walker(aref->refassgnexpr, context))
+ if (walker(sbsref->refassgnexpr, context))
return true;
}
break;
@@ -2521,12 +2521,12 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *arrayref = (ArrayRef *) node;
- ArrayRef *newnode;
+ SubscriptingRef *arrayref = (SubscriptingRef *) node;
+ SubscriptingRef *newnode;
- FLATCOPY(newnode, arrayref, ArrayRef);
+ FLATCOPY(newnode, arrayref, SubscriptingRef);
MUTATE(newnode->refupperindexpr, arrayref->refupperindexpr,
List *);
MUTATE(newnode->reflowerindexpr, arrayref->reflowerindexpr,
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 9fe9873..642d0b1 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1062,11 +1062,11 @@ _outWindowFunc(StringInfo str, const WindowFunc *node)
}
static void
-_outArrayRef(StringInfo str, const ArrayRef *node)
+_outSubscriptingRef(StringInfo str, const SubscriptingRef *node)
{
- WRITE_NODE_TYPE("ARRAYREF");
+ WRITE_NODE_TYPE("SUBSCRIPTINGREF");
- WRITE_OID_FIELD(refarraytype);
+ WRITE_OID_FIELD(refcontainertype);
WRITE_OID_FIELD(refelemtype);
WRITE_INT_FIELD(reftypmod);
WRITE_OID_FIELD(refcollid);
@@ -3500,8 +3500,8 @@ outNode(StringInfo str, const void *obj)
case T_WindowFunc:
_outWindowFunc(str, obj);
break;
- case T_ArrayRef:
- _outArrayRef(str, obj);
+ case T_SubscriptingRef:
+ _outSubscriptingRef(str, obj);
break;
case T_FuncExpr:
_outFuncExpr(str, obj);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 63f6336..afdde4a 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -606,14 +606,14 @@ _readWindowFunc(void)
}
/*
- * _readArrayRef
+ * _readSubscriptingRef
*/
-static ArrayRef *
-_readArrayRef(void)
+static SubscriptingRef *
+_readSubscriptingRef(void)
{
- READ_LOCALS(ArrayRef);
+ READ_LOCALS(SubscriptingRef);
- READ_OID_FIELD(refarraytype);
+ READ_OID_FIELD(refcontainertype);
READ_OID_FIELD(refelemtype);
READ_INT_FIELD(reftypmod);
READ_OID_FIELD(refcollid);
@@ -2351,8 +2351,8 @@ parseNodeString(void)
return_value = _readGroupingFunc();
else if (MATCH("WINDOWFUNC", 10))
return_value = _readWindowFunc();
- else if (MATCH("ARRAYREF", 8))
- return_value = _readArrayRef();
+ else if (MATCH("SUBSCRIPTINGREF", 15))
+ return_value = _readSubscriptingRef();
else if (MATCH("FUNCEXPR", 8))
return_value = _readFuncExpr();
else if (MATCH("NAMEDARGEXPR", 12))
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 9af29dd..d37f47d 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -1345,10 +1345,10 @@ contain_nonstrict_functions_walker(Node *node, void *context)
/* a window function could return non-null with null input */
return true;
}
- if (IsA(node, ArrayRef))
+ if (IsA(node, SubscriptingRef))
{
/* array assignment is nonstrict, but subscripting is strict */
- if (((ArrayRef *) node)->refassgnexpr != NULL)
+ if (((SubscriptingRef *) node)->refassgnexpr != NULL)
return true;
/* else fall through to check args */
}
@@ -1528,7 +1528,7 @@ contain_leaked_vars_walker(Node *node, void *context)
case T_Var:
case T_Const:
case T_Param:
- case T_ArrayRef:
+ case T_SubscriptingRef:
case T_ArrayExpr:
case T_FieldSelect:
case T_FieldStore:
@@ -3606,7 +3606,7 @@ eval_const_expressions_mutator(Node *node,
* For any node type not handled above, we recurse using
* expression_tree_mutator, which will copy the node unchanged but try to
* simplify its arguments (if any) using this routine. For example: we
- * cannot eliminate an ArrayRef node, but we might be able to simplify
+ * cannot eliminate an SubscriptingRef node, but we might be able to simplify
* constant expressions in its subscripts.
*/
return expression_tree_mutator(node, eval_const_expressions_mutator,
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index a558083..b29d38a 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -944,13 +944,13 @@ transformInsertRow(ParseState *pstate, List *exprlist,
expr = (Expr *) linitial(fstore->newvals);
}
- else if (IsA(expr, ArrayRef))
+ else if (IsA(expr, SubscriptingRef))
{
- ArrayRef *aref = (ArrayRef *) expr;
+ SubscriptingRef *sbsref = (SubscriptingRef *) expr;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
break;
- expr = aref->refassgnexpr;
+ expr = sbsref->refassgnexpr;
}
else
break;
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 8a2bdf0..1a52a31 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -472,13 +472,13 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
/* process subscripts before this field selection */
if (subscripts)
- result = (Node *) transformArraySubscripts(pstate,
- result,
- exprType(result),
- InvalidOid,
- exprTypmod(result),
- subscripts,
- NULL);
+ result = (Node *) transformContainerSubscripts(pstate,
+ result,
+ exprType(result),
+ InvalidOid,
+ exprTypmod(result),
+ subscripts,
+ NULL);
subscripts = NIL;
newresult = ParseFuncOrColumn(pstate,
@@ -493,13 +493,13 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
}
/* process trailing subscripts, if any */
if (subscripts)
- result = (Node *) transformArraySubscripts(pstate,
- result,
- exprType(result),
- InvalidOid,
- exprTypmod(result),
- subscripts,
- NULL);
+ result = (Node *) transformContainerSubscripts(pstate,
+ result,
+ exprType(result),
+ InvalidOid,
+ exprTypmod(result),
+ subscripts,
+ NULL);
return result;
}
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 62d2f71..d213a1c 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -201,18 +201,22 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location)
/*
* transformArrayType()
- * Identify the types involved in a subscripting operation
+ * Identify the types involved in a subscripting operation for array
*
* On entry, arrayType/arrayTypmod identify the type of the input value
* to be subscripted (which could be a domain type). These are modified
* if necessary to identify the actual array type and typmod, and the
* array's element type is returned. An error is thrown if the input isn't
* an array type.
+ *
+ * NOTE: This part of type-specific code is not separated into type-specific
+ * subscripting procedure for now, but it does not affect on the whole logic,
+ * since InvalidOid will be return in case of other types not an error.
+ * An error will appears only if a subscripting procedure is not defined.
*/
Oid
-transformArrayType(Oid *arrayType, int32 *arrayTypmod)
+transformArrayType(Oid *containerType, int32 *containerTypmod)
{
- Oid origArrayType = *arrayType;
Oid elementType;
HeapTuple type_tuple_array;
Form_pg_type type_struct_array;
@@ -224,7 +228,7 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
* itself. (Note that we provide no method whereby the creator of a
* domain over an array type could hide its ability to be subscripted.)
*/
- *arrayType = getBaseTypeAndTypmod(*arrayType, arrayTypmod);
+ *containerType = getBaseTypeAndTypmod(*containerType, containerTypmod);
/*
* We treat int2vector and oidvector as though they were domains over
@@ -233,25 +237,20 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
* xxxvector type; so we want the result of a slice operation to be
* considered to be of the more general type.
*/
- if (*arrayType == INT2VECTOROID)
- *arrayType = INT2ARRAYOID;
- else if (*arrayType == OIDVECTOROID)
- *arrayType = OIDARRAYOID;
+ if (*containerType == INT2VECTOROID)
+ *containerType = INT2ARRAYOID;
+ else if (*containerType == OIDVECTOROID)
+ *containerType = OIDARRAYOID;
/* Get the type tuple for the array */
- type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*arrayType));
+ type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*containerType));
if (!HeapTupleIsValid(type_tuple_array))
- elog(ERROR, "cache lookup failed for type %u", *arrayType);
+ elog(ERROR, "cache lookup failed for type %u", *containerType);
type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array);
/* needn't check typisdefined since this will fail anyway */
elementType = type_struct_array->typelem;
- if (elementType == InvalidOid)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("cannot subscript type %s because it is not an array",
- format_type_be(origArrayType))));
ReleaseSysCache(type_tuple_array);
@@ -259,61 +258,80 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
}
/*
- * transformArraySubscripts()
- * Transform array subscripting. This is used for both
- * array fetch and array assignment.
+ * transformContainerSubscripts()
+ * Transform container subscripting. This is used for both
+ * container fetch and container assignment.
*
- * In an array fetch, we are given a source array value and we produce an
- * expression that represents the result of extracting a single array element
- * or an array slice.
+ * In a container fetch, we are given a source container value and we produce
+ * an expression that represents the result of extracting a single container
+ * element or a container slice.
*
- * In an array assignment, we are given a destination array value plus a
- * source value that is to be assigned to a single element or a slice of
- * that array. We produce an expression that represents the new array value
- * with the source data inserted into the right part of the array.
+ * In a container assignment, we are given a destination container value plus a
+ * source value that is to be assigned to a single element or a slice of that
+ * container. We produce an expression that represents the new container value
+ * with the source data inserted into the right part of the container.
*
- * For both cases, if the source array is of a domain-over-array type,
- * the result is of the base array type or its element type; essentially,
- * we must fold a domain to its base type before applying subscripting.
- * (Note that int2vector and oidvector are treated as domains here.)
+ * For both cases, this function contains only general subscripting logic while
+ * type-specific logic (e.g. type verifications and coersion) is placend in
+ * separate procedure indicated by typsubscripting. There is only one exception
+ * for now about domain-over-container, if the source container is of a
+ * domain-over-container type, the result is of the base container type or its
+ * element type; essentially, we must fold a domain to its base type before
+ * applying subscripting. (Note that int2vector and oidvector are treated as
+ * domains here.) If domain verification failed we assume, that element type
+ * must be the same as container type (e.g. in case of jsonb).
+ * An error will appear in case if current container type doesn't have a
+ * subscripting procedure.
*
- * pstate Parse state
- * arrayBase Already-transformed expression for the array as a whole
- * arrayType OID of array's datatype (should match type of arrayBase,
- * or be the base type of arrayBase's domain type)
- * elementType OID of array's element type (fetch with transformArrayType,
- * or pass InvalidOid to do it here)
- * arrayTypMod typmod for the array (which is also typmod for the elements)
- * indirection Untransformed list of subscripts (must not be NIL)
- * assignFrom NULL for array fetch, else transformed expression for source.
+ * pstate Parse state
+ * containerBase Already-transformed expression for the container as a whole
+ * containerType OID of container's datatype (should match type of containerBase,
+ * or be the base type of containerBase's domain type)
+ * elementType OID of container's element type (fetch with transformArrayType,
+ * or pass InvalidOid to do it here)
+ * containerTypMod typmod for the container (which is also typmod for the elements)
+ * indirection Untransformed list of subscripts (must not be NIL)
+ * assignFrom NULL for container fetch, else transformed expression for source.
*/
-ArrayRef *
-transformArraySubscripts(ParseState *pstate,
- Node *arrayBase,
- Oid arrayType,
- Oid elementType,
- int32 arrayTypMod,
- List *indirection,
- Node *assignFrom)
+
+SubscriptingRef *
+transformContainerSubscripts(ParseState *pstate,
+ Node *containerBase,
+ Oid containerType,
+ Oid elementType,
+ int32 containerTypMod,
+ List *indirection,
+ Node *assignFrom)
{
- bool isSlice = false;
- List *upperIndexpr = NIL;
- List *lowerIndexpr = NIL;
- ListCell *idx;
- ArrayRef *aref;
+ bool isSlice = false;
+ List *upperIndexpr = NIL;
+ List *lowerIndexpr = NIL;
+ ListCell *idx;
+ SubscriptingRef *sbsref,
+ *prepared_sbsref;
+ RegProcedure typsubscripting = get_typsubscripting(containerType);
+
+ if (!OidIsValid(typsubscripting))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("cannot subscript type %s because it does not support subscripting",
+ format_type_be(containerType))));
/*
* Caller may or may not have bothered to determine elementType. Note
- * that if the caller did do so, arrayType/arrayTypMod must be as modified
+ * that if the caller did do so, containerType/containerTypMod must be as modified
* by transformArrayType, ie, smash domain to base type.
*/
if (!OidIsValid(elementType))
- elementType = transformArrayType(&arrayType, &arrayTypMod);
+ elementType = transformArrayType(&containerType, &containerTypMod);
+
+ if (!OidIsValid(elementType))
+ elementType = containerType;
/*
- * A list containing only simple subscripts refers to a single array
+ * A list containing only simple subscripts refers to a single container
* element. If any of the items are slice specifiers (lower:upper), then
- * the subscript expression means an array slice operation. In this case,
+ * the subscript expression means an container slice operation. In this case,
* we convert any non-slice items to slices by treating the single
* subscript as the upper bound and supplying an assumed lower bound of 1.
* We have to prescan the list to see if there are any slice items.
@@ -343,107 +361,37 @@ transformArraySubscripts(ParseState *pstate,
if (ai->lidx)
{
subexpr = transformExpr(pstate, ai->lidx, pstate->p_expr_kind);
- /* If it's not int4 already, try to coerce */
- subexpr = coerce_to_target_type(pstate,
- subexpr, exprType(subexpr),
- INT4OID, -1,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (subexpr == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array subscript must have type integer"),
- parser_errposition(pstate, exprLocation(ai->lidx))));
- }
- else if (!ai->is_slice)
- {
- /* Make a constant 1 */
- subexpr = (Node *) makeConst(INT4OID,
- -1,
- InvalidOid,
- sizeof(int32),
- Int32GetDatum(1),
- false,
- true); /* pass by value */
}
else
{
/* Slice with omitted lower bound, put NULL into the list */
subexpr = NULL;
}
- lowerIndexpr = lappend(lowerIndexpr, subexpr);
- }
- else
- Assert(ai->lidx == NULL && !ai->is_slice);
-
- if (ai->uidx)
- {
- subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
- /* If it's not int4 already, try to coerce */
- subexpr = coerce_to_target_type(pstate,
- subexpr, exprType(subexpr),
- INT4OID, -1,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (subexpr == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array subscript must have type integer"),
- parser_errposition(pstate, exprLocation(ai->uidx))));
- }
- else
- {
- /* Slice with omitted upper bound, put NULL into the list */
- Assert(isSlice && ai->is_slice);
- subexpr = NULL;
+ lowerIndexpr = lappend(lowerIndexpr, list_make2(subexpr, ai));
}
+ subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
upperIndexpr = lappend(upperIndexpr, subexpr);
}
/*
- * If doing an array store, coerce the source value to the right type.
- * (This should agree with the coercion done by transformAssignedExpr.)
- */
- if (assignFrom != NULL)
- {
- Oid typesource = exprType(assignFrom);
- Oid typeneeded = isSlice ? arrayType : elementType;
- Node *newFrom;
-
- newFrom = coerce_to_target_type(pstate,
- assignFrom, typesource,
- typeneeded, arrayTypMod,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (newFrom == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array assignment requires type %s"
- " but expression is of type %s",
- format_type_be(typeneeded),
- format_type_be(typesource)),
- errhint("You will need to rewrite or cast the expression."),
- parser_errposition(pstate, exprLocation(assignFrom))));
- assignFrom = newFrom;
- }
-
- /*
- * Ready to build the ArrayRef node.
+ * Ready to build the SubscriptingRef node.
*/
- aref = makeNode(ArrayRef);
- aref->refarraytype = arrayType;
- aref->refelemtype = elementType;
- aref->reftypmod = arrayTypMod;
+ sbsref = makeNode(SubscriptingRef);
+ sbsref->refcontainertype = containerType;
+ sbsref->refelemtype = elementType;
+ sbsref->reftypmod = containerTypMod;
/* refcollid will be set by parse_collate.c */
- aref->refupperindexpr = upperIndexpr;
- aref->reflowerindexpr = lowerIndexpr;
- aref->refexpr = (Expr *) arrayBase;
- aref->refassgnexpr = (Expr *) assignFrom;
+ sbsref->refupperindexpr = upperIndexpr;
+ sbsref->reflowerindexpr = lowerIndexpr;
+ sbsref->refexpr = (Expr *) containerBase;
+ sbsref->refassgnexpr = (Expr *) assignFrom;
+
+ prepared_sbsref = (SubscriptingRef *) OidFunctionCall3(typsubscripting,
+ Int32GetDatum(SBS_VALIDATION),
+ PointerGetDatum(sbsref),
+ PointerGetDatum(pstate));
- return aref;
+ return prepared_sbsref;
}
/*
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index d440dec..b829e2a 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -790,41 +790,24 @@ transformAssignmentIndirection(ParseState *pstate,
/* base case: just coerce RHS to match target type ID */
- result = coerce_to_target_type(pstate,
- rhs, exprType(rhs),
- targetTypeId, targetTypMod,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
+ if (targetTypeId != InvalidOid)
+ result = coerce_to_target_type(pstate,
+ rhs, exprType(rhs),
+ targetTypeId, targetTypMod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ else
+ result = rhs;
+
if (result == NULL)
- {
- if (targetIsArray)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array assignment to \"%s\" requires type %s"
- " but expression is of type %s",
- targetName,
- format_type_be(targetTypeId),
- format_type_be(exprType(rhs))),
- errhint("You will need to rewrite or cast the expression."),
- parser_errposition(pstate, location)));
- else
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("subfield \"%s\" is of type %s"
- " but expression is of type %s",
- targetName,
- format_type_be(targetTypeId),
- format_type_be(exprType(rhs))),
- errhint("You will need to rewrite or cast the expression."),
- parser_errposition(pstate, location)));
- }
+ result = rhs;
return result;
}
/*
- * helper for transformAssignmentIndirection: process array assignment
+ * helper for transformAssignmentIndirection: process container assignment
*/
static Node *
transformAssignmentSubscripts(ParseState *pstate,
@@ -840,55 +823,55 @@ transformAssignmentSubscripts(ParseState *pstate,
int location)
{
Node *result;
- Oid arrayType;
- int32 arrayTypMod;
+ Oid containerType;
+ int32 containerTypMod;
Oid elementTypeId;
Oid typeNeeded;
Oid collationNeeded;
Assert(subscripts != NIL);
- /* Identify the actual array type and element type involved */
- arrayType = targetTypeId;
- arrayTypMod = targetTypMod;
- elementTypeId = transformArrayType(&arrayType, &arrayTypMod);
+ /* Identify the actual container type and element type involved */
+ containerType = targetTypeId;
+ containerTypMod = targetTypMod;
+ elementTypeId = transformArrayType(&containerType, &containerTypMod);
/* Identify type that RHS must provide */
- typeNeeded = isSlice ? arrayType : elementTypeId;
+ typeNeeded = isSlice ? containerType : elementTypeId;
/*
- * Array normally has same collation as elements, but there's an
- * exception: we might be subscripting a domain over an array type. In
+ * container normally has same collation as elements, but there's an
+ * exception: we might be subscripting a domain over an container type. In
* that case use collation of the base type.
*/
- if (arrayType == targetTypeId)
+ if (containerType == targetTypeId)
collationNeeded = targetCollation;
else
- collationNeeded = get_typcollation(arrayType);
+ collationNeeded = get_typcollation(containerType);
- /* recurse to create appropriate RHS for array assign */
+ /* recurse to create appropriate RHS for container assign */
rhs = transformAssignmentIndirection(pstate,
NULL,
targetName,
true,
typeNeeded,
- arrayTypMod,
+ containerTypMod,
collationNeeded,
next_indirection,
rhs,
location);
/* process subscripts */
- result = (Node *) transformArraySubscripts(pstate,
- basenode,
- arrayType,
- elementTypeId,
- arrayTypMod,
- subscripts,
- rhs);
-
- /* If target was a domain over array, need to coerce up to the domain */
- if (arrayType != targetTypeId)
+ result = (Node *) transformContainerSubscripts(pstate,
+ basenode,
+ containerType,
+ exprType(rhs),
+ containerTypMod,
+ subscripts,
+ rhs);
+
+ /* If target was a domain over container, need to coerce up to the domain */
+ if (containerType != targetTypeId)
{
Oid resulttype = exprType(result);
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index bf4f098..94f2222 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -904,7 +904,7 @@ process_matched_tle(TargetEntry *src_tle,
/*----------
* Multiple assignments to same attribute. Allow only if all are
- * FieldStore or ArrayRef assignment operations. This is a bit
+ * FieldStore or SubscriptingRef assignment operations. This is a bit
* tricky because what we may actually be looking at is a nest of
* such nodes; consider
* UPDATE tab SET col.fld1.subfld1 = x, col.fld2.subfld2 = y
@@ -912,7 +912,7 @@ process_matched_tle(TargetEntry *src_tle,
* FieldStore(col, fld1, FieldStore(placeholder, subfld1, x))
* FieldStore(col, fld2, FieldStore(placeholder, subfld2, y))
* However, we can ignore the substructure and just consider the top
- * FieldStore or ArrayRef from each assignment, because it works to
+ * FieldStore or SubscriptingRef from each assignment, because it works to
* combine these as
* FieldStore(FieldStore(col, fld1,
* FieldStore(placeholder, subfld1, x)),
@@ -922,7 +922,7 @@ process_matched_tle(TargetEntry *src_tle,
*
* For FieldStore, instead of nesting we can generate a single
* FieldStore with multiple target fields. We must nest when
- * ArrayRefs are involved though.
+ * SubscriptingRefs are involved though.
*----------
*/
src_expr = (Node *) src_tle->expr;
@@ -981,13 +981,13 @@ process_matched_tle(TargetEntry *src_tle,
}
newexpr = (Node *) fstore;
}
- else if (IsA(src_expr, ArrayRef))
+ else if (IsA(src_expr, SubscriptingRef))
{
- ArrayRef *aref = makeNode(ArrayRef);
+ SubscriptingRef *sbsref = makeNode(SubscriptingRef);
- memcpy(aref, src_expr, sizeof(ArrayRef));
- aref->refexpr = (Expr *) prior_expr;
- newexpr = (Node *) aref;
+ memcpy(sbsref, src_expr, sizeof(SubscriptingRef));
+ sbsref->refexpr = (Expr *) prior_expr;
+ newexpr = (Node *) sbsref;
}
else
{
@@ -1014,14 +1014,15 @@ get_assignment_input(Node *node)
return (Node *) fstore->arg;
}
- else if (IsA(node, ArrayRef))
+ else if (IsA(node, SubscriptingRef))
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
return NULL;
- return (Node *) aref->refexpr;
+ return (Node *) sbsref->refexpr;
}
+
return NULL;
}
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 1db7bf0..b5b6811 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -24,6 +24,8 @@
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "libpq/pqformat.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
#include "utils/array.h"
#include "utils/arrayaccess.h"
#include "utils/builtins.h"
@@ -31,6 +33,8 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
+#include "parser/parse_node.h"
+#include "parser/parse_coerce.h"
/*
@@ -88,6 +92,7 @@ typedef struct ArrayIteratorData
static bool array_isspace(char ch);
static int ArrayCount(const char *str, int *dim, char typdelim);
+static bool isAssignmentIndirectionExpr(ExprState *exprstate);
static void ReadArrayStr(char *arrayStr, const char *origStr,
int nitems, int ndim, int *dim,
FmgrInfo *inputproc, Oid typioparam, int32 typmod,
@@ -157,7 +162,8 @@ static int width_bucket_array_variable(Datum operand,
ArrayType *thresholds,
Oid collation,
TypeCacheEntry *typentry);
-
+static Datum array_subscripting_prepare(PG_FUNCTION_ARGS);
+static Datum array_subscripting_evaluate(PG_FUNCTION_ARGS);
/*
* array_in :
@@ -6520,3 +6526,367 @@ width_bucket_array_variable(Datum operand,
return left;
}
+
+/*
+ * Helper for ExecEvalSubscriptingRef: is expr a nested FieldStore or SubscriptingRef
+ * that might need the old element value passed down?
+ *
+ * (We could use this in ExecEvalFieldStore too, but in that case passing
+ * the old value is so cheap there's no need.)
+ */
+static bool
+isAssignmentIndirectionExpr(ExprState *exprstate)
+{
+ if (exprstate == NULL)
+ return false; /* just paranoia */
+ if (IsA(exprstate, FieldStoreState))
+ {
+ FieldStore *fstore = (FieldStore *) exprstate->expr;
+
+ if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
+ return true;
+ }
+ else if (IsA(exprstate, SubscriptingRefExprState))
+ {
+ SubscriptingRef *array_ref = (SubscriptingRef *) exprstate->expr;
+
+ if (array_ref->refexpr && IsA(array_ref->refexpr, CaseTestExpr))
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Perform an actual data extraction or modification for the array
+ * subscripting. As a result the extracted Datum or the modified containers
+ * value will be returned.
+ */
+Datum
+array_subscripting_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptingRefExprState *sbstate = (SubscriptingRefExprState *) PG_GETARG_POINTER(0);
+ SubscriptingExecData *sbsdata = (SubscriptingExecData *) PG_GETARG_POINTER(1);
+ ExprContext *econtext = sbsdata->xprcontext;
+ bool *is_null = sbsdata->isNull;
+ SubscriptingRef *array_ref = (SubscriptingRef *) sbstate->xprstate.expr;
+ bool is_assignment = (array_ref->refassgnexpr != NULL);
+ bool is_slice = (array_ref->reflowerindexpr != NIL);
+ IntArray u_index, l_index;
+ bool eisnull;
+ int i = 0;
+
+ if (sbstate->refelemlength == 0)
+ {
+ /* do one-time catalog lookups for type info */
+ get_typlenbyvalalign(array_ref->refelemtype,
+ &sbstate->refelemlength,
+ &sbstate->refelembyval,
+ &sbstate->refelemalign);
+ }
+
+ for(i = 0; i < sbsdata->indexprNumber; i++)
+ u_index.indx[i] = DatumGetInt32(sbsdata->upper[i]);
+
+ if (is_slice)
+ {
+ for(i = 0; i < sbsdata->indexprNumber; i++)
+ l_index.indx[i] = DatumGetInt32(sbsdata->lower[i]);
+ }
+
+ if (is_assignment)
+ {
+ Datum sourceData;
+ Datum save_datum;
+ bool save_isNull;
+
+ /*
+ * We might have a nested-assignment situation, in which the
+ * refassgnexpr is itself a FieldStore or SubscriptingRef that needs to
+ * obtain and modify the previous value of the array element or slice
+ * being replaced. If so, we have to extract that value from the
+ * array and pass it down via the econtext's caseValue. It's safe to
+ * reuse the CASE mechanism because there cannot be a CASE between
+ * here and where the value would be needed, and an array assignment
+ * can't be within a CASE either. (So saving and restoring the
+ * caseValue is just paranoia, but let's do it anyway.)
+ *
+ * Since fetching the old element might be a nontrivial expense, do it
+ * only if the argument appears to actually need it.
+ */
+ save_datum = econtext->caseValue_datum;
+ save_isNull = econtext->caseValue_isNull;
+
+ if (isAssignmentIndirectionExpr(sbstate->refassgnexpr))
+ {
+ if (*is_null)
+ {
+ /* whole array is null, so any element or slice is too */
+ econtext->caseValue_datum = (Datum) 0;
+ econtext->caseValue_isNull = true;
+ }
+ else if (!is_slice)
+ {
+ econtext->caseValue_datum =
+ array_get_element(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign,
+ &econtext->caseValue_isNull);
+ }
+ else
+ {
+ econtext->caseValue_datum =
+ array_get_slice(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, l_index.indx,
+ sbsdata->upperProvided,
+ sbsdata->lowerProvided,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+ econtext->caseValue_isNull = false;
+ }
+ }
+ else
+ {
+ /* argument shouldn't need caseValue, but for safety set it null */
+ econtext->caseValue_datum = (Datum) 0;
+ econtext->caseValue_isNull = true;
+ }
+
+ /*
+ * Evaluate the value to be assigned into the array.
+ */
+ sourceData = ExecEvalExpr(sbstate->refassgnexpr,
+ econtext,
+ &eisnull,
+ NULL);
+
+ econtext->caseValue_datum = save_datum;
+ econtext->caseValue_isNull = save_isNull;
+
+ /*
+ * For an assignment to a fixed-length array type, both the original
+ * array and the value to be assigned into it must be non-NULL, else
+ * we punt and return the original array.
+ */
+ if (sbstate->refattrlength > 0) /* fixed-length array? */
+ if (eisnull || *is_null)
+ return sbsdata->containerSource;
+
+ /*
+ * For assignment to varlena arrays, we handle a NULL original array
+ * by substituting an empty (zero-dimensional) array; insertion of the
+ * new element will result in a singleton array value. It does not
+ * matter whether the new element is NULL.
+ */
+ if (*is_null)
+ {
+ sbsdata->containerSource = PointerGetDatum(construct_empty_array(array_ref->refelemtype));
+ *is_null = false;
+ }
+
+ if (!is_slice)
+ return array_set_element(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, sourceData, eisnull,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+ else
+ return array_set_slice(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, l_index.indx,
+ sbsdata->upperProvided,
+ sbsdata->lowerProvided,
+ sourceData, eisnull,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+ }
+
+ if (!is_slice)
+ return array_get_element(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign,
+ is_null);
+ else
+ return array_get_slice(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, l_index.indx,
+ sbsdata->upperProvided,
+ sbsdata->lowerProvided,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+}
+
+/*
+ * Perform preparation for the array subscripting, mostly type verification
+ * and coersion. This function produces an expression that represents the
+ * result of extracting a single container element/container slice or the new
+ * container value with the source data inserted into the right part of the
+ * container.
+ */
+Datum
+array_subscripting_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptingRef *sbsref = (SubscriptingRef *) PG_GETARG_POINTER(0);
+ ParseState *pstate = (ParseState *) PG_GETARG_POINTER(1);
+ Node *node = (Node *)sbsref;
+ Oid array_type = sbsref->refcontainertype;
+ int32 array_typ_mode = (int32) sbsref->reftypmod;
+ bool is_slice = sbsref->reflowerindexpr != NIL;
+ Oid typeneeded = InvalidOid,
+ typesource = InvalidOid;
+ Node *new_from;
+ Oid element_type_id;
+ Node *subexpr;
+ List *upperIndexpr = NIL;
+ List *lowerIndexpr = NIL;
+ ListCell *l;
+
+ element_type_id = transformArrayType(&array_type, &array_typ_mode);
+ sbsref->refelemtype = element_type_id;
+
+ foreach(l, sbsref->refupperindexpr)
+ {
+ subexpr = (Node *) lfirst(l);
+
+ if (subexpr == NULL)
+ {
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ continue;
+ }
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ INT4OID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("array subscript must have type integer"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ }
+
+ sbsref->refupperindexpr = upperIndexpr;
+
+ foreach(l, sbsref->reflowerindexpr)
+ {
+ List *expr_ai = (List *) lfirst(l);
+ A_Indices *ai = (A_Indices *) lfirst(list_tail(expr_ai));
+
+ subexpr = (Node *) lfirst(list_head(expr_ai));
+ if (subexpr == NULL && !ai->is_slice)
+ {
+ /* Make a constant 1 */
+ subexpr = (Node *) makeConst(INT4OID,
+ -1,
+ InvalidOid,
+ sizeof(int32),
+ Int32GetDatum(1),
+ false,
+ true); /* pass by value */
+ }
+
+ if (subexpr == NULL)
+ {
+ lowerIndexpr = lappend(lowerIndexpr, subexpr);
+ continue;
+ }
+
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ INT4OID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("array subscript must have type integer"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ lowerIndexpr = lappend(lowerIndexpr, subexpr);
+ }
+
+ sbsref->reflowerindexpr = lowerIndexpr;
+
+ if (sbsref->refassgnexpr != NULL)
+ {
+ new_from = coerce_to_target_type(pstate,
+ (Node *)sbsref->refassgnexpr, typesource,
+ typeneeded, sbsref->reftypmod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (new_from == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("array assignment requires type %s"
+ " but expression is of type %s",
+ format_type_be(typeneeded),
+ format_type_be(typesource)),
+ errhint("You will need to rewrite or cast the expression."),
+ parser_errposition(pstate, exprLocation((Node *)sbsref->refassgnexpr))));
+ sbsref->refassgnexpr = (Expr *)new_from;
+
+ if (array_type != sbsref->refcontainertype)
+ {
+ typesource = exprType((Node *)sbsref->refassgnexpr);
+ typesource = is_slice ? sbsref->refcontainertype : sbsref->refelemtype;
+
+ node = coerce_to_target_type(pstate,
+ node, array_type,
+ sbsref->refcontainertype, sbsref->reftypmod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+
+ /* can fail if we had int2vector/oidvector, but not for true domains */
+ if (node == NULL && node->type != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_CANNOT_COERCE),
+ errmsg("cannot cast type %s to %s",
+ format_type_be(array_type),
+ format_type_be(sbsref->refcontainertype)),
+ parser_errposition(pstate, 0)));
+
+ PG_RETURN_POINTER(node);
+ }
+
+ }
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+/*
+ * Handle array-type subscripting logic.
+ */
+Datum
+array_subscripting(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo;
+ get_slice_arguments(fcinfo, 1, fcinfo->nargs, &target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return array_subscripting_evaluate(&target_fcinfo);
+
+ if (op_type & SBS_VALIDATION)
+ return array_subscripting_prepare(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscripting function: %d", op_type);
+}
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index 987cfd1..2e64c53 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -1145,23 +1145,34 @@ to_jsonb(PG_FUNCTION_ARGS)
{
Datum val = PG_GETARG_DATUM(0);
Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
- JsonbInState result;
- JsonbTypeCategory tcategory;
- Oid outfuncoid;
+ JsonbValue *res = to_jsonb_worker(val, val_type);
+ PG_RETURN_POINTER(JsonbValueToJsonb(res));
+}
- if (val_type == InvalidOid)
+/*
+ * Do the actual conversion to jsonb for to_jsonb function. This logic is
+ * separated because it can be useful not only in here (e.g. we use it in
+ * jsonb subscripting)
+ */
+JsonbValue *
+to_jsonb_worker(Datum source, Oid source_type)
+{
+ JsonbInState result;
+ JsonbTypeCategory tcategory;
+ Oid outfuncoid;
+
+ if (source_type == InvalidOid)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("could not determine input data type")));
- jsonb_categorize_type(val_type,
+ jsonb_categorize_type(source_type,
&tcategory, &outfuncoid);
memset(&result, 0, sizeof(JsonbInState));
- datum_to_jsonb(val, false, &result, tcategory, outfuncoid, false);
-
- PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
+ datum_to_jsonb(source, false, &result, tcategory, outfuncoid, false);
+ return result.res;
}
/*
diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c
index ddc34ce..5e1b682 100644
--- a/src/backend/utils/adt/jsonb_util.c
+++ b/src/backend/utils/adt/jsonb_util.c
@@ -61,18 +61,29 @@ static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate,
JsonbIteratorToken seq,
JsonbValue *scalarVal);
+JsonbValue *
+JsonbToJsonbValue(Jsonb *jsonb)
+{
+ JsonbValue *val = (JsonbValue *) palloc(sizeof(JsonbValue));
+
+ val->type = jbvBinary;
+ val->val.binary.data = &jsonb->root;
+ val->val.binary.len = VARSIZE(jsonb) - VARHDRSZ;
+
+ return val;
+}
+
/*
* Turn an in-memory JsonbValue into a Jsonb for on-disk storage.
*
- * There isn't a JsonbToJsonbValue(), because generally we find it more
- * convenient to directly iterate through the Jsonb representation and only
- * really convert nested scalar values. JsonbIteratorNext() does this, so that
- * clients of the iteration code don't have to directly deal with the binary
- * representation (JsonbDeepContains() is a notable exception, although all
- * exceptions are internal to this module). In general, functions that accept
- * a JsonbValue argument are concerned with the manipulation of scalar values,
- * or simple containers of scalar values, where it would be inconvenient to
- * deal with a great amount of other state.
+ * Generally we find it more convenient to directly iterate through the Jsonb
+ * representation and only really convert nested scalar values.
+ * JsonbIteratorNext() does this, so that clients of the iteration code don't
+ * have to directly deal with the binary representation (JsonbDeepContains() is
+ * a notable exception, although all exceptions are internal to this module).
+ * In general, functions that accept a JsonbValue argument are concerned with
+ * the manipulation of scalar values, or simple containers of scalar values,
+ * where it would be inconvenient to deal with a great amount of other state.
*/
Jsonb *
JsonbValueToJsonb(JsonbValue *val)
@@ -520,6 +531,30 @@ pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq,
JsonbValue *res = NULL;
JsonbValue v;
JsonbIteratorToken tok;
+ int i;
+
+ if (jbval && (seq == WJB_ELEM || seq == WJB_VALUE) && jbval->type == jbvObject)
+ {
+ pushJsonbValue(pstate, WJB_BEGIN_OBJECT, NULL);
+ for (i = 0; i < jbval->val.object.nPairs; i++)
+ {
+ pushJsonbValue(pstate, WJB_KEY, &jbval->val.object.pairs[i].key);
+ pushJsonbValue(pstate, WJB_VALUE, &jbval->val.object.pairs[i].value);
+ }
+
+ return pushJsonbValue(pstate, WJB_END_OBJECT, NULL);
+ }
+
+ if (jbval && (seq == WJB_ELEM || seq == WJB_VALUE) && jbval->type == jbvArray)
+ {
+ pushJsonbValue(pstate, WJB_BEGIN_ARRAY, NULL);
+ for (i = 0; i < jbval->val.array.nElems; i++)
+ {
+ pushJsonbValue(pstate, WJB_ELEM, &jbval->val.array.elems[i]);
+ }
+
+ return pushJsonbValue(pstate, WJB_END_ARRAY, NULL);
+ }
if (!jbval || (seq != WJB_ELEM && seq != WJB_VALUE) ||
jbval->type != jbvBinary)
@@ -530,9 +565,30 @@ pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq,
/* unpack the binary and add each piece to the pstate */
it = JsonbIteratorInit(jbval->val.binary.data);
+
+ if ((jbval->val.binary.data->header & JB_FSCALAR) && *pstate)
+ {
+ tok = JsonbIteratorNext(&it, &v, true);
+ Assert(tok == WJB_BEGIN_ARRAY);
+ Assert(v.type == jbvArray && v.val.array.rawScalar);
+
+ tok = JsonbIteratorNext(&it, &v, true);
+ Assert(tok == WJB_ELEM);
+
+ res = pushJsonbValueScalar(pstate, seq, &v);
+
+ tok = JsonbIteratorNext(&it, &v, true);
+ Assert(tok == WJB_END_ARRAY);
+ Assert(it == NULL);
+
+ return res;
+ }
+
while ((tok = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
res = pushJsonbValueScalar(pstate, tok,
- tok < WJB_BEGIN_ARRAY ? &v : NULL);
+ tok < WJB_BEGIN_ARRAY ||
+ (tok == WJB_BEGIN_ARRAY &&
+ v.val.array.rawScalar) ? &v : NULL);
return res;
}
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 17ee4e4..a5fed4b 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -23,6 +23,8 @@
#include "lib/stringinfo.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_coerce.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/hsearch.h"
@@ -32,6 +34,7 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
+#include "parser/parse_node.h"
/* Operations available for setPath */
#define JB_PATH_CREATE 0x0001
@@ -136,18 +139,21 @@ static JsonbValue *findJsonbValueFromContainerLen(JsonbContainer *container,
/* functions supporting jsonb_delete, jsonb_set and jsonb_concat */
static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
JsonbParseState **state);
+static Datum jsonb_set_element(Datum datum, Datum *path, int path_len,
+ Datum sourceData, Oid source_type);
+static Datum jsonb_get_element(Jsonb *jb, Datum *path, int npath,
+ bool *isnull, bool as_text);
static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len,
- JsonbParseState **st, int level, Jsonb *newval,
- int op_type);
+ JsonbParseState **st, int level, JsonbValue *newval, int op_type);
static void setPathObject(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len, JsonbParseState **st,
- int level,
- Jsonb *newval, uint32 npairs, int op_type);
+ int level, JsonbValue *newval, uint32 npairs, int op_type);
static void setPathArray(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len, JsonbParseState **st,
- int level, Jsonb *newval, uint32 nelems, int op_type);
-static void addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb);
+ int level, JsonbValue *newval, uint32 nelems, int op_type);
+static Datum jsonb_subscripting_evaluate(PG_FUNCTION_ARGS);
+static Datum jsonb_subscripting_prepare(PG_FUNCTION_ARGS);
/* state for json_object_keys */
typedef struct OkeysState
@@ -1171,16 +1177,11 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
Jsonb *jb = PG_GETARG_JSONB(0);
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
- Jsonb *res;
Datum *pathtext;
bool *pathnulls;
+ bool isnull;
int npath;
- int i;
- bool have_object = false,
- have_array = false;
- JsonbValue *jbvp = NULL;
- JsonbValue tv;
- JsonbContainer *container;
+ Datum res;
/*
* If the array contains any null elements, return NULL, on the grounds
@@ -1195,9 +1196,28 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
deconstruct_array(path, TEXTOID, -1, false, 'i',
&pathtext, &pathnulls, &npath);
- /* Identify whether we have object, array, or scalar at top-level */
- container = &jb->root;
+ res = jsonb_get_element(jb, pathtext, npath, &isnull, as_text);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(res);
+}
+static Datum
+jsonb_get_element(Jsonb *jb, Datum *path, int npath, bool *isnull, bool as_text)
+{
+ Jsonb *res;
+ JsonbContainer *container = &jb->root;
+ JsonbValue *jbvp = NULL;
+ JsonbValue tv;
+ int i;
+ bool have_object = false,
+ have_array = false;
+
+ *isnull = false;
+
+ /* Identify whether we have object, array, or scalar at top-level */
if (JB_ROOT_IS_OBJECT(jb))
have_object = true;
else if (JB_ROOT_IS_ARRAY(jb) && !JB_ROOT_IS_SCALAR(jb))
@@ -1222,14 +1242,14 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
if (as_text)
{
- PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL,
+ return PointerGetDatum(cstring_to_text(JsonbToCString(NULL,
container,
VARSIZE(jb))));
}
else
{
/* not text mode - just hand back the jsonb */
- PG_RETURN_JSONB(jb);
+ return JsonbGetDatum(jb);
}
}
@@ -1239,21 +1259,24 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
jbvp = findJsonbValueFromContainerLen(container,
JB_FOBJECT,
- VARDATA_ANY(pathtext[i]),
- VARSIZE_ANY_EXHDR(pathtext[i]));
+ VARDATA_ANY(path[i]),
+ VARSIZE_ANY_EXHDR(path[i]));
}
else if (have_array)
{
long lindex;
uint32 index;
- char *indextext = TextDatumGetCString(pathtext[i]);
+ char *indextext = TextDatumGetCString(path[i]);
char *endptr;
errno = 0;
lindex = strtol(indextext, &endptr, 10);
if (endptr == indextext || *endptr != '\0' || errno != 0 ||
lindex > INT_MAX || lindex < INT_MIN)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
if (lindex >= 0)
{
@@ -1271,7 +1294,10 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
nelements = container->header & JB_CMASK;
if (-lindex > nelements)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
else
index = nelements + lindex;
}
@@ -1281,11 +1307,15 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
else
{
/* scalar, extraction yields a null */
- PG_RETURN_NULL();
+ *isnull = true;
+ return PointerGetDatum(NULL);
}
if (jbvp == NULL)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
else if (i == npath - 1)
break;
@@ -1310,27 +1340,57 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
/* special-case outputs for string and null values */
if (jbvp->type == jbvString)
- PG_RETURN_TEXT_P(cstring_to_text_with_len(jbvp->val.string.val,
- jbvp->val.string.len));
+ return PointerGetDatum(
+ cstring_to_text_with_len(jbvp->val.string.val,
+ jbvp->val.string.len));
if (jbvp->type == jbvNull)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
}
res = JsonbValueToJsonb(jbvp);
if (as_text)
{
- PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL,
+ return PointerGetDatum(cstring_to_text(JsonbToCString(NULL,
&res->root,
VARSIZE(res))));
}
else
{
/* not text mode - just hand back the jsonb */
- PG_RETURN_JSONB(res);
+ return JsonbGetDatum(res);
}
}
+Datum
+jsonb_set_element(Datum jsonbdatum, Datum *path, int path_len,
+ Datum sourceData, Oid source_type)
+{
+ Jsonb *jb = DatumGetJsonb(jsonbdatum);
+ JsonbValue *newval,
+ *res;
+ JsonbParseState *state = NULL;
+ JsonbIterator *it;
+ bool *path_nulls = palloc0(path_len * sizeof(bool));
+
+ newval = to_jsonb_worker(sourceData, source_type);
+
+ if (newval->type == jbvArray && newval->val.array.rawScalar)
+ *newval = newval->val.array.elems[0];
+
+ it = JsonbIteratorInit(&jb->root);
+
+ res = setPath(&it, path, path_nulls, path_len, &state, 0,
+ newval, JB_PATH_CREATE);
+
+ pfree(path_nulls);
+
+ PG_RETURN_JSONB(JsonbValueToJsonb(res));
+}
+
/*
* SQL function json_array_length(json) -> int
*/
@@ -3278,57 +3338,6 @@ jsonb_strip_nulls(PG_FUNCTION_ARGS)
}
/*
- * Add values from the jsonb to the parse state.
- *
- * If the parse state container is an object, the jsonb is pushed as
- * a value, not a key.
- *
- * This needs to be done using an iterator because pushJsonbValue doesn't
- * like getting jbvBinary values, so we can't just push jb as a whole.
- */
-static void
-addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb)
-{
- JsonbIterator *it;
- JsonbValue *o = &(*jbps)->contVal;
- JsonbValue v;
- JsonbIteratorToken type;
-
- it = JsonbIteratorInit(&jb->root);
-
- Assert(o->type == jbvArray || o->type == jbvObject);
-
- if (JB_ROOT_IS_SCALAR(jb))
- {
- (void) JsonbIteratorNext(&it, &v, false); /* skip array header */
- (void) JsonbIteratorNext(&it, &v, false); /* fetch scalar value */
-
- switch (o->type)
- {
- case jbvArray:
- (void) pushJsonbValue(jbps, WJB_ELEM, &v);
- break;
- case jbvObject:
- (void) pushJsonbValue(jbps, WJB_VALUE, &v);
- break;
- default:
- elog(ERROR, "unexpected parent of nested structure");
- }
- }
- else
- {
- while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
- {
- if (type == WJB_KEY || type == WJB_VALUE || type == WJB_ELEM)
- (void) pushJsonbValue(jbps, type, &v);
- else
- (void) pushJsonbValue(jbps, type, NULL);
- }
- }
-
-}
-
-/*
* SQL function jsonb_pretty (jsonb)
*
* Pretty-printed text for the jsonb
@@ -3514,7 +3523,8 @@ jsonb_set(PG_FUNCTION_ARGS)
{
Jsonb *in = PG_GETARG_JSONB(0);
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
- Jsonb *newval = PG_GETARG_JSONB(2);
+ Jsonb *newjsonb = PG_GETARG_JSONB(2);
+ JsonbValue *newval = JsonbToJsonbValue(newjsonb);
bool create = PG_GETARG_BOOL(3);
JsonbValue *res = NULL;
Datum *path_elems;
@@ -3606,7 +3616,8 @@ jsonb_insert(PG_FUNCTION_ARGS)
{
Jsonb *in = PG_GETARG_JSONB(0);
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
- Jsonb *newval = PG_GETARG_JSONB(2);
+ Jsonb *newjsonb = PG_GETARG_JSONB(2);
+ JsonbValue *newval = JsonbToJsonbValue(newjsonb);
bool after = PG_GETARG_BOOL(3);
JsonbValue *res = NULL;
Datum *path_elems;
@@ -3769,7 +3780,7 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
static JsonbValue *
setPath(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len,
- JsonbParseState **st, int level, Jsonb *newval, int op_type)
+ JsonbParseState **st, int level, JsonbValue *newval, int op_type)
{
JsonbValue v;
JsonbIteratorToken r;
@@ -3822,11 +3833,11 @@ setPath(JsonbIterator **it, Datum *path_elems,
static void
setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
int path_len, JsonbParseState **st, int level,
- Jsonb *newval, uint32 npairs, int op_type)
+ JsonbValue *newval, uint32 npairs, int op_type)
{
- JsonbValue v;
int i;
- JsonbValue k;
+ JsonbValue k,
+ v;
bool done = false;
if (level >= path_len || path_nulls[level])
@@ -3843,7 +3854,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
newkey.val.string.val = VARDATA_ANY(path_elems[level]);
(void) pushJsonbValue(st, WJB_KEY, &newkey);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_VALUE, newval);
}
for (i = 0; i < npairs; i++)
@@ -3874,7 +3885,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
if (!(op_type & JB_PATH_DELETE))
{
(void) pushJsonbValue(st, WJB_KEY, &k);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_VALUE, newval);
}
done = true;
}
@@ -3897,7 +3908,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
newkey.val.string.val = VARDATA_ANY(path_elems[level]);
(void) pushJsonbValue(st, WJB_KEY, &newkey);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_VALUE, newval);
}
(void) pushJsonbValue(st, r, &k);
@@ -3929,7 +3940,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
static void
setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
int path_len, JsonbParseState **st, int level,
- Jsonb *newval, uint32 nelems, int op_type)
+ JsonbValue *newval, uint32 nelems, int op_type)
{
JsonbValue v;
int idx,
@@ -3977,7 +3988,7 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
(op_type & JB_PATH_CREATE_OR_INSERT))
{
Assert(newval != NULL);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, newval);
done = true;
}
@@ -3993,7 +4004,7 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
r = JsonbIteratorNext(it, &v, true); /* skip */
if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_CREATE))
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, newval);
/*
* We should keep current value only in case of
@@ -4004,7 +4015,7 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
(void) pushJsonbValue(st, r, &v);
if (op_type & (JB_PATH_INSERT_AFTER | JB_PATH_REPLACE))
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, newval);
done = true;
}
@@ -4038,8 +4049,169 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
if ((op_type & JB_PATH_CREATE_OR_INSERT) && !done &&
level == path_len - 1 && i == nelems - 1)
{
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, newval);
}
}
}
}
+
+/*
+ * Perform an actual data extraction or modification for the jsonb
+ * subscripting. As a result the extracted Datum or the modified containers
+ * value will be returned.
+ */
+Datum
+jsonb_subscripting_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptingRefExprState *sbstate = (SubscriptingRefExprState *) PG_GETARG_POINTER(0);
+ SubscriptingExecData *sbsdata = (SubscriptingExecData *) PG_GETARG_POINTER(1);
+ SubscriptingRef *jsonb_ref = (SubscriptingRef *) sbstate->xprstate.expr;
+ bool *is_null = sbsdata->isNull;
+ bool is_assignment = (jsonb_ref->refassgnexpr != NULL);
+
+ if (is_assignment)
+ {
+ ExprContext *econtext = sbsdata->xprcontext;
+ Datum sourceData;
+ Datum save_datum;
+ bool save_isNull;
+ bool eisnull;
+
+ /*
+ * We might have a nested-assignment situation, in which the
+ * refassgnexpr is itself a FieldStore or SubscriptingRef that needs to
+ * obtain and modify the previous value of the array element or slice
+ * being replaced. If so, we have to extract that value from the
+ * array and pass it down via the econtext's caseValue. It's safe to
+ * reuse the CASE mechanism because there cannot be a CASE between
+ * here and where the value would be needed, and an array assignment
+ * can't be within a CASE either. (So saving and restoring the
+ * caseValue is just paranoia, but let's do it anyway.)
+ *
+ * Since fetching the old element might be a nontrivial expense, do it
+ * only if the argument appears to actually need it.
+ */
+ save_datum = econtext->caseValue_datum;
+ save_isNull = econtext->caseValue_isNull;
+
+ /*
+ * Evaluate the value to be assigned into the array.
+ */
+ sourceData = ExecEvalExpr(sbstate->refassgnexpr,
+ econtext,
+ &eisnull,
+ NULL);
+
+ econtext->caseValue_datum = save_datum;
+ econtext->caseValue_isNull = save_isNull;
+
+ /*
+ * For an assignment to a fixed-length array type, both the original
+ * array and the value to be assigned into it must be non-NULL, else
+ * we punt and return the original array.
+ */
+ if (sbstate->refattrlength > 0) /* fixed-length array? */
+ if (eisnull || *is_null)
+ return sbsdata->containerSource;
+
+ /*
+ * For assignment to varlena arrays, we handle a NULL original array
+ * by substituting an empty (zero-dimensional) array; insertion of the
+ * new element will result in a singleton array value. It does not
+ * matter whether the new element is NULL.
+ */
+ if (*is_null)
+ {
+ sbsdata->containerSource =
+ PointerGetDatum(construct_empty_array(jsonb_ref->refelemtype));
+ *is_null = false;
+ }
+
+ return jsonb_set_element(sbsdata->containerSource,
+ sbsdata->upper,
+ sbsdata->indexprNumber,
+ sourceData,
+ jsonb_ref->refelemtype);
+ }
+ else
+ return jsonb_get_element(DatumGetJsonb(sbsdata->containerSource),
+ sbsdata->upper,
+ sbsdata->indexprNumber,
+ is_null,
+ false);
+}
+
+/*
+ * Perform preparation for the jsonb subscripting. Since there are not any
+ * particular restrictions for this kind of subscripting, we will verify that
+ * it is not a slice operation. This function produces an expression that
+ * represents the result of extracting a single container element or the new
+ * container value with the source data inserted into the right part of the
+ * container.
+ */
+Datum
+jsonb_subscripting_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptingRef *sbsref = (SubscriptingRef *) PG_GETARG_POINTER(0);
+ ParseState *pstate = (ParseState *) PG_GETARG_POINTER(1);
+ List *upperIndexpr = NIL;
+ ListCell *l;
+
+ if (sbsref->reflowerindexpr != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("jsonb subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *)lfirst(sbsref->reflowerindexpr->head))))));
+
+ foreach(l, sbsref->refupperindexpr)
+ {
+ Node *subexpr = (Node *) lfirst(l);
+
+ Assert(subexpr != NULL);
+
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("jsonb subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *) lfirst(sbsref->refupperindexpr->head))))));
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ TEXTOID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("jsonb subscript must have text type"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ }
+
+ sbsref->refupperindexpr = upperIndexpr;
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+/*
+ * Handle jsonb-type subscripting logic.
+ */
+Datum
+jsonb_subscripting(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo;
+ get_slice_arguments(fcinfo, 1, fcinfo->nargs, &target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return jsonb_subscripting_evaluate(&target_fcinfo);
+
+ if (op_type & SBS_VALIDATION)
+ return jsonb_subscripting_prepare(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscripting function: %d", op_type);
+}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 4e2ba19..05e654e 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -441,7 +441,7 @@ static void get_tablesample_def(TableSampleClause *tablesample,
static void get_opclass_name(Oid opclass, Oid actual_datatype,
StringInfo buf);
static Node *processIndirection(Node *node, deparse_context *context);
-static void printSubscripts(ArrayRef *aref, deparse_context *context);
+static void printSubscripts(SubscriptingRef *aref, deparse_context *context);
static char *get_relation_name(Oid relid);
static char *generate_relation_name(Oid relid, List *namespaces);
static char *generate_qualified_relation_name(Oid relid);
@@ -6013,7 +6013,7 @@ get_update_query_targetlist_def(Query *query, List *targetList,
{
/*
* We must dig down into the expr to see if it's a PARAM_MULTIEXPR
- * Param. That could be buried under FieldStores and ArrayRefs
+ * Param. That could be buried under FieldStores and SubscriptingRefs
* (cf processIndirection()), and underneath those there could be
* an implicit type coercion.
*/
@@ -6026,13 +6026,13 @@ get_update_query_targetlist_def(Query *query, List *targetList,
expr = (Node *) linitial(fstore->newvals);
}
- else if (IsA(expr, ArrayRef))
+ else if (IsA(expr, SubscriptingRef))
{
- ArrayRef *aref = (ArrayRef *) expr;
+ SubscriptingRef *sbsref = (SubscriptingRef *) expr;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
break;
- expr = (Node *) aref->refassgnexpr;
+ expr = (Node *) sbsref->refassgnexpr;
}
else
break;
@@ -7064,7 +7064,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
/* single words: always simple */
return true;
- case T_ArrayRef:
+ case T_SubscriptingRef:
case T_ArrayExpr:
case T_RowExpr:
case T_CoalesceExpr:
@@ -7181,7 +7181,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
return true; /* own parentheses */
}
case T_BoolExpr: /* lower precedence */
- case T_ArrayRef: /* other separators */
+ case T_SubscriptingRef: /* other separators */
case T_ArrayExpr: /* other separators */
case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */
@@ -7231,7 +7231,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
return false;
return true; /* own parentheses */
}
- case T_ArrayRef: /* other separators */
+ case T_SubscriptingRef: /* other separators */
case T_ArrayExpr: /* other separators */
case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */
@@ -7417,9 +7417,9 @@ get_rule_expr(Node *node, deparse_context *context,
get_windowfunc_expr((WindowFunc *) node, context);
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
bool need_parens;
/*
@@ -7430,24 +7430,24 @@ get_rule_expr(Node *node, deparse_context *context,
* here too, and display only the assignment source
* expression.
*/
- if (IsA(aref->refexpr, CaseTestExpr))
+ if (IsA(sbsref->refexpr, CaseTestExpr))
{
- Assert(aref->refassgnexpr);
- get_rule_expr((Node *) aref->refassgnexpr,
+ Assert(sbsref->refassgnexpr);
+ get_rule_expr((Node *) sbsref->refassgnexpr,
context, showimplicit);
break;
}
/*
* Parenthesize the argument unless it's a simple Var or a
- * FieldSelect. (In particular, if it's another ArrayRef, we
+ * FieldSelect. (In particular, if it's another SubscriptingRef, we
* *must* parenthesize to avoid confusion.)
*/
- need_parens = !IsA(aref->refexpr, Var) &&
- !IsA(aref->refexpr, FieldSelect);
+ need_parens = !IsA(sbsref->refexpr, Var) &&
+ !IsA(sbsref->refexpr, FieldSelect);
if (need_parens)
appendStringInfoChar(buf, '(');
- get_rule_expr((Node *) aref->refexpr, context, showimplicit);
+ get_rule_expr((Node *) sbsref->refexpr, context, showimplicit);
if (need_parens)
appendStringInfoChar(buf, ')');
@@ -7460,7 +7460,7 @@ get_rule_expr(Node *node, deparse_context *context,
* EXPLAIN tries to print the targetlist of a plan resulting
* from such a statement.
*/
- if (aref->refassgnexpr)
+ if (sbsref->refassgnexpr)
{
Node *refassgnexpr;
@@ -7477,7 +7477,7 @@ get_rule_expr(Node *node, deparse_context *context,
else
{
/* Just an ordinary array fetch, so print subscripts */
- printSubscripts(aref, context);
+ printSubscripts(sbsref, context);
}
}
break;
@@ -7676,12 +7676,12 @@ get_rule_expr(Node *node, deparse_context *context,
bool need_parens;
/*
- * Parenthesize the argument unless it's an ArrayRef or
+ * Parenthesize the argument unless it's an SubscriptingRef or
* another FieldSelect. Note in particular that it would be
* WRONG to not parenthesize a Var argument; simplicity is not
* the issue here, having the right number of names is.
*/
- need_parens = !IsA(arg, ArrayRef) &&!IsA(arg, FieldSelect);
+ need_parens = !IsA(arg, SubscriptingRef) && !IsA(arg, FieldSelect);
if (need_parens)
appendStringInfoChar(buf, '(');
get_rule_expr(arg, context, true);
@@ -9877,7 +9877,7 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
/*
* processIndirection - take care of array and subfield assignment
*
- * We strip any top-level FieldStore or assignment ArrayRef nodes that
+ * We strip any top-level FieldStore or assignment SubscriptingRef nodes that
* appear in the input, printing them as decoration for the base column
* name (which we assume the caller just printed). Return the subexpression
* that's to be assigned.
@@ -9919,19 +9919,19 @@ processIndirection(Node *node, deparse_context *context)
*/
node = (Node *) linitial(fstore->newvals);
}
- else if (IsA(node, ArrayRef))
+ else if (IsA(node, SubscriptingRef))
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
break;
- printSubscripts(aref, context);
+ printSubscripts(sbsref, context);
/*
* We ignore refexpr since it should be an uninteresting reference
* to the target column or subcolumn.
*/
- node = (Node *) aref->refassgnexpr;
+ node = (Node *) sbsref->refassgnexpr;
}
else
break;
@@ -9941,14 +9941,14 @@ processIndirection(Node *node, deparse_context *context)
}
static void
-printSubscripts(ArrayRef *aref, deparse_context *context)
+printSubscripts(SubscriptingRef *sbsref, deparse_context *context)
{
StringInfo buf = context->buf;
ListCell *lowlist_item;
ListCell *uplist_item;
- lowlist_item = list_head(aref->reflowerindexpr); /* could be NULL */
- foreach(uplist_item, aref->refupperindexpr)
+ lowlist_item = list_head(sbsref->reflowerindexpr); /* could be NULL */
+ foreach(uplist_item, sbsref->refupperindexpr)
{
appendStringInfoChar(buf, '[');
if (lowlist_item)
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 13ae6ad..06a3c1a 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -3061,3 +3061,23 @@ get_range_subtype(Oid rangeOid)
else
return InvalidOid;
}
+
+/*
+ * get_typsubscripting
+ *
+ * Given the type OID, return the type's typsubscripting procedure, if any.
+ */
+RegProcedure
+get_typsubscripting(Oid typid)
+{
+ HeapTuple tp;
+ RegProcedure result = InvalidOid;
+
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (HeapTupleIsValid(tp))
+ {
+ result = ((Form_pg_type) GETSTRUCT(tp))->typsubscripting;
+ ReleaseSysCache(tp);
+ }
+ return result;
+}
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 5d49fe5..f7f2c7e 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -1396,3 +1396,19 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
return tupdesc;
}
+
+void
+get_slice_arguments(FunctionCallInfo fcinfo, int begin, int end, FunctionCallInfoData *sliced_fcinfo)
+{
+ int i;
+
+ InitFunctionCallInfoData(*sliced_fcinfo, fcinfo->flinfo,
+ fcinfo->nargs - 1, fcinfo->fncollation,
+ NULL, NULL);
+
+ for(i = begin; i < end; i++)
+ {
+ sliced_fcinfo->arg[i - begin] = fcinfo->arg[i];
+ sliced_fcinfo->argnull[i - begin] = false;
+ }
+}
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index a61b7a2..93826fe 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -147,7 +147,7 @@ typedef FormData_pg_class *Form_pg_class;
* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId;
* similarly, "1" in relminmxid stands for FirstMultiXactId
*/
-DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f f f t n f 3 1 _null_ _null_ _null_));
+DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 31 0 t f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 21 0 f f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index cd7b909..8d7c1f4 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -5345,6 +5345,12 @@ DESCR("pg_controldata recovery state information as a function");
DATA(insert OID = 3444 ( pg_control_init PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,23,23,23,23,23,23,23,16,16,16,23}" "{o,o,o,o,o,o,o,o,o,o,o,o,o}" "{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,bigint_timestamps,float4_pass_by_value,float8_pass_by_value,data_page_checksum_version}" _null_ _null_ pg_control_init _null_ _null_ _null_ ));
DESCR("pg_controldata init state information as a function");
+/* type subscripting support */
+DATA(insert OID = 3343 ( jsonb_subscripting PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 "2281" "2281" _null_ _null_ _null_ _null_ _null_ jsonb_subscripting _null_ _null_ _null_ ));
+DESCR("Jsonb subscripting logic");
+DATA(insert OID = 3344 ( array_subscripting PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 "2281" "2281" _null_ _null_ _null_ _null_ _null_ array_subscripting _null_ _null_ _null_ ));
+DESCR("Array subscripting logic");
+
/*
* Symbolic values for provolatile column: these indicate whether the result
* of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 162239c..c6cd95e 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -199,6 +199,12 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO
*/
Oid typcollation;
+ /*
+ * Type specific subscripting logic. If typsubscripting is none, it means
+ * that this type doesn't support subscripting.
+ */
+ regproc typsubscripting;
+
#ifdef CATALOG_VARLEN /* variable-length fields start here */
/*
@@ -236,7 +242,7 @@ typedef FormData_pg_type *Form_pg_type;
* compiler constants for pg_type
* ----------------
*/
-#define Natts_pg_type 30
+#define Natts_pg_type 31
#define Anum_pg_type_typname 1
#define Anum_pg_type_typnamespace 2
#define Anum_pg_type_typowner 3
@@ -264,10 +270,10 @@ typedef FormData_pg_type *Form_pg_type;
#define Anum_pg_type_typtypmod 25
#define Anum_pg_type_typndims 26
#define Anum_pg_type_typcollation 27
-#define Anum_pg_type_typdefaultbin 28
-#define Anum_pg_type_typdefault 29
-#define Anum_pg_type_typacl 30
-
+#define Anum_pg_type_typsubscripting 28
+#define Anum_pg_type_typdefaultbin 29
+#define Anum_pg_type_typdefault 30
+#define Anum_pg_type_typacl 31
/* ----------------
* initial contents of pg_type
@@ -283,94 +289,94 @@ typedef FormData_pg_type *Form_pg_type;
*/
/* OIDS 1 - 99 */
-DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("boolean, 'true'/'false'");
#define BOOLOID 16
-DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("variable-length string, binary values escaped");
#define BYTEAOID 17
-DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("single character");
#define CHAROID 18
-DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("63-byte type for storing system identifiers");
#define NAMEOID 19
-DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("~18 digit integer, 8-byte storage");
#define INT8OID 20
-DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("-32 thousand to 32 thousand, 2-byte storage");
#define INT2OID 21
-DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("array of int2, used in system tables");
#define INT2VECTOROID 22
-DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("-2 billion to 2 billion integer, 4-byte storage");
#define INT4OID 23
-DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered procedure");
#define REGPROCOID 24
-DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("variable-length string, no limit specified");
#define TEXTOID 25
-DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("object identifier(oid), maximum 4 billion");
#define OIDOID 26
-DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("(block, offset), physical location of tuple");
#define TIDOID 27
-DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("transaction id");
#define XIDOID 28
-DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("command identifier type, sequence in transaction id");
#define CIDOID 29
-DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("array of oids, used in system tables");
#define OIDVECTOROID 30
/* hand-built rowtype entries for bootstrapped catalogs */
/* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */
-DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
+DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
+DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
+DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
/* OIDS 100 - 199 */
-DATA(insert OID = 114 ( json PGNSP PGUID -1 f b U f t \054 0 0 199 json_in json_out json_recv json_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 114 ( json PGNSP PGUID -1 f b U f t \054 0 0 199 json_in json_out json_recv json_send - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define JSONOID 114
-DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("XML content");
#define XMLOID 142
-DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 199 ( _json PGNSP PGUID -1 f b A f t \054 0 114 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 199 ( _json PGNSP PGUID -1 f b A f t \054 0 114 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
-DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("string representing an internal node tree");
#define PGNODETREEOID 194
-DATA(insert OID = 32 ( pg_ddl_command PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 pg_ddl_command_in pg_ddl_command_out pg_ddl_command_recv pg_ddl_command_send - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 32 ( pg_ddl_command PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 pg_ddl_command_in pg_ddl_command_out pg_ddl_command_recv pg_ddl_command_send - - - ALIGNOF_POINTER p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("internal type for passing CollectedCommand");
#define PGDDLCOMMANDOID 32
/* OIDS 200 - 299 */
-DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("storage manager");
/* OIDS 300 - 399 */
@@ -380,276 +386,276 @@ DESCR("storage manager");
/* OIDS 500 - 599 */
/* OIDS 600 - 699 */
-DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("geometric point '(x, y)'");
#define POINTOID 600
-DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("geometric line segment '(pt1,pt2)'");
#define LSEGOID 601
-DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("geometric path '(pt1,...)'");
#define PATHOID 602
-DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("geometric box '(lower left,upper right)'");
#define BOXOID 603
-DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("geometric polygon '(pt1,...)'");
#define POLYGONOID 604
-DATA(insert OID = 628 ( line PGNSP PGUID 24 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 628 ( line PGNSP PGUID 24 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("geometric line");
#define LINEOID 628
-DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* OIDS 700 - 799 */
-DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("single-precision floating point number, 4-byte storage");
#define FLOAT4OID 700
-DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("double-precision floating point number, 8-byte storage");
#define FLOAT8OID 701
-DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("absolute, limited-range date and time (Unix system time)");
#define ABSTIMEOID 702
-DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("relative, limited-range time interval (Unix delta time)");
#define RELTIMEOID 703
-DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("(abstime,abstime), time interval");
#define TINTERVALOID 704
-DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("");
#define UNKNOWNOID 705
-DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("geometric circle '(center,radius)'");
#define CIRCLEOID 718
-DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("monetary amounts, $d,ddd.cc");
#define CASHOID 790
-DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* OIDS 800 - 899 */
-DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDROID 829
-DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("IP address/netmask, host address, netmask optional");
#define INETOID 869
-DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("network IP address/netmask, network address");
#define CIDROID 650
/* OIDS 900 - 999 */
/* OIDS 1000 - 1099 */
-DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
#define INT2ARRAYOID 1005
-DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
#define INT4ARRAYOID 1007
-DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 100 array_subscripting _null_ _null_ _null_ ));
#define TEXTARRAYOID 1009
-DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
#define OIDARRAYOID 1028
-DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
-DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
-DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout array_typanalyze i x f 0 -1 0 100 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout array_typanalyze i x f 0 -1 0 100 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
#define FLOAT4ARRAYOID 1021
-DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("access control list");
#define ACLITEMOID 1033
-DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
#define CSTRINGARRAYOID 1263
-DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("char(length), blank-padded string, fixed storage length");
#define BPCHAROID 1042
-DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("varchar(length), non-blank-padded string, variable storage length");
#define VARCHAROID 1043
-DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("date");
#define DATEOID 1082
-DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("time of day");
#define TIMEOID 1083
/* OIDS 1100 - 1199 */
-DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("date and time");
#define TIMESTAMPOID 1114
-DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("date and time with time zone");
#define TIMESTAMPTZOID 1184
-DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("@ <number> <units>, time interval");
#define INTERVALOID 1186
-DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* OIDS 1200 - 1299 */
-DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("time of day with time zone");
#define TIMETZOID 1266
-DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* OIDS 1500 - 1599 */
-DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("fixed-length bit string");
#define BITOID 1560
-DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("variable-length bit string");
#define VARBITOID 1562
-DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* OIDS 1600 - 1699 */
/* OIDS 1700 - 1799 */
-DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("numeric(precision, decimal), arbitrary precision number");
#define NUMERICOID 1700
-DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("reference to cursor (portal name)");
#define REFCURSOROID 1790
/* OIDS 2200 - 2299 */
-DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
-DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered procedure (with args)");
#define REGPROCEDUREOID 2202
-DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered operator");
#define REGOPEROID 2203
-DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered operator (with args)");
#define REGOPERATOROID 2204
-DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered class");
#define REGCLASSOID 2205
-DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered type");
#define REGTYPEOID 2206
-DATA(insert OID = 4096 ( regrole PGNSP PGUID 4 t b N f t \054 0 0 4097 regrolein regroleout regrolerecv regrolesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4096 ( regrole PGNSP PGUID 4 t b N f t \054 0 0 4097 regrolein regroleout regrolerecv regrolesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered role");
#define REGROLEOID 4096
-DATA(insert OID = 4089 ( regnamespace PGNSP PGUID 4 t b N f t \054 0 0 4090 regnamespacein regnamespaceout regnamespacerecv regnamespacesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4089 ( regnamespace PGNSP PGUID 4 t b N f t \054 0 0 4090 regnamespacein regnamespaceout regnamespacerecv regnamespacesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered namespace");
#define REGNAMESPACEOID 4089
-DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
#define REGTYPEARRAYOID 2211
-DATA(insert OID = 4097 ( _regrole PGNSP PGUID -1 f b A f t \054 0 4096 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 4090 ( _regnamespace PGNSP PGUID -1 f b A f t \054 0 4089 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4097 ( _regrole PGNSP PGUID -1 f b A f t \054 0 4096 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 4090 ( _regnamespace PGNSP PGUID -1 f b A f t \054 0 4089 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* uuid */
-DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("UUID datatype");
#define UUIDOID 2950
-DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* pg_lsn */
-DATA(insert OID = 3220 ( pg_lsn PGNSP PGUID 8 FLOAT8PASSBYVAL b U f t \054 0 0 3221 pg_lsn_in pg_lsn_out pg_lsn_recv pg_lsn_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3220 ( pg_lsn PGNSP PGUID 8 FLOAT8PASSBYVAL b U f t \054 0 0 3221 pg_lsn_in pg_lsn_out pg_lsn_recv pg_lsn_send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("PostgreSQL LSN datatype");
#define LSNOID 3220
-DATA(insert OID = 3221 ( _pg_lsn PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3221 ( _pg_lsn PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* text search */
-DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("text representation for text search");
#define TSVECTOROID 3614
-DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("GiST index internal text representation for text search");
#define GTSVECTOROID 3642
-DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("query representation for text search");
#define TSQUERYOID 3615
-DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered text search configuration");
#define REGCONFIGOID 3734
-DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered text search dictionary");
#define REGDICTIONARYOID 3769
-DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* jsonb */
-DATA(insert OID = 3802 ( jsonb PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3802 ( jsonb PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 jsonb_subscripting _null_ _null_ _null_ ));
DESCR("Binary JSON");
#define JSONBOID 3802
-DATA(insert OID = 3807 ( _jsonb PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3807 ( _jsonb PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
-DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("txid snapshot");
-DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* range types */
-DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of integers");
#define INT4RANGEOID 3904
-DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of numerics");
-DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of timestamps without time zone");
-DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of timestamps with time zone");
-DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of dates");
-DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of bigints");
-DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/*
* pseudo-types
@@ -664,41 +670,41 @@ DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 arr
* but there is now support for it in records and arrays. Perhaps we should
* just treat it as a regular base type?
*/
-DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define RECORDOID 2249
-DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
#define RECORDARRAYOID 2287
-DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define CSTRINGOID 2275
-DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYOID 2276
-DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYARRAYOID 2277
-DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out void_recv void_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out void_recv void_send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define VOIDOID 2278
-DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define TRIGGEROID 2279
-DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define EVTTRIGGEROID 3838
-DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
-DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define INTERNALOID 2281
-DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define OPAQUEOID 2282
-DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYELEMENTOID 2283
-DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYNONARRAYOID 2776
-DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYENUMOID 3500
-DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define FDW_HANDLEROID 3115
-DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define INDEX_AM_HANDLEROID 325
-DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define TSM_HANDLEROID 3310
-DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYRANGEOID 3831
diff --git a/src/include/catalog/pg_type_fn.h b/src/include/catalog/pg_type_fn.h
index d7bbfdb..d712f6a 100644
--- a/src/include/catalog/pg_type_fn.h
+++ b/src/include/catalog/pg_type_fn.h
@@ -52,7 +52,8 @@ extern ObjectAddress TypeCreate(Oid newTypeOid,
int32 typeMod,
int32 typNDims,
bool typeNotNull,
- Oid typeCollation);
+ Oid typeCollation,
+ Oid subscriptingProcedure);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
@@ -70,6 +71,7 @@ extern void GenerateTypeDependencies(Oid typeNamespace,
bool isImplicitArray,
Oid baseType,
Oid typeCollation,
+ Oid subscriptingProcedure,
Node *defaultExpr,
bool rebuild);
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index e73a824..f53096a 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -183,6 +183,7 @@ extern TupleDesc build_function_result_tupdesc_d(Datum proallargtypes,
Datum proargmodes,
Datum proargnames);
extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
+extern void get_slice_arguments(FunctionCallInfo fcinfo, int begin, int end, FunctionCallInfoData *sliced_fcinfo);
/*----------
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index d43ec56..c377b96 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -662,25 +662,52 @@ typedef struct WindowFuncExprState
} WindowFuncExprState;
/* ----------------
- * ArrayRefExprState node
+ * SubscriptingRefExprState node
*
* Note: array types can be fixed-length (typlen > 0), but only when the
* element type is itself fixed-length. Otherwise they are varlena structures
* and have typlen = -1. In any case, an array type is never pass-by-value.
* ----------------
*/
-typedef struct ArrayRefExprState
+typedef struct SubscriptingRefExprState
{
ExprState xprstate;
List *refupperindexpr; /* states for child nodes */
List *reflowerindexpr;
ExprState *refexpr;
ExprState *refassgnexpr;
- int16 refattrlength; /* typlen of array type */
- int16 refelemlength; /* typlen of the array element type */
+ int16 refattrlength; /* typlen of container type */
+ int16 refelemlength; /* typlen of the container element type */
bool refelembyval; /* is the element type pass-by-value? */
char refelemalign; /* typalign of the element type */
-} ArrayRefExprState;
+
+ /*
+ * Function manager's lookup info for the target subscripting operation. If
+ * fcache.fn_oid is InvalidOid, we haven't initialized it yet.
+ */
+ FmgrInfo fcache;
+} SubscriptingRefExprState;
+
+/* ---------------------------------
+ * Subscripting exec information
+ *
+ * It contains all information which is required to perform type-specific data
+ * extraction or modification. This information will be gathered in
+ * `ExecEvalSubscriptingRef` function and passed to `typsubscripting`
+ * procedure.
+ * ---------------------------------
+ */
+typedef struct SubscriptingExecData
+{
+ ExprContext *xprcontext; /* econtext for subscripting */
+ bool *isNull;
+ Datum *upper; /* upper boundary for subscripting */
+ Datum *lower; /* lower boundary for subscripting */
+ bool *upperProvided;
+ bool *lowerProvided;
+ Datum containerSource;
+ int indexprNumber;
+} SubscriptingExecData;
/* ----------------
* FuncExprState node
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index c514d3f..fe16710 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -140,7 +140,7 @@ typedef enum NodeTag
T_Aggref,
T_GroupingFunc,
T_WindowFunc,
- T_ArrayRef,
+ T_SubscriptingRef,
T_FuncExpr,
T_NamedArgExpr,
T_OpExpr,
@@ -194,7 +194,7 @@ typedef enum NodeTag
T_AggrefExprState,
T_GroupingFuncExprState,
T_WindowFuncExprState,
- T_ArrayRefExprState,
+ T_SubscriptingRefExprState,
T_FuncExprState,
T_ScalarArrayOpExprState,
T_BoolExprState,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 717d822..dab4f15 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -343,18 +343,18 @@ typedef struct WindowFunc
} WindowFunc;
/* ----------------
- * ArrayRef: describes an array subscripting operation
- *
- * An ArrayRef can describe fetching a single element from an array,
- * fetching a subarray (array slice), storing a single element into
- * an array, or storing a slice. The "store" cases work with an
- * initial array value and a source value that is inserted into the
- * appropriate part of the array; the result of the operation is an
- * entire new modified array value.
- *
- * If reflowerindexpr = NIL, then we are fetching or storing a single array
- * element at the subscripts given by refupperindexpr. Otherwise we are
- * fetching or storing an array slice, that is a rectangular subarray
+ * SubscriptingRef: describes a subscripting operation over a container
+ *
+ * An SubscriptingRef can describe fetching a single element from a container,
+ * fetching a part of container (e.g. array slice), storing a single element into
+ * a container, or storing a slice. The "store" cases work with an
+ * initial container value and a source value that is inserted into the
+ * appropriate part of the container; the result of the operation is an
+ * entire new modified container value.
+ *
+ * If reflowerindexpr = NIL, then we are fetching or storing a single container
+ * element at the subscripts given by refupperindexpr. Otherwise we are
+ * fetching or storing a container slice, that is a rectangular subcontainer
* with lower and upper bounds given by the index expressions.
* reflowerindexpr must be the same length as refupperindexpr when it
* is not NIL.
@@ -366,27 +366,27 @@ typedef struct WindowFunc
* element; but it is the array type when doing subarray fetch or either
* type of store.
*
- * Note: for the cases where an array is returned, if refexpr yields a R/W
- * expanded array, then the implementation is allowed to modify that object
+ * Note: for the cases where a container is returned, if refexpr yields a R/W
+ * expanded container, then the implementation is allowed to modify that object
* in-place and return the same object.)
* ----------------
*/
-typedef struct ArrayRef
+typedef struct SubscriptingRef
{
Expr xpr;
- Oid refarraytype; /* type of the array proper */
- Oid refelemtype; /* type of the array elements */
- int32 reftypmod; /* typmod of the array (and elements too) */
- Oid refcollid; /* OID of collation, or InvalidOid if none */
- List *refupperindexpr;/* expressions that evaluate to upper array
- * indexes */
- List *reflowerindexpr;/* expressions that evaluate to lower array
- * indexes, or NIL for single array element */
- Expr *refexpr; /* the expression that evaluates to an array
- * value */
- Expr *refassgnexpr; /* expression for the source value, or NULL if
- * fetch */
-} ArrayRef;
+ Oid refcontainertype; /* type of the container proper */
+ Oid refelemtype; /* type of the container elements */
+ int32 reftypmod; /* typmod of the container (and elements too) */
+ Oid refcollid; /* OID of collation, or InvalidOid if none */
+ List *refupperindexpr; /* expressions that evaluate to upper container
+ * indexes */
+ List *reflowerindexpr; /* expressions that evaluate to lower container
+ * indexes, or NIL for single container element */
+ Expr *refexpr; /* the expression that evaluates to an container
+ * value */
+ Expr *refassgnexpr; /* expression for the source value, or NULL if
+ * fetch */
+} SubscriptingRef;
/*
* CoercionContext - distinguishes the allowed set of type casts
@@ -727,7 +727,7 @@ typedef struct FieldSelect
*
* FieldStore represents the operation of modifying one field in a tuple
* value, yielding a new tuple value (the input is not touched!). Like
- * the assign case of ArrayRef, this is used to implement UPDATE of a
+ * the assign case of SubscriptingRef, this is used to implement UPDATE of a
* portion of a column.
*
* A single FieldStore can actually represent updates of several different
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index bd6dc02..838b3c1 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -225,12 +225,17 @@ extern void cancel_parser_errposition_callback(ParseCallbackState *pcbstate);
extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno,
int location);
-extern Oid transformArrayType(Oid *arrayType, int32 *arrayTypmod);
-extern ArrayRef *transformArraySubscripts(ParseState *pstate,
- Node *arrayBase,
- Oid arrayType,
+extern Oid transformArrayType(Oid *containerType, int32 *containerTypmod);
+
+/* Type of a stage in case of type-specific subscripting procedure */
+#define SBS_VALIDATION 0x0001
+#define SBS_EXEC 0x0002
+
+extern SubscriptingRef *transformContainerSubscripts(ParseState *pstate,
+ Node *containerBase,
+ Oid containerType,
Oid elementType,
- int32 arrayTypMod,
+ int32 containerTypMod,
List *indirection,
Node *assignFrom);
extern Const *make_const(ParseState *pstate, Value *value, int location);
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index 6164f11..7fc1982 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -502,4 +502,9 @@ extern Datum array_positions(PG_FUNCTION_ARGS);
*/
extern Datum array_typanalyze(PG_FUNCTION_ARGS);
+/*
+ * prototypes for functions with array subscripting logic
+ */
+extern Datum array_subscripting(PG_FUNCTION_ARGS);
+
#endif /* ARRAY_H */
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index 470d5b1..6cc8053 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -354,6 +354,7 @@ extern Datum jsonb_typeof(PG_FUNCTION_ARGS);
/* generator routines */
extern Datum to_jsonb(PG_FUNCTION_ARGS);
+extern JsonbValue *to_jsonb_worker(Datum source, Oid source_type);
extern Datum jsonb_build_object(PG_FUNCTION_ARGS);
extern Datum jsonb_build_object_noargs(PG_FUNCTION_ARGS);
@@ -427,6 +428,7 @@ extern JsonbValue *pushJsonbValue(JsonbParseState **pstate,
extern JsonbIterator *JsonbIteratorInit(JsonbContainer *container);
extern JsonbIteratorToken JsonbIteratorNext(JsonbIterator **it, JsonbValue *val,
bool skipNested);
+extern JsonbValue *JsonbToJsonbValue(Jsonb *jsonb);
extern Jsonb *JsonbValueToJsonb(JsonbValue *val);
extern bool JsonbDeepContains(JsonbIterator **val,
JsonbIterator **mContained);
@@ -439,4 +441,7 @@ extern char *JsonbToCStringIndent(StringInfo out, JsonbContainer *in,
int estimated_len);
+/* Jsonb subscripting logic */
+extern Datum jsonb_subscripting(PG_FUNCTION_ARGS);
+
#endif /* __JSONB_H__ */
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index dcb8980..5c09bd0 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -159,6 +159,7 @@ extern void free_attstatsslot(Oid atttype,
extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
+extern RegProcedure get_typsubscripting(Oid typid);
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
/* type_is_array_domain accepts both plain arrays and domains over arrays */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 77e7440..79608a9 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -4710,7 +4710,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
/*
* Evaluate the subscripts, switch into left-to-right order.
- * Like ExecEvalArrayRef(), complain if any subscript is null.
+ * Like ExecEvalSubscriptingRef(), complain if any subscript is null.
*/
for (i = 0; i < nsubscripts; i++)
{
@@ -4758,7 +4758,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
* fixed-length array types we skip the assignment. We can't
* support assignment of a null entry into a fixed-length
* array, either, so that's a no-op too. This is all ugly but
- * corresponds to the current behavior of ExecEvalArrayRef().
+ * corresponds to the current behavior of ExecEvalSubscriptingRef().
*/
if (arrayelem->arraytyplen > 0 && /* fixed-length array? */
(oldarrayisnull || isNull))
@@ -6462,9 +6462,9 @@ exec_simple_check_node(Node *node)
case T_Param:
return TRUE;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *expr = (ArrayRef *) node;
+ SubscriptingRef *expr = (SubscriptingRef *) node;
if (!exec_simple_check_node((Node *) expr->refupperindexpr))
return FALSE;
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
index e2cb08a..dcacf20 100644
--- a/src/test/regress/expected/jsonb.out
+++ b/src/test/regress/expected/jsonb.out
@@ -3447,3 +3447,211 @@ HINT: Try using the function jsonb_set to replace key value.
select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"', true);
ERROR: cannot replace existing key
HINT: Try using the function jsonb_set to replace key value.
+-- jsonb subscript
+select ('123'::jsonb)['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('123'::jsonb)[0];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": 1}'::jsonb)['a'];
+ jsonb
+-------
+ 1
+(1 row)
+
+select ('{"a": 1}'::jsonb)[0];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": 1}'::jsonb)['not_exist'];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[0];
+ jsonb
+-------
+ 1
+(1 row)
+
+select ('[1, "2", null]'::jsonb)['1'];
+ jsonb
+-------
+ "2"
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[1.0];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[2];
+ jsonb
+-------
+ null
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[3];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[-2];
+ jsonb
+-------
+ "2"
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[1]['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[1][0];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['b'];
+ jsonb
+-------
+ "c"
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'];
+ jsonb
+-----------
+ [1, 2, 3]
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'][1];
+ jsonb
+-------
+ 2
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d']['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1'];
+ jsonb
+---------------
+ {"a2": "aaa"}
+(1 row)
+
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2'];
+ jsonb
+-------
+ "aaa"
+(1 row)
+
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']['a3'];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'];
+ jsonb
+-----------------------
+ ["aaa", "bbb", "ccc"]
+(1 row)
+
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'][2];
+ jsonb
+-------
+ "ccc"
+(1 row)
+
+create TEMP TABLE test_jsonb_subscript (
+ id int,
+ test_json jsonb
+);
+insert into test_jsonb_subscript values
+(1, '{}'), -- empty jsonb
+(2, '{"key": "value"}'); -- jsonb with data
+-- update empty jsonb
+update test_jsonb_subscript set test_json['a'] = 1 where id = 1;
+select * from test_jsonb_subscript;
+ id | test_json
+----+------------------
+ 2 | {"key": "value"}
+ 1 | {"a": 1}
+(2 rows)
+
+-- update jsonb with some data
+update test_jsonb_subscript set test_json['a'] = 1 where id = 2;
+select * from test_jsonb_subscript;
+ id | test_json
+----+--------------------------
+ 1 | {"a": 1}
+ 2 | {"a": 1, "key": "value"}
+(2 rows)
+
+-- replace jsonb
+update test_jsonb_subscript set test_json['a'] = 'test';
+select * from test_jsonb_subscript;
+ id | test_json
+----+-------------------------------
+ 1 | {"a": "test"}
+ 2 | {"a": "test", "key": "value"}
+(2 rows)
+
+-- replace by object
+update test_jsonb_subscript set test_json['a'] = '{"b": 1}'::jsonb;
+select * from test_jsonb_subscript;
+ id | test_json
+----+---------------------------------
+ 1 | {"a": {"b": 1}}
+ 2 | {"a": {"b": 1}, "key": "value"}
+(2 rows)
+
+-- replace by array
+update test_jsonb_subscript set test_json['a'] = '[1, 2, 3]'::jsonb;
+select * from test_jsonb_subscript;
+ id | test_json
+----+----------------------------------
+ 1 | {"a": [1, 2, 3]}
+ 2 | {"a": [1, 2, 3], "key": "value"}
+(2 rows)
+
+-- use jsonb subscription in where clause
+select * from test_jsonb_subscript where test_json['key'] = '"value"';
+ id | test_json
+----+----------------------------------
+ 2 | {"a": [1, 2, 3], "key": "value"}
+(1 row)
+
+select * from test_jsonb_subscript where test_json['key_doesnt_exists'] = '"value"';
+ id | test_json
+----+-----------
+(0 rows)
+
+select * from test_jsonb_subscript where test_json['key'] = '"wrong_value"';
+ id | test_json
+----+-----------
+(0 rows)
+
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
index 6b4c796..e277e0d 100644
--- a/src/test/regress/sql/jsonb.sql
+++ b/src/test/regress/sql/jsonb.sql
@@ -868,3 +868,62 @@ select jsonb_insert('{"a": {"b": "value"}}', '{a, c}', '"new_value"', true);
select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"');
select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"', true);
+
+-- jsonb subscript
+select ('123'::jsonb)['a'];
+select ('123'::jsonb)[0];
+select ('{"a": 1}'::jsonb)['a'];
+select ('{"a": 1}'::jsonb)[0];
+select ('{"a": 1}'::jsonb)['not_exist'];
+select ('[1, "2", null]'::jsonb)['a'];
+select ('[1, "2", null]'::jsonb)[0];
+select ('[1, "2", null]'::jsonb)['1'];
+select ('[1, "2", null]'::jsonb)[1.0];
+select ('[1, "2", null]'::jsonb)[2];
+select ('[1, "2", null]'::jsonb)[3];
+select ('[1, "2", null]'::jsonb)[-2];
+select ('[1, "2", null]'::jsonb)[1]['a'];
+select ('[1, "2", null]'::jsonb)[1][0];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['b'];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'][1];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d']['a'];
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1'];
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2'];
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']['a3'];
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'];
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'][2];
+
+create TEMP TABLE test_jsonb_subscript (
+ id int,
+ test_json jsonb
+);
+
+insert into test_jsonb_subscript values
+(1, '{}'), -- empty jsonb
+(2, '{"key": "value"}'); -- jsonb with data
+
+-- update empty jsonb
+update test_jsonb_subscript set test_json['a'] = 1 where id = 1;
+select * from test_jsonb_subscript;
+
+-- update jsonb with some data
+update test_jsonb_subscript set test_json['a'] = 1 where id = 2;
+select * from test_jsonb_subscript;
+
+-- replace jsonb
+update test_jsonb_subscript set test_json['a'] = 'test';
+select * from test_jsonb_subscript;
+
+-- replace by object
+update test_jsonb_subscript set test_json['a'] = '{"b": 1}'::jsonb;
+select * from test_jsonb_subscript;
+
+-- replace by array
+update test_jsonb_subscript set test_json['a'] = '[1, 2, 3]'::jsonb;
+select * from test_jsonb_subscript;
+
+-- use jsonb subscription in where clause
+select * from test_jsonb_subscript where test_json['key'] = '"value"';
+select * from test_jsonb_subscript where test_json['key_doesnt_exists'] = '"value"';
+select * from test_jsonb_subscript where test_json['key'] = '"wrong_value"';
diff --git a/src/tutorial/Makefile b/src/tutorial/Makefile
index 16dc390..0ead60c 100644
--- a/src/tutorial/Makefile
+++ b/src/tutorial/Makefile
@@ -13,8 +13,8 @@
#
#-------------------------------------------------------------------------
-MODULES = complex funcs
-DATA_built = advanced.sql basics.sql complex.sql funcs.sql syscat.sql
+MODULES = complex funcs subscripting
+DATA_built = advanced.sql basics.sql complex.sql funcs.sql syscat.sql subscripting.sql
ifdef NO_PGXS
subdir = src/tutorial
diff --git a/src/tutorial/subscripting.c b/src/tutorial/subscripting.c
new file mode 100644
index 0000000..edd93f1
--- /dev/null
+++ b/src/tutorial/subscripting.c
@@ -0,0 +1,230 @@
+/*
+ * src/tutorial/subscripting.c
+ *
+ ******************************************************************************
+ This file contains routines that can be bound to a Postgres backend and
+ called by the backend in the process of processing queries. The calling
+ format for these routines is dictated by Postgres architecture.
+******************************************************************************/
+
+#include "postgres.h"
+
+#include "catalog/pg_type.h"
+#include "executor/executor.h"
+#include "nodes/execnodes.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_node.h"
+#include "utils/array.h"
+#include "fmgr.h"
+#include "funcapi.h"
+
+PG_MODULE_MAGIC;
+
+typedef struct Custom
+{
+ int first;
+ int second;
+} Custom;
+
+
+/*****************************************************************************
+ * Input/Output functions
+ *****************************************************************************/
+
+PG_FUNCTION_INFO_V1(custom_in);
+
+Datum
+custom_in(PG_FUNCTION_ARGS)
+{
+ char *str = PG_GETARG_CSTRING(0);
+ int firstValue,
+ secondValue;
+ Custom *result;
+
+ if (sscanf(str, " ( %d , %d )", &firstValue, &secondValue) != 2)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid input syntax for complex: \"%s\"",
+ str)));
+
+
+ result = (Custom *) palloc(sizeof(Custom));
+ result->first = firstValue;
+ result->second = secondValue;
+ PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(custom_out);
+
+Datum
+custom_out(PG_FUNCTION_ARGS)
+{
+ Custom *custom = (Custom *) PG_GETARG_POINTER(0);
+ char *result;
+
+ result = psprintf("(%d, %d)", custom->first, custom->second);
+ PG_RETURN_CSTRING(result);
+}
+
+/*****************************************************************************
+ * Custom subscripting logic functions
+ *****************************************************************************/
+
+Datum
+custom_subscripting_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptingRefExprState *sbstate = (SubscriptingRefExprState *) PG_GETARG_POINTER(0);
+ SubscriptingExecData *sbsdata = (SubscriptingExecData *) PG_GETARG_POINTER(1);
+ SubscriptingRef *custom_ref = (SubscriptingRef *) sbstate->xprstate.expr;
+ Custom *result = (Custom *) sbsdata->containerSource;
+ bool *is_null = sbsdata->isNull;
+ bool is_assignment = (custom_ref->refassgnexpr != NULL);
+
+ int index;
+
+ if (sbsdata->indexprNumber != 1)
+ ereport(ERROR, (errmsg("custom does not support nested subscripting")));
+
+ index = DatumGetInt32(sbsdata->upper[0]);
+
+ if (is_assignment)
+ {
+ ExprContext *econtext = sbsdata->xprcontext;
+ Datum sourceData;
+ Datum save_datum;
+ bool save_isNull;
+ bool eisnull;
+
+ /*
+ * We might have a nested-assignment situation, in which the
+ * refassgnexpr is itself a FieldStore or SubscriptingRef that needs to
+ * obtain and modify the previous value of the array element or slice
+ * being replaced. If so, we have to extract that value from the
+ * array and pass it down via the econtext's caseValue. It's safe to
+ * reuse the CASE mechanism because there cannot be a CASE between
+ * here and where the value would be needed, and an array assignment
+ * can't be within a CASE either. (So saving and restoring the
+ * caseValue is just paranoia, but let's do it anyway.)
+ *
+ * Since fetching the old element might be a nontrivial expense, do it
+ * only if the argument appears to actually need it.
+ */
+ save_datum = econtext->caseValue_datum;
+ save_isNull = econtext->caseValue_isNull;
+
+ /*
+ * Evaluate the value to be assigned into the container.
+ */
+ sourceData = ExecEvalExpr(sbstate->refassgnexpr,
+ econtext,
+ &eisnull,
+ NULL);
+
+ econtext->caseValue_datum = save_datum;
+ econtext->caseValue_isNull = save_isNull;
+
+ /*
+ * For an assignment to a fixed-length array type, both the original
+ * array and the value to be assigned into it must be non-NULL, else
+ * we punt and return the original array.
+ */
+ if (sbstate->refattrlength > 0) /* fixed-length container? */
+ if (eisnull || *is_null)
+ return sbsdata->containerSource;
+
+ /*
+ * For assignment to varlena container, we handle a NULL original array
+ * by substituting an empty (zero-dimensional) array; insertion of the
+ * new element will result in a singleton array value. It does not
+ * matter whether the new element is NULL.
+ */
+ if (*is_null)
+ {
+ sbsdata->containerSource =
+ PointerGetDatum(construct_empty_array(custom_ref->refelemtype));
+ *is_null = false;
+ }
+
+ if (index == 1)
+ result->first = DatumGetInt32(sourceData);
+ else
+ result->second = DatumGetInt32(sourceData);
+
+ PG_RETURN_POINTER(result);
+ }
+ else
+ {
+ if (index == 1)
+ PG_RETURN_INT32(result->first);
+ else
+ PG_RETURN_INT32(result->second);
+ }
+}
+
+Datum
+custom_subscripting_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptingRef *sbsref = (SubscriptingRef *) PG_GETARG_POINTER(0);
+ ParseState *pstate = (ParseState *) PG_GETARG_POINTER(1);
+ List *upperIndexpr = NIL;
+ ListCell *l;
+
+ if (sbsref->reflowerindexpr != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("custom subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *)lfirst(sbsref->reflowerindexpr->head))))));
+
+ foreach(l, sbsref->refupperindexpr)
+ {
+ Node *subexpr = (Node *) lfirst(l);
+
+ Assert(subexpr != NULL);
+
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("custom subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *) lfirst(sbsref->refupperindexpr->head))))));
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ INT4OID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("custom subscript must have int type"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ }
+
+ sbsref->refupperindexpr = upperIndexpr;
+ sbsref->refelemtype = INT4OID;
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+PG_FUNCTION_INFO_V1(custom_subscripting);
+
+Datum
+custom_subscripting(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1,
+ fcinfo->nargs);
+
+ if (op_type & SBS_VALIDATION)
+ return custom_subscripting_prepare(&target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return custom_subscripting_evaluate(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscripting function: %d", op_type);
+}
diff --git a/src/tutorial/subscripting.source b/src/tutorial/subscripting.source
new file mode 100644
index 0000000..c37d236
--- /dev/null
+++ b/src/tutorial/subscripting.source
@@ -0,0 +1,71 @@
+---------------------------------------------------------------------------
+--
+-- subscripting.sql-
+-- This file shows how to create a new subscripting procedure for
+-- user-defined type.
+--
+--
+-- Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+-- Portions Copyright (c) 1994, Regents of the University of California
+--
+-- src/tutorial/subscripting.source
+--
+---------------------------------------------------------------------------
+
+-----------------------------
+-- Creating a new type:
+-- We are going to create a new type called 'complex' which represents
+-- complex numbers.
+-- A user-defined type must have an input and an output function, and
+-- optionally can have binary input and output functions. All of these
+-- are usually user-defined C functions.
+-----------------------------
+
+-- Assume the user defined functions are in /home/erthalion/programms/postgresql-master/src/tutorial/complex$DLSUFFIX
+-- (we do not want to assume this is in the dynamic loader search path).
+-- Look at $PWD/complex.c for the source. Note that we declare all of
+-- them as STRICT, so we do not need to cope with NULL inputs in the
+-- C code. We also mark them IMMUTABLE, since they always return the
+-- same outputs given the same inputs.
+
+-- the input function 'complex_in' takes a null-terminated string (the
+-- textual representation of the type) and turns it into the internal
+-- (in memory) representation. You will get a message telling you 'complex'
+-- does not exist yet but that's okay.
+
+CREATE FUNCTION custom_in(cstring)
+ RETURNS custom
+ AS '_OBJWD_/subscripting'
+ LANGUAGE C IMMUTABLE STRICT;
+
+-- the output function 'complex_out' takes the internal representation and
+-- converts it into the textual representation.
+
+CREATE FUNCTION custom_out(custom)
+ RETURNS cstring
+ AS '_OBJWD_/subscripting'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION custom_subscripting(internal)
+ RETURNS internal
+ AS '_OBJWD_/subscripting'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE custom (
+ internallength = 8,
+ input = custom_in,
+ output = custom_out,
+ subscripting = custom_subscripting
+);
+
+-- we can use it in a table
+
+CREATE TABLE test_subscripting (
+ data custom,
+);
+
+INSERT INTO test_subscripting VALUES ('(1, 2)');
+
+SELECT data[0] from test_subscripting;
+
+UPDATE test_subscripting SET data[1] = 3;
As I mentioned above [1]/messages/by-id/20161115080324.GA5351@e733.localdomain in my humble opinion this patch is not at all in a
"good shape" until it breaks existing extensions.
[1]: /messages/by-id/20161115080324.GA5351@e733.localdomain
On Mon, Dec 26, 2016 at 10:49:30PM +0700, Dmitry Dolgov wrote:
On 5 December 2016 at 12:03, Haribabu Kommi <kommi.haribabu@gmail.com>
wrote:
Moved to next CF with "needs review" status.
Looks like we stuck here little bit. Does anyone else have any
suggestions/improvements, or this patch is in good enough shape?
--
Best regards,
Aleksander Alekseev
On 27 December 2016 at 16:09, Aleksander Alekseev <
a.alekseev@postgrespro.ru> wrote:
until it breaks existing extensions.
Hm...I already answered, that I managed to avoid compilation problems for
this particular extension
using the `genparser` command again:
On Thu, Nov 17, 2016 at 10:56 PM, Dmitry Dolgov
<9erthalion6(at)gmail(dot)com>
wrote:
On 15 November 2016 at 15:03, Aleksander Alekseev <
a(dot)alekseev(at)postgrespro(dot)ru> wrote:
Hello.
I took a look on the latest -v4 patch. I would like to note that this
patch breaks a backward compatibility. For instance sr_plan extension[1]
stop to compile with errorsThank you for the feedback.
Well, if we're speaking about this particular extension, if I understood
correctly, it fetches all parse tree nodes from Postgres and generates
code
using this information. So to avoid compilation problems I believe you
need to
run `make USE_PGXS=1 genparser` again (it worked for me, I don't see any
mentions of `ArrayRef`).But speaking generally, I don't see how we can provide backward
compatibility for those extensions, who are strongly coupled with
implementation details
of parsing tree. I mean, in terms of interface it's mostly about to
replace
`ArrayRef` to `SubscriptingRef`, but I think it's better to do it in the
extension code.
Or is there something else that I missed?
2016-12-27 14:42 GMT+05:00 Dmitry Dolgov <9erthalion6@gmail.com>:
On 27 December 2016 at 16:09, Aleksander Alekseev
<a.alekseev@postgrespro.ru> wrote:
until it breaks existing extensions.Hm...I already answered, that I managed to avoid compilation problems for
this particular extension
using the `genparser` command again:
I suppose that a separate node type could solve it. But I'm not
convinced about how to distinguish ArrayRef node with new
SubscriptingRef node. Maybe it could be done in the
transformIndirection() function. If I understand all correctly.
Also Tom pointed that he had bad experience with using ArrayRef node:
/messages/by-id/518.1439846343@sss.pgh.pa.us
No. Make a new expression node type.
(Salesforce did something similar for an internal feature, and it was a
disaster both for code modularity and performance. We had to change it to
a separate node type, which I just got finished doing. Don't go down that
path. While you're at it, I'd advise that fetch and assignment be two
different node types rather than copying ArrayRef's bad precedent of using
only one.)
--
Artur Zakirov
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 4 January 2017 at 18:06, Artur Zakirov <a.zakirov@postgrespro.ru>
wrote:
But I'm not convinced about how to distinguish ArrayRef node with new
SubscriptingRef node.
I'm not sure I understood you correctly. You're talking about having two
nodes
`ArrayRef` and `SubscriptingRef` at the same time for the sake of backward
compatibility, am I right? But they're basically the same, since
`SubscriptingRef` name is used just to indicate more general purpose of this
node.
Also Tom pointed that he had bad experience with using ArrayRef node:
Yes, but it was related to the idea of having `ArrayRef` and `JsonbRef`
nodes
for specific types. Since now there is generic `SubscriptingRef` node, I
think
it should be ok.
Hm...I already answered, that I managed to avoid compilation problems for
this particular extension using the `genparser` command again:
I suppose that a separate node type could solve it.
Just to be clear - as far as I understood, these compilation problems were
caused not because the extension knew something about ArrayRef node in
particular, but because the extension tried to extract all nodes to generate
code from them. It means any change will require "refetching", so I think
it's
natural for this extension.
Here is a small improvement to patch documentation - I forgot to add link
to the page "user-defined subscripting procedure" into the "Extending ..."
section.
Attachments:
generic_type_subscription_v6.patchtext/x-patch; charset=US-ASCII; name=generic_type_subscription_v6.patchDownload
diff --git a/contrib/pg_stat_statements/pg_stat_statements.c b/contrib/pg_stat_statements/pg_stat_statements.c
index 8ce24e0..d9c9d64 100644
--- a/contrib/pg_stat_statements/pg_stat_statements.c
+++ b/contrib/pg_stat_statements/pg_stat_statements.c
@@ -2464,14 +2464,14 @@ JumbleExpr(pgssJumbleState *jstate, Node *node)
JumbleExpr(jstate, (Node *) expr->aggfilter);
}
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
- JumbleExpr(jstate, (Node *) aref->refupperindexpr);
- JumbleExpr(jstate, (Node *) aref->reflowerindexpr);
- JumbleExpr(jstate, (Node *) aref->refexpr);
- JumbleExpr(jstate, (Node *) aref->refassgnexpr);
+ JumbleExpr(jstate, (Node *) sbsref->refupperindexpr);
+ JumbleExpr(jstate, (Node *) sbsref->reflowerindexpr);
+ JumbleExpr(jstate, (Node *) sbsref->refexpr);
+ JumbleExpr(jstate, (Node *) sbsref->refassgnexpr);
}
break;
case T_FuncExpr:
diff --git a/contrib/postgres_fdw/deparse.c b/contrib/postgres_fdw/deparse.c
index 66b059a..00ab80c 100644
--- a/contrib/postgres_fdw/deparse.c
+++ b/contrib/postgres_fdw/deparse.c
@@ -144,7 +144,7 @@ static void deparseExpr(Expr *expr, deparse_expr_cxt *context);
static void deparseVar(Var *node, deparse_expr_cxt *context);
static void deparseConst(Const *node, deparse_expr_cxt *context, int showtype);
static void deparseParam(Param *node, deparse_expr_cxt *context);
-static void deparseArrayRef(ArrayRef *node, deparse_expr_cxt *context);
+static void deparseSubscriptingRef(SubscriptingRef *node, deparse_expr_cxt *context);
static void deparseFuncExpr(FuncExpr *node, deparse_expr_cxt *context);
static void deparseOpExpr(OpExpr *node, deparse_expr_cxt *context);
static void deparseOperatorName(StringInfo buf, Form_pg_operator opform);
@@ -384,9 +384,9 @@ foreign_expr_walker(Node *node,
state = FDW_COLLATE_UNSAFE;
}
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *ar = (ArrayRef *) node;
+ SubscriptingRef *ar = (SubscriptingRef *) node;
/* Assignment should not be in restrictions. */
if (ar->refassgnexpr != NULL)
@@ -2008,8 +2008,8 @@ deparseExpr(Expr *node, deparse_expr_cxt *context)
case T_Param:
deparseParam((Param *) node, context);
break;
- case T_ArrayRef:
- deparseArrayRef((ArrayRef *) node, context);
+ case T_SubscriptingRef:
+ deparseSubscriptingRef((SubscriptingRef *) node, context);
break;
case T_FuncExpr:
deparseFuncExpr((FuncExpr *) node, context);
@@ -2243,7 +2243,7 @@ deparseParam(Param *node, deparse_expr_cxt *context)
* Deparse an array subscript expression.
*/
static void
-deparseArrayRef(ArrayRef *node, deparse_expr_cxt *context)
+deparseSubscriptingRef(SubscriptingRef *node, deparse_expr_cxt *context)
{
StringInfo buf = context->buf;
ListCell *lowlist_item;
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 4930506..c84c218 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -7292,6 +7292,13 @@
</row>
<row>
+ <entry><structfield>typsubscripting</structfield></entry>
+ <entry><type>regproc</type></entry>
+ <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+ <entry>Custom subscripting function with type-specific logic, or 0 if this type doesn't support subscripting.</entry>
+ </row>
+
+ <row>
<entry><structfield>typdefaultbin</structfield></entry>
<entry><type>pg_node_tree</type></entry>
<entry></entry>
diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml
index c4f211b..5b77da4 100644
--- a/doc/src/sgml/extend.sgml
+++ b/doc/src/sgml/extend.sgml
@@ -35,6 +35,11 @@
</listitem>
<listitem>
<para>
+ subscripting procedure (starting in <xref linkend="xsubscripting">)
+ </para>
+ </listitem>
+ <listitem>
+ <para>
operator classes for indexes (starting in <xref linkend="xindex">)
</para>
</listitem>
@@ -283,6 +288,7 @@
&xaggr;
&xtypes;
&xoper;
+ &xsubscripting;
&xindex;
diff --git a/doc/src/sgml/filelist.sgml b/doc/src/sgml/filelist.sgml
index 69649a7..38433ef 100644
--- a/doc/src/sgml/filelist.sgml
+++ b/doc/src/sgml/filelist.sgml
@@ -71,6 +71,7 @@
<!ENTITY xplang SYSTEM "xplang.sgml">
<!ENTITY xoper SYSTEM "xoper.sgml">
<!ENTITY xtypes SYSTEM "xtypes.sgml">
+<!ENTITY xsubscripting SYSTEM "xsubscripting.sgml">
<!ENTITY plperl SYSTEM "plperl.sgml">
<!ENTITY plpython SYSTEM "plpython.sgml">
<!ENTITY plsql SYSTEM "plpgsql.sgml">
diff --git a/doc/src/sgml/json.sgml b/doc/src/sgml/json.sgml
index 3cf78d6..efffd5c 100644
--- a/doc/src/sgml/json.sgml
+++ b/doc/src/sgml/json.sgml
@@ -569,4 +569,29 @@ SELECT jdoc->'guid', jdoc->'name' FROM api WHERE jdoc @> '{"tags": ["qu
compared using the default database collation.
</para>
</sect2>
+
+ <sect2 id="json-subscripting">
+ <title>JSON subscripting</title>
+ <para>
+ JSONB data type support array-style subscripting expressions to extract or update particular element. An example of subscripting syntax:
+<programlisting>
+-- Extract value by key
+SELECT ('{"a": 1}'::jsonb)['a'];
+
+-- Extract nested value by key path
+SELECT ('{"a": {"b": {"c": 1}}}'::jsonb)['a']['b']['c'];
+
+-- Extract element by index
+SELECT ('[1, "2", null]'::jsonb)['1'];
+
+-- Update value by key
+UPDATE table_name set jsonb_field['key'] = 1;
+
+-- Select records using where clause with subscripting
+SELECT * from table_name where jsonb_field['key'] = '"value"';
+</programlisting>
+ </para>
+ </sect2>
+
+
</sect1>
diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index 5a09f19..5448b1b 100644
--- a/doc/src/sgml/ref/create_type.sgml
+++ b/doc/src/sgml/ref/create_type.sgml
@@ -54,6 +54,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
[ , ELEMENT = <replaceable class="parameter">element</replaceable> ]
[ , DELIMITER = <replaceable class="parameter">delimiter</replaceable> ]
[ , COLLATABLE = <replaceable class="parameter">collatable</replaceable> ]
+ [ , SUBSCRIPTING = <replaceable class="parameter">subscripting_function</replaceable> ]
)
CREATE TYPE <replaceable class="parameter">name</replaceable>
@@ -194,7 +195,8 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
<replaceable class="parameter">send_function</replaceable>,
<replaceable class="parameter">type_modifier_input_function</replaceable>,
<replaceable class="parameter">type_modifier_output_function</replaceable> and
- <replaceable class="parameter">analyze_function</replaceable>
+ <replaceable class="parameter">analyze_function</replaceable>,
+ <replaceable class="parameter">subscripting_function</replaceable>
are optional. Generally these functions have to be coded in C
or another low-level language.
</para>
@@ -451,6 +453,22 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
make use of the collation information; this does not happen
automatically merely by marking the type collatable.
</para>
+
+ <para>
+ The optional
+ <replaceable class="parameter">subscripting_function</replaceable>
+ contains type-specific logic for subscripting of the data type.
+ By default, there is no such function, which means that the data
+ type doesn't support subscripting. The subscripting function must be
+ declared to take a single argument of type <type>internal</>, and return
+ a <type>internal</> result. There are two examples of implementation for
+ subscripting function in case of array
+ (<replaceable class="parameter">array_subscripting</replaceable>)
+ and jsonb
+ (<replaceable class="parameter">jsonb_subscripting</replaceable>)
+ types in <filename>src/backend/utils/adt/arrayfuncs.c</> and
+ <filename>src/backend/utils/adt/jsonfuncs.c</> corresponding.
+ </para>
</refsect2>
<refsect2>
@@ -766,6 +784,16 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
</para>
</listitem>
</varlistentry>
+
+ <varlistentry>
+ <term><replaceable class="parameter">subscripting_function</replaceable></term>
+ <listitem>
+ <para>
+ The name of a function that contains type-specific subscripting logic for
+ the data type.
+ </para>
+ </listitem>
+ </varlistentry>
</variablelist>
</refsect1>
diff --git a/doc/src/sgml/xsubscripting.sgml b/doc/src/sgml/xsubscripting.sgml
new file mode 100644
index 0000000..37ab6ea
--- /dev/null
+++ b/doc/src/sgml/xsubscripting.sgml
@@ -0,0 +1,100 @@
+<!-- doc/src/sgml/xsubscripting.sgml -->
+
+ <sect1 id="xsubscripting">
+ <title>User-defined subscripting procedure</title>
+
+ <indexterm zone="xsubscripting">
+ <primary>custom subscripting</primary>
+ </indexterm>
+ <para>
+ When you define a new base type, you can also specify a custom procedure
+ to handle subscripting expressions. It should contains logic for verification
+ and for extraction or update your data. For instance:
+</para>
+<programlisting><![CDATA[
+typedef struct Custom
+{
+ int first;
+ int second;
+} Custom;
+
+Datum
+custom_subscripting_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptingExecData *sbsdata = (SubscriptingExecData *) PG_GETARG_POINTER(1);
+ Custom *result = (Custom *) sbsdata->containerSource;
+
+ // Some extraction or update logic based on sbsdata
+}
+
+Datum
+custom_subscripting_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptingRef *sbsref = (SubscriptingRef *) PG_GETARG_POINTER(0);
+
+ // Some verifications or type coersion
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+PG_FUNCTION_INFO_V1(custom_subscripting);
+
+Datum
+custom_subscripting(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1,
+ fcinfo->nargs);
+
+ if (op_type & SBS_VALIDATION)
+ return custom_subscripting_prepare(&target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return custom_subscripting_evaluate(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscripting function: %d", op_type);
+}]]>
+</programlisting>
+
+<para>
+ Then you can define a subscripting procedure and a custom data type:
+</para>
+<programlisting>
+CREATE FUNCTION custom_subscripting(internal)
+ RETURNS internal
+ AS '<replaceable>filename</replaceable>'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE custom (
+ internallength = 4,
+ input = custom_in,
+ output = custom_out,
+ subscripting = custom_subscripting
+);
+</programlisting>
+
+<para>
+ and use it as usual:
+</para>
+<programlisting>
+CREATE TABLE test_subscripting (
+ data custom,
+);
+
+INSERT INTO test_subscripting VALUES ('(1, 2)');
+
+SELECT data[0] from test_subscripting;
+
+UPDATE test_subscripting SET data[1] = 3;
+</programlisting>
+
+
+ <para>
+ The examples of custom subscripting implementation can be found in
+ <filename>subscripting.sql</filename> and <filename>subscripting.c</filename>
+ in the <filename>src/tutorial</> directory of the source distribution.
+ See the <filename>README</> file in that directory for instructions
+ about running the examples.
+ </para>
+
+</sect1>
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index e5d6aec..dff22ce 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -988,7 +988,8 @@ AddNewRelationType(const char *typeName,
-1, /* typmod */
0, /* array dimensions for typBaseType */
false, /* Type NOT NULL */
- InvalidOid); /* rowtypes never have a collation */
+ InvalidOid, /* rowtypes never have a collation */
+ InvalidOid); /* typsubscripting - none */
}
/* --------------------------------
@@ -1258,7 +1259,8 @@ heap_create_with_catalog(const char *relname,
-1, /* typmod */
0, /* array dimensions for typBaseType */
false, /* Type NOT NULL */
- InvalidOid); /* rowtypes never have a collation */
+ InvalidOid, /* rowtypes never have a collation */
+ F_ARRAY_SUBSCRIPTING); /* array implementation */
pfree(relarrayname);
}
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 4b2d281..7ceee0f 100644
--- a/src/backend/catalog/pg_type.c
+++ b/src/backend/catalog/pg_type.c
@@ -118,6 +118,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(-1);
values[Anum_pg_type_typndims - 1] = Int32GetDatum(0);
values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(InvalidOid);
+ values[Anum_pg_type_typsubscripting - 1] = ObjectIdGetDatum(InvalidOid);
nulls[Anum_pg_type_typdefaultbin - 1] = true;
nulls[Anum_pg_type_typdefault - 1] = true;
nulls[Anum_pg_type_typacl - 1] = true;
@@ -166,6 +167,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace, Oid ownerId)
false,
InvalidOid,
InvalidOid,
+ InvalidOid,
NULL,
false);
@@ -224,7 +226,8 @@ TypeCreate(Oid newTypeOid,
int32 typeMod,
int32 typNDims, /* Array dimensions for baseType */
bool typeNotNull,
- Oid typeCollation)
+ Oid typeCollation,
+ Oid subscriptingProcedure)
{
Relation pg_type_desc;
Oid typeObjectId;
@@ -364,6 +367,7 @@ TypeCreate(Oid newTypeOid,
values[Anum_pg_type_typtypmod - 1] = Int32GetDatum(typeMod);
values[Anum_pg_type_typndims - 1] = Int32GetDatum(typNDims);
values[Anum_pg_type_typcollation - 1] = ObjectIdGetDatum(typeCollation);
+ values[Anum_pg_type_typsubscripting - 1] = ObjectIdGetDatum(subscriptingProcedure);
/*
* initialize the default binary value for this type. Check for nulls of
@@ -484,6 +488,7 @@ TypeCreate(Oid newTypeOid,
isImplicitArray,
baseType,
typeCollation,
+ subscriptingProcedure,
(defaultTypeBin ?
stringToNode(defaultTypeBin) :
NULL),
@@ -530,6 +535,7 @@ GenerateTypeDependencies(Oid typeNamespace,
bool isImplicitArray,
Oid baseType,
Oid typeCollation,
+ Oid subscriptingProcedure,
Node *defaultExpr,
bool rebuild)
{
@@ -682,6 +688,14 @@ GenerateTypeDependencies(Oid typeNamespace,
/* Normal dependency on the default expression. */
if (defaultExpr)
recordDependencyOnExpr(&myself, defaultExpr, NIL, DEPENDENCY_NORMAL);
+
+ if (OidIsValid(subscriptingProcedure))
+ {
+ referenced.classId = ProcedureRelationId;
+ referenced.objectId = subscriptingProcedure;
+ referenced.objectSubId = 0;
+ recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+ }
}
/*
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 5e3989a..4164b2f 100644
--- a/src/backend/commands/typecmds.c
+++ b/src/backend/commands/typecmds.c
@@ -94,6 +94,7 @@ static Oid findTypeSendFunction(List *procname, Oid typeOid);
static Oid findTypeTypmodinFunction(List *procname);
static Oid findTypeTypmodoutFunction(List *procname);
static Oid findTypeAnalyzeFunction(List *procname, Oid typeOid);
+static Oid findTypeSubscriptingFunction(List *procname);
static Oid findRangeSubOpclass(List *opcname, Oid subtype);
static Oid findRangeCanonicalFunction(List *procname, Oid typeOid);
static Oid findRangeSubtypeDiffFunction(List *procname, Oid subtype);
@@ -123,6 +124,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
List *typmodinName = NIL;
List *typmodoutName = NIL;
List *analyzeName = NIL;
+ List *subscriptingName = NIL;
char category = TYPCATEGORY_USER;
bool preferred = false;
char delimiter = DEFAULT_TYPDELIM;
@@ -141,6 +143,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
DefElem *typmodinNameEl = NULL;
DefElem *typmodoutNameEl = NULL;
DefElem *analyzeNameEl = NULL;
+ DefElem *subscriptingNameEl = NULL;
DefElem *categoryEl = NULL;
DefElem *preferredEl = NULL;
DefElem *delimiterEl = NULL;
@@ -163,6 +166,7 @@ DefineType(ParseState *pstate, List *names, List *parameters)
Oid resulttype;
ListCell *pl;
ObjectAddress address;
+ Oid subscriptingOid = InvalidOid;
/*
* As of Postgres 8.4, we require superuser privilege to create a base
@@ -262,6 +266,9 @@ DefineType(ParseState *pstate, List *names, List *parameters)
else if (pg_strcasecmp(defel->defname, "analyze") == 0 ||
pg_strcasecmp(defel->defname, "analyse") == 0)
defelp = &analyzeNameEl;
+ else if (pg_strcasecmp(defel->defname, "subscripting") == 0 ||
+ pg_strcasecmp(defel->defname, "subscripting") == 0)
+ defelp = &subscriptingNameEl;
else if (pg_strcasecmp(defel->defname, "category") == 0)
defelp = &categoryEl;
else if (pg_strcasecmp(defel->defname, "preferred") == 0)
@@ -332,6 +339,8 @@ DefineType(ParseState *pstate, List *names, List *parameters)
typmodoutName = defGetQualifiedName(typmodoutNameEl);
if (analyzeNameEl)
analyzeName = defGetQualifiedName(analyzeNameEl);
+ if (subscriptingNameEl)
+ subscriptingName = defGetQualifiedName(subscriptingNameEl);
if (categoryEl)
{
char *p = defGetString(categoryEl);
@@ -513,6 +522,9 @@ DefineType(ParseState *pstate, List *names, List *parameters)
if (analyzeName)
analyzeOid = findTypeAnalyzeFunction(analyzeName, typoid);
+ if (subscriptingName)
+ subscriptingOid = findTypeSubscriptingFunction(subscriptingName);
+
/*
* Check permissions on functions. We choose to require the creator/owner
* of a type to also own the underlying functions. Since creating a type
@@ -632,7 +644,8 @@ DefineType(ParseState *pstate, List *names, List *parameters)
-1, /* typMod (Domains only) */
0, /* Array Dimensions of typbasetype */
false, /* Type NOT NULL */
- collation); /* type's collation */
+ collation, /* type's collation */
+ subscriptingOid); /* subscripting procedure */
Assert(typoid == address.objectId);
/*
@@ -673,7 +686,8 @@ DefineType(ParseState *pstate, List *names, List *parameters)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- collation); /* type's collation */
+ collation, /* type's collation */
+ F_ARRAY_SUBSCRIPTING);
pfree(array_type);
@@ -735,6 +749,7 @@ DefineDomain(CreateDomainStmt *stmt)
Oid receiveProcedure;
Oid sendProcedure;
Oid analyzeProcedure;
+ Oid subscriptingProcedure;
bool byValue;
char category;
char delimiter;
@@ -858,6 +873,9 @@ DefineDomain(CreateDomainStmt *stmt)
/* Analysis function */
analyzeProcedure = baseType->typanalyze;
+ /* Subscripting function */
+ subscriptingProcedure = baseType->typsubscripting;
+
/* Inherited default value */
datum = SysCacheGetAttr(TYPEOID, typeTup,
Anum_pg_type_typdefault, &isnull);
@@ -1059,7 +1077,8 @@ DefineDomain(CreateDomainStmt *stmt)
basetypeMod, /* typeMod value */
typNDims, /* Array dimensions for base type */
typNotNull, /* Type NOT NULL */
- domaincoll); /* type's collation */
+ domaincoll, /* type's collation */
+ subscriptingProcedure); /* subscripting procedure */
/*
* Process constraints which refer to the domain ID returned by TypeCreate
@@ -1171,7 +1190,8 @@ DefineEnum(CreateEnumStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* type's collation */
+ InvalidOid, /* type's collation */
+ InvalidOid); /* typsubscripting - none */
/* Enter the enum's values into pg_enum */
EnumValuesCreate(enumTypeAddr.objectId, stmt->vals);
@@ -1211,7 +1231,8 @@ DefineEnum(CreateEnumStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* type's collation */
+ InvalidOid, /* type's collation */
+ F_ARRAY_SUBSCRIPTING); /* array subscripting implementation */
pfree(enumArrayName);
@@ -1499,7 +1520,8 @@ DefineRange(CreateRangeStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* type's collation (ranges never have one) */
+ InvalidOid, /* type's collation (ranges never have one) */
+ InvalidOid); /* typsubscripting - none */
Assert(typoid == address.objectId);
/* Create the entry in pg_range */
@@ -1541,7 +1563,8 @@ DefineRange(CreateRangeStmt *stmt)
-1, /* typMod (Domains only) */
0, /* Array dimensions of typbasetype */
false, /* Type NOT NULL */
- InvalidOid); /* typcollation */
+ InvalidOid, /* typcollation */
+ F_ARRAY_SUBSCRIPTING); /* array subscripting implementation */
pfree(rangeArrayName);
@@ -1885,6 +1908,33 @@ findTypeAnalyzeFunction(List *procname, Oid typeOid)
return procOid;
}
+static Oid
+findTypeSubscriptingFunction(List *procname)
+{
+ Oid argList[1];
+ Oid procOid;
+
+ /*
+ * Subscripting functions always take one INTERNAL argument and return INTERNAL.
+ */
+ argList[0] = INTERNALOID;
+
+ procOid = LookupFuncName(procname, 1, argList, true);
+ if (!OidIsValid(procOid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("function %s does not exist",
+ func_signature_string(procname, 1, NIL, argList))));
+
+ if (get_func_rettype(procOid) != INTERNALOID)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+ errmsg("type subscripting function %s must return type %s",
+ NameListToString(procname), "internal")));
+
+ return procOid;
+}
+
/*
* Find suitable support functions and opclasses for a range type.
*/
@@ -2240,6 +2290,7 @@ AlterDomainDefault(List *names, Node *defaultRaw)
false, /* a domain isn't an implicit array */
typTup->typbasetype,
typTup->typcollation,
+ typTup->typsubscripting,
defaultExpr,
true); /* Rebuild is true */
diff --git a/src/backend/executor/execQual.c b/src/backend/executor/execQual.c
index ec1ca01..29611c0 100644
--- a/src/backend/executor/execQual.c
+++ b/src/backend/executor/execQual.c
@@ -55,6 +55,7 @@
#include "utils/builtins.h"
#include "utils/date.h"
#include "utils/lsyscache.h"
+#include "utils/syscache.h"
#include "utils/memutils.h"
#include "utils/timestamp.h"
#include "utils/typcache.h"
@@ -62,10 +63,9 @@
/* static function decls */
-static Datum ExecEvalArrayRef(ArrayRefExprState *astate,
+static Datum ExecEvalSubscriptingRef(SubscriptingRefExprState *astate,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
-static bool isAssignmentIndirectionExpr(ExprState *exprstate);
static Datum ExecEvalAggref(AggrefExprState *aggref,
ExprContext *econtext,
bool *isNull, ExprDoneCond *isDone);
@@ -251,39 +251,48 @@ static Datum ExecEvalGroupingFuncExpr(GroupingFuncExprState *gstate,
/*----------
- * ExecEvalArrayRef
+ * ExecEvalSubscriptingRef
*
- * This function takes an ArrayRef and returns the extracted Datum
- * if it's a simple reference, or the modified array value if it's
- * an array assignment (i.e., array element or slice insertion).
+ * This function takes a SubscriptingRef, extracts all information required
+ * for subscripting and pass it to a particular subscripting procedure,
+ * specified for this data type. As a result the extracted Datum will be
+ * returned if it's a simple reference, or the modified containers value if
+ * it's an containers assignment (i.e., containers element or slice
+ * insertion).
*
* NOTE: if we get a NULL result from a subscript expression, we return NULL
- * when it's an array reference, or raise an error when it's an assignment.
+ * when it's an containers reference, or raise an error when it's an assignment.
*----------
*/
+
static Datum
-ExecEvalArrayRef(ArrayRefExprState *astate,
- ExprContext *econtext,
- bool *isNull,
- ExprDoneCond *isDone)
+ExecEvalSubscriptingRef(SubscriptingRefExprState *sbstate, ExprContext *econtext,
+ bool *isNull, ExprDoneCond *isDone)
{
- ArrayRef *arrayRef = (ArrayRef *) astate->xprstate.expr;
- Datum array_source;
- bool isAssignment = (arrayRef->refassgnexpr != NULL);
- bool eisnull;
- ListCell *l;
- int i = 0,
- j = 0;
- IntArray upper,
- lower;
- bool upperProvided[MAXDIM],
- lowerProvided[MAXDIM];
- int *lIndex;
-
- array_source = ExecEvalExpr(astate->refexpr,
- econtext,
- isNull,
- isDone);
+ SubscriptingRef *sbsRef = (SubscriptingRef *) sbstate->xprstate.expr;
+ Oid containerType;
+ RegProcedure typsubscripting;
+ bool isAssignment = (sbsRef->refassgnexpr != NULL);
+ bool eisnull;
+ Datum *upper = NULL,
+ *lower = NULL;
+ ListCell *l;
+ int i = 0,
+ j = 0;
+ SubscriptingExecData sbsdata;
+ bool upperProvided[MAXDIM],
+ lowerProvided[MAXDIM];
+
+ if (sbstate->refupperindexpr != NULL)
+ upper = (Datum *) palloc(sbstate->refupperindexpr->length * sizeof(Datum *));
+
+ if (sbstate->reflowerindexpr != NULL)
+ lower = (Datum *) palloc(sbstate->reflowerindexpr->length * sizeof(Datum *));
+
+ sbsdata.xprcontext = econtext;
+ sbsdata.isNull = isNull;
+ sbsdata.containerSource = ExecEvalExpr(sbstate->refexpr, econtext,
+ isNull, isDone);
/*
* If refexpr yields NULL, and it's a fetch, then result is NULL. In the
@@ -297,51 +306,49 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
return (Datum) NULL;
}
- foreach(l, astate->refupperindexpr)
+ foreach(l, sbstate->refupperindexpr)
{
ExprState *eltstate = (ExprState *) lfirst(l);
if (i >= MAXDIM)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ errmsg("number of container dimensions (%d) exceeds the maximum allowed (%d)",
i + 1, MAXDIM)));
if (eltstate == NULL)
{
- /* Slice bound is omitted, so use array's upper bound */
- Assert(astate->reflowerindexpr != NIL);
+ /* Slice bound is omitted, so use containers's upper bound */
+ Assert(sbstate->reflowerindexpr != NIL);
upperProvided[i++] = false;
continue;
}
+
upperProvided[i] = true;
+ upper[i++] = ExecEvalExpr(eltstate, econtext, &eisnull, NULL);
- upper.indx[i++] = DatumGetInt32(ExecEvalExpr(eltstate,
- econtext,
- &eisnull,
- NULL));
/* If any index expr yields NULL, result is NULL or error */
if (eisnull)
{
if (isAssignment)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
- errmsg("array subscript in assignment must not be null")));
+ errmsg("container subscript in assignment must not be null")));
*isNull = true;
return (Datum) NULL;
}
}
- if (astate->reflowerindexpr != NIL)
+ if (sbstate->reflowerindexpr != NIL)
{
- foreach(l, astate->reflowerindexpr)
+ foreach(l, sbstate->reflowerindexpr)
{
ExprState *eltstate = (ExprState *) lfirst(l);
if (j >= MAXDIM)
ereport(ERROR,
(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
- errmsg("number of array dimensions (%d) exceeds the maximum allowed (%d)",
+ errmsg("number of container dimensions (%d) exceeds the maximum allowed (%d)",
j + 1, MAXDIM)));
if (eltstate == NULL)
@@ -350,191 +357,54 @@ ExecEvalArrayRef(ArrayRefExprState *astate,
lowerProvided[j++] = false;
continue;
}
+
lowerProvided[j] = true;
+ lower[j++] = ExecEvalExpr(eltstate, econtext, &eisnull, NULL);
- lower.indx[j++] = DatumGetInt32(ExecEvalExpr(eltstate,
- econtext,
- &eisnull,
- NULL));
/* If any index expr yields NULL, result is NULL or error */
if (eisnull)
{
if (isAssignment)
ereport(ERROR,
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
- errmsg("array subscript in assignment must not be null")));
+ errmsg("container subscript in assignment must not be null")));
*isNull = true;
return (Datum) NULL;
}
}
+
/* this can't happen unless parser messed up */
if (i != j)
elog(ERROR, "upper and lower index lists are not same length");
- lIndex = lower.indx;
}
- else
- lIndex = NULL;
-
- if (isAssignment)
- {
- Datum sourceData;
- Datum save_datum;
- bool save_isNull;
-
- /*
- * We might have a nested-assignment situation, in which the
- * refassgnexpr is itself a FieldStore or ArrayRef that needs to
- * obtain and modify the previous value of the array element or slice
- * being replaced. If so, we have to extract that value from the
- * array and pass it down via the econtext's caseValue. It's safe to
- * reuse the CASE mechanism because there cannot be a CASE between
- * here and where the value would be needed, and an array assignment
- * can't be within a CASE either. (So saving and restoring the
- * caseValue is just paranoia, but let's do it anyway.)
- *
- * Since fetching the old element might be a nontrivial expense, do it
- * only if the argument appears to actually need it.
- */
- save_datum = econtext->caseValue_datum;
- save_isNull = econtext->caseValue_isNull;
-
- if (isAssignmentIndirectionExpr(astate->refassgnexpr))
- {
- if (*isNull)
- {
- /* whole array is null, so any element or slice is too */
- econtext->caseValue_datum = (Datum) 0;
- econtext->caseValue_isNull = true;
- }
- else if (lIndex == NULL)
- {
- econtext->caseValue_datum =
- array_get_element(array_source, i,
- upper.indx,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign,
- &econtext->caseValue_isNull);
- }
- else
- {
- econtext->caseValue_datum =
- array_get_slice(array_source, i,
- upper.indx, lower.indx,
- upperProvided, lowerProvided,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
- econtext->caseValue_isNull = false;
- }
- }
- else
- {
- /* argument shouldn't need caseValue, but for safety set it null */
- econtext->caseValue_datum = (Datum) 0;
- econtext->caseValue_isNull = true;
- }
-
- /*
- * Evaluate the value to be assigned into the array.
- */
- sourceData = ExecEvalExpr(astate->refassgnexpr,
- econtext,
- &eisnull,
- NULL);
-
- econtext->caseValue_datum = save_datum;
- econtext->caseValue_isNull = save_isNull;
- /*
- * For an assignment to a fixed-length array type, both the original
- * array and the value to be assigned into it must be non-NULL, else
- * we punt and return the original array.
- */
- if (astate->refattrlength > 0) /* fixed-length array? */
- if (eisnull || *isNull)
- return array_source;
-
- /*
- * For assignment to varlena arrays, we handle a NULL original array
- * by substituting an empty (zero-dimensional) array; insertion of the
- * new element will result in a singleton array value. It does not
- * matter whether the new element is NULL.
- */
- if (*isNull)
- {
- array_source = PointerGetDatum(construct_empty_array(arrayRef->refelemtype));
- *isNull = false;
- }
+ sbsdata.upper = upper;
+ sbsdata.upperProvided = upperProvided;
+ sbsdata.lower = lower;
+ sbsdata.lowerProvided = lowerProvided;
+ sbsdata.indexprNumber = i;
- if (lIndex == NULL)
- return array_set_element(array_source, i,
- upper.indx,
- sourceData,
- eisnull,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
- else
- return array_set_slice(array_source, i,
- upper.indx, lower.indx,
- upperProvided, lowerProvided,
- sourceData,
- eisnull,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
- }
+ if (!OidIsValid(sbstate->fcache.fn_oid))
+ {
+ FmgrInfo flinfo;
- if (lIndex == NULL)
- return array_get_element(array_source, i,
- upper.indx,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign,
- isNull);
- else
- return array_get_slice(array_source, i,
- upper.indx, lower.indx,
- upperProvided, lowerProvided,
- astate->refattrlength,
- astate->refelemlength,
- astate->refelembyval,
- astate->refelemalign);
-}
+ containerType = getBaseTypeAndTypmod(sbsRef->refcontainertype,
+ &sbsRef->reftypmod);
+ typsubscripting = get_typsubscripting(containerType);
-/*
- * Helper for ExecEvalArrayRef: is expr a nested FieldStore or ArrayRef
- * that might need the old element value passed down?
- *
- * (We could use this in ExecEvalFieldStore too, but in that case passing
- * the old value is so cheap there's no need.)
- */
-static bool
-isAssignmentIndirectionExpr(ExprState *exprstate)
-{
- if (exprstate == NULL)
- return false; /* just paranoia */
- if (IsA(exprstate, FieldStoreState))
- {
- FieldStore *fstore = (FieldStore *) exprstate->expr;
+ if (!OidIsValid(typsubscripting))
+ /* this can't happen */
+ elog(ERROR, "can not find subscripting procedure for type %s",
+ format_type_be(containerType));
- if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
- return true;
+ fmgr_info(typsubscripting, &flinfo);
+ sbstate->fcache = flinfo;
}
- else if (IsA(exprstate, ArrayRefExprState))
- {
- ArrayRef *arrayRef = (ArrayRef *) exprstate->expr;
- if (arrayRef->refexpr && IsA(arrayRef->refexpr, CaseTestExpr))
- return true;
- }
- return false;
+ return FunctionCall3(&sbstate->fcache,
+ Int32GetDatum(SBS_EXEC),
+ PointerGetDatum(sbstate),
+ PointerGetDatum(&sbsdata));
}
/* ----------------------------------------------------------------
@@ -4345,7 +4215,7 @@ ExecEvalFieldStore(FieldStoreState *fstate,
/*
* Use the CaseTestExpr mechanism to pass down the old value of the
* field being replaced; this is needed in case the newval is itself a
- * FieldStore or ArrayRef that has to obtain and modify the old value.
+ * FieldStore or SubscriptingRef that has to obtain and modify the old value.
* It's safe to reuse the CASE mechanism because there cannot be a
* CASE between here and where the value would be needed, and a field
* assignment can't be within a CASE either. (So saving and restoring
@@ -4708,25 +4578,21 @@ ExecInitExpr(Expr *node, PlanState *parent)
state = (ExprState *) wfstate;
}
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *aref = (ArrayRef *) node;
- ArrayRefExprState *astate = makeNode(ArrayRefExprState);
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
+ SubscriptingRefExprState *astate = makeNode(SubscriptingRefExprState);
- astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalArrayRef;
+ astate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalSubscriptingRef;
astate->refupperindexpr = (List *)
- ExecInitExpr((Expr *) aref->refupperindexpr, parent);
+ ExecInitExpr((Expr *) sbsref->refupperindexpr, parent);
astate->reflowerindexpr = (List *)
- ExecInitExpr((Expr *) aref->reflowerindexpr, parent);
- astate->refexpr = ExecInitExpr(aref->refexpr, parent);
- astate->refassgnexpr = ExecInitExpr(aref->refassgnexpr,
+ ExecInitExpr((Expr *) sbsref->reflowerindexpr, parent);
+ astate->refexpr = ExecInitExpr(sbsref->refexpr, parent);
+ astate->refassgnexpr = ExecInitExpr(sbsref->refassgnexpr,
parent);
/* do one-time catalog lookups for type info */
- astate->refattrlength = get_typlen(aref->refarraytype);
- get_typlenbyvalalign(aref->refelemtype,
- &astate->refelemlength,
- &astate->refelembyval,
- &astate->refelemalign);
+ astate->refattrlength = get_typlen(sbsref->refcontainertype);
state = (ExprState *) astate;
}
break;
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index 6955298..841d6a7 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -1292,14 +1292,14 @@ _copyWindowFunc(const WindowFunc *from)
}
/*
- * _copyArrayRef
+ * _copySubscriptingRef
*/
-static ArrayRef *
-_copyArrayRef(const ArrayRef *from)
+static SubscriptingRef *
+_copySubscriptingRef(const SubscriptingRef *from)
{
- ArrayRef *newnode = makeNode(ArrayRef);
+ SubscriptingRef *newnode = makeNode(SubscriptingRef);
- COPY_SCALAR_FIELD(refarraytype);
+ COPY_SCALAR_FIELD(refcontainertype);
COPY_SCALAR_FIELD(refelemtype);
COPY_SCALAR_FIELD(reftypmod);
COPY_SCALAR_FIELD(refcollid);
@@ -4545,8 +4545,8 @@ copyObject(const void *from)
case T_WindowFunc:
retval = _copyWindowFunc(from);
break;
- case T_ArrayRef:
- retval = _copyArrayRef(from);
+ case T_SubscriptingRef:
+ retval = _copySubscriptingRef(from);
break;
case T_FuncExpr:
retval = _copyFuncExpr(from);
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 548a2aa..5a6d8b7 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -244,9 +244,9 @@ _equalWindowFunc(const WindowFunc *a, const WindowFunc *b)
}
static bool
-_equalArrayRef(const ArrayRef *a, const ArrayRef *b)
+_equalSubscriptingRef(const SubscriptingRef *a, const SubscriptingRef *b)
{
- COMPARE_SCALAR_FIELD(refarraytype);
+ COMPARE_SCALAR_FIELD(refcontainertype);
COMPARE_SCALAR_FIELD(refelemtype);
COMPARE_SCALAR_FIELD(reftypmod);
COMPARE_SCALAR_FIELD(refcollid);
@@ -2845,8 +2845,8 @@ equal(const void *a, const void *b)
case T_WindowFunc:
retval = _equalWindowFunc(a, b);
break;
- case T_ArrayRef:
- retval = _equalArrayRef(a, b);
+ case T_SubscriptingRef:
+ retval = _equalSubscriptingRef(a, b);
break;
case T_FuncExpr:
retval = _equalFuncExpr(a, b);
diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c
index 973fb15..c4ca3a0 100644
--- a/src/backend/nodes/nodeFuncs.c
+++ b/src/backend/nodes/nodeFuncs.c
@@ -66,13 +66,13 @@ exprType(const Node *expr)
case T_WindowFunc:
type = ((const WindowFunc *) expr)->wintype;
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- const ArrayRef *arrayref = (const ArrayRef *) expr;
+ const SubscriptingRef *arrayref = (const SubscriptingRef *) expr;
/* slice and/or store operations yield the array type */
if (arrayref->reflowerindexpr || arrayref->refassgnexpr)
- type = arrayref->refarraytype;
+ type = arrayref->refcontainertype;
else
type = arrayref->refelemtype;
}
@@ -284,9 +284,9 @@ exprTypmod(const Node *expr)
return ((const Const *) expr)->consttypmod;
case T_Param:
return ((const Param *) expr)->paramtypmod;
- case T_ArrayRef:
+ case T_SubscriptingRef:
/* typmod is the same for array or element */
- return ((const ArrayRef *) expr)->reftypmod;
+ return ((const SubscriptingRef *) expr)->reftypmod;
case T_FuncExpr:
{
int32 coercedTypmod;
@@ -772,8 +772,8 @@ exprCollation(const Node *expr)
case T_WindowFunc:
coll = ((const WindowFunc *) expr)->wincollid;
break;
- case T_ArrayRef:
- coll = ((const ArrayRef *) expr)->refcollid;
+ case T_SubscriptingRef:
+ coll = ((const SubscriptingRef *) expr)->refcollid;
break;
case T_FuncExpr:
coll = ((const FuncExpr *) expr)->funccollid;
@@ -1014,8 +1014,8 @@ exprSetCollation(Node *expr, Oid collation)
case T_WindowFunc:
((WindowFunc *) expr)->wincollid = collation;
break;
- case T_ArrayRef:
- ((ArrayRef *) expr)->refcollid = collation;
+ case T_SubscriptingRef:
+ ((SubscriptingRef *) expr)->refcollid = collation;
break;
case T_FuncExpr:
((FuncExpr *) expr)->funccollid = collation;
@@ -1237,9 +1237,9 @@ exprLocation(const Node *expr)
/* function name should always be the first thing */
loc = ((const WindowFunc *) expr)->location;
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
/* just use array argument's location */
- loc = exprLocation((Node *) ((const ArrayRef *) expr)->refexpr);
+ loc = exprLocation((Node *) ((const SubscriptingRef *) expr)->refexpr);
break;
case T_FuncExpr:
{
@@ -1932,21 +1932,21 @@ expression_tree_walker(Node *node,
return true;
}
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
/* recurse directly for upper/lower array index lists */
- if (expression_tree_walker((Node *) aref->refupperindexpr,
+ if (expression_tree_walker((Node *) sbsref->refupperindexpr,
walker, context))
return true;
- if (expression_tree_walker((Node *) aref->reflowerindexpr,
+ if (expression_tree_walker((Node *) sbsref->reflowerindexpr,
walker, context))
return true;
/* walker must see the refexpr and refassgnexpr, however */
- if (walker(aref->refexpr, context))
+ if (walker(sbsref->refexpr, context))
return true;
- if (walker(aref->refassgnexpr, context))
+ if (walker(sbsref->refassgnexpr, context))
return true;
}
break;
@@ -2521,12 +2521,12 @@ expression_tree_mutator(Node *node,
return (Node *) newnode;
}
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *arrayref = (ArrayRef *) node;
- ArrayRef *newnode;
+ SubscriptingRef *arrayref = (SubscriptingRef *) node;
+ SubscriptingRef *newnode;
- FLATCOPY(newnode, arrayref, ArrayRef);
+ FLATCOPY(newnode, arrayref, SubscriptingRef);
MUTATE(newnode->refupperindexpr, arrayref->refupperindexpr,
List *);
MUTATE(newnode->reflowerindexpr, arrayref->reflowerindexpr,
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 9fe9873..642d0b1 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -1062,11 +1062,11 @@ _outWindowFunc(StringInfo str, const WindowFunc *node)
}
static void
-_outArrayRef(StringInfo str, const ArrayRef *node)
+_outSubscriptingRef(StringInfo str, const SubscriptingRef *node)
{
- WRITE_NODE_TYPE("ARRAYREF");
+ WRITE_NODE_TYPE("SUBSCRIPTINGREF");
- WRITE_OID_FIELD(refarraytype);
+ WRITE_OID_FIELD(refcontainertype);
WRITE_OID_FIELD(refelemtype);
WRITE_INT_FIELD(reftypmod);
WRITE_OID_FIELD(refcollid);
@@ -3500,8 +3500,8 @@ outNode(StringInfo str, const void *obj)
case T_WindowFunc:
_outWindowFunc(str, obj);
break;
- case T_ArrayRef:
- _outArrayRef(str, obj);
+ case T_SubscriptingRef:
+ _outSubscriptingRef(str, obj);
break;
case T_FuncExpr:
_outFuncExpr(str, obj);
diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c
index 63f6336..afdde4a 100644
--- a/src/backend/nodes/readfuncs.c
+++ b/src/backend/nodes/readfuncs.c
@@ -606,14 +606,14 @@ _readWindowFunc(void)
}
/*
- * _readArrayRef
+ * _readSubscriptingRef
*/
-static ArrayRef *
-_readArrayRef(void)
+static SubscriptingRef *
+_readSubscriptingRef(void)
{
- READ_LOCALS(ArrayRef);
+ READ_LOCALS(SubscriptingRef);
- READ_OID_FIELD(refarraytype);
+ READ_OID_FIELD(refcontainertype);
READ_OID_FIELD(refelemtype);
READ_INT_FIELD(reftypmod);
READ_OID_FIELD(refcollid);
@@ -2351,8 +2351,8 @@ parseNodeString(void)
return_value = _readGroupingFunc();
else if (MATCH("WINDOWFUNC", 10))
return_value = _readWindowFunc();
- else if (MATCH("ARRAYREF", 8))
- return_value = _readArrayRef();
+ else if (MATCH("SUBSCRIPTINGREF", 15))
+ return_value = _readSubscriptingRef();
else if (MATCH("FUNCEXPR", 8))
return_value = _readFuncExpr();
else if (MATCH("NAMEDARGEXPR", 12))
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 9af29dd..d37f47d 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -1345,10 +1345,10 @@ contain_nonstrict_functions_walker(Node *node, void *context)
/* a window function could return non-null with null input */
return true;
}
- if (IsA(node, ArrayRef))
+ if (IsA(node, SubscriptingRef))
{
/* array assignment is nonstrict, but subscripting is strict */
- if (((ArrayRef *) node)->refassgnexpr != NULL)
+ if (((SubscriptingRef *) node)->refassgnexpr != NULL)
return true;
/* else fall through to check args */
}
@@ -1528,7 +1528,7 @@ contain_leaked_vars_walker(Node *node, void *context)
case T_Var:
case T_Const:
case T_Param:
- case T_ArrayRef:
+ case T_SubscriptingRef:
case T_ArrayExpr:
case T_FieldSelect:
case T_FieldStore:
@@ -3606,7 +3606,7 @@ eval_const_expressions_mutator(Node *node,
* For any node type not handled above, we recurse using
* expression_tree_mutator, which will copy the node unchanged but try to
* simplify its arguments (if any) using this routine. For example: we
- * cannot eliminate an ArrayRef node, but we might be able to simplify
+ * cannot eliminate an SubscriptingRef node, but we might be able to simplify
* constant expressions in its subscripts.
*/
return expression_tree_mutator(node, eval_const_expressions_mutator,
diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c
index a558083..b29d38a 100644
--- a/src/backend/parser/analyze.c
+++ b/src/backend/parser/analyze.c
@@ -944,13 +944,13 @@ transformInsertRow(ParseState *pstate, List *exprlist,
expr = (Expr *) linitial(fstore->newvals);
}
- else if (IsA(expr, ArrayRef))
+ else if (IsA(expr, SubscriptingRef))
{
- ArrayRef *aref = (ArrayRef *) expr;
+ SubscriptingRef *sbsref = (SubscriptingRef *) expr;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
break;
- expr = aref->refassgnexpr;
+ expr = sbsref->refassgnexpr;
}
else
break;
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 8a2bdf0..1a52a31 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -472,13 +472,13 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
/* process subscripts before this field selection */
if (subscripts)
- result = (Node *) transformArraySubscripts(pstate,
- result,
- exprType(result),
- InvalidOid,
- exprTypmod(result),
- subscripts,
- NULL);
+ result = (Node *) transformContainerSubscripts(pstate,
+ result,
+ exprType(result),
+ InvalidOid,
+ exprTypmod(result),
+ subscripts,
+ NULL);
subscripts = NIL;
newresult = ParseFuncOrColumn(pstate,
@@ -493,13 +493,13 @@ transformIndirection(ParseState *pstate, Node *basenode, List *indirection)
}
/* process trailing subscripts, if any */
if (subscripts)
- result = (Node *) transformArraySubscripts(pstate,
- result,
- exprType(result),
- InvalidOid,
- exprTypmod(result),
- subscripts,
- NULL);
+ result = (Node *) transformContainerSubscripts(pstate,
+ result,
+ exprType(result),
+ InvalidOid,
+ exprTypmod(result),
+ subscripts,
+ NULL);
return result;
}
diff --git a/src/backend/parser/parse_node.c b/src/backend/parser/parse_node.c
index 62d2f71..d213a1c 100644
--- a/src/backend/parser/parse_node.c
+++ b/src/backend/parser/parse_node.c
@@ -201,18 +201,22 @@ make_var(ParseState *pstate, RangeTblEntry *rte, int attrno, int location)
/*
* transformArrayType()
- * Identify the types involved in a subscripting operation
+ * Identify the types involved in a subscripting operation for array
*
* On entry, arrayType/arrayTypmod identify the type of the input value
* to be subscripted (which could be a domain type). These are modified
* if necessary to identify the actual array type and typmod, and the
* array's element type is returned. An error is thrown if the input isn't
* an array type.
+ *
+ * NOTE: This part of type-specific code is not separated into type-specific
+ * subscripting procedure for now, but it does not affect on the whole logic,
+ * since InvalidOid will be return in case of other types not an error.
+ * An error will appears only if a subscripting procedure is not defined.
*/
Oid
-transformArrayType(Oid *arrayType, int32 *arrayTypmod)
+transformArrayType(Oid *containerType, int32 *containerTypmod)
{
- Oid origArrayType = *arrayType;
Oid elementType;
HeapTuple type_tuple_array;
Form_pg_type type_struct_array;
@@ -224,7 +228,7 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
* itself. (Note that we provide no method whereby the creator of a
* domain over an array type could hide its ability to be subscripted.)
*/
- *arrayType = getBaseTypeAndTypmod(*arrayType, arrayTypmod);
+ *containerType = getBaseTypeAndTypmod(*containerType, containerTypmod);
/*
* We treat int2vector and oidvector as though they were domains over
@@ -233,25 +237,20 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
* xxxvector type; so we want the result of a slice operation to be
* considered to be of the more general type.
*/
- if (*arrayType == INT2VECTOROID)
- *arrayType = INT2ARRAYOID;
- else if (*arrayType == OIDVECTOROID)
- *arrayType = OIDARRAYOID;
+ if (*containerType == INT2VECTOROID)
+ *containerType = INT2ARRAYOID;
+ else if (*containerType == OIDVECTOROID)
+ *containerType = OIDARRAYOID;
/* Get the type tuple for the array */
- type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*arrayType));
+ type_tuple_array = SearchSysCache1(TYPEOID, ObjectIdGetDatum(*containerType));
if (!HeapTupleIsValid(type_tuple_array))
- elog(ERROR, "cache lookup failed for type %u", *arrayType);
+ elog(ERROR, "cache lookup failed for type %u", *containerType);
type_struct_array = (Form_pg_type) GETSTRUCT(type_tuple_array);
/* needn't check typisdefined since this will fail anyway */
elementType = type_struct_array->typelem;
- if (elementType == InvalidOid)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("cannot subscript type %s because it is not an array",
- format_type_be(origArrayType))));
ReleaseSysCache(type_tuple_array);
@@ -259,61 +258,80 @@ transformArrayType(Oid *arrayType, int32 *arrayTypmod)
}
/*
- * transformArraySubscripts()
- * Transform array subscripting. This is used for both
- * array fetch and array assignment.
+ * transformContainerSubscripts()
+ * Transform container subscripting. This is used for both
+ * container fetch and container assignment.
*
- * In an array fetch, we are given a source array value and we produce an
- * expression that represents the result of extracting a single array element
- * or an array slice.
+ * In a container fetch, we are given a source container value and we produce
+ * an expression that represents the result of extracting a single container
+ * element or a container slice.
*
- * In an array assignment, we are given a destination array value plus a
- * source value that is to be assigned to a single element or a slice of
- * that array. We produce an expression that represents the new array value
- * with the source data inserted into the right part of the array.
+ * In a container assignment, we are given a destination container value plus a
+ * source value that is to be assigned to a single element or a slice of that
+ * container. We produce an expression that represents the new container value
+ * with the source data inserted into the right part of the container.
*
- * For both cases, if the source array is of a domain-over-array type,
- * the result is of the base array type or its element type; essentially,
- * we must fold a domain to its base type before applying subscripting.
- * (Note that int2vector and oidvector are treated as domains here.)
+ * For both cases, this function contains only general subscripting logic while
+ * type-specific logic (e.g. type verifications and coersion) is placend in
+ * separate procedure indicated by typsubscripting. There is only one exception
+ * for now about domain-over-container, if the source container is of a
+ * domain-over-container type, the result is of the base container type or its
+ * element type; essentially, we must fold a domain to its base type before
+ * applying subscripting. (Note that int2vector and oidvector are treated as
+ * domains here.) If domain verification failed we assume, that element type
+ * must be the same as container type (e.g. in case of jsonb).
+ * An error will appear in case if current container type doesn't have a
+ * subscripting procedure.
*
- * pstate Parse state
- * arrayBase Already-transformed expression for the array as a whole
- * arrayType OID of array's datatype (should match type of arrayBase,
- * or be the base type of arrayBase's domain type)
- * elementType OID of array's element type (fetch with transformArrayType,
- * or pass InvalidOid to do it here)
- * arrayTypMod typmod for the array (which is also typmod for the elements)
- * indirection Untransformed list of subscripts (must not be NIL)
- * assignFrom NULL for array fetch, else transformed expression for source.
+ * pstate Parse state
+ * containerBase Already-transformed expression for the container as a whole
+ * containerType OID of container's datatype (should match type of containerBase,
+ * or be the base type of containerBase's domain type)
+ * elementType OID of container's element type (fetch with transformArrayType,
+ * or pass InvalidOid to do it here)
+ * containerTypMod typmod for the container (which is also typmod for the elements)
+ * indirection Untransformed list of subscripts (must not be NIL)
+ * assignFrom NULL for container fetch, else transformed expression for source.
*/
-ArrayRef *
-transformArraySubscripts(ParseState *pstate,
- Node *arrayBase,
- Oid arrayType,
- Oid elementType,
- int32 arrayTypMod,
- List *indirection,
- Node *assignFrom)
+
+SubscriptingRef *
+transformContainerSubscripts(ParseState *pstate,
+ Node *containerBase,
+ Oid containerType,
+ Oid elementType,
+ int32 containerTypMod,
+ List *indirection,
+ Node *assignFrom)
{
- bool isSlice = false;
- List *upperIndexpr = NIL;
- List *lowerIndexpr = NIL;
- ListCell *idx;
- ArrayRef *aref;
+ bool isSlice = false;
+ List *upperIndexpr = NIL;
+ List *lowerIndexpr = NIL;
+ ListCell *idx;
+ SubscriptingRef *sbsref,
+ *prepared_sbsref;
+ RegProcedure typsubscripting = get_typsubscripting(containerType);
+
+ if (!OidIsValid(typsubscripting))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("cannot subscript type %s because it does not support subscripting",
+ format_type_be(containerType))));
/*
* Caller may or may not have bothered to determine elementType. Note
- * that if the caller did do so, arrayType/arrayTypMod must be as modified
+ * that if the caller did do so, containerType/containerTypMod must be as modified
* by transformArrayType, ie, smash domain to base type.
*/
if (!OidIsValid(elementType))
- elementType = transformArrayType(&arrayType, &arrayTypMod);
+ elementType = transformArrayType(&containerType, &containerTypMod);
+
+ if (!OidIsValid(elementType))
+ elementType = containerType;
/*
- * A list containing only simple subscripts refers to a single array
+ * A list containing only simple subscripts refers to a single container
* element. If any of the items are slice specifiers (lower:upper), then
- * the subscript expression means an array slice operation. In this case,
+ * the subscript expression means an container slice operation. In this case,
* we convert any non-slice items to slices by treating the single
* subscript as the upper bound and supplying an assumed lower bound of 1.
* We have to prescan the list to see if there are any slice items.
@@ -343,107 +361,37 @@ transformArraySubscripts(ParseState *pstate,
if (ai->lidx)
{
subexpr = transformExpr(pstate, ai->lidx, pstate->p_expr_kind);
- /* If it's not int4 already, try to coerce */
- subexpr = coerce_to_target_type(pstate,
- subexpr, exprType(subexpr),
- INT4OID, -1,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (subexpr == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array subscript must have type integer"),
- parser_errposition(pstate, exprLocation(ai->lidx))));
- }
- else if (!ai->is_slice)
- {
- /* Make a constant 1 */
- subexpr = (Node *) makeConst(INT4OID,
- -1,
- InvalidOid,
- sizeof(int32),
- Int32GetDatum(1),
- false,
- true); /* pass by value */
}
else
{
/* Slice with omitted lower bound, put NULL into the list */
subexpr = NULL;
}
- lowerIndexpr = lappend(lowerIndexpr, subexpr);
- }
- else
- Assert(ai->lidx == NULL && !ai->is_slice);
-
- if (ai->uidx)
- {
- subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
- /* If it's not int4 already, try to coerce */
- subexpr = coerce_to_target_type(pstate,
- subexpr, exprType(subexpr),
- INT4OID, -1,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (subexpr == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array subscript must have type integer"),
- parser_errposition(pstate, exprLocation(ai->uidx))));
- }
- else
- {
- /* Slice with omitted upper bound, put NULL into the list */
- Assert(isSlice && ai->is_slice);
- subexpr = NULL;
+ lowerIndexpr = lappend(lowerIndexpr, list_make2(subexpr, ai));
}
+ subexpr = transformExpr(pstate, ai->uidx, pstate->p_expr_kind);
upperIndexpr = lappend(upperIndexpr, subexpr);
}
/*
- * If doing an array store, coerce the source value to the right type.
- * (This should agree with the coercion done by transformAssignedExpr.)
- */
- if (assignFrom != NULL)
- {
- Oid typesource = exprType(assignFrom);
- Oid typeneeded = isSlice ? arrayType : elementType;
- Node *newFrom;
-
- newFrom = coerce_to_target_type(pstate,
- assignFrom, typesource,
- typeneeded, arrayTypMod,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
- if (newFrom == NULL)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array assignment requires type %s"
- " but expression is of type %s",
- format_type_be(typeneeded),
- format_type_be(typesource)),
- errhint("You will need to rewrite or cast the expression."),
- parser_errposition(pstate, exprLocation(assignFrom))));
- assignFrom = newFrom;
- }
-
- /*
- * Ready to build the ArrayRef node.
+ * Ready to build the SubscriptingRef node.
*/
- aref = makeNode(ArrayRef);
- aref->refarraytype = arrayType;
- aref->refelemtype = elementType;
- aref->reftypmod = arrayTypMod;
+ sbsref = makeNode(SubscriptingRef);
+ sbsref->refcontainertype = containerType;
+ sbsref->refelemtype = elementType;
+ sbsref->reftypmod = containerTypMod;
/* refcollid will be set by parse_collate.c */
- aref->refupperindexpr = upperIndexpr;
- aref->reflowerindexpr = lowerIndexpr;
- aref->refexpr = (Expr *) arrayBase;
- aref->refassgnexpr = (Expr *) assignFrom;
+ sbsref->refupperindexpr = upperIndexpr;
+ sbsref->reflowerindexpr = lowerIndexpr;
+ sbsref->refexpr = (Expr *) containerBase;
+ sbsref->refassgnexpr = (Expr *) assignFrom;
+
+ prepared_sbsref = (SubscriptingRef *) OidFunctionCall3(typsubscripting,
+ Int32GetDatum(SBS_VALIDATION),
+ PointerGetDatum(sbsref),
+ PointerGetDatum(pstate));
- return aref;
+ return prepared_sbsref;
}
/*
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index d440dec..b829e2a 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -790,41 +790,24 @@ transformAssignmentIndirection(ParseState *pstate,
/* base case: just coerce RHS to match target type ID */
- result = coerce_to_target_type(pstate,
- rhs, exprType(rhs),
- targetTypeId, targetTypMod,
- COERCION_ASSIGNMENT,
- COERCE_IMPLICIT_CAST,
- -1);
+ if (targetTypeId != InvalidOid)
+ result = coerce_to_target_type(pstate,
+ rhs, exprType(rhs),
+ targetTypeId, targetTypMod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ else
+ result = rhs;
+
if (result == NULL)
- {
- if (targetIsArray)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("array assignment to \"%s\" requires type %s"
- " but expression is of type %s",
- targetName,
- format_type_be(targetTypeId),
- format_type_be(exprType(rhs))),
- errhint("You will need to rewrite or cast the expression."),
- parser_errposition(pstate, location)));
- else
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("subfield \"%s\" is of type %s"
- " but expression is of type %s",
- targetName,
- format_type_be(targetTypeId),
- format_type_be(exprType(rhs))),
- errhint("You will need to rewrite or cast the expression."),
- parser_errposition(pstate, location)));
- }
+ result = rhs;
return result;
}
/*
- * helper for transformAssignmentIndirection: process array assignment
+ * helper for transformAssignmentIndirection: process container assignment
*/
static Node *
transformAssignmentSubscripts(ParseState *pstate,
@@ -840,55 +823,55 @@ transformAssignmentSubscripts(ParseState *pstate,
int location)
{
Node *result;
- Oid arrayType;
- int32 arrayTypMod;
+ Oid containerType;
+ int32 containerTypMod;
Oid elementTypeId;
Oid typeNeeded;
Oid collationNeeded;
Assert(subscripts != NIL);
- /* Identify the actual array type and element type involved */
- arrayType = targetTypeId;
- arrayTypMod = targetTypMod;
- elementTypeId = transformArrayType(&arrayType, &arrayTypMod);
+ /* Identify the actual container type and element type involved */
+ containerType = targetTypeId;
+ containerTypMod = targetTypMod;
+ elementTypeId = transformArrayType(&containerType, &containerTypMod);
/* Identify type that RHS must provide */
- typeNeeded = isSlice ? arrayType : elementTypeId;
+ typeNeeded = isSlice ? containerType : elementTypeId;
/*
- * Array normally has same collation as elements, but there's an
- * exception: we might be subscripting a domain over an array type. In
+ * container normally has same collation as elements, but there's an
+ * exception: we might be subscripting a domain over an container type. In
* that case use collation of the base type.
*/
- if (arrayType == targetTypeId)
+ if (containerType == targetTypeId)
collationNeeded = targetCollation;
else
- collationNeeded = get_typcollation(arrayType);
+ collationNeeded = get_typcollation(containerType);
- /* recurse to create appropriate RHS for array assign */
+ /* recurse to create appropriate RHS for container assign */
rhs = transformAssignmentIndirection(pstate,
NULL,
targetName,
true,
typeNeeded,
- arrayTypMod,
+ containerTypMod,
collationNeeded,
next_indirection,
rhs,
location);
/* process subscripts */
- result = (Node *) transformArraySubscripts(pstate,
- basenode,
- arrayType,
- elementTypeId,
- arrayTypMod,
- subscripts,
- rhs);
-
- /* If target was a domain over array, need to coerce up to the domain */
- if (arrayType != targetTypeId)
+ result = (Node *) transformContainerSubscripts(pstate,
+ basenode,
+ containerType,
+ exprType(rhs),
+ containerTypMod,
+ subscripts,
+ rhs);
+
+ /* If target was a domain over container, need to coerce up to the domain */
+ if (containerType != targetTypeId)
{
Oid resulttype = exprType(result);
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index bf4f098..94f2222 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -904,7 +904,7 @@ process_matched_tle(TargetEntry *src_tle,
/*----------
* Multiple assignments to same attribute. Allow only if all are
- * FieldStore or ArrayRef assignment operations. This is a bit
+ * FieldStore or SubscriptingRef assignment operations. This is a bit
* tricky because what we may actually be looking at is a nest of
* such nodes; consider
* UPDATE tab SET col.fld1.subfld1 = x, col.fld2.subfld2 = y
@@ -912,7 +912,7 @@ process_matched_tle(TargetEntry *src_tle,
* FieldStore(col, fld1, FieldStore(placeholder, subfld1, x))
* FieldStore(col, fld2, FieldStore(placeholder, subfld2, y))
* However, we can ignore the substructure and just consider the top
- * FieldStore or ArrayRef from each assignment, because it works to
+ * FieldStore or SubscriptingRef from each assignment, because it works to
* combine these as
* FieldStore(FieldStore(col, fld1,
* FieldStore(placeholder, subfld1, x)),
@@ -922,7 +922,7 @@ process_matched_tle(TargetEntry *src_tle,
*
* For FieldStore, instead of nesting we can generate a single
* FieldStore with multiple target fields. We must nest when
- * ArrayRefs are involved though.
+ * SubscriptingRefs are involved though.
*----------
*/
src_expr = (Node *) src_tle->expr;
@@ -981,13 +981,13 @@ process_matched_tle(TargetEntry *src_tle,
}
newexpr = (Node *) fstore;
}
- else if (IsA(src_expr, ArrayRef))
+ else if (IsA(src_expr, SubscriptingRef))
{
- ArrayRef *aref = makeNode(ArrayRef);
+ SubscriptingRef *sbsref = makeNode(SubscriptingRef);
- memcpy(aref, src_expr, sizeof(ArrayRef));
- aref->refexpr = (Expr *) prior_expr;
- newexpr = (Node *) aref;
+ memcpy(sbsref, src_expr, sizeof(SubscriptingRef));
+ sbsref->refexpr = (Expr *) prior_expr;
+ newexpr = (Node *) sbsref;
}
else
{
@@ -1014,14 +1014,15 @@ get_assignment_input(Node *node)
return (Node *) fstore->arg;
}
- else if (IsA(node, ArrayRef))
+ else if (IsA(node, SubscriptingRef))
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
return NULL;
- return (Node *) aref->refexpr;
+ return (Node *) sbsref->refexpr;
}
+
return NULL;
}
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 1db7bf0..b5b6811 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -24,6 +24,8 @@
#include "catalog/pg_type.h"
#include "funcapi.h"
#include "libpq/pqformat.h"
+#include "nodes/makefuncs.h"
+#include "nodes/nodeFuncs.h"
#include "utils/array.h"
#include "utils/arrayaccess.h"
#include "utils/builtins.h"
@@ -31,6 +33,8 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
+#include "parser/parse_node.h"
+#include "parser/parse_coerce.h"
/*
@@ -88,6 +92,7 @@ typedef struct ArrayIteratorData
static bool array_isspace(char ch);
static int ArrayCount(const char *str, int *dim, char typdelim);
+static bool isAssignmentIndirectionExpr(ExprState *exprstate);
static void ReadArrayStr(char *arrayStr, const char *origStr,
int nitems, int ndim, int *dim,
FmgrInfo *inputproc, Oid typioparam, int32 typmod,
@@ -157,7 +162,8 @@ static int width_bucket_array_variable(Datum operand,
ArrayType *thresholds,
Oid collation,
TypeCacheEntry *typentry);
-
+static Datum array_subscripting_prepare(PG_FUNCTION_ARGS);
+static Datum array_subscripting_evaluate(PG_FUNCTION_ARGS);
/*
* array_in :
@@ -6520,3 +6526,367 @@ width_bucket_array_variable(Datum operand,
return left;
}
+
+/*
+ * Helper for ExecEvalSubscriptingRef: is expr a nested FieldStore or SubscriptingRef
+ * that might need the old element value passed down?
+ *
+ * (We could use this in ExecEvalFieldStore too, but in that case passing
+ * the old value is so cheap there's no need.)
+ */
+static bool
+isAssignmentIndirectionExpr(ExprState *exprstate)
+{
+ if (exprstate == NULL)
+ return false; /* just paranoia */
+ if (IsA(exprstate, FieldStoreState))
+ {
+ FieldStore *fstore = (FieldStore *) exprstate->expr;
+
+ if (fstore->arg && IsA(fstore->arg, CaseTestExpr))
+ return true;
+ }
+ else if (IsA(exprstate, SubscriptingRefExprState))
+ {
+ SubscriptingRef *array_ref = (SubscriptingRef *) exprstate->expr;
+
+ if (array_ref->refexpr && IsA(array_ref->refexpr, CaseTestExpr))
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Perform an actual data extraction or modification for the array
+ * subscripting. As a result the extracted Datum or the modified containers
+ * value will be returned.
+ */
+Datum
+array_subscripting_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptingRefExprState *sbstate = (SubscriptingRefExprState *) PG_GETARG_POINTER(0);
+ SubscriptingExecData *sbsdata = (SubscriptingExecData *) PG_GETARG_POINTER(1);
+ ExprContext *econtext = sbsdata->xprcontext;
+ bool *is_null = sbsdata->isNull;
+ SubscriptingRef *array_ref = (SubscriptingRef *) sbstate->xprstate.expr;
+ bool is_assignment = (array_ref->refassgnexpr != NULL);
+ bool is_slice = (array_ref->reflowerindexpr != NIL);
+ IntArray u_index, l_index;
+ bool eisnull;
+ int i = 0;
+
+ if (sbstate->refelemlength == 0)
+ {
+ /* do one-time catalog lookups for type info */
+ get_typlenbyvalalign(array_ref->refelemtype,
+ &sbstate->refelemlength,
+ &sbstate->refelembyval,
+ &sbstate->refelemalign);
+ }
+
+ for(i = 0; i < sbsdata->indexprNumber; i++)
+ u_index.indx[i] = DatumGetInt32(sbsdata->upper[i]);
+
+ if (is_slice)
+ {
+ for(i = 0; i < sbsdata->indexprNumber; i++)
+ l_index.indx[i] = DatumGetInt32(sbsdata->lower[i]);
+ }
+
+ if (is_assignment)
+ {
+ Datum sourceData;
+ Datum save_datum;
+ bool save_isNull;
+
+ /*
+ * We might have a nested-assignment situation, in which the
+ * refassgnexpr is itself a FieldStore or SubscriptingRef that needs to
+ * obtain and modify the previous value of the array element or slice
+ * being replaced. If so, we have to extract that value from the
+ * array and pass it down via the econtext's caseValue. It's safe to
+ * reuse the CASE mechanism because there cannot be a CASE between
+ * here and where the value would be needed, and an array assignment
+ * can't be within a CASE either. (So saving and restoring the
+ * caseValue is just paranoia, but let's do it anyway.)
+ *
+ * Since fetching the old element might be a nontrivial expense, do it
+ * only if the argument appears to actually need it.
+ */
+ save_datum = econtext->caseValue_datum;
+ save_isNull = econtext->caseValue_isNull;
+
+ if (isAssignmentIndirectionExpr(sbstate->refassgnexpr))
+ {
+ if (*is_null)
+ {
+ /* whole array is null, so any element or slice is too */
+ econtext->caseValue_datum = (Datum) 0;
+ econtext->caseValue_isNull = true;
+ }
+ else if (!is_slice)
+ {
+ econtext->caseValue_datum =
+ array_get_element(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign,
+ &econtext->caseValue_isNull);
+ }
+ else
+ {
+ econtext->caseValue_datum =
+ array_get_slice(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, l_index.indx,
+ sbsdata->upperProvided,
+ sbsdata->lowerProvided,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+ econtext->caseValue_isNull = false;
+ }
+ }
+ else
+ {
+ /* argument shouldn't need caseValue, but for safety set it null */
+ econtext->caseValue_datum = (Datum) 0;
+ econtext->caseValue_isNull = true;
+ }
+
+ /*
+ * Evaluate the value to be assigned into the array.
+ */
+ sourceData = ExecEvalExpr(sbstate->refassgnexpr,
+ econtext,
+ &eisnull,
+ NULL);
+
+ econtext->caseValue_datum = save_datum;
+ econtext->caseValue_isNull = save_isNull;
+
+ /*
+ * For an assignment to a fixed-length array type, both the original
+ * array and the value to be assigned into it must be non-NULL, else
+ * we punt and return the original array.
+ */
+ if (sbstate->refattrlength > 0) /* fixed-length array? */
+ if (eisnull || *is_null)
+ return sbsdata->containerSource;
+
+ /*
+ * For assignment to varlena arrays, we handle a NULL original array
+ * by substituting an empty (zero-dimensional) array; insertion of the
+ * new element will result in a singleton array value. It does not
+ * matter whether the new element is NULL.
+ */
+ if (*is_null)
+ {
+ sbsdata->containerSource = PointerGetDatum(construct_empty_array(array_ref->refelemtype));
+ *is_null = false;
+ }
+
+ if (!is_slice)
+ return array_set_element(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, sourceData, eisnull,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+ else
+ return array_set_slice(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, l_index.indx,
+ sbsdata->upperProvided,
+ sbsdata->lowerProvided,
+ sourceData, eisnull,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+ }
+
+ if (!is_slice)
+ return array_get_element(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign,
+ is_null);
+ else
+ return array_get_slice(sbsdata->containerSource, sbsdata->indexprNumber,
+ u_index.indx, l_index.indx,
+ sbsdata->upperProvided,
+ sbsdata->lowerProvided,
+ sbstate->refattrlength,
+ sbstate->refelemlength,
+ sbstate->refelembyval,
+ sbstate->refelemalign);
+}
+
+/*
+ * Perform preparation for the array subscripting, mostly type verification
+ * and coersion. This function produces an expression that represents the
+ * result of extracting a single container element/container slice or the new
+ * container value with the source data inserted into the right part of the
+ * container.
+ */
+Datum
+array_subscripting_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptingRef *sbsref = (SubscriptingRef *) PG_GETARG_POINTER(0);
+ ParseState *pstate = (ParseState *) PG_GETARG_POINTER(1);
+ Node *node = (Node *)sbsref;
+ Oid array_type = sbsref->refcontainertype;
+ int32 array_typ_mode = (int32) sbsref->reftypmod;
+ bool is_slice = sbsref->reflowerindexpr != NIL;
+ Oid typeneeded = InvalidOid,
+ typesource = InvalidOid;
+ Node *new_from;
+ Oid element_type_id;
+ Node *subexpr;
+ List *upperIndexpr = NIL;
+ List *lowerIndexpr = NIL;
+ ListCell *l;
+
+ element_type_id = transformArrayType(&array_type, &array_typ_mode);
+ sbsref->refelemtype = element_type_id;
+
+ foreach(l, sbsref->refupperindexpr)
+ {
+ subexpr = (Node *) lfirst(l);
+
+ if (subexpr == NULL)
+ {
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ continue;
+ }
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ INT4OID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("array subscript must have type integer"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ }
+
+ sbsref->refupperindexpr = upperIndexpr;
+
+ foreach(l, sbsref->reflowerindexpr)
+ {
+ List *expr_ai = (List *) lfirst(l);
+ A_Indices *ai = (A_Indices *) lfirst(list_tail(expr_ai));
+
+ subexpr = (Node *) lfirst(list_head(expr_ai));
+ if (subexpr == NULL && !ai->is_slice)
+ {
+ /* Make a constant 1 */
+ subexpr = (Node *) makeConst(INT4OID,
+ -1,
+ InvalidOid,
+ sizeof(int32),
+ Int32GetDatum(1),
+ false,
+ true); /* pass by value */
+ }
+
+ if (subexpr == NULL)
+ {
+ lowerIndexpr = lappend(lowerIndexpr, subexpr);
+ continue;
+ }
+
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ INT4OID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("array subscript must have type integer"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ lowerIndexpr = lappend(lowerIndexpr, subexpr);
+ }
+
+ sbsref->reflowerindexpr = lowerIndexpr;
+
+ if (sbsref->refassgnexpr != NULL)
+ {
+ new_from = coerce_to_target_type(pstate,
+ (Node *)sbsref->refassgnexpr, typesource,
+ typeneeded, sbsref->reftypmod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (new_from == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("array assignment requires type %s"
+ " but expression is of type %s",
+ format_type_be(typeneeded),
+ format_type_be(typesource)),
+ errhint("You will need to rewrite or cast the expression."),
+ parser_errposition(pstate, exprLocation((Node *)sbsref->refassgnexpr))));
+ sbsref->refassgnexpr = (Expr *)new_from;
+
+ if (array_type != sbsref->refcontainertype)
+ {
+ typesource = exprType((Node *)sbsref->refassgnexpr);
+ typesource = is_slice ? sbsref->refcontainertype : sbsref->refelemtype;
+
+ node = coerce_to_target_type(pstate,
+ node, array_type,
+ sbsref->refcontainertype, sbsref->reftypmod,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+
+ /* can fail if we had int2vector/oidvector, but not for true domains */
+ if (node == NULL && node->type != 0)
+ ereport(ERROR,
+ (errcode(ERRCODE_CANNOT_COERCE),
+ errmsg("cannot cast type %s to %s",
+ format_type_be(array_type),
+ format_type_be(sbsref->refcontainertype)),
+ parser_errposition(pstate, 0)));
+
+ PG_RETURN_POINTER(node);
+ }
+
+ }
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+/*
+ * Handle array-type subscripting logic.
+ */
+Datum
+array_subscripting(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo;
+ get_slice_arguments(fcinfo, 1, fcinfo->nargs, &target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return array_subscripting_evaluate(&target_fcinfo);
+
+ if (op_type & SBS_VALIDATION)
+ return array_subscripting_prepare(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscripting function: %d", op_type);
+}
diff --git a/src/backend/utils/adt/jsonb.c b/src/backend/utils/adt/jsonb.c
index 987cfd1..2e64c53 100644
--- a/src/backend/utils/adt/jsonb.c
+++ b/src/backend/utils/adt/jsonb.c
@@ -1145,23 +1145,34 @@ to_jsonb(PG_FUNCTION_ARGS)
{
Datum val = PG_GETARG_DATUM(0);
Oid val_type = get_fn_expr_argtype(fcinfo->flinfo, 0);
- JsonbInState result;
- JsonbTypeCategory tcategory;
- Oid outfuncoid;
+ JsonbValue *res = to_jsonb_worker(val, val_type);
+ PG_RETURN_POINTER(JsonbValueToJsonb(res));
+}
- if (val_type == InvalidOid)
+/*
+ * Do the actual conversion to jsonb for to_jsonb function. This logic is
+ * separated because it can be useful not only in here (e.g. we use it in
+ * jsonb subscripting)
+ */
+JsonbValue *
+to_jsonb_worker(Datum source, Oid source_type)
+{
+ JsonbInState result;
+ JsonbTypeCategory tcategory;
+ Oid outfuncoid;
+
+ if (source_type == InvalidOid)
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("could not determine input data type")));
- jsonb_categorize_type(val_type,
+ jsonb_categorize_type(source_type,
&tcategory, &outfuncoid);
memset(&result, 0, sizeof(JsonbInState));
- datum_to_jsonb(val, false, &result, tcategory, outfuncoid, false);
-
- PG_RETURN_POINTER(JsonbValueToJsonb(result.res));
+ datum_to_jsonb(source, false, &result, tcategory, outfuncoid, false);
+ return result.res;
}
/*
diff --git a/src/backend/utils/adt/jsonb_util.c b/src/backend/utils/adt/jsonb_util.c
index ddc34ce..5e1b682 100644
--- a/src/backend/utils/adt/jsonb_util.c
+++ b/src/backend/utils/adt/jsonb_util.c
@@ -61,18 +61,29 @@ static JsonbValue *pushJsonbValueScalar(JsonbParseState **pstate,
JsonbIteratorToken seq,
JsonbValue *scalarVal);
+JsonbValue *
+JsonbToJsonbValue(Jsonb *jsonb)
+{
+ JsonbValue *val = (JsonbValue *) palloc(sizeof(JsonbValue));
+
+ val->type = jbvBinary;
+ val->val.binary.data = &jsonb->root;
+ val->val.binary.len = VARSIZE(jsonb) - VARHDRSZ;
+
+ return val;
+}
+
/*
* Turn an in-memory JsonbValue into a Jsonb for on-disk storage.
*
- * There isn't a JsonbToJsonbValue(), because generally we find it more
- * convenient to directly iterate through the Jsonb representation and only
- * really convert nested scalar values. JsonbIteratorNext() does this, so that
- * clients of the iteration code don't have to directly deal with the binary
- * representation (JsonbDeepContains() is a notable exception, although all
- * exceptions are internal to this module). In general, functions that accept
- * a JsonbValue argument are concerned with the manipulation of scalar values,
- * or simple containers of scalar values, where it would be inconvenient to
- * deal with a great amount of other state.
+ * Generally we find it more convenient to directly iterate through the Jsonb
+ * representation and only really convert nested scalar values.
+ * JsonbIteratorNext() does this, so that clients of the iteration code don't
+ * have to directly deal with the binary representation (JsonbDeepContains() is
+ * a notable exception, although all exceptions are internal to this module).
+ * In general, functions that accept a JsonbValue argument are concerned with
+ * the manipulation of scalar values, or simple containers of scalar values,
+ * where it would be inconvenient to deal with a great amount of other state.
*/
Jsonb *
JsonbValueToJsonb(JsonbValue *val)
@@ -520,6 +531,30 @@ pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq,
JsonbValue *res = NULL;
JsonbValue v;
JsonbIteratorToken tok;
+ int i;
+
+ if (jbval && (seq == WJB_ELEM || seq == WJB_VALUE) && jbval->type == jbvObject)
+ {
+ pushJsonbValue(pstate, WJB_BEGIN_OBJECT, NULL);
+ for (i = 0; i < jbval->val.object.nPairs; i++)
+ {
+ pushJsonbValue(pstate, WJB_KEY, &jbval->val.object.pairs[i].key);
+ pushJsonbValue(pstate, WJB_VALUE, &jbval->val.object.pairs[i].value);
+ }
+
+ return pushJsonbValue(pstate, WJB_END_OBJECT, NULL);
+ }
+
+ if (jbval && (seq == WJB_ELEM || seq == WJB_VALUE) && jbval->type == jbvArray)
+ {
+ pushJsonbValue(pstate, WJB_BEGIN_ARRAY, NULL);
+ for (i = 0; i < jbval->val.array.nElems; i++)
+ {
+ pushJsonbValue(pstate, WJB_ELEM, &jbval->val.array.elems[i]);
+ }
+
+ return pushJsonbValue(pstate, WJB_END_ARRAY, NULL);
+ }
if (!jbval || (seq != WJB_ELEM && seq != WJB_VALUE) ||
jbval->type != jbvBinary)
@@ -530,9 +565,30 @@ pushJsonbValue(JsonbParseState **pstate, JsonbIteratorToken seq,
/* unpack the binary and add each piece to the pstate */
it = JsonbIteratorInit(jbval->val.binary.data);
+
+ if ((jbval->val.binary.data->header & JB_FSCALAR) && *pstate)
+ {
+ tok = JsonbIteratorNext(&it, &v, true);
+ Assert(tok == WJB_BEGIN_ARRAY);
+ Assert(v.type == jbvArray && v.val.array.rawScalar);
+
+ tok = JsonbIteratorNext(&it, &v, true);
+ Assert(tok == WJB_ELEM);
+
+ res = pushJsonbValueScalar(pstate, seq, &v);
+
+ tok = JsonbIteratorNext(&it, &v, true);
+ Assert(tok == WJB_END_ARRAY);
+ Assert(it == NULL);
+
+ return res;
+ }
+
while ((tok = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
res = pushJsonbValueScalar(pstate, tok,
- tok < WJB_BEGIN_ARRAY ? &v : NULL);
+ tok < WJB_BEGIN_ARRAY ||
+ (tok == WJB_BEGIN_ARRAY &&
+ v.val.array.rawScalar) ? &v : NULL);
return res;
}
diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c
index 17ee4e4..a5fed4b 100644
--- a/src/backend/utils/adt/jsonfuncs.c
+++ b/src/backend/utils/adt/jsonfuncs.c
@@ -23,6 +23,8 @@
#include "lib/stringinfo.h"
#include "mb/pg_wchar.h"
#include "miscadmin.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_coerce.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/hsearch.h"
@@ -32,6 +34,7 @@
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/typcache.h"
+#include "parser/parse_node.h"
/* Operations available for setPath */
#define JB_PATH_CREATE 0x0001
@@ -136,18 +139,21 @@ static JsonbValue *findJsonbValueFromContainerLen(JsonbContainer *container,
/* functions supporting jsonb_delete, jsonb_set and jsonb_concat */
static JsonbValue *IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
JsonbParseState **state);
+static Datum jsonb_set_element(Datum datum, Datum *path, int path_len,
+ Datum sourceData, Oid source_type);
+static Datum jsonb_get_element(Jsonb *jb, Datum *path, int npath,
+ bool *isnull, bool as_text);
static JsonbValue *setPath(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len,
- JsonbParseState **st, int level, Jsonb *newval,
- int op_type);
+ JsonbParseState **st, int level, JsonbValue *newval, int op_type);
static void setPathObject(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len, JsonbParseState **st,
- int level,
- Jsonb *newval, uint32 npairs, int op_type);
+ int level, JsonbValue *newval, uint32 npairs, int op_type);
static void setPathArray(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len, JsonbParseState **st,
- int level, Jsonb *newval, uint32 nelems, int op_type);
-static void addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb);
+ int level, JsonbValue *newval, uint32 nelems, int op_type);
+static Datum jsonb_subscripting_evaluate(PG_FUNCTION_ARGS);
+static Datum jsonb_subscripting_prepare(PG_FUNCTION_ARGS);
/* state for json_object_keys */
typedef struct OkeysState
@@ -1171,16 +1177,11 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
Jsonb *jb = PG_GETARG_JSONB(0);
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
- Jsonb *res;
Datum *pathtext;
bool *pathnulls;
+ bool isnull;
int npath;
- int i;
- bool have_object = false,
- have_array = false;
- JsonbValue *jbvp = NULL;
- JsonbValue tv;
- JsonbContainer *container;
+ Datum res;
/*
* If the array contains any null elements, return NULL, on the grounds
@@ -1195,9 +1196,28 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
deconstruct_array(path, TEXTOID, -1, false, 'i',
&pathtext, &pathnulls, &npath);
- /* Identify whether we have object, array, or scalar at top-level */
- container = &jb->root;
+ res = jsonb_get_element(jb, pathtext, npath, &isnull, as_text);
+
+ if (isnull)
+ PG_RETURN_NULL();
+ else
+ PG_RETURN_DATUM(res);
+}
+static Datum
+jsonb_get_element(Jsonb *jb, Datum *path, int npath, bool *isnull, bool as_text)
+{
+ Jsonb *res;
+ JsonbContainer *container = &jb->root;
+ JsonbValue *jbvp = NULL;
+ JsonbValue tv;
+ int i;
+ bool have_object = false,
+ have_array = false;
+
+ *isnull = false;
+
+ /* Identify whether we have object, array, or scalar at top-level */
if (JB_ROOT_IS_OBJECT(jb))
have_object = true;
else if (JB_ROOT_IS_ARRAY(jb) && !JB_ROOT_IS_SCALAR(jb))
@@ -1222,14 +1242,14 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
if (as_text)
{
- PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL,
+ return PointerGetDatum(cstring_to_text(JsonbToCString(NULL,
container,
VARSIZE(jb))));
}
else
{
/* not text mode - just hand back the jsonb */
- PG_RETURN_JSONB(jb);
+ return JsonbGetDatum(jb);
}
}
@@ -1239,21 +1259,24 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
jbvp = findJsonbValueFromContainerLen(container,
JB_FOBJECT,
- VARDATA_ANY(pathtext[i]),
- VARSIZE_ANY_EXHDR(pathtext[i]));
+ VARDATA_ANY(path[i]),
+ VARSIZE_ANY_EXHDR(path[i]));
}
else if (have_array)
{
long lindex;
uint32 index;
- char *indextext = TextDatumGetCString(pathtext[i]);
+ char *indextext = TextDatumGetCString(path[i]);
char *endptr;
errno = 0;
lindex = strtol(indextext, &endptr, 10);
if (endptr == indextext || *endptr != '\0' || errno != 0 ||
lindex > INT_MAX || lindex < INT_MIN)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
if (lindex >= 0)
{
@@ -1271,7 +1294,10 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
nelements = container->header & JB_CMASK;
if (-lindex > nelements)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
else
index = nelements + lindex;
}
@@ -1281,11 +1307,15 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
else
{
/* scalar, extraction yields a null */
- PG_RETURN_NULL();
+ *isnull = true;
+ return PointerGetDatum(NULL);
}
if (jbvp == NULL)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
else if (i == npath - 1)
break;
@@ -1310,27 +1340,57 @@ get_jsonb_path_all(FunctionCallInfo fcinfo, bool as_text)
{
/* special-case outputs for string and null values */
if (jbvp->type == jbvString)
- PG_RETURN_TEXT_P(cstring_to_text_with_len(jbvp->val.string.val,
- jbvp->val.string.len));
+ return PointerGetDatum(
+ cstring_to_text_with_len(jbvp->val.string.val,
+ jbvp->val.string.len));
if (jbvp->type == jbvNull)
- PG_RETURN_NULL();
+ {
+ *isnull = true;
+ return PointerGetDatum(NULL);
+ }
}
res = JsonbValueToJsonb(jbvp);
if (as_text)
{
- PG_RETURN_TEXT_P(cstring_to_text(JsonbToCString(NULL,
+ return PointerGetDatum(cstring_to_text(JsonbToCString(NULL,
&res->root,
VARSIZE(res))));
}
else
{
/* not text mode - just hand back the jsonb */
- PG_RETURN_JSONB(res);
+ return JsonbGetDatum(res);
}
}
+Datum
+jsonb_set_element(Datum jsonbdatum, Datum *path, int path_len,
+ Datum sourceData, Oid source_type)
+{
+ Jsonb *jb = DatumGetJsonb(jsonbdatum);
+ JsonbValue *newval,
+ *res;
+ JsonbParseState *state = NULL;
+ JsonbIterator *it;
+ bool *path_nulls = palloc0(path_len * sizeof(bool));
+
+ newval = to_jsonb_worker(sourceData, source_type);
+
+ if (newval->type == jbvArray && newval->val.array.rawScalar)
+ *newval = newval->val.array.elems[0];
+
+ it = JsonbIteratorInit(&jb->root);
+
+ res = setPath(&it, path, path_nulls, path_len, &state, 0,
+ newval, JB_PATH_CREATE);
+
+ pfree(path_nulls);
+
+ PG_RETURN_JSONB(JsonbValueToJsonb(res));
+}
+
/*
* SQL function json_array_length(json) -> int
*/
@@ -3278,57 +3338,6 @@ jsonb_strip_nulls(PG_FUNCTION_ARGS)
}
/*
- * Add values from the jsonb to the parse state.
- *
- * If the parse state container is an object, the jsonb is pushed as
- * a value, not a key.
- *
- * This needs to be done using an iterator because pushJsonbValue doesn't
- * like getting jbvBinary values, so we can't just push jb as a whole.
- */
-static void
-addJsonbToParseState(JsonbParseState **jbps, Jsonb *jb)
-{
- JsonbIterator *it;
- JsonbValue *o = &(*jbps)->contVal;
- JsonbValue v;
- JsonbIteratorToken type;
-
- it = JsonbIteratorInit(&jb->root);
-
- Assert(o->type == jbvArray || o->type == jbvObject);
-
- if (JB_ROOT_IS_SCALAR(jb))
- {
- (void) JsonbIteratorNext(&it, &v, false); /* skip array header */
- (void) JsonbIteratorNext(&it, &v, false); /* fetch scalar value */
-
- switch (o->type)
- {
- case jbvArray:
- (void) pushJsonbValue(jbps, WJB_ELEM, &v);
- break;
- case jbvObject:
- (void) pushJsonbValue(jbps, WJB_VALUE, &v);
- break;
- default:
- elog(ERROR, "unexpected parent of nested structure");
- }
- }
- else
- {
- while ((type = JsonbIteratorNext(&it, &v, false)) != WJB_DONE)
- {
- if (type == WJB_KEY || type == WJB_VALUE || type == WJB_ELEM)
- (void) pushJsonbValue(jbps, type, &v);
- else
- (void) pushJsonbValue(jbps, type, NULL);
- }
- }
-
-}
-
-/*
* SQL function jsonb_pretty (jsonb)
*
* Pretty-printed text for the jsonb
@@ -3514,7 +3523,8 @@ jsonb_set(PG_FUNCTION_ARGS)
{
Jsonb *in = PG_GETARG_JSONB(0);
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
- Jsonb *newval = PG_GETARG_JSONB(2);
+ Jsonb *newjsonb = PG_GETARG_JSONB(2);
+ JsonbValue *newval = JsonbToJsonbValue(newjsonb);
bool create = PG_GETARG_BOOL(3);
JsonbValue *res = NULL;
Datum *path_elems;
@@ -3606,7 +3616,8 @@ jsonb_insert(PG_FUNCTION_ARGS)
{
Jsonb *in = PG_GETARG_JSONB(0);
ArrayType *path = PG_GETARG_ARRAYTYPE_P(1);
- Jsonb *newval = PG_GETARG_JSONB(2);
+ Jsonb *newjsonb = PG_GETARG_JSONB(2);
+ JsonbValue *newval = JsonbToJsonbValue(newjsonb);
bool after = PG_GETARG_BOOL(3);
JsonbValue *res = NULL;
Datum *path_elems;
@@ -3769,7 +3780,7 @@ IteratorConcat(JsonbIterator **it1, JsonbIterator **it2,
static JsonbValue *
setPath(JsonbIterator **it, Datum *path_elems,
bool *path_nulls, int path_len,
- JsonbParseState **st, int level, Jsonb *newval, int op_type)
+ JsonbParseState **st, int level, JsonbValue *newval, int op_type)
{
JsonbValue v;
JsonbIteratorToken r;
@@ -3822,11 +3833,11 @@ setPath(JsonbIterator **it, Datum *path_elems,
static void
setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
int path_len, JsonbParseState **st, int level,
- Jsonb *newval, uint32 npairs, int op_type)
+ JsonbValue *newval, uint32 npairs, int op_type)
{
- JsonbValue v;
int i;
- JsonbValue k;
+ JsonbValue k,
+ v;
bool done = false;
if (level >= path_len || path_nulls[level])
@@ -3843,7 +3854,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
newkey.val.string.val = VARDATA_ANY(path_elems[level]);
(void) pushJsonbValue(st, WJB_KEY, &newkey);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_VALUE, newval);
}
for (i = 0; i < npairs; i++)
@@ -3874,7 +3885,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
if (!(op_type & JB_PATH_DELETE))
{
(void) pushJsonbValue(st, WJB_KEY, &k);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_VALUE, newval);
}
done = true;
}
@@ -3897,7 +3908,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
newkey.val.string.val = VARDATA_ANY(path_elems[level]);
(void) pushJsonbValue(st, WJB_KEY, &newkey);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_VALUE, newval);
}
(void) pushJsonbValue(st, r, &k);
@@ -3929,7 +3940,7 @@ setPathObject(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
static void
setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
int path_len, JsonbParseState **st, int level,
- Jsonb *newval, uint32 nelems, int op_type)
+ JsonbValue *newval, uint32 nelems, int op_type)
{
JsonbValue v;
int idx,
@@ -3977,7 +3988,7 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
(op_type & JB_PATH_CREATE_OR_INSERT))
{
Assert(newval != NULL);
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, newval);
done = true;
}
@@ -3993,7 +4004,7 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
r = JsonbIteratorNext(it, &v, true); /* skip */
if (op_type & (JB_PATH_INSERT_BEFORE | JB_PATH_CREATE))
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, newval);
/*
* We should keep current value only in case of
@@ -4004,7 +4015,7 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
(void) pushJsonbValue(st, r, &v);
if (op_type & (JB_PATH_INSERT_AFTER | JB_PATH_REPLACE))
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, newval);
done = true;
}
@@ -4038,8 +4049,169 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls,
if ((op_type & JB_PATH_CREATE_OR_INSERT) && !done &&
level == path_len - 1 && i == nelems - 1)
{
- addJsonbToParseState(st, newval);
+ (void) pushJsonbValue(st, WJB_ELEM, newval);
}
}
}
}
+
+/*
+ * Perform an actual data extraction or modification for the jsonb
+ * subscripting. As a result the extracted Datum or the modified containers
+ * value will be returned.
+ */
+Datum
+jsonb_subscripting_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptingRefExprState *sbstate = (SubscriptingRefExprState *) PG_GETARG_POINTER(0);
+ SubscriptingExecData *sbsdata = (SubscriptingExecData *) PG_GETARG_POINTER(1);
+ SubscriptingRef *jsonb_ref = (SubscriptingRef *) sbstate->xprstate.expr;
+ bool *is_null = sbsdata->isNull;
+ bool is_assignment = (jsonb_ref->refassgnexpr != NULL);
+
+ if (is_assignment)
+ {
+ ExprContext *econtext = sbsdata->xprcontext;
+ Datum sourceData;
+ Datum save_datum;
+ bool save_isNull;
+ bool eisnull;
+
+ /*
+ * We might have a nested-assignment situation, in which the
+ * refassgnexpr is itself a FieldStore or SubscriptingRef that needs to
+ * obtain and modify the previous value of the array element or slice
+ * being replaced. If so, we have to extract that value from the
+ * array and pass it down via the econtext's caseValue. It's safe to
+ * reuse the CASE mechanism because there cannot be a CASE between
+ * here and where the value would be needed, and an array assignment
+ * can't be within a CASE either. (So saving and restoring the
+ * caseValue is just paranoia, but let's do it anyway.)
+ *
+ * Since fetching the old element might be a nontrivial expense, do it
+ * only if the argument appears to actually need it.
+ */
+ save_datum = econtext->caseValue_datum;
+ save_isNull = econtext->caseValue_isNull;
+
+ /*
+ * Evaluate the value to be assigned into the array.
+ */
+ sourceData = ExecEvalExpr(sbstate->refassgnexpr,
+ econtext,
+ &eisnull,
+ NULL);
+
+ econtext->caseValue_datum = save_datum;
+ econtext->caseValue_isNull = save_isNull;
+
+ /*
+ * For an assignment to a fixed-length array type, both the original
+ * array and the value to be assigned into it must be non-NULL, else
+ * we punt and return the original array.
+ */
+ if (sbstate->refattrlength > 0) /* fixed-length array? */
+ if (eisnull || *is_null)
+ return sbsdata->containerSource;
+
+ /*
+ * For assignment to varlena arrays, we handle a NULL original array
+ * by substituting an empty (zero-dimensional) array; insertion of the
+ * new element will result in a singleton array value. It does not
+ * matter whether the new element is NULL.
+ */
+ if (*is_null)
+ {
+ sbsdata->containerSource =
+ PointerGetDatum(construct_empty_array(jsonb_ref->refelemtype));
+ *is_null = false;
+ }
+
+ return jsonb_set_element(sbsdata->containerSource,
+ sbsdata->upper,
+ sbsdata->indexprNumber,
+ sourceData,
+ jsonb_ref->refelemtype);
+ }
+ else
+ return jsonb_get_element(DatumGetJsonb(sbsdata->containerSource),
+ sbsdata->upper,
+ sbsdata->indexprNumber,
+ is_null,
+ false);
+}
+
+/*
+ * Perform preparation for the jsonb subscripting. Since there are not any
+ * particular restrictions for this kind of subscripting, we will verify that
+ * it is not a slice operation. This function produces an expression that
+ * represents the result of extracting a single container element or the new
+ * container value with the source data inserted into the right part of the
+ * container.
+ */
+Datum
+jsonb_subscripting_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptingRef *sbsref = (SubscriptingRef *) PG_GETARG_POINTER(0);
+ ParseState *pstate = (ParseState *) PG_GETARG_POINTER(1);
+ List *upperIndexpr = NIL;
+ ListCell *l;
+
+ if (sbsref->reflowerindexpr != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("jsonb subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *)lfirst(sbsref->reflowerindexpr->head))))));
+
+ foreach(l, sbsref->refupperindexpr)
+ {
+ Node *subexpr = (Node *) lfirst(l);
+
+ Assert(subexpr != NULL);
+
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("jsonb subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *) lfirst(sbsref->refupperindexpr->head))))));
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ TEXTOID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("jsonb subscript must have text type"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ }
+
+ sbsref->refupperindexpr = upperIndexpr;
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+/*
+ * Handle jsonb-type subscripting logic.
+ */
+Datum
+jsonb_subscripting(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo;
+ get_slice_arguments(fcinfo, 1, fcinfo->nargs, &target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return jsonb_subscripting_evaluate(&target_fcinfo);
+
+ if (op_type & SBS_VALIDATION)
+ return jsonb_subscripting_prepare(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscripting function: %d", op_type);
+}
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 4e2ba19..05e654e 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -441,7 +441,7 @@ static void get_tablesample_def(TableSampleClause *tablesample,
static void get_opclass_name(Oid opclass, Oid actual_datatype,
StringInfo buf);
static Node *processIndirection(Node *node, deparse_context *context);
-static void printSubscripts(ArrayRef *aref, deparse_context *context);
+static void printSubscripts(SubscriptingRef *aref, deparse_context *context);
static char *get_relation_name(Oid relid);
static char *generate_relation_name(Oid relid, List *namespaces);
static char *generate_qualified_relation_name(Oid relid);
@@ -6013,7 +6013,7 @@ get_update_query_targetlist_def(Query *query, List *targetList,
{
/*
* We must dig down into the expr to see if it's a PARAM_MULTIEXPR
- * Param. That could be buried under FieldStores and ArrayRefs
+ * Param. That could be buried under FieldStores and SubscriptingRefs
* (cf processIndirection()), and underneath those there could be
* an implicit type coercion.
*/
@@ -6026,13 +6026,13 @@ get_update_query_targetlist_def(Query *query, List *targetList,
expr = (Node *) linitial(fstore->newvals);
}
- else if (IsA(expr, ArrayRef))
+ else if (IsA(expr, SubscriptingRef))
{
- ArrayRef *aref = (ArrayRef *) expr;
+ SubscriptingRef *sbsref = (SubscriptingRef *) expr;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
break;
- expr = (Node *) aref->refassgnexpr;
+ expr = (Node *) sbsref->refassgnexpr;
}
else
break;
@@ -7064,7 +7064,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
/* single words: always simple */
return true;
- case T_ArrayRef:
+ case T_SubscriptingRef:
case T_ArrayExpr:
case T_RowExpr:
case T_CoalesceExpr:
@@ -7181,7 +7181,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
return true; /* own parentheses */
}
case T_BoolExpr: /* lower precedence */
- case T_ArrayRef: /* other separators */
+ case T_SubscriptingRef: /* other separators */
case T_ArrayExpr: /* other separators */
case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */
@@ -7231,7 +7231,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
return false;
return true; /* own parentheses */
}
- case T_ArrayRef: /* other separators */
+ case T_SubscriptingRef: /* other separators */
case T_ArrayExpr: /* other separators */
case T_RowExpr: /* other separators */
case T_CoalesceExpr: /* own parentheses */
@@ -7417,9 +7417,9 @@ get_rule_expr(Node *node, deparse_context *context,
get_windowfunc_expr((WindowFunc *) node, context);
break;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
bool need_parens;
/*
@@ -7430,24 +7430,24 @@ get_rule_expr(Node *node, deparse_context *context,
* here too, and display only the assignment source
* expression.
*/
- if (IsA(aref->refexpr, CaseTestExpr))
+ if (IsA(sbsref->refexpr, CaseTestExpr))
{
- Assert(aref->refassgnexpr);
- get_rule_expr((Node *) aref->refassgnexpr,
+ Assert(sbsref->refassgnexpr);
+ get_rule_expr((Node *) sbsref->refassgnexpr,
context, showimplicit);
break;
}
/*
* Parenthesize the argument unless it's a simple Var or a
- * FieldSelect. (In particular, if it's another ArrayRef, we
+ * FieldSelect. (In particular, if it's another SubscriptingRef, we
* *must* parenthesize to avoid confusion.)
*/
- need_parens = !IsA(aref->refexpr, Var) &&
- !IsA(aref->refexpr, FieldSelect);
+ need_parens = !IsA(sbsref->refexpr, Var) &&
+ !IsA(sbsref->refexpr, FieldSelect);
if (need_parens)
appendStringInfoChar(buf, '(');
- get_rule_expr((Node *) aref->refexpr, context, showimplicit);
+ get_rule_expr((Node *) sbsref->refexpr, context, showimplicit);
if (need_parens)
appendStringInfoChar(buf, ')');
@@ -7460,7 +7460,7 @@ get_rule_expr(Node *node, deparse_context *context,
* EXPLAIN tries to print the targetlist of a plan resulting
* from such a statement.
*/
- if (aref->refassgnexpr)
+ if (sbsref->refassgnexpr)
{
Node *refassgnexpr;
@@ -7477,7 +7477,7 @@ get_rule_expr(Node *node, deparse_context *context,
else
{
/* Just an ordinary array fetch, so print subscripts */
- printSubscripts(aref, context);
+ printSubscripts(sbsref, context);
}
}
break;
@@ -7676,12 +7676,12 @@ get_rule_expr(Node *node, deparse_context *context,
bool need_parens;
/*
- * Parenthesize the argument unless it's an ArrayRef or
+ * Parenthesize the argument unless it's an SubscriptingRef or
* another FieldSelect. Note in particular that it would be
* WRONG to not parenthesize a Var argument; simplicity is not
* the issue here, having the right number of names is.
*/
- need_parens = !IsA(arg, ArrayRef) &&!IsA(arg, FieldSelect);
+ need_parens = !IsA(arg, SubscriptingRef) && !IsA(arg, FieldSelect);
if (need_parens)
appendStringInfoChar(buf, '(');
get_rule_expr(arg, context, true);
@@ -9877,7 +9877,7 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
/*
* processIndirection - take care of array and subfield assignment
*
- * We strip any top-level FieldStore or assignment ArrayRef nodes that
+ * We strip any top-level FieldStore or assignment SubscriptingRef nodes that
* appear in the input, printing them as decoration for the base column
* name (which we assume the caller just printed). Return the subexpression
* that's to be assigned.
@@ -9919,19 +9919,19 @@ processIndirection(Node *node, deparse_context *context)
*/
node = (Node *) linitial(fstore->newvals);
}
- else if (IsA(node, ArrayRef))
+ else if (IsA(node, SubscriptingRef))
{
- ArrayRef *aref = (ArrayRef *) node;
+ SubscriptingRef *sbsref = (SubscriptingRef *) node;
- if (aref->refassgnexpr == NULL)
+ if (sbsref->refassgnexpr == NULL)
break;
- printSubscripts(aref, context);
+ printSubscripts(sbsref, context);
/*
* We ignore refexpr since it should be an uninteresting reference
* to the target column or subcolumn.
*/
- node = (Node *) aref->refassgnexpr;
+ node = (Node *) sbsref->refassgnexpr;
}
else
break;
@@ -9941,14 +9941,14 @@ processIndirection(Node *node, deparse_context *context)
}
static void
-printSubscripts(ArrayRef *aref, deparse_context *context)
+printSubscripts(SubscriptingRef *sbsref, deparse_context *context)
{
StringInfo buf = context->buf;
ListCell *lowlist_item;
ListCell *uplist_item;
- lowlist_item = list_head(aref->reflowerindexpr); /* could be NULL */
- foreach(uplist_item, aref->refupperindexpr)
+ lowlist_item = list_head(sbsref->reflowerindexpr); /* could be NULL */
+ foreach(uplist_item, sbsref->refupperindexpr)
{
appendStringInfoChar(buf, '[');
if (lowlist_item)
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 13ae6ad..06a3c1a 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -3061,3 +3061,23 @@ get_range_subtype(Oid rangeOid)
else
return InvalidOid;
}
+
+/*
+ * get_typsubscripting
+ *
+ * Given the type OID, return the type's typsubscripting procedure, if any.
+ */
+RegProcedure
+get_typsubscripting(Oid typid)
+{
+ HeapTuple tp;
+ RegProcedure result = InvalidOid;
+
+ tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
+ if (HeapTupleIsValid(tp))
+ {
+ result = ((Form_pg_type) GETSTRUCT(tp))->typsubscripting;
+ ReleaseSysCache(tp);
+ }
+ return result;
+}
diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c
index 5d49fe5..f7f2c7e 100644
--- a/src/backend/utils/fmgr/funcapi.c
+++ b/src/backend/utils/fmgr/funcapi.c
@@ -1396,3 +1396,19 @@ TypeGetTupleDesc(Oid typeoid, List *colaliases)
return tupdesc;
}
+
+void
+get_slice_arguments(FunctionCallInfo fcinfo, int begin, int end, FunctionCallInfoData *sliced_fcinfo)
+{
+ int i;
+
+ InitFunctionCallInfoData(*sliced_fcinfo, fcinfo->flinfo,
+ fcinfo->nargs - 1, fcinfo->fncollation,
+ NULL, NULL);
+
+ for(i = begin; i < end; i++)
+ {
+ sliced_fcinfo->arg[i - begin] = fcinfo->arg[i];
+ sliced_fcinfo->argnull[i - begin] = false;
+ }
+}
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index a61b7a2..93826fe 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -147,7 +147,7 @@ typedef FormData_pg_class *Form_pg_class;
* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId;
* similarly, "1" in relminmxid stands for FirstMultiXactId
*/
-DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 30 0 t f f f f f f t n f 3 1 _null_ _null_ _null_));
+DATA(insert OID = 1247 ( pg_type PGNSP 71 0 PGUID 0 0 0 0 0 0 0 f f p r 31 0 t f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
DATA(insert OID = 1249 ( pg_attribute PGNSP 75 0 PGUID 0 0 0 0 0 0 0 f f p r 21 0 f f f f f f f t n f 3 1 _null_ _null_ _null_));
DESCR("");
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index cd7b909..8d7c1f4 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -5345,6 +5345,12 @@ DESCR("pg_controldata recovery state information as a function");
DATA(insert OID = 3444 ( pg_control_init PGNSP PGUID 12 1 0 0 0 f f f f t f v s 0 0 2249 "" "{23,23,23,23,23,23,23,23,23,16,16,16,23}" "{o,o,o,o,o,o,o,o,o,o,o,o,o}" "{max_data_alignment,database_block_size,blocks_per_segment,wal_block_size,bytes_per_wal_segment,max_identifier_length,max_index_columns,max_toast_chunk_size,large_object_chunk_size,bigint_timestamps,float4_pass_by_value,float8_pass_by_value,data_page_checksum_version}" _null_ _null_ pg_control_init _null_ _null_ _null_ ));
DESCR("pg_controldata init state information as a function");
+/* type subscripting support */
+DATA(insert OID = 3343 ( jsonb_subscripting PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 "2281" "2281" _null_ _null_ _null_ _null_ _null_ jsonb_subscripting _null_ _null_ _null_ ));
+DESCR("Jsonb subscripting logic");
+DATA(insert OID = 3344 ( array_subscripting PGNSP PGUID 12 1 0 0 0 f f f f t f s s 1 0 "2281" "2281" _null_ _null_ _null_ _null_ _null_ array_subscripting _null_ _null_ _null_ ));
+DESCR("Array subscripting logic");
+
/*
* Symbolic values for provolatile column: these indicate whether the result
* of a function is dependent *only* on the values of its explicit arguments,
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 162239c..c6cd95e 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -199,6 +199,12 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP BKI_ROWTYPE_OID(71) BKI_SCHEMA_MACRO
*/
Oid typcollation;
+ /*
+ * Type specific subscripting logic. If typsubscripting is none, it means
+ * that this type doesn't support subscripting.
+ */
+ regproc typsubscripting;
+
#ifdef CATALOG_VARLEN /* variable-length fields start here */
/*
@@ -236,7 +242,7 @@ typedef FormData_pg_type *Form_pg_type;
* compiler constants for pg_type
* ----------------
*/
-#define Natts_pg_type 30
+#define Natts_pg_type 31
#define Anum_pg_type_typname 1
#define Anum_pg_type_typnamespace 2
#define Anum_pg_type_typowner 3
@@ -264,10 +270,10 @@ typedef FormData_pg_type *Form_pg_type;
#define Anum_pg_type_typtypmod 25
#define Anum_pg_type_typndims 26
#define Anum_pg_type_typcollation 27
-#define Anum_pg_type_typdefaultbin 28
-#define Anum_pg_type_typdefault 29
-#define Anum_pg_type_typacl 30
-
+#define Anum_pg_type_typsubscripting 28
+#define Anum_pg_type_typdefaultbin 29
+#define Anum_pg_type_typdefault 30
+#define Anum_pg_type_typacl 31
/* ----------------
* initial contents of pg_type
@@ -283,94 +289,94 @@ typedef FormData_pg_type *Form_pg_type;
*/
/* OIDS 1 - 99 */
-DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 16 ( bool PGNSP PGUID 1 t b B t t \054 0 0 1000 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("boolean, 'true'/'false'");
#define BOOLOID 16
-DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 17 ( bytea PGNSP PGUID -1 f b U f t \054 0 0 1001 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("variable-length string, binary values escaped");
#define BYTEAOID 17
-DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 18 ( char PGNSP PGUID 1 t b S f t \054 0 0 1002 charin charout charrecv charsend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("single character");
#define CHAROID 18
-DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 19 ( name PGNSP PGUID NAMEDATALEN f b S f t \054 0 18 1003 namein nameout namerecv namesend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("63-byte type for storing system identifiers");
#define NAMEOID 19
-DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 20 ( int8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 1016 int8in int8out int8recv int8send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("~18 digit integer, 8-byte storage");
#define INT8OID 20
-DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 21 ( int2 PGNSP PGUID 2 t b N f t \054 0 0 1005 int2in int2out int2recv int2send - - - s p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("-32 thousand to 32 thousand, 2-byte storage");
#define INT2OID 21
-DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 22 ( int2vector PGNSP PGUID -1 f b A f t \054 0 21 1006 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("array of int2, used in system tables");
#define INT2VECTOROID 22
-DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 23 ( int4 PGNSP PGUID 4 t b N f t \054 0 0 1007 int4in int4out int4recv int4send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("-2 billion to 2 billion integer, 4-byte storage");
#define INT4OID 23
-DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 24 ( regproc PGNSP PGUID 4 t b N f t \054 0 0 1008 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered procedure");
#define REGPROCOID 24
-DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 25 ( text PGNSP PGUID -1 f b S t t \054 0 0 1009 textin textout textrecv textsend - - - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("variable-length string, no limit specified");
#define TEXTOID 25
-DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 26 ( oid PGNSP PGUID 4 t b N t t \054 0 0 1028 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("object identifier(oid), maximum 4 billion");
#define OIDOID 26
-DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 27 ( tid PGNSP PGUID 6 f b U f t \054 0 0 1010 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("(block, offset), physical location of tuple");
#define TIDOID 27
-DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 28 ( xid PGNSP PGUID 4 t b U f t \054 0 0 1011 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("transaction id");
#define XIDOID 28
-DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 29 ( cid PGNSP PGUID 4 t b U f t \054 0 0 1012 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("command identifier type, sequence in transaction id");
#define CIDOID 29
-DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 30 ( oidvector PGNSP PGUID -1 f b A f t \054 0 26 1013 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("array of oids, used in system tables");
#define OIDVECTOROID 30
/* hand-built rowtype entries for bootstrapped catalogs */
/* NB: OIDs assigned here must match the BKI_ROWTYPE_OID declarations */
-DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 71 ( pg_type PGNSP PGUID -1 f c C f t \054 1247 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
+DATA(insert OID = 75 ( pg_attribute PGNSP PGUID -1 f c C f t \054 1249 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
+DATA(insert OID = 81 ( pg_proc PGNSP PGUID -1 f c C f t \054 1255 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
+DATA(insert OID = 83 ( pg_class PGNSP PGUID -1 f c C f t \054 1259 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
/* OIDS 100 - 199 */
-DATA(insert OID = 114 ( json PGNSP PGUID -1 f b U f t \054 0 0 199 json_in json_out json_recv json_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 114 ( json PGNSP PGUID -1 f b U f t \054 0 0 199 json_in json_out json_recv json_send - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define JSONOID 114
-DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 142 ( xml PGNSP PGUID -1 f b U f t \054 0 0 143 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("XML content");
#define XMLOID 142
-DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 199 ( _json PGNSP PGUID -1 f b A f t \054 0 114 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 143 ( _xml PGNSP PGUID -1 f b A f t \054 0 142 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 199 ( _json PGNSP PGUID -1 f b A f t \054 0 114 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
-DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 194 ( pg_node_tree PGNSP PGUID -1 f b S f t \054 0 0 0 pg_node_tree_in pg_node_tree_out pg_node_tree_recv pg_node_tree_send - - - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("string representing an internal node tree");
#define PGNODETREEOID 194
-DATA(insert OID = 32 ( pg_ddl_command PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 pg_ddl_command_in pg_ddl_command_out pg_ddl_command_recv pg_ddl_command_send - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 32 ( pg_ddl_command PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 pg_ddl_command_in pg_ddl_command_out pg_ddl_command_recv pg_ddl_command_send - - - ALIGNOF_POINTER p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("internal type for passing CollectedCommand");
#define PGDDLCOMMANDOID 32
/* OIDS 200 - 299 */
-DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 210 ( smgr PGNSP PGUID 2 t b U f t \054 0 0 0 smgrin smgrout - - - - - s p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("storage manager");
/* OIDS 300 - 399 */
@@ -380,276 +386,276 @@ DESCR("storage manager");
/* OIDS 500 - 599 */
/* OIDS 600 - 699 */
-DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 600 ( point PGNSP PGUID 16 f b G f t \054 0 701 1017 point_in point_out point_recv point_send - - - d p f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("geometric point '(x, y)'");
#define POINTOID 600
-DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 601 ( lseg PGNSP PGUID 32 f b G f t \054 0 600 1018 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("geometric line segment '(pt1,pt2)'");
#define LSEGOID 601
-DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 602 ( path PGNSP PGUID -1 f b G f t \054 0 0 1019 path_in path_out path_recv path_send - - - d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("geometric path '(pt1,...)'");
#define PATHOID 602
-DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 603 ( box PGNSP PGUID 32 f b G f t \073 0 600 1020 box_in box_out box_recv box_send - - - d p f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("geometric box '(lower left,upper right)'");
#define BOXOID 603
-DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 604 ( polygon PGNSP PGUID -1 f b G f t \054 0 0 1027 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("geometric polygon '(pt1,...)'");
#define POLYGONOID 604
-DATA(insert OID = 628 ( line PGNSP PGUID 24 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 628 ( line PGNSP PGUID 24 f b G f t \054 0 701 629 line_in line_out line_recv line_send - - - d p f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
DESCR("geometric line");
#define LINEOID 628
-DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 629 ( _line PGNSP PGUID -1 f b A f t \054 0 628 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* OIDS 700 - 799 */
-DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 700 ( float4 PGNSP PGUID 4 FLOAT4PASSBYVAL b N f t \054 0 0 1021 float4in float4out float4recv float4send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("single-precision floating point number, 4-byte storage");
#define FLOAT4OID 700
-DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 701 ( float8 PGNSP PGUID 8 FLOAT8PASSBYVAL b N t t \054 0 0 1022 float8in float8out float8recv float8send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("double-precision floating point number, 8-byte storage");
#define FLOAT8OID 701
-DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 702 ( abstime PGNSP PGUID 4 t b D f t \054 0 0 1023 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("absolute, limited-range date and time (Unix system time)");
#define ABSTIMEOID 702
-DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 703 ( reltime PGNSP PGUID 4 t b T f t \054 0 0 1024 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("relative, limited-range time interval (Unix delta time)");
#define RELTIMEOID 703
-DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 704 ( tinterval PGNSP PGUID 12 f b T f t \054 0 0 1025 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("(abstime,abstime), time interval");
#define TINTERVALOID 704
-DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 705 ( unknown PGNSP PGUID -2 f b X f t \054 0 0 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("");
#define UNKNOWNOID 705
-DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 718 ( circle PGNSP PGUID 24 f b G f t \054 0 0 719 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("geometric circle '(center,radius)'");
#define CIRCLEOID 718
-DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 719 ( _circle PGNSP PGUID -1 f b A f t \054 0 718 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 790 ( money PGNSP PGUID 8 FLOAT8PASSBYVAL b N f t \054 0 0 791 cash_in cash_out cash_recv cash_send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("monetary amounts, $d,ddd.cc");
#define CASHOID 790
-DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 791 ( _money PGNSP PGUID -1 f b A f t \054 0 790 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* OIDS 800 - 899 */
-DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 829 ( macaddr PGNSP PGUID 6 f b U f t \054 0 0 1040 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("XX:XX:XX:XX:XX:XX, MAC address");
#define MACADDROID 829
-DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 869 ( inet PGNSP PGUID -1 f b I t t \054 0 0 1041 inet_in inet_out inet_recv inet_send - - - i m f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("IP address/netmask, host address, netmask optional");
#define INETOID 869
-DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 650 ( cidr PGNSP PGUID -1 f b I f t \054 0 0 651 cidr_in cidr_out cidr_recv cidr_send - - - i m f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("network IP address/netmask, network address");
#define CIDROID 650
/* OIDS 900 - 999 */
/* OIDS 1000 - 1099 */
-DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1000 ( _bool PGNSP PGUID -1 f b A f t \054 0 16 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1001 ( _bytea PGNSP PGUID -1 f b A f t \054 0 17 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1002 ( _char PGNSP PGUID -1 f b A f t \054 0 18 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1003 ( _name PGNSP PGUID -1 f b A f t \054 0 19 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1005 ( _int2 PGNSP PGUID -1 f b A f t \054 0 21 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
#define INT2ARRAYOID 1005
-DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1006 ( _int2vector PGNSP PGUID -1 f b A f t \054 0 22 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1007 ( _int4 PGNSP PGUID -1 f b A f t \054 0 23 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
#define INT4ARRAYOID 1007
-DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1008 ( _regproc PGNSP PGUID -1 f b A f t \054 0 24 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1009 ( _text PGNSP PGUID -1 f b A f t \054 0 25 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 100 array_subscripting _null_ _null_ _null_ ));
#define TEXTARRAYOID 1009
-DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1028 ( _oid PGNSP PGUID -1 f b A f t \054 0 26 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
#define OIDARRAYOID 1028
-DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
-DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout array_typanalyze i x f 0 -1 0 100 _null_ _null_ _null_ ));
-DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1010 ( _tid PGNSP PGUID -1 f b A f t \054 0 27 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1011 ( _xid PGNSP PGUID -1 f b A f t \054 0 28 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1012 ( _cid PGNSP PGUID -1 f b A f t \054 0 29 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1013 ( _oidvector PGNSP PGUID -1 f b A f t \054 0 30 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1014 ( _bpchar PGNSP PGUID -1 f b A f t \054 0 1042 0 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout array_typanalyze i x f 0 -1 0 100 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1015 ( _varchar PGNSP PGUID -1 f b A f t \054 0 1043 0 array_in array_out array_recv array_send varchartypmodin varchartypmodout array_typanalyze i x f 0 -1 0 100 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1016 ( _int8 PGNSP PGUID -1 f b A f t \054 0 20 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1017 ( _point PGNSP PGUID -1 f b A f t \054 0 600 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1018 ( _lseg PGNSP PGUID -1 f b A f t \054 0 601 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1019 ( _path PGNSP PGUID -1 f b A f t \054 0 602 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1020 ( _box PGNSP PGUID -1 f b A f t \073 0 603 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1021 ( _float4 PGNSP PGUID -1 f b A f t \054 0 700 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
#define FLOAT4ARRAYOID 1021
-DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1022 ( _float8 PGNSP PGUID -1 f b A f t \054 0 701 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1023 ( _abstime PGNSP PGUID -1 f b A f t \054 0 702 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1024 ( _reltime PGNSP PGUID -1 f b A f t \054 0 703 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1025 ( _tinterval PGNSP PGUID -1 f b A f t \054 0 704 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1027 ( _polygon PGNSP PGUID -1 f b A f t \054 0 604 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1033 ( aclitem PGNSP PGUID 12 f b U f t \054 0 0 1034 aclitemin aclitemout - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("access control list");
#define ACLITEMOID 1033
-DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1034 ( _aclitem PGNSP PGUID -1 f b A f t \054 0 1033 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1040 ( _macaddr PGNSP PGUID -1 f b A f t \054 0 829 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1041 ( _inet PGNSP PGUID -1 f b A f t \054 0 869 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 651 ( _cidr PGNSP PGUID -1 f b A f t \054 0 650 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1263 ( _cstring PGNSP PGUID -1 f b A f t \054 0 2275 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
#define CSTRINGARRAYOID 1263
-DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1042 ( bpchar PGNSP PGUID -1 f b S f t \054 0 0 1014 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("char(length), blank-padded string, fixed storage length");
#define BPCHAROID 1042
-DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 100 _null_ _null_ _null_ ));
+DATA(insert OID = 1043 ( varchar PGNSP PGUID -1 f b S f t \054 0 0 1015 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 100 - _null_ _null_ _null_ ));
DESCR("varchar(length), non-blank-padded string, variable storage length");
#define VARCHAROID 1043
-DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1082 ( date PGNSP PGUID 4 t b D f t \054 0 0 1182 date_in date_out date_recv date_send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("date");
#define DATEOID 1082
-DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1083 ( time PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1183 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("time of day");
#define TIMEOID 1083
/* OIDS 1100 - 1199 */
-DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1114 ( timestamp PGNSP PGUID 8 FLOAT8PASSBYVAL b D f t \054 0 0 1115 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("date and time");
#define TIMESTAMPOID 1114
-DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1115 ( _timestamp PGNSP PGUID -1 f b A f t \054 0 1114 0 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1182 ( _date PGNSP PGUID -1 f b A f t \054 0 1082 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1183 ( _time PGNSP PGUID -1 f b A f t \054 0 1083 0 array_in array_out array_recv array_send timetypmodin timetypmodout array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1184 ( timestamptz PGNSP PGUID 8 FLOAT8PASSBYVAL b D t t \054 0 0 1185 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("date and time with time zone");
#define TIMESTAMPTZOID 1184
-DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b A f t \054 0 1184 0 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1186 ( interval PGNSP PGUID 16 f b T t t \054 0 0 1187 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("@ <number> <units>, time interval");
#define INTERVALOID 1186
-DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1187 ( _interval PGNSP PGUID -1 f b A f t \054 0 1186 0 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* OIDS 1200 - 1299 */
-DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1231 ( _numeric PGNSP PGUID -1 f b A f t \054 0 1700 0 array_in array_out array_recv array_send numerictypmodin numerictypmodout array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1266 ( timetz PGNSP PGUID 12 f b D f t \054 0 0 1270 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("time of day with time zone");
#define TIMETZOID 1266
-DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1270 ( _timetz PGNSP PGUID -1 f b A f t \054 0 1266 0 array_in array_out array_recv array_send timetztypmodin timetztypmodout array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* OIDS 1500 - 1599 */
-DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1560 ( bit PGNSP PGUID -1 f b V f t \054 0 0 1561 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("fixed-length bit string");
#define BITOID 1560
-DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1561 ( _bit PGNSP PGUID -1 f b A f t \054 0 1560 0 array_in array_out array_recv array_send bittypmodin bittypmodout array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 1562 ( varbit PGNSP PGUID -1 f b V t t \054 0 0 1563 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("variable-length bit string");
#define VARBITOID 1562
-DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1563 ( _varbit PGNSP PGUID -1 f b A f t \054 0 1562 0 array_in array_out array_recv array_send varbittypmodin varbittypmodout array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* OIDS 1600 - 1699 */
/* OIDS 1700 - 1799 */
-DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1700 ( numeric PGNSP PGUID -1 f b N f t \054 0 0 1231 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("numeric(precision, decimal), arbitrary precision number");
#define NUMERICOID 1700
-DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 1790 ( refcursor PGNSP PGUID -1 f b U f t \054 0 0 2201 textin textout textrecv textsend - - - i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("reference to cursor (portal name)");
#define REFCURSOROID 1790
/* OIDS 2200 - 2299 */
-DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2201 ( _refcursor PGNSP PGUID -1 f b A f t \054 0 1790 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
-DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2202 ( regprocedure PGNSP PGUID 4 t b N f t \054 0 0 2207 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered procedure (with args)");
#define REGPROCEDUREOID 2202
-DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2203 ( regoper PGNSP PGUID 4 t b N f t \054 0 0 2208 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered operator");
#define REGOPEROID 2203
-DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2204 ( regoperator PGNSP PGUID 4 t b N f t \054 0 0 2209 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered operator (with args)");
#define REGOPERATOROID 2204
-DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2205 ( regclass PGNSP PGUID 4 t b N f t \054 0 0 2210 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered class");
#define REGCLASSOID 2205
-DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2206 ( regtype PGNSP PGUID 4 t b N f t \054 0 0 2211 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered type");
#define REGTYPEOID 2206
-DATA(insert OID = 4096 ( regrole PGNSP PGUID 4 t b N f t \054 0 0 4097 regrolein regroleout regrolerecv regrolesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4096 ( regrole PGNSP PGUID 4 t b N f t \054 0 0 4097 regrolein regroleout regrolerecv regrolesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered role");
#define REGROLEOID 4096
-DATA(insert OID = 4089 ( regnamespace PGNSP PGUID 4 t b N f t \054 0 0 4090 regnamespacein regnamespaceout regnamespacerecv regnamespacesend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4089 ( regnamespace PGNSP PGUID 4 t b N f t \054 0 0 4090 regnamespacein regnamespaceout regnamespacerecv regnamespacesend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered namespace");
#define REGNAMESPACEOID 4089
-DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b A f t \054 0 2202 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 2208 ( _regoper PGNSP PGUID -1 f b A f t \054 0 2203 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 2209 ( _regoperator PGNSP PGUID -1 f b A f t \054 0 2204 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b A f t \054 0 2205 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b A f t \054 0 2206 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
#define REGTYPEARRAYOID 2211
-DATA(insert OID = 4097 ( _regrole PGNSP PGUID -1 f b A f t \054 0 4096 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 4090 ( _regnamespace PGNSP PGUID -1 f b A f t \054 0 4089 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 4097 ( _regrole PGNSP PGUID -1 f b A f t \054 0 4096 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 4090 ( _regnamespace PGNSP PGUID -1 f b A f t \054 0 4089 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* uuid */
-DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b U f t \054 0 0 2951 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("UUID datatype");
#define UUIDOID 2950
-DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b A f t \054 0 2950 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* pg_lsn */
-DATA(insert OID = 3220 ( pg_lsn PGNSP PGUID 8 FLOAT8PASSBYVAL b U f t \054 0 0 3221 pg_lsn_in pg_lsn_out pg_lsn_recv pg_lsn_send - - - d p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3220 ( pg_lsn PGNSP PGUID 8 FLOAT8PASSBYVAL b U f t \054 0 0 3221 pg_lsn_in pg_lsn_out pg_lsn_recv pg_lsn_send - - - d p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("PostgreSQL LSN datatype");
#define LSNOID 3220
-DATA(insert OID = 3221 ( _pg_lsn PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3221 ( _pg_lsn PGNSP PGUID -1 f b A f t \054 0 3220 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* text search */
-DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3614 ( tsvector PGNSP PGUID -1 f b U f t \054 0 0 3643 tsvectorin tsvectorout tsvectorrecv tsvectorsend - - ts_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("text representation for text search");
#define TSVECTOROID 3614
-DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3642 ( gtsvector PGNSP PGUID -1 f b U f t \054 0 0 3644 gtsvectorin gtsvectorout - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("GiST index internal text representation for text search");
#define GTSVECTOROID 3642
-DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3615 ( tsquery PGNSP PGUID -1 f b U f t \054 0 0 3645 tsqueryin tsqueryout tsqueryrecv tsquerysend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("query representation for text search");
#define TSQUERYOID 3615
-DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3734 ( regconfig PGNSP PGUID 4 t b N f t \054 0 0 3735 regconfigin regconfigout regconfigrecv regconfigsend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered text search configuration");
#define REGCONFIGOID 3734
-DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3769 ( regdictionary PGNSP PGUID 4 t b N f t \054 0 0 3770 regdictionaryin regdictionaryout regdictionaryrecv regdictionarysend - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("registered text search dictionary");
#define REGDICTIONARYOID 3769
-DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3643 ( _tsvector PGNSP PGUID -1 f b A f t \054 0 3614 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3644 ( _gtsvector PGNSP PGUID -1 f b A f t \054 0 3642 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3645 ( _tsquery PGNSP PGUID -1 f b A f t \054 0 3615 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3735 ( _regconfig PGNSP PGUID -1 f b A f t \054 0 3734 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3770 ( _regdictionary PGNSP PGUID -1 f b A f t \054 0 3769 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* jsonb */
-DATA(insert OID = 3802 ( jsonb PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3802 ( jsonb PGNSP PGUID -1 f b U f t \054 0 0 3807 jsonb_in jsonb_out jsonb_recv jsonb_send - - - i x f 0 -1 0 0 jsonb_subscripting _null_ _null_ _null_ ));
DESCR("Binary JSON");
#define JSONBOID 3802
-DATA(insert OID = 3807 ( _jsonb PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3807 ( _jsonb PGNSP PGUID -1 f b A f t \054 0 3802 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
-DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2970 ( txid_snapshot PGNSP PGUID -1 f b U f t \054 0 0 2949 txid_snapshot_in txid_snapshot_out txid_snapshot_recv txid_snapshot_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("txid snapshot");
-DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2949 ( _txid_snapshot PGNSP PGUID -1 f b A f t \054 0 2970 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/* range types */
-DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3904 ( int4range PGNSP PGUID -1 f r R f t \054 0 0 3905 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of integers");
#define INT4RANGEOID 3904
-DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3905 ( _int4range PGNSP PGUID -1 f b A f t \054 0 3904 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3906 ( numrange PGNSP PGUID -1 f r R f t \054 0 0 3907 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of numerics");
-DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3907 ( _numrange PGNSP PGUID -1 f b A f t \054 0 3906 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3908 ( tsrange PGNSP PGUID -1 f r R f t \054 0 0 3909 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of timestamps without time zone");
-DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3909 ( _tsrange PGNSP PGUID -1 f b A f t \054 0 3908 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3910 ( tstzrange PGNSP PGUID -1 f r R f t \054 0 0 3911 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of timestamps with time zone");
-DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3911 ( _tstzrange PGNSP PGUID -1 f b A f t \054 0 3910 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3912 ( daterange PGNSP PGUID -1 f r R f t \054 0 0 3913 range_in range_out range_recv range_send - - range_typanalyze i x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of dates");
-DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 _null_ _null_ _null_ ));
-DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3913 ( _daterange PGNSP PGUID -1 f b A f t \054 0 3912 0 array_in array_out array_recv array_send - - array_typanalyze i x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
+DATA(insert OID = 3926 ( int8range PGNSP PGUID -1 f r R f t \054 0 0 3927 range_in range_out range_recv range_send - - range_typanalyze d x f 0 -1 0 0 - _null_ _null_ _null_ ));
DESCR("range of bigints");
-DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
/*
* pseudo-types
@@ -664,41 +670,41 @@ DATA(insert OID = 3927 ( _int8range PGNSP PGUID -1 f b A f t \054 0 3926 0 arr
* but there is now support for it in records and arrays. Perhaps we should
* just treat it as a regular base type?
*/
-DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2249 ( record PGNSP PGUID -1 f p P f t \054 0 0 2287 record_in record_out record_recv record_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define RECORDOID 2249
-DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2287 ( _record PGNSP PGUID -1 f p P f t \054 0 2249 0 array_in array_out array_recv array_send - - array_typanalyze d x f 0 -1 0 0 array_subscripting _null_ _null_ _null_ ));
#define RECORDARRAYOID 2287
-DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2275 ( cstring PGNSP PGUID -2 f p P f t \054 0 0 1263 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define CSTRINGOID 2275
-DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2276 ( any PGNSP PGUID 4 t p P f t \054 0 0 0 any_in any_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYOID 2276
-DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2277 ( anyarray PGNSP PGUID -1 f p P f t \054 0 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYARRAYOID 2277
-DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out void_recv void_send - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2278 ( void PGNSP PGUID 4 t p P f t \054 0 0 0 void_in void_out void_recv void_send - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define VOIDOID 2278
-DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2279 ( trigger PGNSP PGUID 4 t p P f t \054 0 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define TRIGGEROID 2279
-DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3838 ( event_trigger PGNSP PGUID 4 t p P f t \054 0 0 0 event_trigger_in event_trigger_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define EVTTRIGGEROID 3838
-DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2280 ( language_handler PGNSP PGUID 4 t p P f t \054 0 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define LANGUAGE_HANDLEROID 2280
-DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2281 ( internal PGNSP PGUID SIZEOF_POINTER t p P f t \054 0 0 0 internal_in internal_out - - - - - ALIGNOF_POINTER p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define INTERNALOID 2281
-DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p P f t \054 0 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define OPAQUEOID 2282
-DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p P f t \054 0 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYELEMENTOID 2283
-DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 2776 ( anynonarray PGNSP PGUID 4 t p P f t \054 0 0 0 anynonarray_in anynonarray_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYNONARRAYOID 2776
-DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3500 ( anyenum PGNSP PGUID 4 t p P f t \054 0 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYENUMOID 3500
-DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3115 ( fdw_handler PGNSP PGUID 4 t p P f t \054 0 0 0 fdw_handler_in fdw_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define FDW_HANDLEROID 3115
-DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 325 ( index_am_handler PGNSP PGUID 4 t p P f t \054 0 0 0 index_am_handler_in index_am_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define INDEX_AM_HANDLEROID 325
-DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3310 ( tsm_handler PGNSP PGUID 4 t p P f t \054 0 0 0 tsm_handler_in tsm_handler_out - - - - - i p f 0 -1 0 0 - _null_ _null_ _null_ ));
#define TSM_HANDLEROID 3310
-DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 _null_ _null_ _null_ ));
+DATA(insert OID = 3831 ( anyrange PGNSP PGUID -1 f p P f t \054 0 0 0 anyrange_in anyrange_out - - - - - d x f 0 -1 0 0 - _null_ _null_ _null_ ));
#define ANYRANGEOID 3831
diff --git a/src/include/catalog/pg_type_fn.h b/src/include/catalog/pg_type_fn.h
index d7bbfdb..d712f6a 100644
--- a/src/include/catalog/pg_type_fn.h
+++ b/src/include/catalog/pg_type_fn.h
@@ -52,7 +52,8 @@ extern ObjectAddress TypeCreate(Oid newTypeOid,
int32 typeMod,
int32 typNDims,
bool typeNotNull,
- Oid typeCollation);
+ Oid typeCollation,
+ Oid subscriptingProcedure);
extern void GenerateTypeDependencies(Oid typeNamespace,
Oid typeObjectId,
@@ -70,6 +71,7 @@ extern void GenerateTypeDependencies(Oid typeNamespace,
bool isImplicitArray,
Oid baseType,
Oid typeCollation,
+ Oid subscriptingProcedure,
Node *defaultExpr,
bool rebuild);
diff --git a/src/include/funcapi.h b/src/include/funcapi.h
index e73a824..f53096a 100644
--- a/src/include/funcapi.h
+++ b/src/include/funcapi.h
@@ -183,6 +183,7 @@ extern TupleDesc build_function_result_tupdesc_d(Datum proallargtypes,
Datum proargmodes,
Datum proargnames);
extern TupleDesc build_function_result_tupdesc_t(HeapTuple procTuple);
+extern void get_slice_arguments(FunctionCallInfo fcinfo, int begin, int end, FunctionCallInfoData *sliced_fcinfo);
/*----------
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index d43ec56..c377b96 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -662,25 +662,52 @@ typedef struct WindowFuncExprState
} WindowFuncExprState;
/* ----------------
- * ArrayRefExprState node
+ * SubscriptingRefExprState node
*
* Note: array types can be fixed-length (typlen > 0), but only when the
* element type is itself fixed-length. Otherwise they are varlena structures
* and have typlen = -1. In any case, an array type is never pass-by-value.
* ----------------
*/
-typedef struct ArrayRefExprState
+typedef struct SubscriptingRefExprState
{
ExprState xprstate;
List *refupperindexpr; /* states for child nodes */
List *reflowerindexpr;
ExprState *refexpr;
ExprState *refassgnexpr;
- int16 refattrlength; /* typlen of array type */
- int16 refelemlength; /* typlen of the array element type */
+ int16 refattrlength; /* typlen of container type */
+ int16 refelemlength; /* typlen of the container element type */
bool refelembyval; /* is the element type pass-by-value? */
char refelemalign; /* typalign of the element type */
-} ArrayRefExprState;
+
+ /*
+ * Function manager's lookup info for the target subscripting operation. If
+ * fcache.fn_oid is InvalidOid, we haven't initialized it yet.
+ */
+ FmgrInfo fcache;
+} SubscriptingRefExprState;
+
+/* ---------------------------------
+ * Subscripting exec information
+ *
+ * It contains all information which is required to perform type-specific data
+ * extraction or modification. This information will be gathered in
+ * `ExecEvalSubscriptingRef` function and passed to `typsubscripting`
+ * procedure.
+ * ---------------------------------
+ */
+typedef struct SubscriptingExecData
+{
+ ExprContext *xprcontext; /* econtext for subscripting */
+ bool *isNull;
+ Datum *upper; /* upper boundary for subscripting */
+ Datum *lower; /* lower boundary for subscripting */
+ bool *upperProvided;
+ bool *lowerProvided;
+ Datum containerSource;
+ int indexprNumber;
+} SubscriptingExecData;
/* ----------------
* FuncExprState node
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index c514d3f..fe16710 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -140,7 +140,7 @@ typedef enum NodeTag
T_Aggref,
T_GroupingFunc,
T_WindowFunc,
- T_ArrayRef,
+ T_SubscriptingRef,
T_FuncExpr,
T_NamedArgExpr,
T_OpExpr,
@@ -194,7 +194,7 @@ typedef enum NodeTag
T_AggrefExprState,
T_GroupingFuncExprState,
T_WindowFuncExprState,
- T_ArrayRefExprState,
+ T_SubscriptingRefExprState,
T_FuncExprState,
T_ScalarArrayOpExprState,
T_BoolExprState,
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index 717d822..dab4f15 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -343,18 +343,18 @@ typedef struct WindowFunc
} WindowFunc;
/* ----------------
- * ArrayRef: describes an array subscripting operation
- *
- * An ArrayRef can describe fetching a single element from an array,
- * fetching a subarray (array slice), storing a single element into
- * an array, or storing a slice. The "store" cases work with an
- * initial array value and a source value that is inserted into the
- * appropriate part of the array; the result of the operation is an
- * entire new modified array value.
- *
- * If reflowerindexpr = NIL, then we are fetching or storing a single array
- * element at the subscripts given by refupperindexpr. Otherwise we are
- * fetching or storing an array slice, that is a rectangular subarray
+ * SubscriptingRef: describes a subscripting operation over a container
+ *
+ * An SubscriptingRef can describe fetching a single element from a container,
+ * fetching a part of container (e.g. array slice), storing a single element into
+ * a container, or storing a slice. The "store" cases work with an
+ * initial container value and a source value that is inserted into the
+ * appropriate part of the container; the result of the operation is an
+ * entire new modified container value.
+ *
+ * If reflowerindexpr = NIL, then we are fetching or storing a single container
+ * element at the subscripts given by refupperindexpr. Otherwise we are
+ * fetching or storing a container slice, that is a rectangular subcontainer
* with lower and upper bounds given by the index expressions.
* reflowerindexpr must be the same length as refupperindexpr when it
* is not NIL.
@@ -366,27 +366,27 @@ typedef struct WindowFunc
* element; but it is the array type when doing subarray fetch or either
* type of store.
*
- * Note: for the cases where an array is returned, if refexpr yields a R/W
- * expanded array, then the implementation is allowed to modify that object
+ * Note: for the cases where a container is returned, if refexpr yields a R/W
+ * expanded container, then the implementation is allowed to modify that object
* in-place and return the same object.)
* ----------------
*/
-typedef struct ArrayRef
+typedef struct SubscriptingRef
{
Expr xpr;
- Oid refarraytype; /* type of the array proper */
- Oid refelemtype; /* type of the array elements */
- int32 reftypmod; /* typmod of the array (and elements too) */
- Oid refcollid; /* OID of collation, or InvalidOid if none */
- List *refupperindexpr;/* expressions that evaluate to upper array
- * indexes */
- List *reflowerindexpr;/* expressions that evaluate to lower array
- * indexes, or NIL for single array element */
- Expr *refexpr; /* the expression that evaluates to an array
- * value */
- Expr *refassgnexpr; /* expression for the source value, or NULL if
- * fetch */
-} ArrayRef;
+ Oid refcontainertype; /* type of the container proper */
+ Oid refelemtype; /* type of the container elements */
+ int32 reftypmod; /* typmod of the container (and elements too) */
+ Oid refcollid; /* OID of collation, or InvalidOid if none */
+ List *refupperindexpr; /* expressions that evaluate to upper container
+ * indexes */
+ List *reflowerindexpr; /* expressions that evaluate to lower container
+ * indexes, or NIL for single container element */
+ Expr *refexpr; /* the expression that evaluates to an container
+ * value */
+ Expr *refassgnexpr; /* expression for the source value, or NULL if
+ * fetch */
+} SubscriptingRef;
/*
* CoercionContext - distinguishes the allowed set of type casts
@@ -727,7 +727,7 @@ typedef struct FieldSelect
*
* FieldStore represents the operation of modifying one field in a tuple
* value, yielding a new tuple value (the input is not touched!). Like
- * the assign case of ArrayRef, this is used to implement UPDATE of a
+ * the assign case of SubscriptingRef, this is used to implement UPDATE of a
* portion of a column.
*
* A single FieldStore can actually represent updates of several different
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index bd6dc02..838b3c1 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -225,12 +225,17 @@ extern void cancel_parser_errposition_callback(ParseCallbackState *pcbstate);
extern Var *make_var(ParseState *pstate, RangeTblEntry *rte, int attrno,
int location);
-extern Oid transformArrayType(Oid *arrayType, int32 *arrayTypmod);
-extern ArrayRef *transformArraySubscripts(ParseState *pstate,
- Node *arrayBase,
- Oid arrayType,
+extern Oid transformArrayType(Oid *containerType, int32 *containerTypmod);
+
+/* Type of a stage in case of type-specific subscripting procedure */
+#define SBS_VALIDATION 0x0001
+#define SBS_EXEC 0x0002
+
+extern SubscriptingRef *transformContainerSubscripts(ParseState *pstate,
+ Node *containerBase,
+ Oid containerType,
Oid elementType,
- int32 arrayTypMod,
+ int32 containerTypMod,
List *indirection,
Node *assignFrom);
extern Const *make_const(ParseState *pstate, Value *value, int location);
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index 6164f11..7fc1982 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -502,4 +502,9 @@ extern Datum array_positions(PG_FUNCTION_ARGS);
*/
extern Datum array_typanalyze(PG_FUNCTION_ARGS);
+/*
+ * prototypes for functions with array subscripting logic
+ */
+extern Datum array_subscripting(PG_FUNCTION_ARGS);
+
#endif /* ARRAY_H */
diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h
index 470d5b1..6cc8053 100644
--- a/src/include/utils/jsonb.h
+++ b/src/include/utils/jsonb.h
@@ -354,6 +354,7 @@ extern Datum jsonb_typeof(PG_FUNCTION_ARGS);
/* generator routines */
extern Datum to_jsonb(PG_FUNCTION_ARGS);
+extern JsonbValue *to_jsonb_worker(Datum source, Oid source_type);
extern Datum jsonb_build_object(PG_FUNCTION_ARGS);
extern Datum jsonb_build_object_noargs(PG_FUNCTION_ARGS);
@@ -427,6 +428,7 @@ extern JsonbValue *pushJsonbValue(JsonbParseState **pstate,
extern JsonbIterator *JsonbIteratorInit(JsonbContainer *container);
extern JsonbIteratorToken JsonbIteratorNext(JsonbIterator **it, JsonbValue *val,
bool skipNested);
+extern JsonbValue *JsonbToJsonbValue(Jsonb *jsonb);
extern Jsonb *JsonbValueToJsonb(JsonbValue *val);
extern bool JsonbDeepContains(JsonbIterator **val,
JsonbIterator **mContained);
@@ -439,4 +441,7 @@ extern char *JsonbToCStringIndent(StringInfo out, JsonbContainer *in,
int estimated_len);
+/* Jsonb subscripting logic */
+extern Datum jsonb_subscripting(PG_FUNCTION_ARGS);
+
#endif /* __JSONB_H__ */
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index dcb8980..5c09bd0 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -159,6 +159,7 @@ extern void free_attstatsslot(Oid atttype,
extern char *get_namespace_name(Oid nspid);
extern char *get_namespace_name_or_temp(Oid nspid);
extern Oid get_range_subtype(Oid rangeOid);
+extern RegProcedure get_typsubscripting(Oid typid);
#define type_is_array(typid) (get_element_type(typid) != InvalidOid)
/* type_is_array_domain accepts both plain arrays and domains over arrays */
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 77e7440..79608a9 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -4710,7 +4710,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
/*
* Evaluate the subscripts, switch into left-to-right order.
- * Like ExecEvalArrayRef(), complain if any subscript is null.
+ * Like ExecEvalSubscriptingRef(), complain if any subscript is null.
*/
for (i = 0; i < nsubscripts; i++)
{
@@ -4758,7 +4758,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
* fixed-length array types we skip the assignment. We can't
* support assignment of a null entry into a fixed-length
* array, either, so that's a no-op too. This is all ugly but
- * corresponds to the current behavior of ExecEvalArrayRef().
+ * corresponds to the current behavior of ExecEvalSubscriptingRef().
*/
if (arrayelem->arraytyplen > 0 && /* fixed-length array? */
(oldarrayisnull || isNull))
@@ -6462,9 +6462,9 @@ exec_simple_check_node(Node *node)
case T_Param:
return TRUE;
- case T_ArrayRef:
+ case T_SubscriptingRef:
{
- ArrayRef *expr = (ArrayRef *) node;
+ SubscriptingRef *expr = (SubscriptingRef *) node;
if (!exec_simple_check_node((Node *) expr->refupperindexpr))
return FALSE;
diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out
index e2cb08a..dcacf20 100644
--- a/src/test/regress/expected/jsonb.out
+++ b/src/test/regress/expected/jsonb.out
@@ -3447,3 +3447,211 @@ HINT: Try using the function jsonb_set to replace key value.
select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"', true);
ERROR: cannot replace existing key
HINT: Try using the function jsonb_set to replace key value.
+-- jsonb subscript
+select ('123'::jsonb)['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('123'::jsonb)[0];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": 1}'::jsonb)['a'];
+ jsonb
+-------
+ 1
+(1 row)
+
+select ('{"a": 1}'::jsonb)[0];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": 1}'::jsonb)['not_exist'];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[0];
+ jsonb
+-------
+ 1
+(1 row)
+
+select ('[1, "2", null]'::jsonb)['1'];
+ jsonb
+-------
+ "2"
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[1.0];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[2];
+ jsonb
+-------
+ null
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[3];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[-2];
+ jsonb
+-------
+ "2"
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[1]['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('[1, "2", null]'::jsonb)[1][0];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['b'];
+ jsonb
+-------
+ "c"
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'];
+ jsonb
+-----------
+ [1, 2, 3]
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'][1];
+ jsonb
+-------
+ 2
+(1 row)
+
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d']['a'];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1'];
+ jsonb
+---------------
+ {"a2": "aaa"}
+(1 row)
+
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2'];
+ jsonb
+-------
+ "aaa"
+(1 row)
+
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']['a3'];
+ jsonb
+-------
+
+(1 row)
+
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'];
+ jsonb
+-----------------------
+ ["aaa", "bbb", "ccc"]
+(1 row)
+
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'][2];
+ jsonb
+-------
+ "ccc"
+(1 row)
+
+create TEMP TABLE test_jsonb_subscript (
+ id int,
+ test_json jsonb
+);
+insert into test_jsonb_subscript values
+(1, '{}'), -- empty jsonb
+(2, '{"key": "value"}'); -- jsonb with data
+-- update empty jsonb
+update test_jsonb_subscript set test_json['a'] = 1 where id = 1;
+select * from test_jsonb_subscript;
+ id | test_json
+----+------------------
+ 2 | {"key": "value"}
+ 1 | {"a": 1}
+(2 rows)
+
+-- update jsonb with some data
+update test_jsonb_subscript set test_json['a'] = 1 where id = 2;
+select * from test_jsonb_subscript;
+ id | test_json
+----+--------------------------
+ 1 | {"a": 1}
+ 2 | {"a": 1, "key": "value"}
+(2 rows)
+
+-- replace jsonb
+update test_jsonb_subscript set test_json['a'] = 'test';
+select * from test_jsonb_subscript;
+ id | test_json
+----+-------------------------------
+ 1 | {"a": "test"}
+ 2 | {"a": "test", "key": "value"}
+(2 rows)
+
+-- replace by object
+update test_jsonb_subscript set test_json['a'] = '{"b": 1}'::jsonb;
+select * from test_jsonb_subscript;
+ id | test_json
+----+---------------------------------
+ 1 | {"a": {"b": 1}}
+ 2 | {"a": {"b": 1}, "key": "value"}
+(2 rows)
+
+-- replace by array
+update test_jsonb_subscript set test_json['a'] = '[1, 2, 3]'::jsonb;
+select * from test_jsonb_subscript;
+ id | test_json
+----+----------------------------------
+ 1 | {"a": [1, 2, 3]}
+ 2 | {"a": [1, 2, 3], "key": "value"}
+(2 rows)
+
+-- use jsonb subscription in where clause
+select * from test_jsonb_subscript where test_json['key'] = '"value"';
+ id | test_json
+----+----------------------------------
+ 2 | {"a": [1, 2, 3], "key": "value"}
+(1 row)
+
+select * from test_jsonb_subscript where test_json['key_doesnt_exists'] = '"value"';
+ id | test_json
+----+-----------
+(0 rows)
+
+select * from test_jsonb_subscript where test_json['key'] = '"wrong_value"';
+ id | test_json
+----+-----------
+(0 rows)
+
diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql
index 6b4c796..e277e0d 100644
--- a/src/test/regress/sql/jsonb.sql
+++ b/src/test/regress/sql/jsonb.sql
@@ -868,3 +868,62 @@ select jsonb_insert('{"a": {"b": "value"}}', '{a, c}', '"new_value"', true);
select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"');
select jsonb_insert('{"a": {"b": "value"}}', '{a, b}', '"new_value"', true);
+
+-- jsonb subscript
+select ('123'::jsonb)['a'];
+select ('123'::jsonb)[0];
+select ('{"a": 1}'::jsonb)['a'];
+select ('{"a": 1}'::jsonb)[0];
+select ('{"a": 1}'::jsonb)['not_exist'];
+select ('[1, "2", null]'::jsonb)['a'];
+select ('[1, "2", null]'::jsonb)[0];
+select ('[1, "2", null]'::jsonb)['1'];
+select ('[1, "2", null]'::jsonb)[1.0];
+select ('[1, "2", null]'::jsonb)[2];
+select ('[1, "2", null]'::jsonb)[3];
+select ('[1, "2", null]'::jsonb)[-2];
+select ('[1, "2", null]'::jsonb)[1]['a'];
+select ('[1, "2", null]'::jsonb)[1][0];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['b'];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d'][1];
+select ('{"a": 1, "b": "c", "d": [1, 2, 3]}'::jsonb)['d']['a'];
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1'];
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2'];
+select ('{"a": {"a1": {"a2": "aaa"}}, "b": "bbb", "c": "ccc"}'::jsonb)['a']['a1']['a2']['a3'];
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'];
+select ('{"a": ["a1", {"b1": ["aaa", "bbb", "ccc"]}], "b": "bb"}'::jsonb)['a'][1]['b1'][2];
+
+create TEMP TABLE test_jsonb_subscript (
+ id int,
+ test_json jsonb
+);
+
+insert into test_jsonb_subscript values
+(1, '{}'), -- empty jsonb
+(2, '{"key": "value"}'); -- jsonb with data
+
+-- update empty jsonb
+update test_jsonb_subscript set test_json['a'] = 1 where id = 1;
+select * from test_jsonb_subscript;
+
+-- update jsonb with some data
+update test_jsonb_subscript set test_json['a'] = 1 where id = 2;
+select * from test_jsonb_subscript;
+
+-- replace jsonb
+update test_jsonb_subscript set test_json['a'] = 'test';
+select * from test_jsonb_subscript;
+
+-- replace by object
+update test_jsonb_subscript set test_json['a'] = '{"b": 1}'::jsonb;
+select * from test_jsonb_subscript;
+
+-- replace by array
+update test_jsonb_subscript set test_json['a'] = '[1, 2, 3]'::jsonb;
+select * from test_jsonb_subscript;
+
+-- use jsonb subscription in where clause
+select * from test_jsonb_subscript where test_json['key'] = '"value"';
+select * from test_jsonb_subscript where test_json['key_doesnt_exists'] = '"value"';
+select * from test_jsonb_subscript where test_json['key'] = '"wrong_value"';
diff --git a/src/tutorial/Makefile b/src/tutorial/Makefile
index 16dc390..0ead60c 100644
--- a/src/tutorial/Makefile
+++ b/src/tutorial/Makefile
@@ -13,8 +13,8 @@
#
#-------------------------------------------------------------------------
-MODULES = complex funcs
-DATA_built = advanced.sql basics.sql complex.sql funcs.sql syscat.sql
+MODULES = complex funcs subscripting
+DATA_built = advanced.sql basics.sql complex.sql funcs.sql syscat.sql subscripting.sql
ifdef NO_PGXS
subdir = src/tutorial
diff --git a/src/tutorial/subscripting.c b/src/tutorial/subscripting.c
new file mode 100644
index 0000000..edd93f1
--- /dev/null
+++ b/src/tutorial/subscripting.c
@@ -0,0 +1,230 @@
+/*
+ * src/tutorial/subscripting.c
+ *
+ ******************************************************************************
+ This file contains routines that can be bound to a Postgres backend and
+ called by the backend in the process of processing queries. The calling
+ format for these routines is dictated by Postgres architecture.
+******************************************************************************/
+
+#include "postgres.h"
+
+#include "catalog/pg_type.h"
+#include "executor/executor.h"
+#include "nodes/execnodes.h"
+#include "nodes/nodeFuncs.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_node.h"
+#include "utils/array.h"
+#include "fmgr.h"
+#include "funcapi.h"
+
+PG_MODULE_MAGIC;
+
+typedef struct Custom
+{
+ int first;
+ int second;
+} Custom;
+
+
+/*****************************************************************************
+ * Input/Output functions
+ *****************************************************************************/
+
+PG_FUNCTION_INFO_V1(custom_in);
+
+Datum
+custom_in(PG_FUNCTION_ARGS)
+{
+ char *str = PG_GETARG_CSTRING(0);
+ int firstValue,
+ secondValue;
+ Custom *result;
+
+ if (sscanf(str, " ( %d , %d )", &firstValue, &secondValue) != 2)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid input syntax for complex: \"%s\"",
+ str)));
+
+
+ result = (Custom *) palloc(sizeof(Custom));
+ result->first = firstValue;
+ result->second = secondValue;
+ PG_RETURN_POINTER(result);
+}
+
+PG_FUNCTION_INFO_V1(custom_out);
+
+Datum
+custom_out(PG_FUNCTION_ARGS)
+{
+ Custom *custom = (Custom *) PG_GETARG_POINTER(0);
+ char *result;
+
+ result = psprintf("(%d, %d)", custom->first, custom->second);
+ PG_RETURN_CSTRING(result);
+}
+
+/*****************************************************************************
+ * Custom subscripting logic functions
+ *****************************************************************************/
+
+Datum
+custom_subscripting_evaluate(PG_FUNCTION_ARGS)
+{
+ SubscriptingRefExprState *sbstate = (SubscriptingRefExprState *) PG_GETARG_POINTER(0);
+ SubscriptingExecData *sbsdata = (SubscriptingExecData *) PG_GETARG_POINTER(1);
+ SubscriptingRef *custom_ref = (SubscriptingRef *) sbstate->xprstate.expr;
+ Custom *result = (Custom *) sbsdata->containerSource;
+ bool *is_null = sbsdata->isNull;
+ bool is_assignment = (custom_ref->refassgnexpr != NULL);
+
+ int index;
+
+ if (sbsdata->indexprNumber != 1)
+ ereport(ERROR, (errmsg("custom does not support nested subscripting")));
+
+ index = DatumGetInt32(sbsdata->upper[0]);
+
+ if (is_assignment)
+ {
+ ExprContext *econtext = sbsdata->xprcontext;
+ Datum sourceData;
+ Datum save_datum;
+ bool save_isNull;
+ bool eisnull;
+
+ /*
+ * We might have a nested-assignment situation, in which the
+ * refassgnexpr is itself a FieldStore or SubscriptingRef that needs to
+ * obtain and modify the previous value of the array element or slice
+ * being replaced. If so, we have to extract that value from the
+ * array and pass it down via the econtext's caseValue. It's safe to
+ * reuse the CASE mechanism because there cannot be a CASE between
+ * here and where the value would be needed, and an array assignment
+ * can't be within a CASE either. (So saving and restoring the
+ * caseValue is just paranoia, but let's do it anyway.)
+ *
+ * Since fetching the old element might be a nontrivial expense, do it
+ * only if the argument appears to actually need it.
+ */
+ save_datum = econtext->caseValue_datum;
+ save_isNull = econtext->caseValue_isNull;
+
+ /*
+ * Evaluate the value to be assigned into the container.
+ */
+ sourceData = ExecEvalExpr(sbstate->refassgnexpr,
+ econtext,
+ &eisnull,
+ NULL);
+
+ econtext->caseValue_datum = save_datum;
+ econtext->caseValue_isNull = save_isNull;
+
+ /*
+ * For an assignment to a fixed-length array type, both the original
+ * array and the value to be assigned into it must be non-NULL, else
+ * we punt and return the original array.
+ */
+ if (sbstate->refattrlength > 0) /* fixed-length container? */
+ if (eisnull || *is_null)
+ return sbsdata->containerSource;
+
+ /*
+ * For assignment to varlena container, we handle a NULL original array
+ * by substituting an empty (zero-dimensional) array; insertion of the
+ * new element will result in a singleton array value. It does not
+ * matter whether the new element is NULL.
+ */
+ if (*is_null)
+ {
+ sbsdata->containerSource =
+ PointerGetDatum(construct_empty_array(custom_ref->refelemtype));
+ *is_null = false;
+ }
+
+ if (index == 1)
+ result->first = DatumGetInt32(sourceData);
+ else
+ result->second = DatumGetInt32(sourceData);
+
+ PG_RETURN_POINTER(result);
+ }
+ else
+ {
+ if (index == 1)
+ PG_RETURN_INT32(result->first);
+ else
+ PG_RETURN_INT32(result->second);
+ }
+}
+
+Datum
+custom_subscripting_prepare(PG_FUNCTION_ARGS)
+{
+ SubscriptingRef *sbsref = (SubscriptingRef *) PG_GETARG_POINTER(0);
+ ParseState *pstate = (ParseState *) PG_GETARG_POINTER(1);
+ List *upperIndexpr = NIL;
+ ListCell *l;
+
+ if (sbsref->reflowerindexpr != NIL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("custom subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *)lfirst(sbsref->reflowerindexpr->head))))));
+
+ foreach(l, sbsref->refupperindexpr)
+ {
+ Node *subexpr = (Node *) lfirst(l);
+
+ Assert(subexpr != NULL);
+
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("custom subscript does not support slices"),
+ parser_errposition(pstate, exprLocation(
+ ((Node *) lfirst(sbsref->refupperindexpr->head))))));
+
+ subexpr = coerce_to_target_type(pstate,
+ subexpr, exprType(subexpr),
+ INT4OID, -1,
+ COERCION_ASSIGNMENT,
+ COERCE_IMPLICIT_CAST,
+ -1);
+ if (subexpr == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("custom subscript must have int type"),
+ parser_errposition(pstate, exprLocation(subexpr))));
+
+ upperIndexpr = lappend(upperIndexpr, subexpr);
+ }
+
+ sbsref->refupperindexpr = upperIndexpr;
+ sbsref->refelemtype = INT4OID;
+
+ PG_RETURN_POINTER(sbsref);
+}
+
+PG_FUNCTION_INFO_V1(custom_subscripting);
+
+Datum
+custom_subscripting(PG_FUNCTION_ARGS)
+{
+ int op_type = PG_GETARG_INT32(0);
+ FunctionCallInfoData target_fcinfo = get_slice_arguments(fcinfo, 1,
+ fcinfo->nargs);
+
+ if (op_type & SBS_VALIDATION)
+ return custom_subscripting_prepare(&target_fcinfo);
+
+ if (op_type & SBS_EXEC)
+ return custom_subscripting_evaluate(&target_fcinfo);
+
+ elog(ERROR, "incorrect op_type for subscripting function: %d", op_type);
+}
diff --git a/src/tutorial/subscripting.source b/src/tutorial/subscripting.source
new file mode 100644
index 0000000..c37d236
--- /dev/null
+++ b/src/tutorial/subscripting.source
@@ -0,0 +1,71 @@
+---------------------------------------------------------------------------
+--
+-- subscripting.sql-
+-- This file shows how to create a new subscripting procedure for
+-- user-defined type.
+--
+--
+-- Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
+-- Portions Copyright (c) 1994, Regents of the University of California
+--
+-- src/tutorial/subscripting.source
+--
+---------------------------------------------------------------------------
+
+-----------------------------
+-- Creating a new type:
+-- We are going to create a new type called 'complex' which represents
+-- complex numbers.
+-- A user-defined type must have an input and an output function, and
+-- optionally can have binary input and output functions. All of these
+-- are usually user-defined C functions.
+-----------------------------
+
+-- Assume the user defined functions are in /home/erthalion/programms/postgresql-master/src/tutorial/complex$DLSUFFIX
+-- (we do not want to assume this is in the dynamic loader search path).
+-- Look at $PWD/complex.c for the source. Note that we declare all of
+-- them as STRICT, so we do not need to cope with NULL inputs in the
+-- C code. We also mark them IMMUTABLE, since they always return the
+-- same outputs given the same inputs.
+
+-- the input function 'complex_in' takes a null-terminated string (the
+-- textual representation of the type) and turns it into the internal
+-- (in memory) representation. You will get a message telling you 'complex'
+-- does not exist yet but that's okay.
+
+CREATE FUNCTION custom_in(cstring)
+ RETURNS custom
+ AS '_OBJWD_/subscripting'
+ LANGUAGE C IMMUTABLE STRICT;
+
+-- the output function 'complex_out' takes the internal representation and
+-- converts it into the textual representation.
+
+CREATE FUNCTION custom_out(custom)
+ RETURNS cstring
+ AS '_OBJWD_/subscripting'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE FUNCTION custom_subscripting(internal)
+ RETURNS internal
+ AS '_OBJWD_/subscripting'
+ LANGUAGE C IMMUTABLE STRICT;
+
+CREATE TYPE custom (
+ internallength = 8,
+ input = custom_in,
+ output = custom_out,
+ subscripting = custom_subscripting
+);
+
+-- we can use it in a table
+
+CREATE TABLE test_subscripting (
+ data custom,
+);
+
+INSERT INTO test_subscripting VALUES ('(1, 2)');
+
+SELECT data[0] from test_subscripting;
+
+UPDATE test_subscripting SET data[1] = 3;
Yes, but it was related to the idea of having `ArrayRef` and `JsonbRef` nodes
for specific types. Since now there is generic `SubscriptingRef` node, I think
it should be ok.
Sorry I misunderstood it.
Just to be clear - as far as I understood, these compilation problems were
caused not because the extension knew something about ArrayRef node in
particular, but because the extension tried to extract all nodes to generate
code from them. It means any change will require "refetching", so I think it's
natural for this extension.
Agree. It will be hard to maintain both nodes. And it is not so smart
to have two nodes ArrayRef (deprecated) and SubscriptingRef. It is not
hard to replace ArrayRef node with SubscriptingRef in other
extensions.
There is a little note:
#include "utils/lsyscache.h"
+#include "utils/syscache.h"
#include "utils/memutils.h"
I think "utils/syscache.h" isn't necessary here. PostgreSQL could be
compiled without this include.
I suppose that this patch can be marked as "Ready for commiter". Any opinions?
--
Artur Zakirov
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Dmitry Dolgov <9erthalion6@gmail.com> writes:
[ generic_type_subscription_v6.patch ]
Not too surprisingly, this doesn't apply anymore in the wake of commit
ea15e1867. Could you rebase? Changes for that should be pretty trivial
I'd expect.
I took an extremely quick look over the patch --- mostly looking
at the header file changes not the code --- and have a couple of
comments:
1. As I mentioned previously, it's a seriously bad idea that ArrayRef
is used for both array subscripting and array assignment. Let's fix
that while we're at it, rather than setting that mistake in even more
stone by embedding it in a datatype extension API.
2. I'm not very pleased that the subscripting functions have signature
"subscripting(internal) returns internal"; we have more than enough of
those already, and each one is a hazard for plugging the wrong function
into the wrong place. Worse yet, you're flat out lying about the number
of arguments that these functions actually expect to receive, which is
something that could get broken by any number of plausible future changes.
Can we arrange to do that differently? I'd prefer something in which the
argument and result types are visibly connected to the actual datatypes
at hand, for instance
array_subscript(anyarray, internal) returns anyelement
array_assign(anyarray, internal, anyelement) returns anyarray
where the "internal" argument is some representation of only the subscript
expressions. This would allow CREATE TYPE to perform some amount of
checking that the right function(s) had been specified. (If that means
we use two functions not one per datatype, that's fine with me.) If that
seems impractical, let's at least pick a signature that doesn't conflict
with any other INTERNAL-using APIs, and preferably has some connection
to what the arguments really are.
3. The contents of ArrayRef are designed on the assumption that the same
typmod and collation values apply to both an array and its elements.
That's okay for standard arrays, but I do not think it holds for every
other container situation. For example, hstore doesn't have collation
last I checked, but it would likely want to treat its element type as
being text, which does. So this needs to be generalized.
4. It looks like your work on the node processing infrastructure has been
limited to s/ArrayRef/SubscriptingRef/g, but that's not nearly enough.
SubscriptingRef needs to be regarded as an opportunity to invoke a
user-defined function, which means that it now acts quite a bit like
FuncExpr. For example, the function-to-be-invoked needs to be checked for
volatility, leakproofness, parallel safety, etc in operations that want to
know about such things. So check_functions_in_node(), for one, needs to
consider SubscriptingRef, and really you'll have to look at everyplace
that deals with FuncExpr and see if it needs a case for SubscriptingRef
now. I'd advise adding the OID of the subscripting function to
SubscriptingRef, so that those places don't need to do additional catalog
lookups to get it.
BTW, a different approach that might be worth considering is to say that
the nodetree representation of one of these things *is* a FuncExpr, and
the new magic thing is just that we invent a new CoercionForm value
which causes ruleutils.c to print the expression as a subscripting op.
I'm not quite convinced that that's a good idea --- a "coercion format"
that says "subscript" seems a bit weird --- but it would greatly reduce
the number of places you'd have to touch.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
I wrote:
... (If that means
we use two functions not one per datatype, that's fine with me.)
Actually, after thinking about that a bit more: you've really squeezed
*three* different APIs into one function. Namely, subscript-reference
parse analysis, array subscripting execution, and array assignment
execution. It would be cleaner, and would reduce runtime overhead a bit,
if those were embodied as three separate functions.
It might be possible to get away with having only one pg_type column,
pointing at the parse-analysis function. That function would generate
a SubscriptingRef tree node containing the OID of the appropriate
execution function, which execQual.c could call without ever knowing
its name explicitly.
This clearly would work for built-in types, since the parse-analysis
function could rely on fmgroids.h for the OIDs of the execution functions.
But I'm less sure if it would work in extensions, which won't have
predetermined OIDs for their functions. Is there a way for a function
in an extension to find the OID of one of its sibling functions?
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 1/23/17 1:36 PM, Tom Lane wrote:
Is there a way for a function
in an extension to find the OID of one of its sibling functions?
Obviously there's regprocedure (or it's C equivalent), but then you're
stuck re-computing at runtime. I've messed around with that a bit in an
effort to have an extension depend on another extension that allows the
user to specify it's schema. If you're already doing metaprogramming
it's not an enormous problem... if you're not already doing that it
sucks. Trying to make that work in C would be somewhere between
impossible and a nightmare.
Since this kind of thing affects extensions that depend on extensions,
it'd certainly be nice if there was some way to address it.
BTW, I actually do use SPI to call one of the reg casts in my variant
type, but that's just a hack I used in the beginning and haven't gotten
around to replacing. Since there's a static variable that gets set to
the relevant OID it's not that bad performance-wise from what I can
tell, but I suspect that's not something we want to be recommending to
others...
--
Jim Nasby, Data Architect, Blue Treble Consulting, Austin TX
Experts in Analytics, Data Architecture and PostgreSQL
Data in Trouble? Get it in Treble! http://BlueTreble.com
855-TREBLE2 (855-873-2532)
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
I wrote:
BTW, a different approach that might be worth considering is to say that
the nodetree representation of one of these things *is* a FuncExpr, and
the new magic thing is just that we invent a new CoercionForm value
which causes ruleutils.c to print the expression as a subscripting op.
I'm not quite convinced that that's a good idea --- a "coercion format"
that says "subscript" seems a bit weird --- but it would greatly reduce
the number of places you'd have to touch.
After sleeping on it, that approach is starting to sound better to me.
Consider a design like this:
* Leave ArrayRef strictly alone, and introduce new infrastructure beside
it for non-array containers. That sounds ugly at first, but it has two
significant advantages: you don't have to refactor or even touch any
array-related code, and you do not have to worry about somebody objecting
to the patch because it adds unacceptable overhead to existing array
operations. Converting array ops to go through a function-call API surely
must add *some* overhead, and at this point we don't have enough info to
be certain it would be negligibly small. (Testing the existing patch
cannot prove that, since as I noted yesterday, you're missing a lot of
plan-time manipulations that need to happen for a generic function call.)
* Let the node representation for non-array-container access be FuncExpr
with new value(s) of funcformat. You'd need to design the exact
representation to be used for the subscript arguments, but that doesn't
seem horribly complicated. In this way, you're not on the hook to
duplicate all the node-processing infrastructure for either ArrayRef
or FuncExpr --- ideally, you won't need to do much more in the core
system than provide the parse-time callout and write suitable deparsing
logic in ruleutils.c.
The ugliness of thinking that "subscripting" is a kind of coercion could
be dealt with by changing funcformat to some new enum type named something
like, say, FuncDisplayForm. This would require touching all the places
that mess with funcformat, but that could be a good thing anyway, because
you'd be sure that you'd looked at everything that might need changes.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Mon, Jan 23, 2017 at 2:07 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Can we arrange to do that differently? I'd prefer something in which the
argument and result types are visibly connected to the actual datatypes
at hand, for instance
array_subscript(anyarray, internal) returns anyelement
array_assign(anyarray, internal, anyelement) returns anyarray
What about having no internal arguments here at all? Like if you want
to support foo[4] define a subscript function that takes (mytype, int)
and returns whatever. You might have to allow for multiple
subscripting functions with different argument types for this to
really work, though.
/me ducks
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
Robert Haas <robertmhaas@gmail.com> writes:
On Mon, Jan 23, 2017 at 2:07 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
Can we arrange to do that differently? I'd prefer something in which the
argument and result types are visibly connected to the actual datatypes
at hand, for instance
array_subscript(anyarray, internal) returns anyelement
array_assign(anyarray, internal, anyelement) returns anyarray
What about having no internal arguments here at all? Like if you want
to support foo[4] define a subscript function that takes (mytype, int)
and returns whatever. You might have to allow for multiple
subscripting functions with different argument types for this to
really work, though.
Yeah, the problem is if you're trying to support the full generality
of the array slice syntax, it gets kind of painful to shoehorn that
into simple SQL types. Consider array[1][2:3][4:] or something like
that. We could say that extensions only have access to the basic
non-slice notation, but I'm sure someone would be unhappy with that
--- and even then, you don't really want to define six functions to
deal with six possible numbers of subscripts.
Another issue is that, if someone is trying to use this facility to define
array semantics that are less screwball than what Berkeley bequeathed us,
they might not be happy with the idea that a single function
"array_subscript(anyarray, internal) returns anyelement" is what
determines the result type of a subscripting operation. It might be for
example that you need to look at the subscripted object, as well as the
number of subscripts, before you know whether the result is an element or
a lower-dimension array. So I'd really prefer that the functionality
involve a parser callout, and that would certainly need "internal"
argument(s).
Given that it's a parser callout, what the signatures of the runtime
function(s) are is really not our problem; the callout can construct
any darn expression tree it pleases. There would only be some subset
of that space that ruleutils.c would know how to print as a subscripting
construct, but many people might be happy with the results reverse-listing
as a regular function or operator call.
regards, tom lane
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On Thu, Jan 26, 2017 at 1:38 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
So I'd really prefer that the functionality
involve a parser callout, and that would certainly need "internal"
argument(s).
Thanks, I see now.
--
Robert Haas
EnterpriseDB: http://www.enterprisedb.com
The Enterprise PostgreSQL Company
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 24 January 2017 at 02:07, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I took an extremely quick look over the patch
Thank you for the feedback. It took some time for me to think about all
suggestions and notes.
1. As I mentioned previously, it's a seriously bad idea that ArrayRef
is used for both array subscripting and array assignment. Let's fix
that while we're at it, rather than setting that mistake in even more
stone by embedding it in a datatype extension API.
Sure. But I assume that `SubscriptingRef` and `SubscriptingAssignmentRef`
will
be almost identical since they carry the same information to get a value
and to assign a new value (so, probably it will be just an alias with a
different related function).
2. I'm not very pleased that the subscripting functions have signature
"subscripting(internal) returns internal";
Basically, current implementation of subscripting operation contains node
related logic (e.g. like to verify that we're not using slice syntax for
jsonb)
and data type related logic (e.g. to get/to assign a value in an array).
And if
it's easy enough to use:
`array_subscript(anyarray, internal) returns anyelement`
`array_assign(anyarray, internal, anyelement) returns anyarray`
form for the second one, the first one should accept node as an argument and
return node - I'm not sure if it's possible to use something else than
`internal` here. Speaking about other signature issues, sure, I'll fix them.
3. The contents of ArrayRef are designed on the assumption that the same
typmod and collation values apply to both an array and its elements.
Yes, I missed that. It looks straightforward for me, we can just split
`refcollid` and `reftypmod` to `refcontainercollid`, `refelementcollid` and
`refcontainertypmod`, `refelementtypmod`.
4. It looks like your work on the node processing infrastructure has been
limited to s/ArrayRef/SubscriptingRef/g, but that's not nearly enough.
SubscriptingRef needs to be regarded as an opportunity to invoke a
user-defined function, which means that it now acts quite a bit like
FuncExpr. For example, the function-to-be-invoked needs to be checked for
volatility, leakproofness, parallel safety, etc in operations that want to
know about such things.
....
I noted yesterday, you're missing a lot of plan-time manipulations that
need
to happen for a generic function call.
Yes, I totally missed these too. I'm going to improve this situation soon.
Actually, after thinking about that a bit more: you've really squeezed
*three* different APIs into one function. Namely, subscript-reference
parse analysis, array subscripting execution, and array assignment
execution. It would be cleaner, and would reduce runtime overhead a bit,
if those were embodied as three separate functions.It might be possible to get away with having only one pg_type column,
pointing at the parse-analysis function. That function would generate
a SubscriptingRef tree node containing the OID of the appropriate
execution function, which execQual.c could call without ever knowing
its name explicitly.This clearly would work for built-in types, since the parse-analysis
function could rely on fmgroids.h for the OIDs of the execution functions.
But I'm less sure if it would work in extensions, which won't have
predetermined OIDs for their functions. Is there a way for a function
in an extension to find the OID of one of its sibling functions?On 24 January 2017 at 07:54, Jim Nasby <Jim.Nasby@bluetreble.com> wrote:
Obviously there's regprocedure (or it's C equivalent), but then you're
stuck
re-computing at runtime. I've messed around with that a bit in an effort
to
have an extension depend on another extension that allows the user to
specify
it's schema. If you're already doing metaprogramming it's not an enormous
problem... if you're not already doing that it sucks. Trying to make that
work in C would be somewhere between impossible and a nightmare.
The idea of having one function that will generate SubscriptingRef node
sounds
good. But I'm afraid of consequences about using it for extensions
(especially
since the request for general subscripting implementation came also from
their side). Is there a way to make it less troublesome?
To summarize, right now there are three options to handle a
`SubscriptingRef`
node analysis, subscripting execution and assignment execution:
* one `pg_type` column with an OID of corresponding function for each
purpose
(which isn't cool)
* one "controller" function that will call directly another function with
required logic (which is a "squeezing" and it's the current
implementation)
* one function that will return `SubscriptingRef` with an OID of required
function (which is potentially troublesome for extensions)
BTW, a different approach that might be worth considering is to say that
the nodetree representation of one of these things *is* a FuncExpr, and
the new magic thing is just that we invent a new CoercionForm value
which causes ruleutils.c to print the expression as a subscripting op.Leave ArrayRef strictly alone, and introduce new infrastructure beside it
for
non-array containers.
Yes, it will eliminate any objections about an overhead to existing array
operations. But I'm concerned that it will "virtually" reduce flexibility of
the solution on the whole, because it means we think only about one data
type
as a target for implementation and it's easy to miss something for more
complex
(in terms of subscripting logic) data structures.
Let the node representation for non-array-container access be FuncExpr
with
new value(s) of funcformat.
Probably I misunderstood something, but if we want to keep all necessary
information about subscripting (like index value, slices etc.) together with
OID's of required functions, it will be almost the same as `SubscriptingRef`
isn't it? For me the current implementation looks more natural - there was a
component that was responsible for the subscripting for one data type, and
it's
implementation evolved and became more general (but of course that's
probably
just because I implemented this and it looks more natural only for me).
On Sat, Jan 28, 2017 at 2:31 AM, Dmitry Dolgov <9erthalion6@gmail.com> wrote:
On 24 January 2017 at 02:07, Tom Lane <tgl@sss.pgh.pa.us> wrote:
I took an extremely quick look over the patchThank you for the feedback. It took some time for me to think about all
suggestions and notes.
Okay, I am marking the patch as returned with feedback seeing those
reviews. Please don't hesitate to submit a new version.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 24 January 2017 at 02:36, Tom Lane <tgl@sss.pgh.pa.us> wrote:
It might be possible to get away with having only one pg_type column,
pointing at the parse-analysis function. That function would generate
a SubscriptingRef tree node containing the OID of the appropriate
execution function, which execQual.c could call without ever knowing
its name explicitly.
Btw, is it acceptable if such generated SubscriptingRef will contain just a
function pointer to the appropriate execution function instead of an OID
(e.g.
like `ExprStateEvalFunc`)? It will help to avoid problems in case of
extensions.