A PostgreSQL (recursive) WITH query [1] can deal with that:
CREATE TABLE employee (
id int primary key,
name text,
manager int references employee(id)
);
INSERT INTO employee(id, name, manager)
VALUES (1, 'jane', null),
(2, 'john', 1),
(3, 'jake', 1),
(4, 'jeff', null),
(5, 'jessica', 3);
WITH RECURSIVE t(manager, managed) AS (
-- Direct managers
SELECT manager, id
FROM employee
WHERE manager IS NOT NULL
UNION ALL
-- Indirect managers
SELECT employee.manager, t.managed
FROM t, employee
WHERE t.manager = employee.id
AND employee.manager IS NOT NULL)
SELECT
(SELECT name FROM employee WHERE id = t.managed),
array_agg(employee.name) AS chain_of_command
FROM t, employee
WHERE t.manager = employee.id
GROUP BY managed;
Results of the query:
name | chain_of_command
---------+------------------
jessica | {jake,jane}
jake | {jane}
john | {jane}
(3 rows)
You can argue about the readability (syntax highlighting would help), but to my eyes it isn't that bad (if you know how WITH queries work). It's also more concise than the corresponding query would be in many a procedural language.
The efficiency should be pretty acceptable too (instead of having direct pointers O(1) to follow the manager, you follow them indirectly through an index O(log N)).
[1] https://www.postgresql.org/docs/9.6/static/queries-with.html