Skip to content

Commit 51ab218

Browse files
committed
fix: from clause with multiple parentheses in mysql
1 parent c2ebdf1 commit 51ab218

File tree

5 files changed

+87
-21
lines changed

5 files changed

+87
-21
lines changed

pegjs/mariadb.pegjs

+30-9
Original file line numberDiff line numberDiff line change
@@ -1930,7 +1930,10 @@ select_stmt_nake
19301930
if ((ci && fi) || (ci && li) || (fi && li) || (ci && fi && li)) {
19311931
throw new Error('A given SQL statement can contain at most one INTO clause')
19321932
}
1933-
if(f) f.forEach(info => info.table && tableList.add(`select::${info.db}::${info.table}`));
1933+
if(f) {
1934+
const tables = Array.isArray(f) ? f : f.expr
1935+
tables.forEach(info => info.table && tableList.add(`select::${info.db}::${info.table}`))
1936+
}
19341937
return {
19351938
with: cte,
19361939
type: 'select',
@@ -2128,8 +2131,7 @@ index_option
21282131
/ keyword_comment
21292132

21302133
table_ref_list
2131-
= head:table_base
2132-
tail:table_ref* {
2134+
= head:table_base tail:table_ref* {
21332135
tail.unshift(head);
21342136
tail.forEach(tableInfo => {
21352137
const { table, as } = tableInfo
@@ -2139,6 +2141,22 @@ table_ref_list
21392141
})
21402142
return tail;
21412143
}
2144+
/ lp:LPAREN+ __ head:table_base tail:table_ref* __ rp:RPAREN+ {
2145+
if (lp.length !== rp.length) throw new Error('parentheses not match in from clause')
2146+
tail.unshift(head);
2147+
tail.forEach(tableInfo => {
2148+
const { table, as } = tableInfo
2149+
tableAlias[table] = table
2150+
if (as) tableAlias[as] = table
2151+
refreshColumnList(columnList)
2152+
})
2153+
return {
2154+
expr: tail,
2155+
parentheses: {
2156+
length: rp.length
2157+
}
2158+
}
2159+
}
21422160

21432161
table_ref
21442162
= __ COMMA __ t:table_base { return t; }
@@ -2357,12 +2375,15 @@ delete_stmt
23572375
t: table_ref_list? __
23582376
f:from_clause __
23592377
w:where_clause? {
2360-
if(f) f.forEach(tableInfo => {
2361-
const { db, as, table, join } = tableInfo
2362-
const action = join ? 'select' : 'delete'
2363-
if (table) tableList.add(`${action}::${db}::${table}`)
2364-
if (!join) columnList.add(`delete::${table}::(.*)`);
2365-
});
2378+
if(f) {
2379+
const tables = Array.isArray(f) ? f : f.expr
2380+
tables.forEach(tableInfo => {
2381+
const { db, as, table, join } = tableInfo
2382+
const action = join ? 'select' : 'delete'
2383+
if (table) tableList.add(`${action}::${db}::${table}`)
2384+
if (!join) columnList.add(`delete::${table}::(.*)`);
2385+
})
2386+
}
23662387
if (t === null && f.length === 1) {
23672388
const tableInfo = f[0]
23682389
t = [{

pegjs/mysql.pegjs

+30-9
Original file line numberDiff line numberDiff line change
@@ -2182,7 +2182,10 @@ select_stmt_nake
21822182
if ((ci && fi) || (ci && li) || (fi && li) || (ci && fi && li)) {
21832183
throw new Error('A given SQL statement can contain at most one INTO clause')
21842184
}
2185-
if(f) f.forEach(info => info.table && tableList.add(`select::${info.db}::${info.table}`));
2185+
if(f) {
2186+
const tables = Array.isArray(f) ? f : f.expr
2187+
tables.forEach(info => info.table && tableList.add(`select::${info.db}::${info.table}`))
2188+
}
21862189
return {
21872190
with: cte,
21882191
type: 'select',
@@ -2385,8 +2388,7 @@ index_option
23852388
/ keyword_comment
23862389

23872390
table_ref_list
2388-
= head:table_base
2389-
tail:table_ref* {
2391+
= head:table_base tail:table_ref* {
23902392
tail.unshift(head);
23912393
tail.forEach(tableInfo => {
23922394
const { table, as } = tableInfo
@@ -2396,6 +2398,22 @@ table_ref_list
23962398
})
23972399
return tail;
23982400
}
2401+
/ lp:LPAREN+ __ head:table_base tail:table_ref* __ rp:RPAREN+ {
2402+
if (lp.length !== rp.length) throw new Error('parentheses not match in from clause')
2403+
tail.unshift(head);
2404+
tail.forEach(tableInfo => {
2405+
const { table, as } = tableInfo
2406+
tableAlias[table] = table
2407+
if (as) tableAlias[as] = table
2408+
refreshColumnList(columnList)
2409+
})
2410+
return {
2411+
expr: tail,
2412+
parentheses: {
2413+
length: rp.length
2414+
}
2415+
}
2416+
}
23992417

24002418
table_ref
24012419
= __ COMMA __ t:table_base { return t; }
@@ -2620,12 +2638,15 @@ delete_stmt
26202638
w:where_clause? __
26212639
or:order_by_clause? __
26222640
l:limit_clause? {
2623-
if(f) f.forEach(tableInfo => {
2624-
const { db, as, table, join } = tableInfo
2625-
const action = join ? 'select' : 'delete'
2626-
if (table) tableList.add(`${action}::${db}::${table}`)
2627-
if (!join) columnList.add(`delete::${table}::(.*)`);
2628-
});
2641+
if(f) {
2642+
const tables = Array.isArray(f) ? f : f.expr
2643+
tables.forEach(tableInfo => {
2644+
const { db, as, table, join } = tableInfo
2645+
const action = join ? 'select' : 'delete'
2646+
if (table) tableList.add(`${action}::${db}::${table}`)
2647+
if (!join) columnList.add(`delete::${table}::(.*)`);
2648+
})
2649+
}
26292650
if (t === null && f.length === 1) {
26302651
const tableInfo = f[0]
26312652
t = [{

src/tables.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,17 @@ function tablesToSQL(tables) {
162162
if (!Array.isArray(tables)) {
163163
const { expr, parentheses } = tables
164164
const sql = tablesToSQL(expr)
165-
if (parentheses) return `(${sql})`
165+
if (parentheses) {
166+
const leftParentheses = []
167+
const rightParentheses = []
168+
const parenthesesNumber = parentheses === true ? 1 : parentheses.length
169+
let i = 0
170+
while (i++ < parenthesesNumber) {
171+
leftParentheses.push('(')
172+
rightParentheses.push(')')
173+
}
174+
return leftParentheses.join('') + sql + rightParentheses.join('')
175+
}
166176
return sql
167177
}
168178
const baseTable = tables[0]

test/mysql-mariadb.spec.js

+14-1
Original file line numberDiff line numberDiff line change
@@ -364,12 +364,25 @@ describe('mysql', () => {
364364
]
365365
},
366366
{
367-
title: 'parentheses in from clause',
367+
title: 'parentheses in from table clause',
368368
sql: [
369369
'SELECT * FROM (user), (`name`)',
370370
'SELECT * FROM (`user`), (`name`)'
371371
]
372372
},
373+
{
374+
title: 'parentheses in from table join clause',
375+
sql: [
376+
`select *
377+
from (\`t1\` \`eti\` join bagel on bagel.id = eti.id)
378+
;
379+
select *
380+
from ((\`t1\`))
381+
`,
382+
'SELECT * FROM (`t1` AS `eti` INNER JOIN `bagel` ON `bagel`.`id` = `eti`.`id`) ; SELECT * FROM ((`t1`))'
383+
]
384+
},
385+
373386
{
374387
title: 'blob data type',
375388
sql: [

types.d.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export interface TableExpr {
5151
ast: Select;
5252
};
5353
as?: string | null;
54+
parentheses: boolean | { length: number }
5455
}
5556
export interface Dual {
5657
type: "dual";
@@ -255,7 +256,7 @@ export interface Select {
255256
options: any[] | null;
256257
distinct: "DISTINCT" | null;
257258
columns: any[] | Column[];
258-
from: From[] | null;
259+
from: From[] | TableExpr | null ;
259260
where: Binary | Function | null;
260261
groupby: { columns: ColumnRef[] | null, modifiers: ValueExpr<string>[] };
261262
having: any[] | null;

0 commit comments

Comments
 (0)