*** a/src/pl/plpgsql/src/pl_exec.c --- b/src/pl/plpgsql/src/pl_exec.c *************** *** 1500,1508 **** static int exec_stmt_perform(PLpgSQL_execstate *estate, PLpgSQL_stmt_perform *stmt) { PLpgSQL_expr *expr = stmt->expr; (void) exec_run_select(estate, expr, 0, NULL); ! exec_set_found(estate, (estate->eval_processed != 0)); exec_eval_cleanup(estate); return PLPGSQL_RC_OK; --- 1500,1519 ---- exec_stmt_perform(PLpgSQL_execstate *estate, PLpgSQL_stmt_perform *stmt) { PLpgSQL_expr *expr = stmt->expr; + uint32 n; (void) exec_run_select(estate, expr, 0, NULL); ! n = estate->eval_processed; ! if (stmt->strict && n == 0) ! ereport(ERROR, ! (errcode(ERRCODE_NO_DATA_FOUND), ! errmsg("query returned no rows"))); ! else if (stmt->strict && n > 1) ! ereport(ERROR, ! (errcode(ERRCODE_TOO_MANY_ROWS), ! errmsg("query returned more than one row"))); ! ! exec_set_found(estate, (n != 0)); exec_eval_cleanup(estate); return PLPGSQL_RC_OK; *************** *** 3211,3217 **** exec_stmt_execsql(PLpgSQL_execstate *estate, * forcing completion of a sequential scan. So don't do it unless we need * to enforce strictness. */ ! if (stmt->into) { if (stmt->strict || stmt->mod_stmt) tcount = 2; --- 3222,3228 ---- * forcing completion of a sequential scan. So don't do it unless we need * to enforce strictness. */ ! if (stmt->into || stmt->strict) { if (stmt->strict || stmt->mod_stmt) tcount = 2; *************** *** 3335,3340 **** exec_stmt_execsql(PLpgSQL_execstate *estate, --- 3346,3366 ---- exec_eval_cleanup(estate); SPI_freetuptable(SPI_tuptable); } + else if (stmt->strict) + { + /* + * If a mod stmt specified STRICT, and the query didn't find + * exactly one row, throw an error. + */ + if (SPI_processed == 0) + ereport(ERROR, + (errcode(ERRCODE_NO_DATA_FOUND), + errmsg("query returned no rows"))); + else if (SPI_processed > 1) + ereport(ERROR, + (errcode(ERRCODE_TOO_MANY_ROWS), + errmsg("query returned more than one row"))); + } else { /* If the statement returned a tuple table, complain */ *** a/src/pl/plpgsql/src/pl_funcs.c --- b/src/pl/plpgsql/src/pl_funcs.c *************** *** 1187,1193 **** static void dump_perform(PLpgSQL_stmt_perform *stmt) { dump_ind(); ! printf("PERFORM expr = "); dump_expr(stmt->expr); printf("\n"); } --- 1187,1193 ---- dump_perform(PLpgSQL_stmt_perform *stmt) { dump_ind(); ! printf("PERFORM%s expr = ", stmt->strict ? " STRICT" : ""); dump_expr(stmt->expr); printf("\n"); } *** a/src/pl/plpgsql/src/pl_gram.y --- b/src/pl/plpgsql/src/pl_gram.y *************** *** 178,183 **** static List *read_raise_options(void); --- 178,184 ---- %type expr_until_semi expr_until_rightbracket %type expr_until_then expr_until_loop opt_expr_until_when %type opt_exitcond + %type opt_strict %type assign_var foreach_slice %type cursor_variable *************** *** 834,847 **** proc_stmt : pl_block ';' { $$ = $1; } ; ! stmt_perform : K_PERFORM expr_until_semi { PLpgSQL_stmt_perform *new; new = palloc0(sizeof(PLpgSQL_stmt_perform)); new->cmd_type = PLPGSQL_STMT_PERFORM; new->lineno = plpgsql_location_to_lineno(@1); ! new->expr = $2; $$ = (PLpgSQL_stmt *)new; } --- 835,849 ---- { $$ = $1; } ; ! stmt_perform : K_PERFORM opt_strict expr_until_semi { PLpgSQL_stmt_perform *new; new = palloc0(sizeof(PLpgSQL_stmt_perform)); new->cmd_type = PLPGSQL_STMT_PERFORM; new->lineno = plpgsql_location_to_lineno(@1); ! new->strict = $2; ! new->expr = $3; $$ = (PLpgSQL_stmt *)new; } *************** *** 2207,2212 **** opt_exitcond : ';' --- 2209,2223 ---- { $$ = $2; } ; + opt_strict : + { + $$ = false; + } + | K_STRICT + { + $$ = true; + } + /* * need to allow DATUM because scanner will have tried to resolve as variable */ *************** *** 2665,2679 **** make_execsql_stmt(int firsttoken, int location) { prev_tok = tok; tok = yylex(); ! if (have_into && into_end_loc < 0) ! into_end_loc = yylloc; /* token after the INTO part */ if (tok == ';') break; if (tok == 0) yyerror("unexpected end of function definition"); if (tok == K_INTO && prev_tok != K_INSERT) { if (have_into) yyerror("INTO specified more than once"); have_into = true; --- 2676,2698 ---- { prev_tok = tok; tok = yylex(); ! if ((have_strict || have_into) && into_end_loc < 0) ! into_end_loc = yylloc; /* token after the INTO (or STRICT) part */ if (tok == ';') break; if (tok == 0) yyerror("unexpected end of function definition"); + if (tok == K_STRICT) + { + into_start_loc = yylloc; + have_strict = true; + } + if (tok == K_INTO && prev_tok != K_INSERT) { + if (have_strict) + yyerror("STRICT must be part of INTO clause of INTO is specified"); if (have_into) yyerror("INTO specified more than once"); have_into = true; *************** *** 2686,2692 **** make_execsql_stmt(int firsttoken, int location) plpgsql_IdentifierLookup = save_IdentifierLookup; ! if (have_into) { /* * Insert an appropriate number of spaces corresponding to the --- 2705,2711 ---- plpgsql_IdentifierLookup = save_IdentifierLookup; ! if (have_strict || have_into) { /* * Insert an appropriate number of spaces corresponding to the *** a/src/pl/plpgsql/src/plpgsql.h --- b/src/pl/plpgsql/src/plpgsql.h *************** *** 375,380 **** typedef struct --- 375,381 ---- { /* PERFORM statement */ int cmd_type; int lineno; + bool strict; PLpgSQL_expr *expr; } PLpgSQL_stmt_perform;