Skip to content

Place joins at the same indentation level as FROM - #89

Merged
nene merged 2 commits into
nene:masterfrom
abrahamguo:from-join-same-level
Oct 10, 2026
Merged

nene merged 2 commits into
nene:masterfrom
abrahamguo:from-join-same-level

Conversation

@abrahamguo

Copy link
Copy Markdown
Contributor

As discussed in #69, this places joins at the same indentation level as FROM, instead of indenting the whole FROM clause body:

SELECT *
FROM client
LEFT JOIN client_sale ON client_sale.client_id = client.id
RIGHT OUTER JOIN client_attribute
  USING (client_attribute_id, fabulously_long_col_name)

A few related details:

  • Comma-separated tables (FROM a, b) keep the existing indented list layout, as do FROM clauses that mix commas with joins. Otherwise, the tables after the first one would start at the beginning of the line.
  • PIVOT, UNPIVOT, TABLESAMPLE and FOR SYSTEM_TIME AS OF are now indented under the table they modify, so they don't look like separate clauses.
  • A multi-line subquery directly after FROM now starts on the FROM line, the same way a subquery in a join already does.

The first commit adds missing awaits to three tests in from.test.ts, which were passing regardless of their result.

@nene nene left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. This looks pretty solid 👍

Comment on lines 41 to +58
it(`formats each SELECT clause with indented body when it doesn't fit on a single line`, async () => {
expect(
await pretty(
`SELECT very_long_col_name, another_long_col_name
FROM my_super_long_table_name
WHERE my_table_name.x > my_table_name.y
GROUP BY long_col, even_longer_col
HAVING foo > some_long_col_name
ORDER BY foo ASC, bar DESC NULLS FIRST
LIMIT 250 OFFSET 100000000
`,
{ printWidth: 25 },
),
).toBe(dedent`
SELECT
very_long_col_name,
another_long_col_name
FROM
my_super_long_table_name
FROM my_super_long_table_name

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would be better to change this test so that it would use multiple tables in FROM clause. Like FROM one_long_table_name, another_long_table_name.

Then that would align with the description of the test and it would also match the SELECT clause behavior - there we also would run into the same problem when SELECT would only contain a single column.

But we should cover this single-long-table-name case with a separate test inside the from.test.ts file.

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll tweak it by myself.

Comment thread test/select/from.test.ts
Comment on lines 128 to +130
describe("LATERAL", () => {
it(`formats LATERAL subquery`, () => {
testMysql(dedent`
it(`formats LATERAL subquery`, async () => {
await testMysql(dedent`

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice 👍

Comment thread test/select/from.test.ts
NATURAL LEFT JOIN inventory NOT INDEXED
client,
inventory
LEFT JOIN client_sale ON client_sale.client_id = client.id

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, this is definitely an awkward case.

I guess that's more of a code which nobody really writes on practice... though I might be mistaken.

In any case it's definitely an edge-case which we don't need to worry too much about as long as the formatting is not completely horrible.

@nene
nene merged commit 59ef6c5 into nene:master Oct 10, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants