Skip to content

Commit 752659d

Browse files
committed
Rethink formatting.
1 parent b9ff26d commit 752659d

File tree

1 file changed

+43
-9
lines changed

1 file changed

+43
-9
lines changed

graphql.sql

Lines changed: 43 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -71,22 +71,20 @@ CREATE FUNCTION to_sql(selector text, predicate text, body text)
7171
RETURNS text AS $$
7272
DECLARE
7373
q text;
74-
tab regclass = selector::regclass; -- Without a parent, we need a table
74+
tab regclass = selector::regclass; -- We need a table, to select from
7575
cols text[];
7676
col text;
7777
sub record;
7878
pk text = NULL;
7979
fk record;
8080
subselects text[];
81+
predicates text[];
8182
BEGIN
82-
q := 'FROM ' || tab; -- Regclass is auto-escaped
8383
body := substr(body, 2, length(body)-2);
8484
IF predicate IS NOT NULL THEN
8585
SELECT array_to_string(array_agg(format('%I', col)), ', ')
8686
FROM unnest(graphql.pk(tab)) INTO pk;
87-
SELECT array_to_string(array_agg(format('%I', col)), ', ')
88-
FROM graphql.pk(tab) INTO pk;
89-
q := q || E'\n WHERE (' || pk || ') = (' || predicate || ')';
87+
predicates := predicates || format('(%s) = (%s)', pk, predicate);
9088
--- Compound primary keys are okay, since we naively trust the input...
9189
END IF;
9290
FOR sub IN SELECT * FROM graphql.parse_many(body) LOOP
@@ -98,12 +96,13 @@ BEGIN
9896
CASE
9997
WHEN FOUND AND sub.body IS NULL THEN -- A simple column reference
10098
SELECT * FROM graphql.fk(tab)
101-
WHERE cols[1] = col AND cardinality(cols) = 1
99+
WHERE cardinality(cols) = 1 AND cols[1] = col
102100
INTO fk; -- TODO: If there's more than one, emit a clear message.
103101
IF FOUND THEN
104102
subselects := subselects
105-
|| format(E'SELECT to_json(%1$I) AS %4$I FROM %1$I\n'
106-
' WHERE %1$I.%2$I = %3$I.%4$I',
103+
|| format('SELECT to_json(%1$I) AS %4$I FROM %1$I'
104+
E'\n'
105+
' WHERE %1$I.%2$I = %3$I.%4$I',
107106
fk.other, fk.refs[1], tab, col);
108107
cols := cols || format('%I.%I', 'sub/'||cardinality(subselects), col);
109108
ELSE
@@ -131,14 +130,22 @@ BEGIN
131130
--- Otherwise:
132131
--- * We use the existence of the foreign key to look up the record in
133132
--- the table that JOINs with us.
133+
---
134134
--- Whenever we are looking at a table that REFERENCES us, we assume it
135135
--- is a many-to-one relationship; and expect to return an array-valued
136-
--- result.
136+
--- result. However, it should be possible to recognize a one-to-one
137+
--- relationship via the presence of a UNIQUE constraint, in which case
138+
--- we ought to return a scalar result.
137139
IF FALSE THEN
140+
subquery := subquery
141+
|| graphql.to_sql(sub.selector, sub.predicate, sub.body);
138142
--- Recursion happens in here
139143
ELSE
140144
--- Recursion happens in here
145+
subquery := subquery
146+
|| graphql.to_sql(sub.selector, sub.predicate, sub.body);
141147
END IF;
148+
--
142149
ELSE
143150
RAISE EXCEPTION 'Not able to interpret this selector: %', sub.selector;
144151
END CASE;
@@ -157,6 +164,22 @@ BEGIN
157164
q := 'SELECT json_agg(' || column_expression || E')\n ' || q;
158165
END IF;
159166
END;
167+
q := q || format(E'\n FROM %I', tab);
168+
FOR n IN 1..cardinality(subselects) LOOP
169+
q := q || E',\n'
170+
|| E'LATERAL (\n'
171+
|| graphql.indent(subselects[i])
172+
|| E'\n) AS ' || format('%I', 'sub/'||i);
173+
--- TODO: Switch to abstract representation of subqueries so we don't end
174+
--- up reindenting the same lines multiple times.
175+
END LOOP;
176+
FOR n IN 1..cardinality(predicates) LOOP
177+
IF n = 1 THEN
178+
q := q || E'\n WHERE (' || predicates[i] || ')';
179+
ELSE
180+
q := q || E'\n AND (' || predicates[i] || ')';
181+
END IF;
182+
END LOOP;
160183
RETURN q;
161184
END
162185
$$ LANGUAGE plpgsql STABLE STRICT;
@@ -237,11 +260,22 @@ BEGIN
237260
END
238261
$$ LANGUAGE plpgsql IMMUTABLE STRICT;
239262

263+
264+
/* * * * * * * * * * * * * * * * Text utilities * * * * * * * * * * * * * * */
265+
240266
CREATE FUNCTION excerpt(str text, start integer, length integer)
241267
RETURNS text AS $$
242268
SELECT substr(regexp_replace(str, '[ \n\t]+', ' ', 'g'), start, length);
243269
$$ LANGUAGE sql IMMUTABLE STRICT;
244270

271+
CREATE FUNCTION indent(str text)
272+
RETURNS text AS $$
273+
SELECT array_to_string(array_agg(s), E'\n')
274+
FROM unnest(string_to_array(str, E'\n')) AS _(ln),
275+
LATERAL (SELECT CASE ln WHEN '' THEN ln ELSE ' ' || ln)
276+
AS indented(s)
277+
$$ LANGUAGE sql IMMUTABLE STRICT;
278+
245279

246280
/* * * * * * * * * * * * * Table inspection functions * * * * * * * * * * * */
247281

0 commit comments

Comments
 (0)