Skip to content

Commit 16496a6

Browse files
authored
Add sql backend + sqlfluff linter (#20854)
1 parent 4209a9d commit 16496a6

25 files changed

+2320
-4
lines changed

build-support/bin/generate_builtin_lockfiles.py

+2
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
from pants.backend.python.typecheck.pytype.subsystem import Pytype
5353
from pants.backend.scala.lint.scalafmt.subsystem import ScalafmtSubsystem
5454
from pants.backend.scala.subsystems.scalatest import Scalatest
55+
from pants.backend.sql.lint.sqlfluff.subsystem import Sqlfluff
5556
from pants.backend.terraform.dependency_inference import TerraformHcl2Parser
5657
from pants.backend.tools.semgrep.subsystem import SemgrepSubsystem
5758
from pants.backend.tools.yamllint.subsystem import Yamllint
@@ -132,6 +133,7 @@ class JvmTool(Tool[JvmToolBase]):
132133
PythonTool(SemgrepSubsystem, "pants.backend.experimental.tools.semgrep"),
133134
PythonTool(Setuptools, "pants.backend.python"),
134135
PythonTool(SetuptoolsSCM, "pants.backend.python"),
136+
PythonTool(Sqlfluff, "pants.backend.experimental.sql.lint.sqlfluff"),
135137
PythonTool(TerraformHcl2Parser, "pants.backend.experimental.terraform"),
136138
PythonTool(TwineSubsystem, "pants.backend.python"),
137139
PythonTool(Yamllint, "pants.backend.experimental.tools.yamllint"),
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"label": "Ad-Hoc Tools",
3-
"position": 12
3+
"position": 13
44
}
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"label": "Contributions",
3-
"position": 15
3+
"position": 16
44
}

docs/docs/releases/_category_.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"label": "Releases",
3-
"position": 14
3+
"position": 15
44
}

docs/docs/sql/_category_.json

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"label": "SQL",
3+
"position": 12
4+
}

docs/docs/sql/index.mdx

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
title: SQL Overview
3+
sidebar_position: 999
4+
---
5+
6+
---
7+
8+
:::caution SQL support is in alpha stage
9+
Pants is currently building support for SQL. Simple use cases might be
10+
supported, but many options are missing.
11+
12+
Please share feedback for what you need to use Pants with your SQL queries by
13+
either [opening a GitHub
14+
issue](https://github.com/pantsbuild/pants/issues/new/choose) or [joining our
15+
Slack](/community/getting-help)!
16+
:::
17+
18+
## Initial setup
19+
20+
First, activate the relevant backend in `pants.toml`:
21+
22+
```toml title="pants.toml"
23+
[GLOBAL]
24+
backend_packages = [
25+
...
26+
"pants.backend.experimental.sql",
27+
...
28+
]
29+
```
30+
31+
The SQL backend adds [`sql_source`](../../reference/targets/sql_source.mdx) and
32+
[`sql_sources`](../../reference/targets/sql_sources.mdx) target types for SQL
33+
files. The [`sql_source`](../../reference/targets/sql_source.mdx) behaves like
34+
[`resource`](../../reference/targets/resource.mdx), so you can use it directly
35+
without wrappers. The `tailor` goal will automatically generate the targets for
36+
your .sql files.
37+
38+
## Enable sqlfluff linter
39+
40+
To enable the linter activate the relevant backend in `pants.toml`:
41+
42+
```toml title="pants.toml"
43+
[GLOBAL]
44+
backend_packages = [
45+
...
46+
"pants.backend.experimental.sql.lint.sqlfluff",
47+
...
48+
]
49+
```
50+
51+
You can run the linter via `lint` goal:
52+
```
53+
pants lint --only=sqlfluff ::
54+
```
+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"label": "Writing Plugins",
3-
"position": 13
3+
"position": 14
44
}

docs/notes/2.22.x.md

+6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ If you encounter such discrepancies, and you can't resolve them easily, please [
2424

2525
### Backends
2626

