Skip to content

Commit af42dce

Browse files
committed
fix: multiple joins in mysql and mariadb
1 parent 8de012b commit af42dce

5 files changed

Lines changed: 39 additions & 22 deletions

File tree

pegjs/mariadb.pegjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2356,12 +2356,12 @@ table_ref
23562356

23572357

23582358
table_join
2359-
= op:join_op __ t:table_base __ KW_USING __ LPAREN __ head:ident_without_kw_type tail:(__ COMMA __ ident_without_kw_type)* __ RPAREN {
2359+
= op:join_op __ t:(table_base / table_ref_list) __ KW_USING __ LPAREN __ head:ident_without_kw_type tail:(__ COMMA __ ident_without_kw_type)* __ RPAREN {
23602360
t.join = op;
23612361
t.using = createList(head, tail);
23622362
return t;
23632363
}
2364-
/ op:join_op __ t:table_base __ expr:on_clause? {
2364+
/ op:join_op __ t:(table_base / table_ref_list) __ expr:on_clause? {
23652365
t.join = op;
23662366
t.on = expr;
23672367
return t;

pegjs/mysql.pegjs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2619,12 +2619,12 @@ table_ref
26192619

26202620

26212621
table_join
2622-
= op:join_op __ t:table_base __ KW_USING __ LPAREN __ head:ident_without_kw_type tail:(__ COMMA __ ident_without_kw_type)* __ RPAREN {
2622+
= op:join_op __ t:(table_base / table_ref_list) __ KW_USING __ LPAREN __ head:ident_without_kw_type tail:(__ COMMA __ ident_without_kw_type)* __ RPAREN {
26232623
t.join = op;
26242624
t.using = createList(head, tail);
26252625
return t;
26262626
}
2627-
/ op:join_op __ t:table_base __ expr:on_clause? {
2627+
/ op:join_op __ t:(table_base / table_ref_list) __ expr:on_clause? {
26282628
t.join = op;
26292629
t.on = expr;
26302630
return t;

src/tables.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,9 @@ function tablesToSQL(tables) {
184184
const joinExpr = tables[i]
185185
const { on, using, join } = joinExpr
186186
const str = []
187+
const isTables = Array.isArray(joinExpr) || Object.hasOwnProperty.call(joinExpr, 'joins')
187188
str.push(join ? ` ${toUpper(join)}` : ',')
188-
str.push(tableToSQL(joinExpr))
189+
str.push(isTables ? tablesToSQL(joinExpr) : tableToSQL(joinExpr))
189190
str.push(commonOptionConnector('ON', exprToSQL, on))
190191
if (using) str.push(`USING (${using.map(literalToSQL).join(', ')})`)
191192
clauses.push(str.filter(hasVal).join(' '))

test/mysql-mariadb.spec.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1270,6 +1270,20 @@ describe('mysql', () => {
12701270
'SELECT * FROM `T` WHERE `date` BETWEEN `start` AND `start` + INTERVAL (`duration` + 1) DAY'
12711271
]
12721272
},
1273+
{
1274+
title: 'join using clause',
1275+
sql: [
1276+
'SELECT * FROM A JOIN (B JOIN C USING (x)) USING (y)',
1277+
'SELECT * FROM `A` INNER JOIN (`B` INNER JOIN `C` USING (x)) USING (y)'
1278+
]
1279+
},
1280+
{
1281+
title: 'join on clause',
1282+
sql: [
1283+
'SELECT * FROM A JOIN (B JOIN C ON B.x = C.x) ON A.y = C.y',
1284+
'SELECT * FROM `A` INNER JOIN (`B` INNER JOIN `C` ON `B`.`x` = `C`.`x`) ON `A`.`y` = `C`.`y`'
1285+
]
1286+
},
12731287
]
12741288
SQL_LIST.forEach(sqlInfo => {
12751289
const { title, sql } = sqlInfo
@@ -1280,7 +1294,7 @@ describe('mysql', () => {
12801294
expect(getParsedSql(sql[0], mariadb)).to.equal(sql[1])
12811295
})
12821296
})
1283-
1297+
12841298
it('should throw error when args is not right', () => {
12851299
let sql = `select convert(json_unquote(json_extract('{"thing": "252"}', "$.thing")));`
12861300
expect(parser.astify.bind(parser, sql)).to.throw('Expected "!=", "#", "#-", "#>", "#>>", "%", "&", "&&", "*", "+", ",", "-", "--", "->", "->>", "/", "/*", "<", "<<", "<=", "<>", "<@", "=", ">", ">=", ">>", "?", "?&", "?|", "@>", "AND", "BETWEEN", "IN", "IS", "LIKE", "NOT", "ON", "OR", "OVER", "REGEXP", "RLIKE", "USING", "XOR", "^", "div", "mod", "|", "||", or [ \\t\\n\\r] but ")" found.')

test/update.spec.js

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -238,20 +238,22 @@ describe('update', () => {
238238
"update::employees::salary"
239239
])
240240
})
241-
sql = `UPDATE empdb.employees t1 INNER JOIN (select t2.percentage,t2.performance from empdb.merits t2) as t3 ON t1.performance = t3.performance SET t1.salary = t1.salary + t1.salary * t3.percentage;`
242-
ast = parser.parse(sql)
243-
expect(ast.tableList).to.eql(["select::empdb::merits", "update::empdb::employees"])
244-
expect(ast.columnList).to.eql([
245-
"select::merits::percentage",
246-
"select::merits::performance",
247-
"select::employees::performance",
248-
"select::t3::performance",
249-
"select::employees::salary",
250-
"select::t3::percentage",
251-
"update::employees::salary"
252-
])
253-
sql = `DELETE t1 FROM t1 INNER JOIN t2 ON t2.ref = t1.id;`
254-
ast = parser.parse(sql)
255-
expect(ast.tableList).to.eql(["delete::null::t1","select::null::t2"])
256-
expect(ast.columnList).to.eql(["select::t2::ref", "select::t1::id", "delete::t1::(.*)" ])
241+
it('should parse update statement', () => {
242+
sql = `UPDATE empdb.employees t1 INNER JOIN (select t2.percentage,t2.performance from empdb.merits t2) as t3 ON t1.performance = t3.performance SET t1.salary = t1.salary + t1.salary * t3.percentage;`
243+
ast = parser.parse(sql)
244+
expect(ast.tableList).to.eql(["select::empdb::merits", "update::empdb::employees"])
245+
expect(ast.columnList).to.eql([
246+
"select::merits::percentage",
247+
"select::merits::performance",
248+
"select::employees::performance",
249+
"select::t3::performance",
250+
"select::employees::salary",
251+
"select::t3::percentage",
252+
"update::employees::salary"
253+
])
254+
sql = `DELETE t1 FROM t1 INNER JOIN t2 ON t2.ref = t1.id;`
255+
ast = parser.parse(sql)
256+
expect(ast.tableList).to.eql(["delete::null::t1", "select::null::t2"])
257+
expect(ast.columnList).to.eql(["select::t2::ref", "select::t1::id", "delete::t1::(.*)"])
258+
})
257259
});

0 commit comments

Comments
 (0)