diff --git a/home/mark/Dev/GSoC 2017/New Patches/Array-ELEMENT-foreign-key-v3-REBASED-42794d6.patch b/home/mark/Dev/GSoC 2017/Github repo/postgresql/Array-ELEMENT-foreign-key-v4.5.patch
index b125541d09..248a4c4623 100644
--- a/home/mark/Dev/GSoC 2017/New Patches/Array-ELEMENT-foreign-key-v3-REBASED-42794d6.patch
+++ b/home/mark/Dev/GSoC 2017/Github repo/postgresql/Array-ELEMENT-foreign-key-v4.5.patch
@@ -1,8 +1,8 @@
diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
ref/create_table.sgml
@@ -199,13 +199,29 @@ index b15c19d3d0..c3fe34888e 100644
+
+ It is advisable to index the refrencing column using GIN index as it considerably enhances the performance.
+ Also concerning coercion while using the GIN index:
+
+
+ CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+ CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+ This syntax is fine since this will cast ptest1 to int4 upon RI checks, however,
+
+
+ CREATE TABLE PKTABLEFORARRAY ( ptest1 int4 PRIMARY KEY, ptest2 text );
+ CREATE TABLE FKTABLEFORARRAY ( ftest1 int2[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+ This syntax will cast ftest1 to int4 upon RI checks, thus defeating the purpose of the index.
+
diff --git a/src/backend/access/gin/ginarrayproc.c b/src/backend/access/gin/ginarrayproc.c
index a5238c3af5..2587cf9ccf 100644
--- a/src/backend/access/gin/ginarrayproc.c
+++ b/src/backend/access/gin/ginarrayproc.c
@@ -24,6 +24,7 @@
#define GinContainsStrategy 2
#define GinContainedStrategy 3
#define GinEqualStrategy 4
+#define GinContainsElemStrategy 5
@@ -79,7 +80,7 @@ Datum
ginqueryarrayextract(PG_FUNCTION_ARGS)
{
/* Make copy of array input to ensure it doesn't disappear while in use */
- ArrayType *array = PG_GETARG_ARRAYTYPE_P_COPY(0);
+ ArrayType *array;
int32 *nkeys = (int32 *) PG_GETARG_POINTER(1);
StrategyNumber strategy = PG_GETARG_UINT16(2);
@@ -94,19 +95,35 @@ ginqueryarrayextract(PG_FUNCTION_ARGS)
bool *nulls;
int nelems;
- get_typlenbyvalalign(ARR_ELEMTYPE(array),
- &elmlen, &elmbyval, &elmalign);
+ if (strategy != GinContainsElemStrategy){
+ array = PG_GETARG_ARRAYTYPE_P_COPY(0);
- deconstruct_array(array,
- ARR_ELEMTYPE(array),
- elmlen, elmbyval, elmalign,
- &elems, &nulls, &nelems);
+ get_typlenbyvalalign(ARR_ELEMTYPE(array),
+ &elmlen, &elmbyval, &elmalign);
- *nkeys = nelems;
- *nullFlags = nulls;
+ deconstruct_array(array,
+ ARR_ELEMTYPE(array),
+ elmlen, elmbyval, elmalign,
+ &elems, &nulls, &nelems);
- switch (strategy)
- {
+ *nkeys = nelems;
+ *nullFlags = nulls;
+ }
+ else{
+ /*
+ * since this function return a pointer to a
+ * deconstructed array, there is nothing to do if
+ * operand is a single element except to return
+ * as is and configure the searchmode
+ */
+
+ elems = PG_GETARG_POINTER(0);
+ *nkeys = 1;
+ *nullFlags = PG_ARGISNULL(0);
+ }
+
+ switch (strategy)
+ {
case GinOverlapStrategy:
*searchMode = GIN_SEARCH_MODE_DEFAULT;
break;
@@ -126,6 +143,14 @@ ginqueryarrayextract(PG_FUNCTION_ARGS)
else
*searchMode = GIN_SEARCH_MODE_INCLUDE_EMPTY;
break;
+ case GinContainsElemStrategy:
+ /*
+ * only items that match the queried element
+ * are considered candidate
+ */
+
+ *searchMode = GIN_SEARCH_MODE_DEFAULT;
+ break;
default:
elog(ERROR, "ginqueryarrayextract: unknown strategy number: %d",
strategy);
@@ -171,6 +196,7 @@ ginarrayconsistent(PG_FUNCTION_ARGS)
}
}
break;
+ case GinContainsElemStrategy:
case GinContainsStrategy:
/* result is not lossy */
*recheck = false;
@@ -258,7 +284,8 @@ ginarraytriconsistent(PG_FUNCTION_ARGS)
}
}
break;
- case GinContainsStrategy:
+ case GinContainsElemStrategy:
+ case GinContainsStrategy:
/* must have all elements in check[] true, and no nulls */
res = GIN_TRUE;
for (i = 0; i < nkeys; i++)
diff --git a/src/backend/access/gin/ginscan.c b/src/backend/access/gin/ginscan.c
index 7ceea7a741..ca8390a147 100644
--- a/src/backend/access/gin/ginscan.c
+++ b/src/backend/access/gin/ginscan.c
@@ -178,7 +178,22 @@ ginFillScanKey(GinScanOpaque so, OffsetNumber attnum,
if (i < nUserQueryValues)
{
/* set up normal entry using extractQueryFn's outputs */
- queryKey = queryValues[i];
+
+ if (strategy == 5 && nQueryValues == 1)
+ {
+ /*
+ * strategy 5 is GinContainsElemStrategy.
+ * we are sure the strategy is GinContainsElemStrategy,
+ * so take the datum directly without accessing an array
+ * since it's an element not an array
+ */
+ queryKey = queryValues;
+ }
+ else
+ {
+ queryKey = queryValues[i];
+ }
+
queryCategory = queryCategories[i];
isPartialMatch =
(ginstate->canPartialMatch[attnum - 1] && partial_matches)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -458,89 +602,90 @@ index bb00858ad1..dc18fd1eae 100644
i++;
}
- /* Array foreign keys support only NO ACTION and RESTRICT actions */
+ /* Array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions */
if (has_array)
{
if ((fkconstraint->fk_upd_action != FKCONSTR_ACTION_NOACTION &&
fkconstraint->fk_upd_action != FKCONSTR_ACTION_RESTRICT) ||
(fkconstraint->fk_del_action != FKCONSTR_ACTION_NOACTION &&
- fkconstraint->fk_del_action != FKCONSTR_ACTION_RESTRICT))
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_RESTRICT &&
+ fkconstraint->fk_del_action != FKCONSTR_ACTION_CASCADE))
ereport(ERROR,
(errcode(ERRCODE_INVALID_FOREIGN_KEY),
- errmsg("array foreign keys support only NO ACTION and RESTRICT actions")));
+ errmsg("array foreign keys support only UPDATE/DELETE NO ACTION, UPDATE/DELETE RESTRICT amd DELETE CASCADE actions")));
}
- /*
+ /*
* If the attribute list for the referenced table was omitted, lookup the
* definition of the primary key and use it. Otherwise, validate the
* supplied attribute list. In either case, discover the index OID and
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index e95cee1ebf..cf273e7c03 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -1320,6 +1320,90 @@ select_common_type(ParseState *pstate, List *exprs, const char *context,
}
/*
+ * look at select_common_type(); in parse_coerce.c(src/backend/parser/parse_coerce.c)
+ *
+ * select_common_type_2args()
+ * Determine the common supertype of a list of input expressions.
+ * 'leftOid': left operand's type
+ * 'rightOid': left operand's type
+ */
+Oid select_common_type_2args(Oid leftOid, Oid rightOid)
+{
+ TYPCATEGORY leftcategory;
+ TYPCATEGORY rightcategory;
+ bool leftispreferred;
+ bool rightispreferred;
+
+ Assert(leftOid != InvalidOid);
+ Assert(rightOid != InvalidOid);
+
+ /*
+ * If input types are valid and exactly the same, just pick that type.
+ */
+ if (leftOid != UNKNOWNOID)
+ {
+ if (rightOid == leftOid)
+ return leftOid;
+ }
+
+ /*
+ * Nope, so set up for the full algorithm.
+ */
+ leftOid = getBaseType(leftOid);
+ rightOid = getBaseType(rightOid);
+ get_type_category_preferred(leftOid, &leftcategory, &leftispreferred);
+ get_type_category_preferred(rightOid, &rightcategory, &rightispreferred);
+
+ if (rightOid != UNKNOWNOID && rightOid != leftOid)
+ {
+ if (rightcategory != leftcategory)
+ {
+ /* both types in different categories? then not much hope... */
+ return InvalidOid;
+ }
+
+ if (can_coerce_type(1, &rightOid, &leftOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left */
+ if (!can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can not coerce to it implicitly right to left, thus coercion only works one way */
+ return leftOid;
+ }
+ else
+ {
+ /* coercion works both ways, then decide depending on preferred flag */
+ if (leftispreferred)
+ return leftOid;
+ else
+ rightOid;
+ }
+ }
+ else{
+ /* can not coerce to it implicitly right to left */
+ if (can_coerce_type(1, &leftOid, &rightOid, COERCION_IMPLICIT))
+ {
+ /* can coerce to it implicitly right to left, thus coercion only works one way */
+ return rightOid;
+ }
+ else{
+ /* can not coerce either way */
+ return InvalidOid;
+ }
+ }
+ }
+
+ /*
+ * If all the inputs were UNKNOWN type --- ie, unknown-type literals ---
+ * then resolve as type TEXT.
+ */
+ if (leftOid == UNKNOWNOID)
+ leftOid = TEXTOID;
+
+ return leftOid;
+}
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 34dadd6e19..b2db8baffd 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -4232,6 +4232,112 @@ arraycontained(PG_FUNCTION_ARGS)
PG_RETURN_BOOL(result);
}
+/*
+ * array_contains_elem : checks an array for a spefific element
+ * adapted from array_contain_compare() for containment of a single element
+ */
+static bool
+array_contains_elem(AnyArrayType *array, Datum elem,
+ Oid collation, void **fn_extra)
+{
+ Oid arr_type = AARR_ELEMTYPE(array);
+ TypeCacheEntry *typentry;
+ int nelems;
+ int typlen;
+ bool typbyval;
+ char typalign;
+ int i;
+ array_iter it1;
+ FunctionCallInfoData locfcinfo;
+
+ /*
+ * We arrange to look up the equality function only once per series of
+ * calls, assuming the element type doesn't change underneath us. The
+ * typcache is used so that we have no memory leakage when being used as
+ * an index support function.
+ */
+ typentry = (TypeCacheEntry *)*fn_extra;
+ if (typentry == NULL ||
+ typentry->type_id != arr_type)
+ {
+ typentry = lookup_type_cache(arr_type,
+ TYPECACHE_EQ_OPR_FINFO);
+ if (!OidIsValid(typentry->eq_opr_finfo.fn_oid))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_FUNCTION),
+ errmsg("could not identify an equality operator for type %s",
+ format_type_be(arr_type))));
+ *fn_extra = (void *)typentry;
+ }
+ typlen = typentry->typlen;
+ typbyval = typentry->typbyval;
+ typalign = typentry->typalign;
+
+ /*
+ * Apply the comparison operator to each pair of array elements.
+ */
+ InitFunctionCallInfoData(locfcinfo, &typentry->eq_opr_finfo, 2,
+ collation, NULL, NULL);
+
+ /* Loop over source data */
+ nelems = ArrayGetNItems(AARR_NDIM(array), AARR_DIMS(array));
+ array_iter_setup(&it1, array);
+
+ for (i = 0; i < nelems; i++)
+ {
+ Datum elt1;
+ bool isnull;
+ bool oprresult;
+
+ /* Get element, checking for NULL */
+ elt1 = array_iter_next(&it1, &isnull, i, typlen, typbyval, typalign);
+
+ /*
+ * We assume that the comparison operator is strict, so a NULL can't
+ * match anything. XXX this diverges from the "NULL=NULL" behavior of
+ * array_eq, should we act like that?
+ */
+ if (isnull)
+ continue;
+
+ /*
+ * Apply the operator to the element pair
+ */
+ locfcinfo.arg[0] = elt1;
+ locfcinfo.arg[1] = elem;
+ locfcinfo.argnull[0] = false;
+ locfcinfo.argnull[1] = false;
+ locfcinfo.isnull = false;
+ oprresult = DatumGetBool(FunctionCallInvoke(&locfcinfo));
+ if (oprresult)
+ return true;
+ }
+
+ return false;
+}
+
+Datum
+arraycontainselem(PG_FUNCTION_ARGS)
+{
+ AnyArrayType *array = PG_GETARG_ANY_ARRAY(0);
+ Datum elem = PG_GETARG_DATUM(1);
+ Oid collation = PG_GET_COLLATION();
+ bool result;
+
+ /* we don't need to check if the elem is null or if
+ the elem datatype and array datatype match since this
+ is handled within internal calls already (a property
+ of polymorphic functions) */
+
+ result = array_contains_elem(array, elem, collation,
+ &fcinfo->flinfo->fn_extra);
+
+ /* Avoid leaking memory when handed toasted input. */
+ AARR_FREE_IF_COPY(array, 0);
+
+ PG_RETURN_BOOL(result);
+}
diff --git a/src/backend/utils/adt/ri_triggers.c b/src/backend/utils/adt/ri_triggers.c
--- a/src/backend/utils/adt/ri_triggers.c
+++ b/src/backend/utils/adt/ri_triggers.c
-@@ -2586,14 +2682,32 @@ ri_GenerateQual(StringInfo buf,
+@@ -2586,14 +2681,81 @@ ri_GenerateQual(StringInfo buf,
nspname = get_namespace_name(operform->oprnamespace);
- if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
- {
- oprright = get_array_type(operform->oprright);
- if (!OidIsValid(oprright))
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_OBJECT),
- errmsg("could not find array type for data type %s",
- format_type_be(operform->oprright))));
- }
- else
- oprright = operform->oprright;
-
- appendStringInfo(buf, " %s %s", sep, leftop);
- if (leftoptype != operform->oprleft)
- ri_add_cast_to(buf, operform->oprleft);
+ appendStringInfo(buf, " %s %s", sep, leftop);
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
appendStringInfoString(buf, oprname);
appendStringInfo(buf, ") %s", rightop);
if (rightoptype != operform->oprright)
ri_add_cast_to(buf, operform->oprright);
-
- appendStringInfo(buf, " OPERATOR(%s.%s) ",
- quote_identifier(nspname), oprname);
-
- if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
- appendStringInfoString(buf, "ANY (");
- appendStringInfoString(buf, rightop);
- if (rightoptype != oprright)
- ri_add_cast_to(buf, oprright);
- if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
- appendStringInfoChar(buf, ')');
+ if (fkreftype == FKCONSTR_REF_EACH_ELEMENT)
+ {
+ Oid oprright;
+ Oid oprleft;
+ Oid oprcommon;
+
+ /*
+ * since the @>> operator has the array as
+ * the first operand and the element as the second
+ * operand we switch their places in the query
+ * i.e opright is oprleft and vice versar
+ */
+
+ /*
+ * we first need to get the array types and decide
+ * the more appropriate one
+ */
+
+ /* get array type of refrenced element*/
+ oprright = get_array_type(operform->oprleft);
+ if (!OidIsValid(oprright))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprleft))));
+
+ /* get array type of refrencing element*/
+ oprleft = get_array_type(operform->oprright);
+ if (!OidIsValid(oprleft))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find array type for data type %s",
+ format_type_be(operform->oprright))));
+
+ /* compare the two array types and try to find
+ a common supertype */
+ oprcommon = select_common_type_2args(oprleft, oprright);
+ if (!OidIsValid(oprcommon))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find common type between %s and %s",
+ format_type_be(oprleft), format_type_be(oprright))));
+
+ oprright = get_base_element_type(oprcommon);
+ if (!OidIsValid(oprright))
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_OBJECT),
+ errmsg("could not find element type for data type %s",
+ format_type_be(oprcommon))));
+
+ appendStringInfo(buf, " %s %s", sep, rightop);
+ if (oprleft != oprcommon)
+ ri_add_cast_to(buf, oprcommon);
+
+ appendStringInfo(buf, " OPERATOR(pg_catalog.@>>) ");
+
+ appendStringInfoString(buf, leftop);
+ if (operform->oprleft != oprright)
+ ri_add_cast_to(buf, oprright);
+ }
+ else
+ {
+ appendStringInfo(buf, " %s %s", sep, leftop);
+
+ if (leftoptype != operform->oprleft)
+ ri_add_cast_to(buf, operform->oprleft);
+
+ appendStringInfo(buf, " OPERATOR(%s.%s) ",
+ quote_identifier(nspname), oprname);
+
++ appendStringInfoString(buf, rightop);
++
++ if (rightoptype != operform->oprright)
++ ri_add_cast_to(buf, operform->oprright);
++ }
ReleaseSysCache(opertup);
}
diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h
index f850be490a..4e304a9fe4 100644
--- a/src/include/catalog/pg_amop.h
+++ b/src/include/catalog/pg_amop.h
@@ -689,6 +689,7 @@ DATA(insert ( 2745 2277 2277 1 s 2750 2742 0 ));
DATA(insert ( 2745 2277 2277 2 s 2751 2742 0 ));
DATA(insert ( 2745 2277 2277 3 s 2752 2742 0 ));
DATA(insert ( 2745 2277 2277 4 s 1070 2742 0 ));
+DATA(insert ( 2745 2277 2283 5 s 6108 2742 0 ));
/*
* btree enum_ops
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index 06f65293cb..f5bad57b70 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -68,6 +68,7 @@ extern int parser_coercion_errposition(ParseState *pstate,
extern Oid select_common_type(ParseState *pstate, List *exprs,
const char *context, Node **which_expr);
+extern Oid select_common_type_2args(Oid ptype, Oid ntype);
extern Node *coerce_to_common_type(ParseState *pstate, Node *node,
Oid targetTypeId,
const char *context);
diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out
index c730563f03..0a9eff4d04 100644
--- a/src/test/regress/expected/arrays.out
+++ b/src/test/regress/expected/arrays.out
@@ -737,6 +737,17 @@ SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
(6 rows)
+SELECT * FROM array_op_test WHERE i @>> 32 ORDER BY seqno;
+ seqno | i | t
+-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
+ 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
+ 74 | {32} | {AAAAAAAAAAAAAAAA1729,AAAAAAAAAAAAA22860,AAAAAA99807,AAAAA17383,AAAAAAAAAAAAAAA67062,AAAAAAAAAAA15165,AAAAAAAAAAA50956}
+ 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
+ 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
+ 98 | {38,34,32,89} | {AAAAAAAAAAAAAAAAAA71621,AAAA8857,AAAAAAAAAAAAAAAAAAA65037,AAAAAAAAAAAAAAAA31334,AAAAAAAAAA48845}
+ 100 | {85,32,57,39,49,84,32,3,30} | {AAAAAAA80240,AAAAAAAAAAAAAAAA1729,AAAAA60038,AAAAAAAAAAA92631,AAAAAAAA9523}
+(6 rows)
+
SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@@ -761,6 +772,19 @@ SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno;
89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
(8 rows)
+SELECT * FROM array_op_test WHERE i @>> 17 ORDER BY seqno;
+ seqno | i | t
+-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
+ 6 | {39,35,5,94,17,92,60,32} | {AAAAAAAAAAAAAAA35875,AAAAAAAAAAAAAAAA23657}
+ 12 | {17,99,18,52,91,72,0,43,96,23} | {AAAAA33250,AAAAAAAAAAAAAAAAAAA85420,AAAAAAAAAAA33576}
+ 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
+ 19 | {52,82,17,74,23,46,69,51,75} | {AAAAAAAAAAAAA73084,AAAAA75968,AAAAAAAAAAAAAAAA14047,AAAAAAA80240,AAAAAAAAAAAAAAAAAAA1205,A68938}
+ 53 | {38,17} | {AAAAAAAAAAA21658}
+ 65 | {61,5,76,59,17} | {AAAAAA99807,AAAAA64741,AAAAAAAAAAA53908,AA21643,AAAAAAAAA10012}
+ 77 | {97,15,32,17,55,59,18,37,50,39} | {AAAAAAAAAAAA67946,AAAAAA54032,AAAAAAAA81587,55847,AAAAAAAAAAAAAA28620,AAAAAAAAAAAAAAAAA43052,AAAAAA75463,AAAA49534,AAAAAAAA44066}
+ 89 | {40,32,17,6,30,88} | {AA44673,AAAAAAAAAAA6119,AAAAAAAAAAAAAAAA23657,AAAAAAAAAAAAAAAAAA47955,AAAAAAAAAAAAAAAA33598,AAAAAAAAAAA33576,AA44673}
+(8 rows)
+
SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno;
seqno | i | t
-------+---------------------------------+------------------------------------------------------------------------------------------------------------------------------------
@@ -942,6 +966,11 @@ SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
-------+---+---
(0 rows)
+SELECT * FROM array_op_test WHERE i @>> NULL ORDER BY seqno;
+ seqno | i | t
+-------+---+---
+(0 rows)
+
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
seqno | i | t
-------+---+---
@@ -953,6 +982,15 @@ SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
101 | {} | {}
(1 row)
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAA72908' ORDER BY seqno;
+ seqno | i | t
+-------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------
+ 22 | {11,6,56,62,53,30} | {AAAAAAAA72908}
+ 45 | {99,45} | {AAAAAAAA72908,AAAAAAAAAAAAAAAAAAA17075,AA88409,AAAAAAAAAAAAAAAAAA36842,AAAAAAA48038,AAAAAAAAAAAAAA10611}
+ 72 | {22,1,16,78,20,91,83} | {47735,AAAAAAA56483,AAAAAAAAAAAAA93788,AA42406,AAAAAAAAAAAAA73084,AAAAAAAA72908,AAAAAAAAAAAAAAAAAA61286,AAAAA66674,AAAAAAAAAAAAAAAAA50407}
+ 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
+(4 rows)
+
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
seqno | i | t
-------+-----------------------+--------------------------------------------------------------------------------------------------------------------------------------------
@@ -971,6 +1009,14 @@ SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
(4 rows)
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAAAA646' ORDER BY seqno;
+ seqno | i | t
+-------+------------------+--------------------------------------------------------------------
+ 15 | {17,14,16,63,67} | {AA6416,AAAAAAAAAA646,AAAAA95309}
+ 79 | {45} | {AAAAAAAAAA646,AAAAAAAAAAAAAAAAAAA70415,AAAAAA43678,AAAAAAAA72908}
+ 96 | {23,97,43} | {AAAAAAAAAA646,A87088}
+(3 rows)
+
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
seqno | i | t
-------+------------------+--------------------------------------------------------------------
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index fcf8bd7565..25ef369951 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -1811,6 +1811,7 @@ ORDER BY 1, 2, 3;
2742 | 2 | @@@
2742 | 3 | <@
2742 | 4 | =
+ 2742 | 5 | @>>
2742 | 7 | @>
2742 | 9 | ?
2742 | 10 | ?|
@@ -1878,7 +1879,7 @@ ORDER BY 1, 2, 3;
4000 | 25 | <<=
4000 | 26 | >>
4000 | 27 | >>=
-(121 rows)
+(122 rows)
diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql
index 25dd4e2c6d..f6fb507bdd 100644
--- a/src/test/regress/sql/arrays.sql
+++ b/src/test/regress/sql/arrays.sql
@@ -313,8 +313,10 @@ SELECT ARRAY[0,0] || ARRAY[1,1] || ARRAY[2,2] AS "{0,0,1,1,2,2}";
SELECT 0 || ARRAY[1,2] || 3 AS "{0,1,2,3}";
SELECT * FROM array_op_test WHERE i @> '{32}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> 32 ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{32}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{17}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> 17 ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{32,17}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{32,17}' ORDER BY seqno;
@@ -325,11 +327,14 @@ SELECT * FROM array_op_test WHERE i && '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i = '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i @> '{NULL}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE i @>> NULL ORDER BY seqno;
SELECT * FROM array_op_test WHERE i && '{NULL}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE i <@ '{NULL}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAA72908' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAA72908}' ORDER BY seqno;
+SELECT * FROM array_op_test WHERE t @>> 'AAAAAAAAAA646' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t && '{AAAAAAAAAA646}' ORDER BY seqno;
SELECT * FROM array_op_test WHERE t @> '{AAAAAAAA72908,AAAAAAAAAA646}' ORDER BY seqno;
diff --git a/src/test/regress/sql/element_foreign_key.sql b/src/test/regress/sql/element_foreign_key.sql
new file mode 100644
--- /dev/null
+++ b/src/test/regress/sql/element_foreign_key.sql
@@ -2242,6 +2927,10 @@ index 0000000000..8c1e4d9601
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
+DROP TABLE FKTABLEFORARRAY;
@@ -2251,10 +2940,6 @@ index 0000000000..8c1e4d9601
-CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE NO ACTION, ftest2 int );
-DROP TABLE FKTABLEFORARRAY;
-CREATE TABLE FKTABLEFORARRAY ( ftest1 int[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY ON DELETE CASCADE ON UPDATE RESTRICT, ftest2 int );
-DROP TABLE FKTABLEFORARRAY;
@@ -2361,6 +3046,31 @@ index 0000000000..8c1e4d9601
+-- Repeat a similar test using INT4 keys coerced from INT2
+CREATE TABLE PKTABLEFORARRAY ( ptest1 int2 PRIMARY KEY, ptest2 text );
+
+-- Populate the primary table
+INSERT INTO PKTABLEFORARRAY VALUES (1, 'Test 1');
+INSERT INTO PKTABLEFORARRAY VALUES (2, 'Test 2');
+INSERT INTO PKTABLEFORARRAY VALUES (3, 'Test 3');
+
+-- Create the foreign table
+CREATE TABLE FKTABLEFORARRAY ( ftest1 int4[], FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAY, ftest2 int );
+
+-- Insert valid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1], 1);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[2], 2);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[3], 3);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,3], 4);
+
+-- Insert invalid rows into FK TABLE
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[4], 5);
+INSERT INTO FKTABLEFORARRAY VALUES (ARRAY[1,2,5], 6);
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAY;
+DROP TABLE PKTABLEFORARRAY;
+
@@ -2604,3 +3302,87 @@ index 0000000000..8c1e4d9601
+
+-- Check GIN index
+-- Define PKTABLEFORARRAYGIN
+CREATE TABLE PKTABLEFORARRAYGIN ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAYGIN
+INSERT INTO PKTABLEFORARRAYGIN VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYGIN VALUES (5, 'Test5');
+
+-- Define FKTABLEFORARRAYGIN
+CREATE TABLE FKTABLEFORARRAYGIN ( ftest1 int[],
+ ftest2 int PRIMARY KEY,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYGIN
+ ON DELETE NO ACTION ON UPDATE NO ACTION);
+
+-- -- Create index on FKTABLEFORARRAYGIN
+CREATE INDEX ON FKTABLEFORARRAYGIN USING gin (ftest1 array_ops);
+
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYGIN VALUES ('{3,4,5,3}', 10);
+
+-- Try using the indexable operator
+SELECT * FROM FKTABLEFORARRAYGIN WHERE ftest1 @>> 5;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYGIN;
+DROP TABLE PKTABLEFORARRAYGIN;
+
+-- Check DELECTE CASCADE ACTION
+CREATE TABLE PKTABLEFORARRAYDELETE ( ptest1 int PRIMARY KEY, ptest2 text );
+
+-- Insert test data into PKTABLEFORARRAYDELETE
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (1, 'Test1');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (2, 'Test2');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (3, 'Test3');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (4, 'Test4');
+INSERT INTO PKTABLEFORARRAYDELETE VALUES (5, 'Test5');
+
+-- Define FKTABLEFORARRAYDELETE
+CREATE TABLE FKTABLEFORARRAYDELETE ( ftest1 int[],
+ ftest2 int,
+ FOREIGN KEY (EACH ELEMENT OF ftest1) REFERENCES PKTABLEFORARRAYDELETE
+ ON DELETE CASCADE ON UPDATE NO ACTION );
+
+-- Create index on FKTABLEFORARRAYDELETE
+CREATE INDEX ON FKTABLEFORARRAYDELETE USING gin (ftest1);
+
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5}', 1);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,2}', 2);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,2,5}', 3);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,4}', 4);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,5,4,1,3}', 5);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{1}', 6);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{5,1}', 7);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{2,1,2,4,1}', 8);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{4,2}', 9);
+INSERT INTO FKTABLEFORARRAYDELETE VALUES ('{3,4,5,3}', 10);
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+
+-- Check FKTABLE for target rows
+SELECT * FROM FKTABLEFORARRAYDELETE WHERE ftest1 @>> 5;
+
+-- Perfrorm delete
+DELETE FROM PKTABLEFORARRAYDELETE WHERE ptest1 = 5;
+
+-- Check FKTABLE
+SELECT * FROM FKTABLEFORARRAYDELETE;
+
+-- Cleanup
+DROP TABLE FKTABLEFORARRAYDELETE;
+DROP TABLE PKTABLEFORARRAYDELETE;