In PostgreSQL, every table name serves as type name for the row type (a.k.a. composite type) automatically - not a table type, there are no "table types" or "table variables" in Postgres (but there are typed tables).
So you can just declare a variable of that type in
CREATE FUNCTION foo()
RETURNS void LANGUAGE plpgsql AS
q1 foo; -- "foo" ...
q2 bar; -- ... and "bar" are existing (visible) table names
FOR q1 IN
SELECT * from foo
FOR q2 IN
SELECT * from bar
-- do something with q1 and q2
-- since q1 and q2 are well known types, you can access columns
-- with attribute notation. Like: q1.col1
FOR loop works with a built-in cursor. There are also explicit cursors in plpgsql.
You could also just declare variables of the generic type
record. It can take any row type at assignment automatically. But special rules apply. Be sure to follow the link and read the chapter of the manual!
While it's often convenient to have the function return
SETOF <table name>, returning
SETOF record is not as convenient. The system does not know what the function returns this way and you have to add a column definition list with every call. Which is a pain. Details about table functions in the manual.
Often there are more efficient solutions with plain SQL, though. Looping is a measure of last resort, when you can do things in one scan where you would need multiple scans in pure SQL.