*** a/src/pl/plpgsql/src/pl_exec.c --- b/src/pl/plpgsql/src/pl_exec.c *************** *** 3256,3261 **** exec_prepare_plan(PLpgSQL_execstate *estate, --- 3256,3291 ---- exec_simple_check_plan(expr); } + /* + * exec_check_execsql_targetlist: Check that the target list of a query with + * INTO matches the number of INTO targets. + */ + static void + exec_check_execsql_targetlist(PLpgSQL_stmt_execsql *stmt, + List *targetList) + { + int fnum; + int target_columns; + + /* no need to check if it's a record */ + if (stmt->row == NULL) + return; + + target_columns = 0; + for (fnum = 0; fnum < stmt->row->nfields; fnum++) + { + if (stmt->row->varnos[fnum] < 0) + continue; + target_columns++; + } + + if (list_length(targetList) != target_columns) + ereport(ERROR, + (errcode(ERRCODE_SYNTAX_ERROR), + errmsg("the number of targets in INTO does not match the number of columns returned by the query"), + errdetail("the query returns %d columns, but the target expects %d", + list_length(targetList), target_columns))); + } /* ---------- * exec_stmt_execsql Execute an SQL statement (possibly with INTO). *************** *** 3295,3301 **** exec_stmt_execsql(PLpgSQL_execstate *estate, --- 3325,3336 ---- if (q->commandType == CMD_INSERT || q->commandType == CMD_UPDATE || q->commandType == CMD_DELETE) + { + exec_check_execsql_targetlist(stmt, q->returningList); stmt->mod_stmt = true; + } + else + exec_check_execsql_targetlist(stmt, q->targetList); } } }