doltgresql icon indicating copy to clipboard operation
doltgresql copied to clipboard

Support returning row sets from functions

Open tbantle22 opened this issue 1 year ago • 1 comments

There are some functions (like unnest) that should return a set of elements. We currently do not support returning sets of elements with our current function framework. For example, the unnest function should return a set of AnyElement

// unnest represents the PostgreSQL function of the same name, taking the same parameters.
var unnest = framework.Function1{
	Name:       "unnest",
	Return:     pgtypes.AnyElement, // TODO: Should return setof AnyElement
	Parameters: [1]pgtypes.DoltgresType{pgtypes.AnyArray},
	Strict:     true,
	Callable: func(ctx *sql.Context, _ [2]pgtypes.DoltgresType, val1 any) (any, error) {
		valArr := val1.([]interface{})
		if len(valArr) == 0 {
			return nil, nil
		}
		if len(valArr) == 1 {
			return valArr[0], nil
		}
		return nil, fmt.Errorf("unnest() only supports single-element arrays")
	},
}

There are skipped tests for unnest here

tbantle22 avatar Aug 21 '24 17:08 tbantle22

From my understanding of reading the documentation, setof type is essentially how standard table operations work. When we create a table, the table creation implicitly creates a composite type that is composed of the types present in the table. For example, the following CREATE TYPE is implicit with the table's creation:

CREATE TABLE example_table (pk INT8 PRIMARY KEY, v1 INT4);
CREATE TYPE example_table AS (pk INT8, v1 INT4);

You can then imagine that querying the above table returns setof example_table, which represents the rows of the table. This concept is then extended to regular functions, which isn't surprising since practically everything in Postgres is a function in some capacity (wouldn't surprise me if SELECT * FROM table; is really SELECT get_rows('table'::regclass); under the hood). There's then the additional layer that can wrap single type returns in setof, so that you can call any function as "table functions" and they'll just work.

I'm mentioning this here since it should influence the design of setof. We don't necessarily need to consider table inheritance and such since we're probably far from implementing that, but modeling it similar to how Postgres does seem to be the fastest, easiest, and most compatible way forward. We shouldn't build something here that's specific to unnest in particular, since this is guaranteed to come up later. I can also envision implementing all of our Dolt table functions as regular functions that return setof type.

References:

  • https://www.postgresql.org/docs/15/rowtypes.html
  • https://www.postgresql.org/docs/15/sql-createtable.html
  • https://www.postgresql.org/docs/15/functions-srf.html

Hydrocharged avatar Aug 22 '24 14:08 Hydrocharged