Skip to content

Commit a0bc4b5

Browse files
test(cqn4sql): refactor table alias test suite (#1642)
Split table-alias.test.js (1553 lines, 96 tests) into 9 focused spec files under table-alias.test/, following the established pattern from #1315, #1321, and #1326.
1 parent 2316210 commit a0bc4b5

10 files changed

Lines changed: 1515 additions & 1553 deletions

db-service/test/cqn4sql/table-alias.test.js

Lines changed: 0 additions & 1553 deletions
This file was deleted.
Lines changed: 288 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
'use strict'
2+
3+
const cds = require('@sap/cds')
4+
const { loadModel } = require('../helpers/model')
5+
const { expectCqn } = require('../helpers/expectCqn')
6+
7+
let cqn4sql = require('../../../lib/cqn4sql')
8+
9+
describe('table alias access - replace $self references', () => {
10+
before(async () => {
11+
const model = await loadModel()
12+
const orig = cqn4sql
13+
cqn4sql = q => orig(q, model)
14+
})
15+
16+
it('escaped identifier does not hurt', () => {
17+
const transformed = cqn4sql(cds.ql`
18+
SELECT FROM bookshop.Books as ![FROM]
19+
{
20+
![FROM].title as group,
21+
}
22+
where $self.group = 'foo'
23+
group by $self.group
24+
having $self.group = 'foo'
25+
order by $self.group
26+
`)
27+
const expected = cds.ql`
28+
SELECT from bookshop.Books as ![FROM]
29+
{
30+
![FROM].title as group,
31+
}
32+
where ![FROM].title = 'foo'
33+
group by ![FROM].title
34+
having ![FROM].title = 'foo'
35+
order by ![FROM].title
36+
`
37+
expectCqn(transformed).to.equal(expected)
38+
})
39+
40+
it('refer to other query element', () => {
41+
const transformed = cqn4sql(cds.ql`SELECT from bookshop.Books as Books {
42+
Books.title,
43+
title as title2,
44+
dedication as struct,
45+
1 + 1 as expression,
46+
42 as value,
47+
48+
$self.dedication2 as dedication3,
49+
$self.struct.text as dedication,
50+
$self.dedication as dedication2,
51+
$self.expression as selfXpr,
52+
$self.value as selfVal,
53+
}`)
54+
const expected = cds.ql`SELECT from bookshop.Books as Books {
55+
Books.title,
56+
Books.title as title2,
57+
Books.dedication_addressee_ID as struct_addressee_ID,
58+
Books.dedication_text as struct_text,
59+
Books.dedication_sub_foo as struct_sub_foo,
60+
Books.dedication_dedication as struct_dedication,
61+
1 + 1 as expression,
62+
42 as value,
63+
64+
Books.dedication_text as dedication3,
65+
Books.dedication_text as dedication,
66+
Books.dedication_text as dedication2,
67+
1 + 1 as selfXpr,
68+
42 as selfVal
69+
}`
70+
expectCqn(transformed).to.equal(expected)
71+
})
72+
73+
it('late replace join relevant paths', () => {
74+
const transformed = cqn4sql(cds.ql`SELECT from bookshop.Authors as Authors {
75+
Authors.name as author,
76+
$self.book as dollarSelfBook,
77+
books.title as book,
78+
} group by $self.book
79+
`)
80+
const expected = cds.ql`SELECT from bookshop.Authors as Authors left join bookshop.Books as books on books.author_ID = Authors.ID {
81+
Authors.name as author,
82+
books.title as dollarSelfBook,
83+
books.title as book
84+
} group by books.title
85+
`
86+
expectCqn(transformed).to.equal(expected)
87+
})
88+
89+
it('in aggregation', () => {
90+
const transformed = cqn4sql(cds.ql`SELECT from bookshop.Authors as Authors {
91+
name as author,
92+
1+1 as xpr,
93+
years_between(dateOfBirth, dateOfDeath) as age
94+
}
95+
group by $self.author, $self.xpr
96+
order by $self.author, $self.xpr
97+
`)
98+
const expected = cds.ql`SELECT from bookshop.Authors as Authors {
99+
Authors.name as author,
100+
1+1 as xpr,
101+
years_between(Authors.dateOfBirth, Authors.dateOfDeath) as age
102+
}
103+
group by Authors.name, 1+1
104+
order by Authors.name, 1+1
105+
`
106+
expectCqn(transformed).to.equal(expected)
107+
})
108+
109+
it('in having', () => {
110+
const transformed = cqn4sql(cds.ql`SELECT from bookshop.Authors {
111+
name as author,
112+
1+1 as xpr,
113+
}
114+
having $self.xpr = 2
115+
`)
116+
const expected = cds.ql`SELECT from bookshop.Authors as $A {
117+
$A.name as author,
118+
1+1 as xpr,
119+
}
120+
having (1+1) = 2
121+
`
122+
expectCqn(transformed).to.equal(expected)
123+
})
124+
125+
it('in having with func', () => {
126+
const transformed = cqn4sql(cds.ql`SELECT from bookshop.Books {
127+
author.name as author,
128+
count(*) as numberOfBooks,
129+
}
130+
group by author.name
131+
having $self.numberOfBooks > 1
132+
`)
133+
const expected = cds.ql`SELECT from bookshop.Books as $B
134+
left join bookshop.Authors as author on author.ID = $B.author_ID
135+
{
136+
author.name as author,
137+
count(*) as numberOfBooks,
138+
}
139+
group by author.name
140+
having count(*) > 1
141+
`
142+
expectCqn(transformed).to.equal(expected)
143+
})
144+
145+
it('in where', () => {
146+
const transformed = cqn4sql(cds.ql`SELECT from bookshop.Authors {
147+
name as author,
148+
1+1 as xpr,
149+
}
150+
where 2 / $self.xpr = 1
151+
`)
152+
const expected = cds.ql`SELECT from bookshop.Authors as $A {
153+
$A.name as author,
154+
1+1 as xpr,
155+
}
156+
where 2 / (1+1) = 1
157+
`
158+
expectCqn(transformed).to.equal(expected)
159+
})
160+
161+
it('refer to my own column in function expression', () => {
162+
const transformed = cqn4sql(cds.ql`
163+
SELECT from bookshop.Books as Books {
164+
cast('2007-07-07' as Date) as twoLeapYearsEarlier,
165+
cast('2013-07-06' as Date) as twoLeapYearsLater,
166+
months_between($self.twoLeapYearsEarlier, $self.twoLeapYearsLater)
167+
}`)
168+
const expected = cds.ql`
169+
SELECT from bookshop.Books as Books {
170+
cast('2007-07-07' as cds.Date) as twoLeapYearsEarlier,
171+
cast('2013-07-06' as cds.Date) as twoLeapYearsLater,
172+
months_between(cast('2007-07-07' as cds.Date), cast('2007-07-06' as cds.Date)) as months_between
173+
}`
174+
// cast expression inside argument is parsed without surrounding "xpr"
175+
// hence we need to adjust the expectation
176+
expected.SELECT.columns[2].args = [
177+
{ xpr: expected.SELECT.columns[0].xpr },
178+
{ xpr: expected.SELECT.columns[1].xpr },
179+
]
180+
expectCqn(transformed).to.equal(expected)
181+
})
182+
183+
it('refer to my own column in calc expression', () => {
184+
const transformed = cqn4sql(cds.ql`
185+
SELECT from bookshop.Books as Books {
186+
(cast('2007-07-07' as Date) + 1) as twoLeapYearsEarlier,
187+
(cast('2013-07-06' as Date) + 1) as twoLeapYearsLater,
188+
$self.twoLeapYearsEarlier + months_between($self.twoLeapYearsEarlier + 15) as calc
189+
}`)
190+
const expected = cds.ql`
191+
SELECT from bookshop.Books as Books {
192+
(cast('2007-07-07' as cds.Date) + 1) as twoLeapYearsEarlier,
193+
(cast('2013-07-06' as cds.Date) + 1) as twoLeapYearsLater,
194+
(cast('2007-07-07' as cds.Date) + 1) + months_between((cast('2007-07-07' as cds.Date) + 1) + 15) as calc
195+
}`
196+
expectCqn(transformed).to.equal(expected)
197+
})
198+
199+
it('$self in infix filter alongside path expression', () => {
200+
const transformed = cqn4sql(cds.ql`
201+
SELECT from bookshop.Books as Books {
202+
title,
203+
exists author.books[ author.name = title and title = $self.title ] as s
204+
}`)
205+
const expected = cds.ql`
206+
SELECT from bookshop.Books as Books {
207+
Books.title,
208+
exists (
209+
SELECT 1 from bookshop.Authors as $a where $a.ID = Books.author_ID and exists (
210+
SELECT 1 from bookshop.Books as $b
211+
inner join bookshop.Authors as author on author.ID = $b.author_ID
212+
where $b.author_ID = $a.ID and author.name = $b.title and $b.title = Books.title
213+
)
214+
) as s
215+
}`
216+
expectCqn(transformed).to.equal(expected)
217+
})
218+
219+
it('$self in nested exists infix filter', () => {
220+
const transformed = cqn4sql(cds.ql`
221+
SELECT from bookshop.Books as Books {
222+
title,
223+
exists author.books[ exists author.books[ title = $self.title ] ] as s
224+
}`)
225+
const expected = cds.ql`
226+
SELECT from bookshop.Books as Books {
227+
Books.title,
228+
exists (
229+
SELECT 1 from bookshop.Authors as $a where $a.ID = Books.author_ID and exists (
230+
SELECT 1 from bookshop.Books as $b where $b.author_ID = $a.ID and exists (
231+
SELECT 1 from bookshop.Authors as $a2 where $a2.ID = $b.author_ID and exists (
232+
SELECT 1 from bookshop.Books as $b2 where $b2.author_ID = $a2.ID and $b2.title = Books.title
233+
)
234+
)
235+
)
236+
) as s
237+
}`
238+
expectCqn(transformed).to.equal(expected)
239+
})
240+
241+
it('$self in deeply nested infix filter with multiple path expressions', () => {
242+
const transformed = cqn4sql(cds.ql`
243+
SELECT from bookshop.Books as Books {
244+
title,
245+
exists author.books[ author.name = title and exists author.books[ author.name = title and title = $self.title ] ] as s
246+
}`)
247+
const expected = cds.ql`
248+
SELECT from bookshop.Books as Books {
249+
Books.title,
250+
exists (
251+
SELECT 1 from bookshop.Authors as $a where $a.ID = Books.author_ID and exists (
252+
SELECT 1 from bookshop.Books as $b
253+
inner join bookshop.Authors as author on author.ID = $b.author_ID
254+
where $b.author_ID = $a.ID and author.name = $b.title and exists (
255+
SELECT 1 from bookshop.Authors as $a2 where $a2.ID = $b.author_ID and exists (
256+
SELECT 1 from bookshop.Books as $b2
257+
inner join bookshop.Authors as author2 on author2.ID = $b2.author_ID
258+
where $b2.author_ID = $a2.ID and author2.name = $b2.title and $b2.title = Books.title
259+
)
260+
)
261+
)
262+
) as s
263+
}`
264+
expectCqn(transformed).to.equal(expected)
265+
})
266+
267+
it('$self in subquery refers to own projection, not outer query', () => {
268+
const transformed = cqn4sql(cds.ql`
269+
SELECT from bookshop.Authors as Authors {
270+
ID,
271+
1+1 as foo
272+
} where exists (
273+
SELECT from bookshop.Books { 2+2 as foo, $self.foo as bar }
274+
where author[$self.foo = 4].ID = 42
275+
)`)
276+
const expected = cds.ql`
277+
SELECT from bookshop.Authors as Authors {
278+
Authors.ID,
279+
1+1 as foo
280+
} where exists (
281+
SELECT from bookshop.Books as $B
282+
left outer join bookshop.Authors as author on author.ID = $B.author_ID and (2+2) = 4
283+
{ 2+2 as foo, 2+2 as bar }
284+
where author.ID = 42
285+
)`
286+
expectCqn(transformed).to.equal(expected)
287+
})
288+
})
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
'use strict'
2+
3+
const cds = require('@sap/cds')
4+
const { loadModel } = require('../helpers/model')
5+
const { expectCqn } = require('../helpers/expectCqn')
6+
7+
let cqn4sql = require('../../../lib/cqn4sql')
8+
9+
describe('table alias access - implicit aliasing', () => {
10+
before(async () => {
11+
const model = await loadModel()
12+
const orig = cqn4sql
13+
cqn4sql = q => orig(q, model)
14+
})
15+
16+
it('can handle entities beginning with $', () => {
17+
const transformed = cqn4sql(cds.ql`SELECT from bookshop.![$special] { ID }`)
18+
const expected = cds.ql`SELECT from bookshop.$special as $s { $s.ID }`
19+
expectCqn(transformed).to.equal(expected)
20+
})
21+
// TODO: also use technical alias for join nodes
22+
it('can handle entities beginning with $ and joins for assocs starting with $', () => {
23+
const transformed = cqn4sql(cds.ql`SELECT from bookshop.![$special] { ID, ![$special].name }`)
24+
const expected = cds.ql`SELECT from bookshop.$special as $s left join bookshop.$special as $special on $special.ID = $s.$special_ID
25+
{
26+
$s.ID,
27+
$special.name as $special_name
28+
}`
29+
expectCqn(transformed).to.equal(expected)
30+
})
31+
it('can handle scoped queries via navigations starting with $', () => {
32+
const transformed = cqn4sql(cds.ql`SELECT from bookshop.$special:$special { ID }`)
33+
const expected = cds.ql`
34+
SELECT from bookshop.$special as $s { $s.ID }
35+
where exists (SELECT 1 from bookshop.$special as $s2 where $s2.$special_ID = $s.ID)
36+
`
37+
expectCqn(transformed).to.equal(expected)
38+
})
39+
it('can handle expand queries via navigations starting with $', () => {
40+
const transformed = cqn4sql(cds.ql`SELECT from bookshop.$special { ID, $special { name } }`)
41+
const expected = cds.ql`
42+
SELECT from bookshop.$special as $s {
43+
$s.ID,
44+
(SELECT $s2.name from bookshop.$special as $s2 where $s.$special_ID = $s2.ID) as $special
45+
}
46+
`
47+
expectCqn(transformed).to.equal(expected)
48+
})
49+
50+
// entity called "$" with association called "$" to entity called "$"
51+
it('can handle entities beginning with $', () => {
52+
const transformed = cqn4sql(cds.ql`SELECT from bookshop.$ { ID }`)
53+
const expected = cds.ql`SELECT from bookshop.$ as $$ { $$.ID }`
54+
expectCqn(transformed).to.equal(expected)
55+
})
56+
57+
// TODO: also use technical alias for join nodes
58+
it('can handle entities called $ and joins for assocs called $', () => {
59+
const transformed = cqn4sql(cds.ql`SELECT from bookshop.$ { ID, $.name }`)
60+
const expected = cds.ql`SELECT from bookshop.$ as $$ left join bookshop.$ as $ on $.ID = $$.$_ID
61+
{
62+
$$.ID,
63+
$.name as $_name
64+
}`
65+
expectCqn(transformed).to.equal(expected)
66+
})
67+
68+
it('can handle scoped queries via navigations called $', () => {
69+
const transformed = cqn4sql(cds.ql`SELECT from bookshop.$:$ { ID }`)
70+
const expected = cds.ql`
71+
SELECT from bookshop.$ as $$ { $$.ID }
72+
where exists (SELECT 1 from bookshop.$ as $$2 where $$2.$_ID = $$.ID)
73+
`
74+
expectCqn(transformed).to.equal(expected)
75+
})
76+
77+
it('can handle expand queries via navigations called $', () => {
78+
const transformed = cqn4sql(cds.ql`SELECT from bookshop.$ { ID, $ { name } }`)
79+
const expected = cds.ql`
80+
SELECT from bookshop.$ as $$ {
81+
$$.ID,
82+
(SELECT $$2.name from bookshop.$ as $$2 where $$.$_ID = $$2.ID) as $
83+
}
84+
`
85+
expectCqn(transformed).to.equal(expected)
86+
})
87+
})

0 commit comments

Comments
 (0)