Skip to content

Support PostgreSql UnNest Array to rows #5673

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

griffio
Copy link
Contributor

@griffio griffio commented Feb 27, 2025

Support Unnest Function - expand an array to a set of rows

fixes #5346

🪹 🏗️ Work
🚧 🪺 In Progress

  • Add "unnest" to PostgreSql.bnf
  • Add mixins to help with column resolving as there are new aliases used by "unnest" in FROM
  • Update PostresqResolver to handle converting Array to T - this was not supported by IntermediateType
  • Add TableFunctionRowType to allow new concept of nested type to row type - not an actual table
  • Add fixture and integration tests

Prototype use project https://github.com/griffio/sqldelight-postgres-unnest

  • PostgreSQL allows a function (e.g unnest) to be written directly as a member of the FROM list. Allows multiple arrays to be unnested
  • SELECT unnest() allows a single array to be unnested

Examples:

Allows "Array DML" - e.g bulk inserts, updates and deletes

CREATE TABLE Business(
    id SERIAL PRIMARY KEY,
    name TEXT NOT NULL,
    zipcodes TEXT[] NOT NULL,
    headcounts INTEGER[] NOT NULL
);

CREATE TABLE Users (
   name TEXT NOT NULL,
   age INTEGER NOT NULL
);

select:
SELECT name, location.headcount, location.zipcode
FROM Business, UNNEST(zipcodes, headcounts) AS location(zipcode, headcount);

counts:
SELECT name, UNNEST(headcounts) AS headcount
FROM Business
ORDER BY headcount DESC;

array:
SELECT unnest('{1,2}'::INTEGER);

insertUsers:
INSERT INTO Users (name, age)
SELECT * FROM UNNEST(?::TEXT[], ?::INTEGER[])  AS u(name, age); 
// currently must explicitly add alias columns due to wildcard expansion in SqlDelight

updateUsers:
UPDATE Users
SET age = updates.updated_age
FROM UNNEST(?::TEXT[], ?::INTEGER[]) AS updates(name, updated_age)
WHERE Users.name = updates.name;

deleteUsers:
DELETE FROM Users
WHERE (name, age) IN (
  SELECT *
  FROM UNNEST(?::TEXT[], ?::INTEGER[]) AS u(name, age)
);

selectLocations:
SELECT DISTINCT b.*
FROM Business b
JOIN LATERAL UNNEST(b.zipcodes) AS loc(zipcode) ON loc.zipcode ILIKE '%' || :query || '%';

selectBusinessExists:
SELECT *
FROM business
WHERE EXISTS (
    SELECT 1
    FROM unnest(locations) AS loc 
    WHERE lower(loc) LIKE '%' || LOWER(:query) || '%'
);
  • TODO
    • Add current limitations and problems
      • PostgreSql allows functions declared after FROM e.g. FROM upper('a') or FROM unnest(...)
        • This is not supported in SqlDelight and is the actual issue

Add unnest to PostgreSql.bnf
Add mixins to help with column resolving
Update PostresqResolver to handle converting Array<T> to T
Add TableFunctionRowType to allow new concept of Nested type to Row type
Try and support WHERE clause
Add where exists
For where clause
Update TableFunctionTableAliasMixin.kt
Add test for LATER UNNEST
@griffio griffio force-pushed the add-5346-postgresql-unnest branch from 1afcdb6 to fb72bf6 Compare March 19, 2025 19:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

postgresql: unable to use unnest(...) in subquery
1 participant