Skip to content

Commit 43d262b

Browse files
authored
chore(splinter): initial commit (#594)
adds a new `pgls_splinter` crate with the basic integration. its not hooked up yet. pr includes - the query execution - the transformation into splinter diagnostics - a codegen tool for the diagnostics categories that extracts them from the sql file - a build file that downloads and parses the splinter.sql file from the repo. its cached and committed. The build script is just a helper when we want to "upgrade". we only have to change the SHA in build.rs for that. - a check that splinter is only executed if supabase roles are in the target database the query currently crashes when executed against a non-supabase database because of the calls to `pg_catalog.has_table_privilege('anon', c.oid, 'SELECT')` in some of the rules. for now, we skip splinter if the roles are not there. I opened an issue to ask for permission to add a flag to each rule so it can be used outside of supabase too: supabase/splinter#135. I would rather not make it our own just yet, but thats certainly a simple fix if things take too long. UPDATE: manually split up the query into supabase-only and generic rules in a follow-up. follow up prs will include: - refactoring of the analyser codegen so we can reuse it for other lint tools to generate config, docs etc - integration of the new DatabaseLocation attribute of the diagnostics struct (pr is open)
1 parent 5533414 commit 43d262b

File tree

28 files changed

+2420
-129
lines changed

28 files changed

+2420
-129
lines changed

.github/workflows/pull_request.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ jobs:
272272
run: cargo run -p xtask_codegen -- configuration
273273
- name: Run the bindings codegen
274274
run: cargo run -p xtask_codegen -- bindings
275+
- name: Run the splinter codegen
276+
run: cargo run -p xtask_codegen -- splinter
275277
- name: Run the docs codegen
276278
run: cargo run -p docs_codegen
277279
- name: Check for git diff -- run "just ready" if you see an error

.sqlx/query-b869d517301aaf69d382f092c09d5a53712d68afd273423f9310cd793586f532.json

Lines changed: 74 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.lock

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ pgls_query = { path = "./crates/pgls_query", version = "0.0.0"
8282
pgls_query_ext = { path = "./crates/pgls_query_ext", version = "0.0.0" }
8383
pgls_query_macros = { path = "./crates/pgls_query_macros", version = "0.0.0" }
8484
pgls_schema_cache = { path = "./crates/pgls_schema_cache", version = "0.0.0" }
85+
pgls_splinter = { path = "./crates/pgls_splinter", version = "0.0.0" }
8586
pgls_statement_splitter = { path = "./crates/pgls_statement_splitter", version = "0.0.0" }
8687
pgls_suppressions = { path = "./crates/pgls_suppressions", version = "0.0.0" }
8788
pgls_text_edit = { path = "./crates/pgls_text_edit", version = "0.0.0" }

crates/pgls_completions/src/providers/roles.rs

Lines changed: 31 additions & 124 deletions
Original file line numberDiff line numberDiff line change
@@ -46,21 +46,31 @@ mod tests {
4646
);
4747
"#;
4848

49+
fn expected_roles() -> Vec<CompletionAssertion> {
50+
vec![
51+
CompletionAssertion::LabelAndKind("anon".into(), crate::CompletionItemKind::Role),
52+
CompletionAssertion::LabelAndKind(
53+
"authenticated".into(),
54+
crate::CompletionItemKind::Role,
55+
),
56+
CompletionAssertion::LabelAndKind("owner".into(), crate::CompletionItemKind::Role),
57+
CompletionAssertion::LabelAndKind(
58+
"service_role".into(),
59+
crate::CompletionItemKind::Role,
60+
),
61+
CompletionAssertion::LabelAndKind("test_login".into(), crate::CompletionItemKind::Role),
62+
CompletionAssertion::LabelAndKind(
63+
"test_nologin".into(),
64+
crate::CompletionItemKind::Role,
65+
),
66+
]
67+
}
68+
4969
#[sqlx::test(migrator = "pgls_test_utils::MIGRATIONS")]
5070
async fn works_in_drop_role(pool: PgPool) {
5171
assert_complete_results(
5272
format!("drop role {}", QueryWithCursorPosition::cursor_marker()).as_str(),
53-
vec![
54-
CompletionAssertion::LabelAndKind("owner".into(), crate::CompletionItemKind::Role),
55-
CompletionAssertion::LabelAndKind(
56-
"test_login".into(),
57-
crate::CompletionItemKind::Role,
58-
),
59-
CompletionAssertion::LabelAndKind(
60-
"test_nologin".into(),
61-
crate::CompletionItemKind::Role,
62-
),
63-
],
73+
expected_roles(),
6474
Some(SETUP),
6575
&pool,
6676
)
@@ -71,17 +81,7 @@ mod tests {
7181
async fn works_in_alter_role(pool: PgPool) {
7282
assert_complete_results(
7383
format!("alter role {}", QueryWithCursorPosition::cursor_marker()).as_str(),
74-
vec![
75-
CompletionAssertion::LabelAndKind("owner".into(), crate::CompletionItemKind::Role),
76-
CompletionAssertion::LabelAndKind(
77-
"test_login".into(),
78-
crate::CompletionItemKind::Role,
79-
),
80-
CompletionAssertion::LabelAndKind(
81-
"test_nologin".into(),
82-
crate::CompletionItemKind::Role,
83-
),
84-
],
84+
expected_roles(),
8585
Some(SETUP),
8686
&pool,
8787
)
@@ -94,17 +94,7 @@ mod tests {
9494

9595
assert_complete_results(
9696
format!("set role {}", QueryWithCursorPosition::cursor_marker()).as_str(),
97-
vec![
98-
CompletionAssertion::LabelAndKind("owner".into(), crate::CompletionItemKind::Role),
99-
CompletionAssertion::LabelAndKind(
100-
"test_login".into(),
101-
crate::CompletionItemKind::Role,
102-
),
103-
CompletionAssertion::LabelAndKind(
104-
"test_nologin".into(),
105-
crate::CompletionItemKind::Role,
106-
),
107-
],
97+
expected_roles(),
10898
None,
10999
&pool,
110100
)
@@ -116,17 +106,7 @@ mod tests {
116106
QueryWithCursorPosition::cursor_marker()
117107
)
118108
.as_str(),
119-
vec![
120-
CompletionAssertion::LabelAndKind("owner".into(), crate::CompletionItemKind::Role),
121-
CompletionAssertion::LabelAndKind(
122-
"test_login".into(),
123-
crate::CompletionItemKind::Role,
124-
),
125-
CompletionAssertion::LabelAndKind(
126-
"test_nologin".into(),
127-
crate::CompletionItemKind::Role,
128-
),
129-
],
109+
expected_roles(),
130110
None,
131111
&pool,
132112
)
@@ -140,24 +120,14 @@ mod tests {
140120
assert_complete_results(
141121
format!(
142122
r#"create policy "my cool policy" on public.users
143-
as restrictive
123+
as restrictive
144124
for all
145125
to {}
146126
using (true);"#,
147127
QueryWithCursorPosition::cursor_marker()
148128
)
149129
.as_str(),
150-
vec![
151-
CompletionAssertion::LabelAndKind("owner".into(), crate::CompletionItemKind::Role),
152-
CompletionAssertion::LabelAndKind(
153-
"test_login".into(),
154-
crate::CompletionItemKind::Role,
155-
),
156-
CompletionAssertion::LabelAndKind(
157-
"test_nologin".into(),
158-
crate::CompletionItemKind::Role,
159-
),
160-
],
130+
expected_roles(),
161131
None,
162132
&pool,
163133
)
@@ -171,17 +141,7 @@ mod tests {
171141
QueryWithCursorPosition::cursor_marker()
172142
)
173143
.as_str(),
174-
vec![
175-
CompletionAssertion::LabelAndKind("owner".into(), crate::CompletionItemKind::Role),
176-
CompletionAssertion::LabelAndKind(
177-
"test_login".into(),
178-
crate::CompletionItemKind::Role,
179-
),
180-
CompletionAssertion::LabelAndKind(
181-
"test_nologin".into(),
182-
crate::CompletionItemKind::Role,
183-
),
184-
],
144+
expected_roles(),
185145
None,
186146
&pool,
187147
)
@@ -200,18 +160,7 @@ mod tests {
200160
QueryWithCursorPosition::cursor_marker()
201161
)
202162
.as_str(),
203-
vec![
204-
// recognizing already mentioned roles is not supported for now
205-
CompletionAssertion::LabelAndKind("owner".into(), crate::CompletionItemKind::Role),
206-
CompletionAssertion::LabelAndKind(
207-
"test_login".into(),
208-
crate::CompletionItemKind::Role,
209-
),
210-
CompletionAssertion::LabelAndKind(
211-
"test_nologin".into(),
212-
crate::CompletionItemKind::Role,
213-
),
214-
],
163+
expected_roles(),
215164
None,
216165
&pool,
217166
)
@@ -225,18 +174,7 @@ mod tests {
225174
QueryWithCursorPosition::cursor_marker()
226175
)
227176
.as_str(),
228-
vec![
229-
// recognizing already mentioned roles is not supported for now
230-
CompletionAssertion::LabelAndKind("owner".into(), crate::CompletionItemKind::Role),
231-
CompletionAssertion::LabelAndKind(
232-
"test_login".into(),
233-
crate::CompletionItemKind::Role,
234-
),
235-
CompletionAssertion::LabelAndKind(
236-
"test_nologin".into(),
237-
crate::CompletionItemKind::Role,
238-
),
239-
],
177+
expected_roles(),
240178
None,
241179
&pool,
242180
)
@@ -248,18 +186,7 @@ mod tests {
248186
QueryWithCursorPosition::cursor_marker()
249187
)
250188
.as_str(),
251-
vec![
252-
// recognizing already mentioned roles is not supported for now
253-
CompletionAssertion::LabelAndKind("owner".into(), crate::CompletionItemKind::Role),
254-
CompletionAssertion::LabelAndKind(
255-
"test_login".into(),
256-
crate::CompletionItemKind::Role,
257-
),
258-
CompletionAssertion::LabelAndKind(
259-
"test_nologin".into(),
260-
crate::CompletionItemKind::Role,
261-
),
262-
],
189+
expected_roles(),
263190
None,
264191
&pool,
265192
)
@@ -298,27 +225,7 @@ mod tests {
298225
];
299226

300227
for query in queries {
301-
assert_complete_results(
302-
query.as_str(),
303-
vec![
304-
// recognizing already mentioned roles is not supported for now
305-
CompletionAssertion::LabelAndKind(
306-
"owner".into(),
307-
crate::CompletionItemKind::Role,
308-
),
309-
CompletionAssertion::LabelAndKind(
310-
"test_login".into(),
311-
crate::CompletionItemKind::Role,
312-
),
313-
CompletionAssertion::LabelAndKind(
314-
"test_nologin".into(),
315-
crate::CompletionItemKind::Role,
316-
),
317-
],
318-
None,
319-
&pool,
320-
)
321-
.await;
228+
assert_complete_results(query.as_str(), expected_roles(), None, &pool).await;
322229
}
323230
}
324231
}

