Skip to content

Commit 053e6ea

Browse files
committed
Add checking that subquery on select clause is not allowed with aggregation query
1 parent 70b581c commit 053e6ea

File tree

3 files changed

+142
-43
lines changed

3 files changed

+142
-43
lines changed

soql/parser/parser_test.go

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,129 @@ func TestParse2(t *testing.T) {
200200
con.Name = acc.Name
201201
and
202202
LEN(con.Name) > 0
203+
ORDER BY
204+
acc.Name desc nulls last
205+
--, acc.Id desc nulls last
206+
, xid
207+
, con.Name
208+
OFFSET 1000 LIMIT 100
209+
FOR update viewstat, tracking
210+
`},
211+
want: nil,
212+
wantErr: false,
213+
}}
214+
215+
for _, tt := range tests {
216+
t.Run(tt.name, func(t *testing.T) {
217+
if tt.dbgBreak {
218+
t.Log("debug")
219+
}
220+
221+
got, err := parser.Parse(tt.args.s)
222+
if (err != nil) != tt.wantErr {
223+
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
224+
return
225+
}
226+
if err != nil {
227+
return
228+
}
229+
230+
{
231+
s := got.Meta.ElapsedTime.String()
232+
t.Log(s)
233+
}
234+
235+
if !reflect.DeepEqual(got, tt.want) {
236+
// t.Errorf("Parse() = %v, want %v", got, tt.want)
237+
// return
238+
t.Logf("Parse() = %v, want %v", got, tt.want)
239+
}
240+
241+
jsonBytes1, err := json.Marshal(got)
242+
if err != nil {
243+
t.Errorf("json.Marshal() (1) error = %v", err)
244+
return
245+
}
246+
var unmarshal1 types.SoqlQuery
247+
err = json.Unmarshal(jsonBytes1, &unmarshal1)
248+
if err != nil {
249+
t.Errorf("json.Unmarshal() (1) error = %v", err)
250+
return
251+
}
252+
253+
jsonBytes2, err := json.Marshal(unmarshal1)
254+
if err != nil {
255+
t.Errorf("json.Marshal() (2) error = %v", err)
256+
return
257+
}
258+
var unmarshal2 types.SoqlQuery
259+
err = json.Unmarshal(jsonBytes2, &unmarshal2)
260+
if err != nil {
261+
t.Errorf("json.Unmarshal() (2) error = %v", err)
262+
return
263+
}
264+
if string(jsonBytes1) != string(jsonBytes2) {
265+
t.Errorf("Marshal(1) = %v, Marshal(2) %v", string(jsonBytes1), string(jsonBytes2))
266+
return
267+
}
268+
269+
jsonBytes3, err := json.Marshal(unmarshal2)
270+
if err != nil {
271+
t.Errorf("json.Marshal() (3) error = %v", err)
272+
return
273+
}
274+
if string(jsonBytes1) != string(jsonBytes3) {
275+
t.Errorf("Marshal(1) = %v, Marshal(3) %v", string(jsonBytes1), string(jsonBytes3))
276+
return
277+
}
278+
})
279+
}
280+
}
281+
282+
func TestParse3(t *testing.T) {
283+
type args struct {
284+
s string
285+
}
286+
tests := []struct {
287+
name string
288+
args args
289+
want interface{}
290+
wantErr bool
291+
dbgBreak bool
292+
}{{
293+
name: "1",
294+
args: args{s: `
295+
SELECT
296+
acc.Id xid
297+
--, con.foo__r.xxx
298+
, foo__r.bar__r.zzz
299+
, foo__r.yyy
300+
, con.Name xname
301+
, con.acc.ddd xddd
302+
, CONCAT(TRIM(acc.Name), '/', TRIM(con.Name), 123.45, 0xacc0) cname
303+
, FLAT(acc.Name)
304+
FROM
305+
Contact con
306+
, con.Account acc
307+
, PPP.QQQ.RRR r3
308+
WHERE
309+
not (Name like 'a%' or Name like 'b%')
310+
and
311+
acc.Name in ('a', 'b', 'c', null)
312+
and
313+
acc.Id in ('a', 'b', 'c', null)
314+
and
315+
r3.Name in (select x,Id,Name from Contact)
316+
and
317+
Name > 0001-01-02
318+
and
319+
(((Name > 0001-01-02T01:01:01.123456789Z)
320+
or
321+
Name = :param1))
322+
and
323+
con.Name = acc.Name
324+
and
325+
LEN(con.Name) > 0
203326
GROUP BY
204327
acc.Name
205328
--, acc.Id

soql/parser/postprocess/normalize.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -304,10 +304,6 @@ func (ctx *normalizeQueryContext) normalizeQuery(
304304
}
305305
}
306306

