I know it's been a few days, but I've finally had time to actually get back to this
I'd think that a generic form of this might actually be a way to go, but I'm not (yet) familiar enough with sqlalchemy's internals to make a lot of headway in designing any 'sane' solution.
It's worth noting PostgreSQL supports column-level aliasing on anything, not just a set-returning-function, such that the following is valid:
CREATE TABLE foo ( id SERIAL NOT NULL);
INSERT INTO foo ( id ) VALUES (1), (2), (3);
SELECT bar.baz FROM foo AS bar(baz);
That said, it's not cleanly usable in most cases -- you can't alias a column by name, so you have to know the exact order columns appear in (which you might not know if you didn't reflect nor create the table -- a column definition that doesn't appear in the model might be in the table, for instance).
That said, it'd seem like a generic "Set Returning Function" implementation would need to do the following:
* Subclass from GenericFunction to track data types of input values and whatever voodoo is required for bind parameters. (I think this also gives us the behavior of "SELECT generate_series(...)" being treated like "SELECT FROM ... generate_series(...)..." for free)
* Subclass from FromClause or perhaps even Alias to track types and names of output values in self.c
* Have an alternate/extended 'alias' implementation capable of defining aliases on a per-column level, so AS alias becomes AS alias(column_alias, ...)
So usage might be something like:
series = sql.srfunc.generate_series(1, 10, output=[Column('value', Integer()])
foo = series.alias("foo").column_alias('value', 'v')
bar = foo.alias("bar") # I'm assuming that aliasing an existing alias Does The Right Thing(tm), I've never tried it.
session.query(series.c.value)
# SELECT generate_series.value FROM generate_series(1, 10) AS generate_series(value)
session.query(foo.c.v)
# SELECT foo.v FROM generate_series(1, 10) AS foo(v)
session.query(foo.c.v + bar.c.v).select_from(foo).join(bar, foo.c.v < foo.c.v.)
# SELECT foo.v + bar.v FROM generate_series(1, 10) AS foo(v) JOIN generate_series(1, 10) AS bar(v) ON foo.v < bar.v;
Thoughts?
-- Daniel