crates/pgls_diagnostics_categories/src/categories.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,30 @@ define_categories! {
4747
"lint/safety/runningStatementWhileHoldingAccessExclusive": "https://pg-language-server.com/latest/rules/running-statement-while-holding-access-exclusive",
4848
"lint/safety/transactionNesting": "https://pg-language-server.com/latest/rules/transaction-nesting",
4949
// end lint rules
50+
// splinter rules start
51+
"splinter/performance/authRlsInitplan": "https://supabase.com/docs/guides/database/database-linter?lint=0003_auth_rls_initplan",
52+
"splinter/performance/duplicateIndex": "https://supabase.com/docs/guides/database/database-linter?lint=0009_duplicate_index",
53+
"splinter/performance/multiplePermissivePolicies": "https://supabase.com/docs/guides/database/database-linter?lint=0006_multiple_permissive_policies",
54+
"splinter/performance/noPrimaryKey": "https://supabase.com/docs/guides/database/database-linter?lint=0004_no_primary_key",
55+
"splinter/performance/tableBloat": "https://supabase.com/docs/guides/database/database-linter?lint=0020_table_bloat",
56+
"splinter/performance/unindexedForeignKeys": "https://supabase.com/docs/guides/database/database-linter?lint=0001_unindexed_foreign_keys",
57+
"splinter/performance/unusedIndex": "https://supabase.com/docs/guides/database/database-linter?lint=0005_unused_index",
58+
"splinter/security/authUsersExposed": "https://supabase.com/docs/guides/database/database-linter?lint=0002_auth_users_exposed",
59+
"splinter/security/extensionInPublic": "https://supabase.com/docs/guides/database/database-linter?lint=0014_extension_in_public",
60+
"splinter/security/extensionVersionsOutdated": "https://supabase.com/docs/guides/database/database-linter?lint=0022_extension_versions_outdated",
61+
"splinter/security/fkeyToAuthUnique": "https://supabase.com/docs/guides/database/database-linter?lint=0021_fkey_to_auth_unique",
62+
"splinter/security/foreignTableInApi": "https://supabase.com/docs/guides/database/database-linter?lint=0017_foreign_table_in_api",
63+
"splinter/security/functionSearchPathMutable": "https://supabase.com/docs/guides/database/database-linter?lint=0011_function_search_path_mutable",
64+
"splinter/security/insecureQueueExposedInApi": "https://supabase.com/docs/guides/database/database-linter?lint=0019_insecure_queue_exposed_in_api",
65+
"splinter/security/materializedViewInApi": "https://supabase.com/docs/guides/database/database-linter?lint=0016_materialized_view_in_api",
66+
"splinter/security/policyExistsRlsDisabled": "https://supabase.com/docs/guides/database/database-linter?lint=0007_policy_exists_rls_disabled",
67+
"splinter/security/rlsDisabledInPublic": "https://supabase.com/docs/guides/database/database-linter?lint=0013_rls_disabled_in_public",
68+
"splinter/security/rlsEnabledNoPolicy": "https://supabase.com/docs/guides/database/database-linter?lint=0008_rls_enabled_no_policy",
69+
"splinter/security/rlsReferencesUserMetadata": "https://supabase.com/docs/guides/database/database-linter?lint=0015_rls_references_user_metadata",
70+
"splinter/security/securityDefinerView": "https://supabase.com/docs/guides/database/database-linter?lint=0010_security_definer_view",
71+
"splinter/security/unsupportedRegTypes": "https://supabase.com/docs/guides/database/database-linter?lint=unsupported_reg_types",
72+
"splinter/unknown/unknown": "https://pg-language-server.com/latest",
73+
// splinter rules end
5074
;
5175
// General categories
5276
"stdin",
@@ -69,4 +93,11 @@ define_categories! {
6993
"lint/performance",
7094
"lint/safety",
7195
// Lint groups end
96+
97+
// Splinter groups start
98+
"splinter",
99+
"splinter/performance",
100+
"splinter/security",
101+
"splinter/unknown",
102+
// Splinter groups end
72103
}

crates/pgls_splinter/Cargo.toml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[package]
2+
authors.workspace = true
3+
categories.workspace = true
4+
description = "<DESCRIPTION>"
5+
edition.workspace = true
6+
homepage.workspace = true
7+
keywords.workspace = true
8+
license.workspace = true
9+
name = "pgls_splinter"
10+
repository.workspace = true
11+
version = "0.0.0"
12+
13+
[dependencies]
14+
pgls_diagnostics.workspace = true
15+
serde.workspace = true
16+
serde_json.workspace = true
17+
sqlx.workspace = true
18+
19+
[build-dependencies]
20+
ureq = "2.10"
21+
22+
[dev-dependencies]
23+
insta.workspace = true
24+
pgls_console.workspace = true
25+
pgls_test_utils.workspace = true
26+
27+
[lib]
28+
doctest = false

0 commit comments

Comments
 (0)