27+
#### NEW: SQL
28+
29+
A new experimental `SQL` backend was added along with the [sqlfluff
30+
linter](https://www.pantsbuild.org/2.22/reference/subsystems/sqlfluff). See
31+
docs [here](https://www.pantsbuild.org/2.22/docs/sql).
32+
2733
#### JVM
2834

2935
##### Scala

src/python/pants/backend/experimental/sql/__init__.py

Whitespace-only changes.

src/python/pants/backend/experimental/sql/lint/__init__.py

Whitespace-only changes.

src/python/pants/backend/experimental/sql/lint/sqlfluff/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md).
2+
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3+
from pants.backend.sql.lint.sqlfluff import rules as sqlfluff_rules
4+
from pants.backend.sql.lint.sqlfluff import skip_field, subsystem
5+
6+
7+
def rules():
8+
return [
9+
*subsystem.rules(),
10+
*sqlfluff_rules.rules(),
11+
*skip_field.rules(),
12+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md).
2+
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3+
from pants.backend.sql import tailor
4+
from pants.backend.sql.target_types import SqlSourcesGeneratorTarget, SqlSourceTarget
5+
6+
7+
def target_types():
8+
return [
9+
SqlSourceTarget,
10+
SqlSourcesGeneratorTarget,
11+
]
12+
13+
14+
def rules():
15+
return [*tailor.rules()]

src/python/pants/backend/sql/BUILD

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md).
2+
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3+
4+
python_sources(
5+
sources=[
6+
"tailor.py",
7+
"target_types.py",
8+
],
9+
)

src/python/pants/backend/sql/__init__.py

Whitespace-only changes.

src/python/pants/backend/sql/lint/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md).
2+
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3+
resource(name="lockfile", source="sqlfluff.lock")
4+
5+
python_sources(
6+
overrides={"subsystem.py": {"dependencies": [":lockfile"]}},
7+
)
8+
9+
python_tests(
10+
name="tests",
11+
)