307-
// TODO: * check object graph when aggregation(group by)
308-
// * subquery on select clause is not allowed.
309-
// * ...
310-
311307
ctx.assignColumnIds(q)
312308

313309
ctx.assignImplicitAliasNames(q, fieldAliasMap)
@@ -366,6 +362,16 @@ func (ctx *normalizeQueryContext) normalizeQuery(
366362
ctx.parameters[q.OffsetAndLimit.LimitParamName] = struct{}{}
367363
}
368364

365+
if q.IsAggregation {
366+
for i := 0; i < len(q.Fields); i++ {
367+
if q.Fields[i].Type == SoqlFieldInfo_SubQuery {
368+
return errors.New(
369+
"Subquery on aggregation query is not allowed: " +
370+
strings.Join(q.From[0].Name, "."))
371+
}
372+
}
373+
}
374+
369375
savedViewIdMap := ctx.viewIdMap
370376
savedColumnIdMap := ctx.columnIdMap
371377
savedColIndexMap := ctx.colIndexMap

web/index.html

Lines changed: 9 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -63,49 +63,19 @@ <h4 class="title"><img
6363
<div class="input-area">
6464
<textarea id="soql" name="soql" rows="35" cols="120"
6565
>SELECT
66-
-- FIELDS(all)
67-
acc.Id xid
68-
, foo__r.bar__r.zzz
69-
, foo__r.yyy
70-
, con.Name xname
71-
, con.acc.ddd xddd
72-
, CONCAT(TRIM(acc.Name), '/', TRIM(con.Name), 123.45, 0xacc0) cname
73-
, FLAT(acc.Name)
74-
, (SELECT Id FROM con.Departments) qwerty
66+
Id
67+
, Name
68+
, Contact.Id
69+
, Contact.Name
7570
FROM
76-
Contact con
77-
, con.Account acc
78-
, PPP.QQQ.RRR r3
71+
Account
7972
WHERE
80-
not (Name like 'a%' or Name like 'b%')
81-
and
82-
Name in ('a', 'b', 'c', null)
83-
and
84-
Name in (select x from Foobar)
85-
and
86-
Name &gt; 0001-01-02
87-
and
88-
(((Name &gt; 0001-01-02T01:01:01.123456789Z)
73+
Contact.Name like 'Jane %'
8974
or
90-
Name = :param1))
91-
and
92-
con.Name = acc.Name
93-
and
94-
LEN(con.Name) > 0
95-
GROUP BY
96-
acc.Name
97-
, con.Name
98-
, xid
99-
, foo__r.bar__r.zzz
100-
, foo__r.yyy
101-
, con.acc.ddd
102-
HAVING
103-
LEN(MAX(con.Name)) &gt; FOO(0)
104-
and
105-
LEN(MAX(con.Name)) &gt; 0
75+
Contact.Name like 'John %'
10676
ORDER BY
107-
acc.Id desc nulls last
108-
, con.Name
77+
Name desc nulls last
78+
, Id
10979
OFFSET 1000 LIMIT 100
11080
FOR update viewstat, tracking
11181
</textarea>

0 commit comments

Comments
 (0)