@@ -65,17 +65,16 @@ DECLARE
65
65
q text ;
66
66
tab regclass = selector::regclass; -- Without a parent, we need a table
67
67
cols text [];
68
- pk text ;
69
68
sub record;
69
+ pk text ;
70
70
BEGIN
71
71
q := ' FROM ' || tab; -- Regclass is auto-escaped
72
72
body := substr(body, 2 , length(body)- 2 );
73
73
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...
79
78
END IF;
80
79
FOR sub IN SELECT * FROM graphql .parse_many (body) LOOP
81
80
IF sub .predicate IS NOT NULL THEN
87
86
cols := cols || format(' %I' , sub .selector );
88
87
END LOOP;
89
88
IF cols > ARRAY[]::text [] THEN
90
- q := ' SELECT ' || array_to_string(cols, ' , ' ) || ' ' || q;
89
+ q := ' SELECT ' || array_to_string(cols, ' , ' ) || E ' \n ' || q;
91
90
ELSE
92
- q := ' SELECT * ' || q;
91
+ q := ' SELECT *' || E ' \n ' || q;
93
92
END IF;
94
93
RETURN q;
95
94
END
@@ -123,10 +122,9 @@ CREATE FUNCTION graphql.parse_one(expr text,
123
122
OUT selector text ,
124
123
OUT predicate text ,
125
124
OUT body text ,
126
- OUT remainder text )
127
- AS $$
125
+ OUT remainder text ) AS $$
128
126
DECLARE
129
- label text = ' [a-zA-Z_][a-zA-Z0-9_]+ ' ;
127
+ label text = ' [a-zA-Z_][a-zA-Z0-9_]* ' ;
130
128
selector_re text = ' ^(' || label || ' )' || ' ([(]([^()]+)[)])?' ;
131
129
matches text [];
132
130
whitespace text = E' \t\n ' ;
@@ -165,14 +163,23 @@ BEGIN
165
163
graphql .excerpt (expr, idx, 50 );
166
164
ELSE
167
165
EXIT WHEN NOT brackety;
168
- -- - Do nothing.
169
166
END CASE;
170
167
END LOOP;
171
168
body := substr(expr, 1 , idx);
172
169
remainder := substr(expr, idx+ 1 );
173
170
END
174
171
$$ LANGUAGE plpgsql IMMUTABLE STRICT;
175
172
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
+
176
183
CREATE FUNCTION graphql .excerpt(str text , start integer , length integer )
177
184
RETURNS text AS $$
178
185
SELECT substr(regexp_replace(str, ' [ \n\t ]+' , ' ' , ' g' ), start, length);
0 commit comments