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;