src/python/pants/backend/sql/lint/sqlfluff/__init__.py

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
# Copyright 2024 Pants project contributors (see CONTRIBUTORS.md).
2+
# Licensed under the Apache License, Version 2.0 (see LICENSE).
3+
from __future__ import annotations
4+
5+
from dataclasses import dataclass
6+
from typing import Any, Tuple
7+
8+
from typing_extensions import assert_never
9+
10+
from pants.backend.python.util_rules import pex
11+
from pants.backend.python.util_rules.pex import PexRequest, VenvPex, VenvPexProcess
12+
from pants.backend.sql.lint.sqlfluff.subsystem import Sqlfluff, SqlfluffFieldSet, SqlfluffMode
13+
from pants.core.goals.fix import FixResult, FixTargetsRequest
14+
from pants.core.goals.fmt import FmtResult, FmtTargetsRequest
15+
from pants.core.goals.lint import LintResult, LintTargetsRequest
16+
from pants.core.util_rules.config_files import ConfigFiles, ConfigFilesRequest
17+
from pants.core.util_rules.partitions import PartitionerType
18+
from pants.core.util_rules.source_files import SourceFiles, SourceFilesRequest
19+
from pants.engine.fs import Digest, MergeDigests
20+
from pants.engine.internals.native_engine import Snapshot
21+
from pants.engine.process import FallibleProcessResult
22+
from pants.engine.rules import Get, MultiGet, collect_rules, rule
23+
from pants.util.logging import LogLevel
24+
from pants.util.meta import classproperty
25+
from pants.util.strutil import pluralize
26+
27+
28+
class SqlfluffFixRequest(FixTargetsRequest):
29+
field_set_type = SqlfluffFieldSet
30+
tool_subsystem = Sqlfluff
31+
partitioner_type = PartitionerType.DEFAULT_SINGLE_PARTITION
32+
33+
# We don't need to include automatically added lint rules for this SqlfluffFixRequest,
34+
# because these lint rules are already checked by SqlfluffLintRequest.
35+
enable_lint_rules = False
36+
37+
38+
class SqlfluffLintRequest(LintTargetsRequest):
39+
field_set_type = SqlfluffFieldSet
40+
tool_subsystem = Sqlfluff
41+
partitioner_type = PartitionerType.DEFAULT_SINGLE_PARTITION
42+
43+
44+
class SqlfluffFormatRequest(FmtTargetsRequest):
45+
field_set_type = SqlfluffFieldSet
46+
tool_subsystem = Sqlfluff
47+
partitioner_type = PartitionerType.DEFAULT_SINGLE_PARTITION
48+
49+
@classproperty
50+
def tool_name(cls) -> str:
51+
return "sqlfluff format"
52+
53+
@classproperty
54+
def tool_id(cls) -> str:
55+
return "sqlfluff-format"
56+
57+
58+
@dataclass(frozen=True)
59+
class _RunSqlfluffRequest:
60+
snapshot: Snapshot
61+
mode: SqlfluffMode
62+
63+
64+
@rule(level=LogLevel.DEBUG)
65+
async def run_sqlfluff(
66+
request: _RunSqlfluffRequest,
67+
sqlfluff: Sqlfluff,
68+
) -> FallibleProcessResult:
69+
sqlfluff_pex_get = Get(VenvPex, PexRequest, sqlfluff.to_pex_request())
70+
71+
config_files_get = Get(
72+
ConfigFiles, ConfigFilesRequest, sqlfluff.config_request(request.snapshot.dirs)
73+
)
74+
75+
sqlfluff_pex, config_files = await MultiGet(sqlfluff_pex_get, config_files_get)
76+
77+
input_digest = await Get(
78+
Digest,
79+
MergeDigests((request.snapshot.digest, config_files.snapshot.digest)),
80+
)
81+
82+
initial_args: Tuple[str, ...] = ()
83+
if request.mode is SqlfluffMode.FMT:
84+
initial_args = ("format",)
85+
elif request.mode is SqlfluffMode.FIX:
86+
initial_args = ("fix", *sqlfluff.fix_args)
87+
elif request.mode is SqlfluffMode.LINT:
88+
initial_args = ("lint",)
89+
else:
90+
assert_never(request.mode)
91+
92+
conf_args = ["--config", sqlfluff.config] if sqlfluff.config else []
93+
94+
result = await Get(
95+
FallibleProcessResult,
96+
VenvPexProcess(
97+
sqlfluff_pex,
98+
argv=(*initial_args, *conf_args, *sqlfluff.args, *request.snapshot.files),
99+
input_digest=input_digest,
100+
output_files=request.snapshot.files,
101+
description=f"Run sqlfluff {' '.join(initial_args)} on {pluralize(len(request.snapshot.files), 'file')}.",
102+
level=LogLevel.DEBUG,
103+
),
104+
)
105+
return result
106+
107+
108+
@rule(desc="Fix with sqlfluff fix", level=LogLevel.DEBUG)
109+
async def sqlfluff_fix(request: SqlfluffFixRequest.Batch, sqlfluff: Sqlfluff) -> FixResult:
110+
result = await Get(
111+
FallibleProcessResult, _RunSqlfluffRequest(snapshot=request.snapshot, mode=SqlfluffMode.FIX)
112+
)
113+
return await FixResult.create(request, result)
114+
115+
116+
@rule(desc="Lint with sqlfluff lint", level=LogLevel.DEBUG)
117+
async def sqlfluff_lint(request: SqlfluffLintRequest.Batch[SqlfluffFieldSet, Any]) -> LintResult:
118+
source_files = await Get(
119+
SourceFiles, SourceFilesRequest(field_set.source for field_set in request.elements)
120+
)
121+
result = await Get(
122+
FallibleProcessResult,
123+
_RunSqlfluffRequest(snapshot=source_files.snapshot, mode=SqlfluffMode.LINT),
124+
)
125+
return LintResult.create(request, result)
126+
127+
128+
@rule(desc="Format with sqlfluff format", level=LogLevel.DEBUG)
129+
async def sqlfluff_fmt(request: SqlfluffFormatRequest.Batch, sqlfluff: Sqlfluff) -> FmtResult:
130+
result = await Get(
131+
FallibleProcessResult,
132+
_RunSqlfluffRequest(snapshot=request.snapshot, mode=SqlfluffMode.FMT),
133+
)
134+
return await FmtResult.create(request, result)
135+
136+
137+
def rules():
138+
return [
139+
*collect_rules(),
140+
*SqlfluffLintRequest.rules(),
141+
*SqlfluffFixRequest.rules(),
142+
*SqlfluffFormatRequest.rules(),
143+
*pex.rules(),
144+
]

0 commit comments

Comments
 (0)