Skip to content

Commit 976e4f2

Browse files
committed
Implement primary key lookup.
1 parent 8e21586 commit 976e4f2

File tree

1 file changed

+19
-12
lines changed

1 file changed

+19
-12
lines changed

graphql.sql

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,16 @@ DECLARE
6565
q text;
6666
tab regclass = selector::regclass; -- Without a parent, we need a table
6767
cols text[];
68-
pk text;
6968
sub record;
69+
pk text;
7070
BEGIN
7171
q := 'FROM ' || tab; -- Regclass is auto-escaped
7272
body := substr(body, 2, length(body)-2);
7373
IF predicate IS NOT NULL THEN
74-
pk := 'id'; -- TODO: Figure how to get the real primary key
75-
q := q || format(' WHERE %I = %L', pk, predicate);
76-
--- TODO: Include cast to type of primary key
77-
--- TODO: When the type of the primary key is an integer literal and the
78-
--- predicate value is all digits, don't quote it at all.
74+
SELECT array_to_string(array_agg(format('%I', col)), ', ')
75+
FROM graphql.pk(tab) INTO pk;
76+
q := q || E'\n WHERE (' || pk || ') = (' || predicate || ')';
77+
--- Compound primary keys are okay, since we naively trust the input...
7978
END IF;
8079
FOR sub IN SELECT * FROM graphql.parse_many(body) LOOP
8180
IF sub.predicate IS NOT NULL THEN
@@ -87,9 +86,9 @@ BEGIN
8786
cols := cols || format('%I', sub.selector);
8887
END LOOP;
8988
IF cols > ARRAY[]::text[] THEN
90-
q := 'SELECT ' || array_to_string(cols, ', ') || ' ' || q;
89+
q := 'SELECT ' || array_to_string(cols, ', ') || E' \n ' || q;
9190
ELSE
92-
q := 'SELECT * ' || q;
91+
q := 'SELECT *' || E' \n ' || q;
9392
END IF;
9493
RETURN q;
9594
END
@@ -123,10 +122,9 @@ CREATE FUNCTION graphql.parse_one(expr text,
123122
OUT selector text,
124123
OUT predicate text,
125124
OUT body text,
126-
OUT remainder text)
127-
AS $$
125+
OUT remainder text) AS $$
128126
DECLARE
129-
label text = '[a-zA-Z_][a-zA-Z0-9_]+';
127+
label text = '[a-zA-Z_][a-zA-Z0-9_]*';
130128
selector_re text = '^(' || label || ')' || '([(]([^()]+)[)])?';
131129
matches text[];
132130
whitespace text = E' \t\n';
@@ -165,14 +163,23 @@ BEGIN
165163
graphql.excerpt(expr, idx, 50);
166164
ELSE
167165
EXIT WHEN NOT brackety;
168-
--- Do nothing.
169166
END CASE;
170167
END LOOP;
171168
body := substr(expr, 1, idx);
172169
remainder := substr(expr, idx+1);
173170
END
174171
$$ LANGUAGE plpgsql IMMUTABLE STRICT;
175172

173+
CREATE FUNCTION graphql.pk(tab regclass)
174+
RETURNS TABLE (col name, typ regtype) AS $$
175+
SELECT attname, atttypid::regtype
176+
FROM pg_index JOIN pg_attribute ON (attnum = ANY (indkey))
177+
WHERE indrelid = tab AND indisprimary AND attrelid = tab AND attnum > 0
178+
ORDER BY attnum
179+
$$ LANGUAGE sql STABLE STRICT;
180+
--- NB: For SELECTs, it would be okay just to return the column numbers. One
181+
--- could skip the JOIN with pg_attribute, resulting in a faster query.
182+
176183
CREATE FUNCTION graphql.excerpt(str text, start integer, length integer)
177184
RETURNS text AS $$
178185
SELECT substr(regexp_replace(str, '[ \n\t]+', ' ', 'g'), start, length);

0 commit comments

Comments
 (0)