diff --git a/contrib/pageinspect/rawpage.c b/contrib/pageinspect/rawpage.c index 38c136f987..c175eaa1b9 100644 --- a/contrib/pageinspect/rawpage.c +++ b/contrib/pageinspect/rawpage.c @@ -28,10 +28,6 @@ PG_MODULE_MAGIC; -static bytea *get_raw_page_internal(text *relname, ForkNumber forknum, - BlockNumber blkno); - - /* * get_raw_page * @@ -87,7 +83,7 @@ get_raw_page_fork(PG_FUNCTION_ARGS) /* * workhorse */ -static bytea * +bytea * get_raw_page_internal(text *relname, ForkNumber forknum, BlockNumber blkno) { bytea *raw_page; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index e0ff6f16a2..08e1e3a1bf 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -176,6 +176,9 @@ static void processCASbits(int cas_bits, int location, const char *constrType, bool *deferrable, bool *initdeferred, bool *not_valid, bool *no_inherit, core_yyscan_t yyscanner); static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); +static SelectStmt * makeElementSubselect(int any_or_each, int kind, bool recursive, Node *of, + const char *aliasname, const char *indexname, + Node *clause, int location); %} @@ -289,6 +292,8 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type opt_or_replace opt_grant_grant_option opt_grant_admin_option opt_nowait opt_if_exists opt_with_data + opt_anywhere +%type opt_with_index %type opt_nowait_or_skip %type OptRoleList AlterOptRoleList @@ -446,6 +451,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type case_expr case_arg when_clause case_default %type when_clause_list %type sub_type +%type any_or_each any_or_each_kind %type ctext_expr %type NumericOnly %type NumericOnly_list @@ -561,7 +567,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); /* ordinary key words in alphabetical order */ %token ABORT_P ABSOLUTE_P ACCESS ACTION ADD_P ADMIN AFTER - AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ARRAY AS ASC + AGGREGATE ALL ALSO ALTER ALWAYS ANALYSE ANALYZE AND ANY ANYWHERE ARRAY AS ASC ASSERTION ASSIGNMENT ASYMMETRIC AT ATTRIBUTE AUTHORIZATION BACKWARD BEFORE BEGIN_P BETWEEN BIGINT BINARY BIT @@ -580,7 +586,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DESC DICTIONARY DISABLE_P DISCARD DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP - EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT + EACH ELEMENT ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EVENT EXCEPT EXCLUDE EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTENSION EXTERNAL EXTRACT @@ -624,7 +630,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); RESET RESTART RESTRICT RETURNING RETURNS REVOKE RIGHT ROLE ROLLBACK ROLLUP ROW ROWS RULE - SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES + SATISFIES SAVEPOINT SCHEMA SCROLL SEARCH SECOND_P SECURITY SELECT SEQUENCE SEQUENCES SERIALIZABLE SERVER SESSION SESSION_USER SET SETS SETOF SHARE SHOW SIMILAR SIMPLE SKIP SMALLINT SNAPSHOT SOME SQL_P STABLE STANDALONE_P START STATEMENT STATISTICS STDIN STDOUT STORAGE STRICT_P STRIP_P SUBSTRING @@ -657,9 +663,9 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); * NOT_LA exists so that productions such as NOT LIKE can be given the same * precedence as LIKE; otherwise they'd effectively have the same precedence * as NOT, at least with respect to their left-hand subexpression. - * NULLS_LA and WITH_LA are needed to make the grammar LALR(1). + * NULLS_LA, WITH_LA and ANY_EL are needed to make the grammar LALR(1). */ -%token NOT_LA NULLS_LA WITH_LA +%token NOT_LA NULLS_LA WITH_LA ANY_EL /* Precedence: lowest to highest */ @@ -12056,6 +12062,50 @@ c_expr: columnref { $$ = $1; } g->location = @1; $$ = (Node *)g; } + | any_or_each ELEMENT opt_anywhere OF b_expr AS ColId opt_with_index SATISFIES '(' a_expr ')' + { + SubLink *n = makeNode(SubLink); + + n->subLinkType = EXPR_SUBLINK; + n->subLinkId = 0; + n->testexpr = NULL; + n->operName = NIL; + n->subselect = (Node*)makeElementSubselect($1, ELEMENT, $3, $5, $7, $8, $11, @1); + n->location = @1; + $$ = (Node *)n; + } + | any_or_each any_or_each_kind opt_anywhere OF b_expr AS ColId SATISFIES '(' a_expr ')' + { + SubLink *n = makeNode(SubLink); + + n->subLinkType = EXPR_SUBLINK; + n->subLinkId = 0; + n->testexpr = NULL; + n->operName = NIL; + n->subselect = (Node*)makeElementSubselect($1, $2, $3, $5, $7, NULL, $10, @1); + n->location = @1; + $$ = (Node *)n; + } + ; + +any_or_each: + ANY_EL { $$ = ANY_EL; } + | EACH { $$ = EACH; } + ; + +any_or_each_kind: + KEY { $$ = KEY; } + | VALUE_P { $$ = VALUE_P; } + ; + +opt_with_index: + WITH INDEX AS ColId { $$ = $4; } + | /* empty */ { $$ = NULL; } + ; + +opt_anywhere: + ANYWHERE { $$ = true; } + | /* empty */ { $$ = false; } ; func_application: func_name '(' ')' @@ -13588,6 +13638,7 @@ unreserved_keyword: | ALSO | ALTER | ALWAYS + | ANYWHERE | ASSERTION | ASSIGNMENT | AT @@ -13644,6 +13695,7 @@ unreserved_keyword: | DOUBLE_P | DROP | EACH + | ELEMENT | ENABLE_P | ENCODING | ENCRYPTED @@ -13773,6 +13825,7 @@ unreserved_keyword: | ROLLUP | ROWS | RULE + | SATISFIES | SAVEPOINT | SCHEMA | SCROLL @@ -14829,6 +14882,98 @@ makeRecursiveViewSelect(char *relname, List *aliases, Node *query) return (Node *) s; } +static SelectStmt * +makeElementSubselect(int any_or_each, int kind, bool recursive, Node *of, + const char *aliasname, const char *indexname, + Node *clause, int location) +{ + ResTarget *target = makeNode(ResTarget); + FuncCall *unnest_call, *agg_call, *count_call; + RangeFunction *table_ref = makeNode(RangeFunction); + SelectStmt *subselect = makeNode(SelectStmt); + char *unnest_name, *agg_name; + CaseExpr *c = makeNode(CaseExpr); + CaseWhen *w = makeNode(CaseWhen); + NullTest *n = makeNode(NullTest);; + + /* SELECT (CASE + * WHEN (COUNT(*) = 0 AND of IS NOT NULL) THEN '?'::bool + * ELSE BOOL_OR_AND(clause) END ) FROM unnest_*(of) AS aliasname */ + + switch(any_or_each) + { + case ANY_EL: + agg_name = "bool_or_not_null"; + break; + case EACH: + agg_name = "bool_and_not_null"; + break; + default: + elog(ERROR, "unkown ANY or ALL"); + } + + agg_call = makeFuncCall(SystemFuncName(agg_name), + list_make1(clause), location); + + count_call = makeFuncCall(SystemFuncName("count"), NIL, location); + count_call->agg_star = true; + + n->arg = (Expr *) of; + n->nulltesttype = IS_NOT_NULL; + n->location = location; + + w->expr = (Expr *)makeAndExpr( + (Node*)makeSimpleA_Expr(AEXPR_OP, "=", + (Node*)count_call, + (Node*)makeIntConst(0, location), + location + ), + (Node*)n, + location + ); + w->result = (Expr *) makeBoolAConst((any_or_each == EACH) ? true : false, location); + w->location = location; + + + c->casetype = InvalidOid; + c->arg = NULL; + c->args = list_make1(w); + c->defresult = (Expr*)agg_call; + c->location = location; + + + target->val = (Node*)c; + target->location = location; + subselect->targetList = list_make1(target); + + switch(kind) + { + case ELEMENT: + unnest_name = indexname ? "unnest_element_index" : "unnest_element"; + break; + case KEY: + unnest_name = "unnest_key"; + break; + case VALUE_P: + unnest_name = "unnest_value"; + break; + default: + elog(ERROR, "unkown ANY_EL"); + } + + unnest_call = makeFuncCall(SystemFuncName(unnest_name), + list_make2(of, makeBoolAConst(recursive, location)), + location); + + table_ref->functions = list_make1(list_make2(unnest_call, NIL)); + table_ref->alias = makeAlias(aliasname, NIL); + if (indexname) + table_ref->alias->colnames = list_make2(makeString(pstrdup(aliasname)), makeString(pstrdup(indexname))); + subselect->fromClause = list_make1(table_ref); + + return subselect; +} + /* parser_init() * Initialize to parse one query string */ diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c index fdf5a6a1ca..dbb6af248a 100644 --- a/src/backend/parser/parser.c +++ b/src/backend/parser/parser.c @@ -107,15 +107,16 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner) */ switch (cur_token) { + case ANY: case NOT: cur_token_length = 3; break; - case NULLS_P: - cur_token_length = 5; - break; case WITH: cur_token_length = 4; break; + case NULLS_P: + cur_token_length = 5; + break; default: return cur_token; } @@ -189,6 +190,17 @@ base_yylex(YYSTYPE *lvalp, YYLTYPE *llocp, core_yyscan_t yyscanner) break; } break; + case ANY: + /* Replace ANY by ANY_EL if it's followed by ELEMENT or KEY or VALUE */ + switch (next_token) + { + case ELEMENT: + case KEY: + case VALUE_P: + cur_token = ANY_EL; + break; + } + break; } return cur_token; diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c index 42cdbc7d6e..802d7b6e54 100644 --- a/src/backend/utils/adt/arrayfuncs.c +++ b/src/backend/utils/adt/arrayfuncs.c @@ -5868,6 +5868,57 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs, return result; } +typedef struct ArrayUnnestState +{ + AnyArrayType *array; + array_iter iter; + int nextelem; + int numelems; + int16 elmlen; + bool elmbyval; + char elmalign; + TupleDesc tupdesc; +} ArrayUnnestState; + +static void +init_array_unnest(FuncCallContext *funcctx, Datum array, FunctionCallInfo fcinfo) +{ + MemoryContext oldcontext; + ArrayUnnestState *state; + + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + state = palloc0(sizeof(*state)); + /* + * Get the array value and detoast if needed. We can't do this + * earlier because if we have to detoast, we want the detoasted copy + * to be in multi_call_memory_ctx, so it will go away when we're done + * and not before. (If no detoast happens, we assume the originally + * passed array will stick around till then.) + */ + state->array = DatumGetAnyArray(array); + array_iter_setup(&state->iter, state->array); + state->numelems = ArrayGetNItems(AARR_NDIM(state->array), AARR_DIMS(state->array)); + + if (VARATT_IS_EXPANDED_HEADER(state->array)) + { + /* we can just grab the type data from expanded array */ + state->elmlen = state->array->xpn.typlen; + state->elmbyval = state->array->xpn.typbyval; + state->elmalign = state->array->xpn.typalign; + } + else + get_typlenbyvalalign(AARR_ELEMTYPE(state->array), + &state->elmlen, + &state->elmbyval, + &state->elmalign); + + if (fcinfo && get_call_result_type(fcinfo, NULL, &state->tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + funcctx->user_fctx = state; + MemoryContextSwitchTo(oldcontext); +} /* * UNNEST @@ -5875,66 +5926,43 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs, Datum array_unnest(PG_FUNCTION_ARGS) { - typedef struct - { - array_iter iter; - int nextelem; - int numelems; - int16 elmlen; - bool elmbyval; - char elmalign; - } array_unnest_fctx; - - FuncCallContext *funcctx; - array_unnest_fctx *fctx; - MemoryContext oldcontext; + FuncCallContext *funcctx; + ArrayUnnestState *fctx; /* stuff done only on the first call of the function */ if (SRF_IS_FIRSTCALL()) - { - AnyArrayType *arr; + init_array_unnest(SRF_FIRSTCALL_INIT(), PG_GETARG_DATUM(0), NULL); - /* create a function context for cross-call persistence */ - funcctx = SRF_FIRSTCALL_INIT(); - - /* - * switch to memory context appropriate for multiple function calls - */ - oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + /* stuff done on every call of the function */ + funcctx = SRF_PERCALL_SETUP(); + fctx = funcctx->user_fctx; - /* - * Get the array value and detoast if needed. We can't do this - * earlier because if we have to detoast, we want the detoasted copy - * to be in multi_call_memory_ctx, so it will go away when we're done - * and not before. (If no detoast happens, we assume the originally - * passed array will stick around till then.) - */ - arr = PG_GETARG_ANY_ARRAY(0); + if (fctx->nextelem < fctx->numelems) + { + int offset = fctx->nextelem++; + Datum elem; - /* allocate memory for user context */ - fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx)); + elem = array_iter_next(&fctx->iter, &fcinfo->isnull, offset, + fctx->elmlen, fctx->elmbyval, fctx->elmalign); - /* initialize state */ - array_iter_setup(&fctx->iter, arr); - fctx->nextelem = 0; - fctx->numelems = ArrayGetNItems(AARR_NDIM(arr), AARR_DIMS(arr)); + SRF_RETURN_NEXT(funcctx, elem); + } + else + { + /* do when there is no more left */ + SRF_RETURN_DONE(funcctx); + } +} - if (VARATT_IS_EXPANDED_HEADER(arr)) - { - /* we can just grab the type data from expanded array */ - fctx->elmlen = arr->xpn.typlen; - fctx->elmbyval = arr->xpn.typbyval; - fctx->elmalign = arr->xpn.typalign; - } - else - get_typlenbyvalalign(AARR_ELEMTYPE(arr), - &fctx->elmlen, - &fctx->elmbyval, - &fctx->elmalign); +Datum +array_unnest_element_index(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + ArrayUnnestState *fctx; - funcctx->user_fctx = fctx; - MemoryContextSwitchTo(oldcontext); - } + /* stuff done only on the first call of the function */ + if (SRF_IS_FIRSTCALL()) + init_array_unnest(SRF_FIRSTCALL_INIT(), PG_GETARG_DATUM(0), fcinfo); /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); @@ -5944,11 +5972,20 @@ array_unnest(PG_FUNCTION_ARGS) { int offset = fctx->nextelem++; Datum elem; + Datum vals[2]; + bool nulls[2] = {false, false}; + bool isnull; - elem = array_iter_next(&fctx->iter, &fcinfo->isnull, offset, + elem = array_iter_next(&fctx->iter, &isnull, offset, fctx->elmlen, fctx->elmbyval, fctx->elmalign); - SRF_RETURN_NEXT(funcctx, elem); + if (isnull) + nulls[0] = true; + else + vals[0] = elem; + vals[1] = Int32GetDatum(fctx->nextelem); + + SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(heap_form_tuple(fctx->tupdesc, vals, nulls))); } else { @@ -5957,6 +5994,14 @@ array_unnest(PG_FUNCTION_ARGS) } } +/* + * Just a simplest wrapper to keep opr_sanity clean + */ +Datum +array_unnest_element(PG_FUNCTION_ARGS) +{ + return array_unnest(fcinfo); +} /* * array_replace/array_remove support diff --git a/src/backend/utils/adt/bool.c b/src/backend/utils/adt/bool.c index 5d3bb10afc..53c247b586 100644 --- a/src/backend/utils/adt/bool.c +++ b/src/backend/utils/adt/bool.c @@ -311,6 +311,7 @@ typedef struct BoolAggState { int64 aggcount; /* number of non-null values aggregated */ int64 aggtrue; /* number of values aggregated that are true */ + int64 aggnull; /* number of values aggregated that are null */ } BoolAggState; static BoolAggState * @@ -326,6 +327,7 @@ makeBoolAggState(FunctionCallInfo fcinfo) sizeof(BoolAggState)); state->aggcount = 0; state->aggtrue = 0; + state->aggnull = 0; return state; } @@ -347,6 +349,10 @@ bool_accum(PG_FUNCTION_ARGS) if (PG_GETARG_BOOL(1)) state->aggtrue++; } + else + { + state->aggnull++; + } PG_RETURN_POINTER(state); } @@ -368,6 +374,10 @@ bool_accum_inv(PG_FUNCTION_ARGS) if (PG_GETARG_BOOL(1)) state->aggtrue--; } + else + { + state->aggnull--; + } PG_RETURN_POINTER(state); } @@ -401,3 +411,41 @@ bool_anytrue(PG_FUNCTION_ARGS) /* true if any non-null value is true */ PG_RETURN_BOOL(state->aggtrue > 0); } + +Datum +bool_alltrue_notnull(PG_FUNCTION_ARGS) +{ + BoolAggState *state; + + state = PG_ARGISNULL(0) ? NULL : (BoolAggState *) PG_GETARG_POINTER(0); + + /* if there were no non-null values, return NULL */ + if (state == NULL || state->aggcount == 0) + PG_RETURN_NULL(); + + /* If all values are true except some NULLs then return NULL */ + if (state->aggtrue == state->aggcount && state->aggnull > 0) + PG_RETURN_NULL(); + + /* true if all non-null values are true */ + PG_RETURN_BOOL(state->aggtrue == state->aggcount); +} + +Datum +bool_anytrue_notnull(PG_FUNCTION_ARGS) +{ + BoolAggState *state; + + state = PG_ARGISNULL(0) ? NULL : (BoolAggState *) PG_GETARG_POINTER(0); + + /* if there were no non-null values, return NULL */ + if (state == NULL || state->aggcount == 0) + PG_RETURN_NULL(); + + /* If all values are false except some NULLs then return NULL */ + if (state->aggtrue == 0 && state->aggnull > 0) + PG_RETURN_NULL(); + + /* true if any non-null value is true */ + PG_RETURN_BOOL(state->aggtrue > 0); +} diff --git a/src/backend/utils/adt/jsonfuncs.c b/src/backend/utils/adt/jsonfuncs.c index f87ba77e3e..381f1359f9 100644 --- a/src/backend/utils/adt/jsonfuncs.c +++ b/src/backend/utils/adt/jsonfuncs.c @@ -3892,3 +3892,218 @@ setPathArray(JsonbIterator **it, Datum *path_elems, bool *path_nulls, } } } + +typedef struct UnnestState +{ + MemoryContext ctx; + Jsonb *jb; + JsonbIterator *it; + bool skipNested; + JsonbIteratorToken type; + int32 counter; + TupleDesc tupdesc; +} +UnnestState; + +static void finiUnnest(UnnestState *state); + +static void +initUnnest(FuncCallContext *funcctx, Datum jsonb, bool recursive, + JsonbIteratorToken type, FunctionCallInfo fcinfo) +{ + MemoryContext oldcontext; + UnnestState *state; + JsonbValue v; + int r; + + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); + + state = palloc0(sizeof(*state)); + state->ctx = funcctx->multi_call_memory_ctx; + state->jb = DatumGetJsonbCopy(jsonb); + state->it = JsonbIteratorInit(&state->jb->root); + state->skipNested = !recursive; + state->type = type; + + r = JsonbIteratorNext(&state->it, &v, false); + + if (JB_ROOT_IS_SCALAR(state->jb)) + { + r = JsonbIteratorNext(&state->it, &v, true); + r = JsonbIteratorNext(&state->it, &v, true); + Assert(r == WJB_DONE); + } + + if (r != WJB_DONE && fcinfo && + get_call_result_type(fcinfo, NULL, &state->tupdesc) != TYPEFUNC_COMPOSITE) + elog(ERROR, "return type must be a row type"); + + MemoryContextSwitchTo(oldcontext); + + if (r == WJB_DONE) + { + finiUnnest(state); + state = NULL; + } + + + funcctx->user_fctx = (void *) state; +} + +static Jsonb* +nextUnnest(UnnestState *state) +{ + MemoryContext oldcontext; + JsonbValue v; + int r; + + /* + * Iterator should work in long-lived memory context + */ + oldcontext = MemoryContextSwitchTo(state->ctx); + + while((r = JsonbIteratorNext(&state->it, &v, state->skipNested)) != WJB_DONE) + { + if (r == state->type) + break; + } + + MemoryContextSwitchTo(oldcontext); + + state->counter ++; + + return (r == state->type) ? JsonbValueToJsonb(&v) : NULL; +} + +static text* +nextUnnestKey(UnnestState *state) +{ + MemoryContext oldcontext; + JsonbValue v; + int r; + + /* + * Iterator should work in long-lived memory context + */ + oldcontext = MemoryContextSwitchTo(state->ctx); + + while((r = JsonbIteratorNext(&state->it, &v, state->skipNested)) != WJB_DONE) + { + if (r == state->type) + break; + } + + MemoryContextSwitchTo(oldcontext); + + return (r == state->type) ? cstring_to_text_with_len(v.val.string.val, v.val.string.len) : NULL; +} + +static void +finiUnnest(UnnestState *state) +{ + if (state) + { + if (state->tupdesc) + FreeTupleDesc(state->tupdesc); + pfree(state->jb); + pfree(state); + } +} + +Datum +jsonb_unnest_element(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + UnnestState *state; + Jsonb *r; + + if (SRF_IS_FIRSTCALL()) + initUnnest(SRF_FIRSTCALL_INIT(), PG_GETARG_DATUM(0), + PG_GETARG_BOOL(1), WJB_ELEM, NULL); + + funcctx = SRF_PERCALL_SETUP(); + state = funcctx->user_fctx; + + if (state == NULL || (r = nextUnnest(state)) == NULL) + { + finiUnnest(state); + SRF_RETURN_DONE(funcctx); + } + + SRF_RETURN_NEXT(funcctx, JsonbGetDatum(r)); +} + +Datum +jsonb_unnest_element_index(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + UnnestState *state; + Jsonb *r; + Datum vals[2]; + bool nulls[2] = {false, false}; + + if (SRF_IS_FIRSTCALL()) + initUnnest(SRF_FIRSTCALL_INIT(), PG_GETARG_DATUM(0), + PG_GETARG_BOOL(1), WJB_ELEM, fcinfo); + + funcctx = SRF_PERCALL_SETUP(); + state = funcctx->user_fctx; + + if (state == NULL || (r = nextUnnest(state)) == NULL) + { + finiUnnest(state); + SRF_RETURN_DONE(funcctx); + } + + vals[0] = JsonbGetDatum(r); + vals[1] = Int32GetDatum(state->counter - 1); + + SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(heap_form_tuple(state->tupdesc, vals, nulls))); +} + +Datum +jsonb_unnest_value(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + UnnestState *state; + Jsonb *r; + + if (SRF_IS_FIRSTCALL()) + initUnnest(SRF_FIRSTCALL_INIT(), PG_GETARG_DATUM(0), + PG_GETARG_BOOL(1), WJB_VALUE, NULL); + + funcctx = SRF_PERCALL_SETUP(); + state = funcctx->user_fctx; + + if (state == NULL || (r = nextUnnest(state)) == NULL) + { + finiUnnest(state); + SRF_RETURN_DONE(funcctx); + } + + SRF_RETURN_NEXT(funcctx, JsonbGetDatum(r)); +} + +Datum +jsonb_unnest_key(PG_FUNCTION_ARGS) +{ + FuncCallContext *funcctx; + UnnestState *state; + text *r; + + if (SRF_IS_FIRSTCALL()) + initUnnest(SRF_FIRSTCALL_INIT(), PG_GETARG_DATUM(0), + PG_GETARG_BOOL(1), WJB_KEY, NULL); + + funcctx = SRF_PERCALL_SETUP(); + state = funcctx->user_fctx; + + if (state == NULL || (r = nextUnnestKey(state)) == NULL) + { + finiUnnest(state); + SRF_RETURN_DONE(funcctx); + } + + SRF_RETURN_NEXT(funcctx, PointerGetDatum(r)); +} + diff --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h index dd6079fbe3..fb046e2e02 100644 --- a/src/include/catalog/pg_aggregate.h +++ b/src/include/catalog/pg_aggregate.h @@ -257,9 +257,11 @@ DATA(insert ( 2828 n 0 float8_regr_accum float8_covar_samp - - - f f 0 DATA(insert ( 2829 n 0 float8_regr_accum float8_corr - - - f f 0 1022 0 0 0 "{0,0,0,0,0,0}" _null_ )); /* boolean-and and boolean-or */ -DATA(insert ( 2517 n 0 booland_statefunc - bool_accum bool_accum_inv bool_alltrue f f 58 16 0 2281 16 _null_ _null_ )); -DATA(insert ( 2518 n 0 boolor_statefunc - bool_accum bool_accum_inv bool_anytrue f f 59 16 0 2281 16 _null_ _null_ )); -DATA(insert ( 2519 n 0 booland_statefunc - bool_accum bool_accum_inv bool_alltrue f f 58 16 0 2281 16 _null_ _null_ )); +DATA(insert ( 2517 n 0 booland_statefunc - bool_accum bool_accum_inv bool_alltrue f f 58 16 0 2281 24 _null_ _null_ )); +DATA(insert ( 2518 n 0 boolor_statefunc - bool_accum bool_accum_inv bool_anytrue f f 59 16 0 2281 24 _null_ _null_ )); +DATA(insert ( 2519 n 0 booland_statefunc - bool_accum bool_accum_inv bool_alltrue f f 58 16 0 2281 24 _null_ _null_ )); +DATA(insert ( 7650 n 0 bool_accum bool_alltrue_notnull bool_accum bool_accum_inv bool_alltrue_notnull f f 58 2281 24 2281 24 _null_ _null_ )); +DATA(insert ( 7651 n 0 bool_accum bool_anytrue_notnull bool_accum bool_accum_inv bool_anytrue_notnull f f 59 2281 24 2281 24 _null_ _null_ )); /* bitwise integer */ DATA(insert ( 2236 n 0 int2and - - - - f f 0 21 0 0 0 _null_ _null_ )); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index fe06ec285d..f27135f9f3 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -924,6 +924,11 @@ DATA(insert OID = 1286 ( array_fill PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 22 DESCR("array constructor with value"); DATA(insert OID = 2331 ( unnest PGNSP PGUID 12 1 100 0 0 f f f f t t i 1 0 2283 "2277" _null_ _null_ _null_ _null_ _null_ array_unnest _null_ _null_ _null_ )); DESCR("expand array to set of rows"); +/* just for compatibility with jsonb_unnest* for any/each element clause */ +DATA(insert OID = 7644 ( unnest_element PGNSP PGUID 12 1 100 0 0 f f f f t t i 2 0 2283 "2277 16" _null_ _null_ _null_ _null_ _null_ array_unnest_element _null_ _null_ _null_ )); +DESCR("expand array to set of rows"); +DATA(insert OID = 7653 ( unnest_element_index PGNSP PGUID 12 1 100 0 0 f f f f t t i 2 0 2249 "2277 16" "{2277,16,2283,23}" "{i,i,o,o}" "{array_in,recursive,element,index}" _null_ _null_ array_unnest_element_index _null_ _null_ _null_ )); +DESCR("expand array to set of rows with index"); DATA(insert OID = 3167 ( array_remove PGNSP PGUID 12 1 0 0 0 f f f f f f i 2 0 2277 "2277 2283" _null_ _null_ _null_ _null_ _null_ array_remove _null_ _null_ _null_ )); DESCR("remove any occurrences of an element from an array"); DATA(insert OID = 3168 ( array_replace PGNSP PGUID 12 1 0 0 0 f f f f f f i 3 0 2277 "2277 2283 2283" _null_ _null_ _null_ _null_ _null_ array_replace _null_ _null_ _null_ )); @@ -4072,6 +4077,10 @@ DATA(insert OID = 3498 ( bool_alltrue PGNSP PGUID 12 1 0 0 0 f f f f t f i DESCR("aggregate final function"); DATA(insert OID = 3499 ( bool_anytrue PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 16 "2281" _null_ _null_ _null_ _null_ _null_ bool_anytrue _null_ _null_ _null_ )); DESCR("aggregate final function"); +DATA(insert OID = 7648 ( bool_alltrue_notnull PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 16 "2281" _null_ _null_ _null_ _null_ _null_ bool_alltrue_notnull _null_ _null_ _null_ )); +DESCR("aggregate final function"); +DATA(insert OID = 7649 ( bool_anytrue_notnull PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 16 "2281" _null_ _null_ _null_ _null_ _null_ bool_anytrue_notnull _null_ _null_ _null_ )); +DESCR("aggregate final function"); DATA(insert OID = 2517 ( bool_and PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 16 "16" _null_ _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ )); DESCR("boolean-and aggregate"); /* ANY, SOME? These names conflict with subquery operators. See doc. */ @@ -4079,6 +4088,10 @@ DATA(insert OID = 2518 ( bool_or PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 DESCR("boolean-or aggregate"); DATA(insert OID = 2519 ( every PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 16 "16" _null_ _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ )); DESCR("boolean-and aggregate"); +DATA(insert OID = 7650 ( bool_and_not_null PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 16 "16" _null_ _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ )); +DESCR("boolean-and aggregate"); +DATA(insert OID = 7651 ( bool_or_not_null PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 16 "16" _null_ _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ )); +DESCR("boolean-or aggregate"); /* bitwise integer aggregates */ DATA(insert OID = 2236 ( bit_and PGNSP PGUID 12 1 0 0 0 t f f f f f i 1 0 21 "21" _null_ _null_ _null_ _null_ _null_ aggregate_dummy _null_ _null_ _null_ )); @@ -4863,6 +4876,14 @@ DATA(insert OID = 3305 ( jsonb_set PGNSP PGUID 12 1 0 0 0 f f f f t f i 4 0 DESCR("Set part of a jsonb"); DATA(insert OID = 3306 ( jsonb_pretty PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 25 "3802" _null_ _null_ _null_ _null_ _null_ jsonb_pretty _null_ _null_ _null_ )); DESCR("Indented text from jsonb"); +DATA(insert OID = 7645 ( unnest_element PGNSP PGUID 12 1 100 0 0 f f f f t t i 2 0 3802 "3802 16" _null_ _null_ _null_ _null_ _null_ jsonb_unnest_element _null_ _null_ _null_ )); +DESCR("expand elements from jsonb"); +DATA(insert OID = 7652 ( unnest_element_index PGNSP PGUID 12 1 100 0 0 f f f f t t i 2 0 2249 "3802 16" "{3802,16,3802,23}" "{i,i,o,o}" "{jsonb_in,recursive,element,index}" _null_ _null_ jsonb_unnest_element_index _null_ _null_ _null_ )); +DESCR("expand elements from jsonb"); +DATA(insert OID = 7646 ( unnest_value PGNSP PGUID 12 1 100 0 0 f f f f t t i 2 0 3802 "3802 16" _null_ _null_ _null_ _null_ _null_ jsonb_unnest_value _null_ _null_ _null_ )); +DESCR("expand values from jsonb"); +DATA(insert OID = 7647 ( unnest_key PGNSP PGUID 12 1 100 0 0 f f f f t t i 2 0 25 "3802 16" _null_ _null_ _null_ _null_ _null_ jsonb_unnest_key _null_ _null_ _null_ )); +DESCR("expand keys from jsonb"); /* txid */ DATA(insert OID = 2939 ( txid_snapshot_in PGNSP PGUID 12 1 0 0 0 f f f f t f i 1 0 2970 "2275" _null_ _null_ _null_ _null_ _null_ txid_snapshot_in _null_ _null_ _null_ )); DESCR("I/O"); diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h index 2414069077..8c44428ec5 100644 --- a/src/include/parser/kwlist.h +++ b/src/include/parser/kwlist.h @@ -42,6 +42,7 @@ PG_KEYWORD("analyse", ANALYSE, RESERVED_KEYWORD) /* British spelling */ PG_KEYWORD("analyze", ANALYZE, RESERVED_KEYWORD) PG_KEYWORD("and", AND, RESERVED_KEYWORD) PG_KEYWORD("any", ANY, RESERVED_KEYWORD) +PG_KEYWORD("anywhere", ANYWHERE, UNRESERVED_KEYWORD) PG_KEYWORD("array", ARRAY, RESERVED_KEYWORD) PG_KEYWORD("as", AS, RESERVED_KEYWORD) PG_KEYWORD("asc", ASC, RESERVED_KEYWORD) @@ -136,6 +137,7 @@ PG_KEYWORD("domain", DOMAIN_P, UNRESERVED_KEYWORD) PG_KEYWORD("double", DOUBLE_P, UNRESERVED_KEYWORD) PG_KEYWORD("drop", DROP, UNRESERVED_KEYWORD) PG_KEYWORD("each", EACH, UNRESERVED_KEYWORD) +PG_KEYWORD("element", ELEMENT, UNRESERVED_KEYWORD) PG_KEYWORD("else", ELSE, RESERVED_KEYWORD) PG_KEYWORD("enable", ENABLE_P, UNRESERVED_KEYWORD) PG_KEYWORD("encoding", ENCODING, UNRESERVED_KEYWORD) @@ -331,6 +333,7 @@ PG_KEYWORD("rollup", ROLLUP, UNRESERVED_KEYWORD) PG_KEYWORD("row", ROW, COL_NAME_KEYWORD) PG_KEYWORD("rows", ROWS, UNRESERVED_KEYWORD) PG_KEYWORD("rule", RULE, UNRESERVED_KEYWORD) +PG_KEYWORD("satisfies", SATISFIES, UNRESERVED_KEYWORD) PG_KEYWORD("savepoint", SAVEPOINT, UNRESERVED_KEYWORD) PG_KEYWORD("schema", SCHEMA, UNRESERVED_KEYWORD) PG_KEYWORD("scroll", SCROLL, UNRESERVED_KEYWORD) diff --git a/src/include/utils/array.h b/src/include/utils/array.h index c25b80d272..b67ace34d5 100644 --- a/src/include/utils/array.h +++ b/src/include/utils/array.h @@ -356,6 +356,8 @@ extern Datum generate_subscripts_nodir(PG_FUNCTION_ARGS); extern Datum array_fill(PG_FUNCTION_ARGS); extern Datum array_fill_with_lower_bounds(PG_FUNCTION_ARGS); extern Datum array_unnest(PG_FUNCTION_ARGS); +extern Datum array_unnest_element(PG_FUNCTION_ARGS); +extern Datum array_unnest_element_index(PG_FUNCTION_ARGS); extern Datum array_remove(PG_FUNCTION_ARGS); extern Datum array_replace(PG_FUNCTION_ARGS); extern Datum width_bucket_array(PG_FUNCTION_ARGS); diff --git a/src/include/utils/jsonb.h b/src/include/utils/jsonb.h index 3049a87321..3943cde86e 100644 --- a/src/include/utils/jsonb.h +++ b/src/include/utils/jsonb.h @@ -65,10 +65,12 @@ typedef enum #define JGIN_MAXLENGTH 125 /* max length of text part before hashing */ /* Convenience macros */ -#define DatumGetJsonb(d) ((Jsonb *) PG_DETOAST_DATUM(d)) -#define JsonbGetDatum(p) PointerGetDatum(p) -#define PG_GETARG_JSONB(x) DatumGetJsonb(PG_GETARG_DATUM(x)) -#define PG_RETURN_JSONB(x) PG_RETURN_POINTER(x) +#define DatumGetJsonb(d) ((Jsonb *) PG_DETOAST_DATUM(d)) +#define DatumGetJsonbCopy(d) ((Jsonb *) PG_DETOAST_DATUM_COPY(d)) +#define JsonbGetDatum(p) PointerGetDatum(p) +#define PG_GETARG_JSONB(x) DatumGetJsonb(PG_GETARG_DATUM(x)) +#define PG_GETARG_JSONB_COPY(x) DatumGetJsonbCopy(PG_GETARG_DATUM(x)) +#define PG_RETURN_JSONB(x) PG_RETURN_POINTER(x) typedef struct JsonbPair JsonbPair; typedef struct JsonbValue JsonbValue; @@ -400,6 +402,12 @@ extern Datum jsonb_pretty(PG_FUNCTION_ARGS); /* concatenation */ extern Datum jsonb_concat(PG_FUNCTION_ARGS); +/* unnesting function */ +extern Datum jsonb_unnest_element(PG_FUNCTION_ARGS); +extern Datum jsonb_unnest_element_index(PG_FUNCTION_ARGS); +extern Datum jsonb_unnest_value(PG_FUNCTION_ARGS); +extern Datum jsonb_unnest_key(PG_FUNCTION_ARGS); + /* deletion */ extern Datum jsonb_delete(PG_FUNCTION_ARGS); extern Datum jsonb_delete_idx(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/arrays.out b/src/test/regress/expected/arrays.out index 5f1532f237..d762df3602 100644 --- a/src/test/regress/expected/arrays.out +++ b/src/test/regress/expected/arrays.out @@ -943,6 +943,43 @@ SELECT * FROM array_op_test WHERE t <@ '{}' ORDER BY seqno; 101 | {} | {} (1 row) +--each/any element +SELECT * FROM array_op_test WHERE ANY ELEMENT OF t AS e SATISFIES (e = 'AAAAAAAAAAAAAAAAA764') ORDER BY seqno; + seqno | i | t +-------+--------------------------------+------------------------------------------------------------------------------------------------------------------------------------------- + 11 | {41,86,74,48,22,74,47,50} | {AAAAAAAA9523,AAAAAAAAAAAA37562,AAAAAAAAAAAAAAAA14047,AAAAAAAAAAA46154,AAAA41702,AAAAAAAAAAAAAAAAA764,AAAAA62737,39557} + 23 | {40,90,5,38,72,40,30,10,43,55} | {A6053,AAAAAAAAAAA6119,AA44673,AAAAAAAAAAAAAAAAA764,AA17009,AAAAA17383,AAAAA70514,AAAAA33250,AAAAA95309,AAAAAAAAAAAA37562} + 52 | {89,0} | {AAAAAAAAAAAAAAAAAA47955,AAAAAAA48038,AAAAAAAAAAAAAAAAA43052,AAAAAAAAAAAAA73084,AAAAA70466,AAAAAAAAAAAAAAAAA764,AAAAAAAAAAA46154,AA66862} + 95 | {47,77} | {AAAAAAAAAAAAAAAAA764,AAAAAAAAAAA74076,AAAAAAAAAA18107,AAAAA40681,AAAAAAAAAAAAAAA35875,AAAAA60038,AAAAAAA56483} +(4 rows) + +SELECT * FROM array_op_test WHERE ANY ELEMENT OF i AS e SATISFIES (e = 11) ORDER BY seqno; + seqno | i | t +-------+--------------------------------+----------------------------------------------------------------------------------------------------------------------------------------------------------- + 3 | {37,64,95,43,3,41,13,30,11,43} | {AAAAAAAAAA48845,AAAAA75968,AAAAA95309,AAA54451,AAAAAAAAAA22292,AAAAAAA99836,A96617,AA17009,AAAAAAAAAAAAAA95246} + 16 | {14,63,85,11} | {AAAAAA66777} + 22 | {11,6,56,62,53,30} | {AAAAAAAA72908} + 25 | {31,1,10,11,27,79,38} | {AAAAAAAAAAAAAAAAAA59334,45449} + 37 | {53,11,81,39,3,78,58,64,74} | {AAAAAAAAAAAAAAAAAAA17075,AAAAAAA66161,AAAAAAAA23648,AAAAAAAAAAAAAA10611} + 63 | {11,4,61,87} | {AAAAAAAAA27249,AAAAAAAAAAAAAAAAAA32918,AAAAAAAAAAAAAAA13198,AAA20874,39557,51533,AAAAAAAAAAA53908,AAAAAAAAAAAAAA96505,AAAAAAAA78938} + 84 | {11,83,35,13,96,94} | {AAAAA95309,AAAAAAAAAAAAAAAAAA32918,AAAAAAAAAAAAAAAAAA24183} + 93 | {11} | {AAAAAAAAAAA176,AAAAAAAAAAAAAA8666,AAAAAAAAAAAAAAA453,AAAAAAAAAAAAA85723,A68938,AAAAAAAAAAAAA9821,AAAAAAA48038,AAAAAAAAAAAAAAAAA59387,AA99927,AAAAA17383} +(8 rows) + +SELECT * FROM array_op_test WHERE EACH ELEMENT OF t AS e SATISFIES (e = 'AAAAAA66777') ORDER BY seqno; + seqno | i | t +-------+---------------+--------------- + 16 | {14,63,85,11} | {AAAAAA66777} + 101 | {} | {} +(2 rows) + +SELECT * FROM array_op_test WHERE EACH ELEMENT OF i AS e SATISFIES (e = 11) ORDER BY seqno; + seqno | i | t +-------+------+----------------------------------------------------------------------------------------------------------------------------------------------------------- + 93 | {11} | {AAAAAAAAAAA176,AAAAAAAAAAAAAA8666,AAAAAAAAAAAAAAA453,AAAAAAAAAAAAA85723,A68938,AAAAAAAAAAAAA9821,AAAAAAA48038,AAAAAAAAAAAAAAAAA59387,AA99927,AAAAA17383} + 101 | {} | {} +(2 rows) + -- array casts SELECT ARRAY[1,2,3]::text[]::int[]::float8[] AS "{1,2,3}"; {1,2,3} @@ -2010,3 +2047,46 @@ SELECT width_bucket(5, ARRAY[3, 4, NULL]); ERROR: thresholds array must not contain NULLs SELECT width_bucket(5, ARRAY[ARRAY[1, 2], ARRAY[3, 4]]); ERROR: thresholds must be one-dimensional array +--anytest +create table anytest(a int[]); +insert into anytest values + (NULL), + ('{}'), + ('{NULL}'), + ('{1}'), + ('{1,NULL}'), + ('{2,NULL}'), + ('{NULL,1}'), + ('{NULL,2}'); +SELECT a, (1 = ANY(a)), ANY ELEMENT OF a AS v SATISFIES (v = 1) FROM anytest; + a | ?column? | bool_or_not_null +----------+----------+------------------ + | | + {} | f | f + {NULL} | | + {1} | t | t + {1,NULL} | t | t + {2,NULL} | | + {NULL,1} | t | t + {NULL,2} | | +(8 rows) + +SELECT a, (1 = ALL(a)), EACH ELEMENT OF a AS v SATISFIES (v = 1) FROM anytest; + a | ?column? | bool_and_not_null +----------+----------+------------------- + | | + {} | t | t + {NULL} | | + {1} | t | t + {1,NULL} | | + {2,NULL} | f | f + {NULL,1} | | + {NULL,2} | f | f +(8 rows) + +SELECT ANY ELEMENT OF '{1,2,3}'::int4[] AS e with index as i SATISFIES (e > 1 and i = 3); + bool_or_not_null +------------------ + t +(1 row) + diff --git a/src/test/regress/expected/jsonb.out b/src/test/regress/expected/jsonb.out index 412bf97b83..0aed838fb9 100644 --- a/src/test/regress/expected/jsonb.out +++ b/src/test/regress/expected/jsonb.out @@ -2118,6 +2118,68 @@ SELECT count(*) FROM testjsonb WHERE j ?& ARRAY['public','disabled']; 42 (1 row) +SELECT count(*) FROM testjsonb WHERE ANY KEY OF j AS k SATISFIES ( k = 'pos' ); + count +------- + 203 +(1 row) + +SELECT count(*) FROM testjsonb WHERE ANY KEY OF j AS k SATISFIES ( k = 'age' ); + count +------- + 2 +(1 row) + +SELECT count(*) FROM testjsonb WHERE EACH KEY OF j AS k SATISFIES ( k = 'age' ); + count +------- + 120 +(1 row) + +SELECT count(*) FROM testjsonb WHERE ANY VALUE OF j AS v SATISFIES ( v = '"CAB"'::jsonb ); + count +------- + 30 +(1 row) + +SELECT count(*) FROM testjsonb WHERE ANY VALUE OF j AS v SATISFIES ( v = '"baz"'::jsonb ); + count +------- + 0 +(1 row) + +SELECT count(*) FROM testjsonb WHERE ANY VALUE ANYWHERE OF j AS v SATISFIES ( v = '"baz"'::jsonb ); + count +------- + 3 +(1 row) + +SELECT count(*) FROM testjsonb WHERE ANY ELEMENT ANYWHERE OF j AS v SATISFIES ( v = '"baz"'::jsonb ); + count +------- + 4 +(1 row) + +SELECT count(*) FROM testjsonb WHERE ANY ELEMENT OF j->'array' AS e SATISFIES ( e = '"baz"'::jsonb ); + count +------- + 4 +(1 row) + +SELECT count(*) FROM testjsonb WHERE ANY KEY OF j AS k SATISFIES ( + k = 'array' AND ANY ELEMENT OF j->k AS e SATISFIES ( e = '"baz"'::jsonb ) +); + count +------- + 4 +(1 row) + +SELECT ANY ELEMENT OF '[1,2,3]'::jsonb AS e with index as i SATISFIES (e > '1'::jsonb and i = 1); + bool_or_not_null +------------------ + t +(1 row) + CREATE INDEX jidx ON testjsonb USING gin (j); SET enable_seqscan = off; SELECT count(*) FROM testjsonb WHERE j @> '{"wait":null}'; diff --git a/src/test/regress/expected/jsonb_1.out b/src/test/regress/expected/jsonb_1.out index 4ead74b572..c2976550e5 100644 --- a/src/test/regress/expected/jsonb_1.out +++ b/src/test/regress/expected/jsonb_1.out @@ -2118,6 +2118,68 @@ SELECT count(*) FROM testjsonb WHERE j ?& ARRAY['public','disabled']; 42 (1 row) +SELECT count(*) FROM testjsonb WHERE ANY KEY OF j AS k SATISFIES ( k = 'pos' ); + count +------- + 203 +(1 row) + +SELECT count(*) FROM testjsonb WHERE ANY KEY OF j AS k SATISFIES ( k = 'age' ); + count +------- + 2 +(1 row) + +SELECT count(*) FROM testjsonb WHERE EACH KEY OF j AS k SATISFIES ( k = 'age' ); + count +------- + 120 +(1 row) + +SELECT count(*) FROM testjsonb WHERE ANY VALUE OF j AS v SATISFIES ( v = '"CAB"'::jsonb ); + count +------- + 30 +(1 row) + +SELECT count(*) FROM testjsonb WHERE ANY VALUE OF j AS v SATISFIES ( v = '"baz"'::jsonb ); + count +------- + 0 +(1 row) + +SELECT count(*) FROM testjsonb WHERE ANY VALUE ANYWHERE OF j AS v SATISFIES ( v = '"baz"'::jsonb ); + count +------- + 3 +(1 row) + +SELECT count(*) FROM testjsonb WHERE ANY ELEMENT ANYWHERE OF j AS v SATISFIES ( v = '"baz"'::jsonb ); + count +------- + 4 +(1 row) + +SELECT count(*) FROM testjsonb WHERE ANY ELEMENT OF j->'array' AS e SATISFIES ( e = '"baz"'::jsonb ); + count +------- + 4 +(1 row) + +SELECT count(*) FROM testjsonb WHERE ANY KEY OF j AS k SATISFIES ( + k = 'array' AND ANY ELEMENT OF j->k AS e SATISFIES ( e = '"baz"'::jsonb ) +); + count +------- + 4 +(1 row) + +SELECT ANY ELEMENT OF '[1,2,3]'::jsonb AS e with index as i SATISFIES (e > '1'::jsonb and i = 1); + bool_or_not_null +------------------ + t +(1 row) + CREATE INDEX jidx ON testjsonb USING gin (j); SET enable_seqscan = off; SELECT count(*) FROM testjsonb WHERE j @> '{"wait":null}'; diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out index df29fe503e..1289040d49 100644 --- a/src/test/regress/expected/opr_sanity.out +++ b/src/test/regress/expected/opr_sanity.out @@ -1355,14 +1355,16 @@ SELECT DISTINCT proname, oprname FROM pg_operator AS o, pg_aggregate AS a, pg_proc AS p WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid ORDER BY 1, 2; - proname | oprname -----------+--------- - bool_and | < - bool_or | > - every | < - max | > - min | < -(5 rows) + proname | oprname +-------------------+--------- + bool_and | < + bool_and_not_null | < + bool_or | > + bool_or_not_null | > + every | < + max | > + min | < +(7 rows) -- Check datatypes match SELECT a.aggfnoid::oid, o.oid @@ -1395,14 +1397,16 @@ WHERE a.aggfnoid = p.oid AND a.aggsortop = o.oid AND amopopr = o.oid AND amopmethod = (SELECT oid FROM pg_am WHERE amname = 'btree') ORDER BY 1, 2; - proname | oprname | amopstrategy -----------+---------+-------------- - bool_and | < | 1 - bool_or | > | 5 - every | < | 1 - max | > | 5 - min | < | 1 -(5 rows) + proname | oprname | amopstrategy +-------------------+---------+-------------- + bool_and | < | 1 + bool_and_not_null | < | 1 + bool_or | > | 5 + bool_or_not_null | > | 5 + every | < | 1 + max | > | 5 + min | < | 1 +(7 rows) -- Check that there are not aggregates with the same name and different -- numbers of arguments. While not technically wrong, we have a project policy diff --git a/src/test/regress/output/misc.source b/src/test/regress/output/misc.source index 70c9cc356a..9ad5ae44ef 100644 --- a/src/test/regress/output/misc.source +++ b/src/test/regress/output/misc.source @@ -583,6 +583,7 @@ SELECT user_relns() AS user_relns abstime_tbl aggtest aggtype + anytest array_index_op_test array_op_test arrtest @@ -705,7 +706,7 @@ SELECT user_relns() AS user_relns tvvmv varchar_tbl xacttest -(127 rows) +(128 rows) SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer'))); name diff --git a/src/test/regress/sql/arrays.sql b/src/test/regress/sql/arrays.sql index 562134b286..b76db16f56 100644 --- a/src/test/regress/sql/arrays.sql +++ b/src/test/regress/sql/arrays.sql @@ -260,6 +260,12 @@ SELECT * FROM array_op_test WHERE t @> '{}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t && '{}' ORDER BY seqno; SELECT * FROM array_op_test WHERE t <@ '{}' ORDER BY seqno; +--each/any element +SELECT * FROM array_op_test WHERE ANY ELEMENT OF t AS e SATISFIES (e = 'AAAAAAAAAAAAAAAAA764') ORDER BY seqno; +SELECT * FROM array_op_test WHERE ANY ELEMENT OF i AS e SATISFIES (e = 11) ORDER BY seqno; +SELECT * FROM array_op_test WHERE EACH ELEMENT OF t AS e SATISFIES (e = 'AAAAAA66777') ORDER BY seqno; +SELECT * FROM array_op_test WHERE EACH ELEMENT OF i AS e SATISFIES (e = 11) ORDER BY seqno; + -- array casts SELECT ARRAY[1,2,3]::text[]::int[]::float8[] AS "{1,2,3}"; SELECT ARRAY[1,2,3]::text[]::int[]::float8[] is of (float8[]) as "TRUE"; @@ -596,3 +602,22 @@ SELECT width_bucket(5, '{}'); SELECT width_bucket('5'::text, ARRAY[3, 4]::integer[]); SELECT width_bucket(5, ARRAY[3, 4, NULL]); SELECT width_bucket(5, ARRAY[ARRAY[1, 2], ARRAY[3, 4]]); + + +--anytest + +create table anytest(a int[]); +insert into anytest values + (NULL), + ('{}'), + ('{NULL}'), + ('{1}'), + ('{1,NULL}'), + ('{2,NULL}'), + ('{NULL,1}'), + ('{NULL,2}'); + +SELECT a, (1 = ANY(a)), ANY ELEMENT OF a AS v SATISFIES (v = 1) FROM anytest; +SELECT a, (1 = ALL(a)), EACH ELEMENT OF a AS v SATISFIES (v = 1) FROM anytest; + +SELECT ANY ELEMENT OF '{1,2,3}'::int4[] AS e with index as i SATISFIES (e > 1 and i = 3); diff --git a/src/test/regress/sql/jsonb.sql b/src/test/regress/sql/jsonb.sql index 2abec221b4..d37c322f08 100644 --- a/src/test/regress/sql/jsonb.sql +++ b/src/test/regress/sql/jsonb.sql @@ -519,6 +519,20 @@ SELECT count(*) FROM testjsonb WHERE j ? 'bar'; SELECT count(*) FROM testjsonb WHERE j ?| ARRAY['public','disabled']; SELECT count(*) FROM testjsonb WHERE j ?& ARRAY['public','disabled']; +SELECT count(*) FROM testjsonb WHERE ANY KEY OF j AS k SATISFIES ( k = 'pos' ); +SELECT count(*) FROM testjsonb WHERE ANY KEY OF j AS k SATISFIES ( k = 'age' ); +SELECT count(*) FROM testjsonb WHERE EACH KEY OF j AS k SATISFIES ( k = 'age' ); +SELECT count(*) FROM testjsonb WHERE ANY VALUE OF j AS v SATISFIES ( v = '"CAB"'::jsonb ); +SELECT count(*) FROM testjsonb WHERE ANY VALUE OF j AS v SATISFIES ( v = '"baz"'::jsonb ); +SELECT count(*) FROM testjsonb WHERE ANY VALUE ANYWHERE OF j AS v SATISFIES ( v = '"baz"'::jsonb ); +SELECT count(*) FROM testjsonb WHERE ANY ELEMENT ANYWHERE OF j AS v SATISFIES ( v = '"baz"'::jsonb ); +SELECT count(*) FROM testjsonb WHERE ANY ELEMENT OF j->'array' AS e SATISFIES ( e = '"baz"'::jsonb ); +SELECT count(*) FROM testjsonb WHERE ANY KEY OF j AS k SATISFIES ( + k = 'array' AND ANY ELEMENT OF j->k AS e SATISFIES ( e = '"baz"'::jsonb ) +); +SELECT ANY ELEMENT OF '[1,2,3]'::jsonb AS e with index as i SATISFIES (e > '1'::jsonb and i = 1); + + CREATE INDEX jidx ON testjsonb USING gin (j); SET enable_seqscan = off;