Skip to content

Commit 7b1082b

Browse files
Merge branch 'main' into fix/false-positives
2 parents 0ba78f3 + f02b57e commit 7b1082b

File tree

8 files changed

+415
-85
lines changed

8 files changed

+415
-85
lines changed

crates/pgt_configuration/src/lib.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ pub mod diagnostics;
99
pub mod files;
1010
pub mod generated;
1111
pub mod migrations;
12+
pub mod plpgsql_check;
1213
pub mod typecheck;
1314
pub mod vcs;
1415

@@ -33,6 +34,10 @@ use files::{FilesConfiguration, PartialFilesConfiguration, partial_files_configu
3334
use migrations::{
3435
MigrationsConfiguration, PartialMigrationsConfiguration, partial_migrations_configuration,
3536
};
37+
use plpgsql_check::{
38+
PartialPlPgSqlCheckConfiguration, PlPgSqlCheckConfiguration,
39+
partial_pl_pg_sql_check_configuration,
40+
};
3641
use serde::{Deserialize, Serialize};
3742
pub use typecheck::{
3843
PartialTypecheckConfiguration, TypecheckConfiguration, partial_typecheck_configuration,
@@ -85,6 +90,10 @@ pub struct Configuration {
8590
#[partial(type, bpaf(external(partial_typecheck_configuration), optional))]
8691
pub typecheck: TypecheckConfiguration,
8792

93+
/// The configuration for type checking
94+
#[partial(type, bpaf(external(partial_pl_pg_sql_check_configuration), optional))]
95+
pub plpgsql_check: PlPgSqlCheckConfiguration,
96+
8897
/// The configuration of the database connection
8998
#[partial(
9099
type,
@@ -121,6 +130,9 @@ impl PartialConfiguration {
121130
typecheck: Some(PartialTypecheckConfiguration {
122131
..Default::default()
123132
}),
133+
plpgsql_check: Some(PartialPlPgSqlCheckConfiguration {
134+
..Default::default()
135+
}),
124136
db: Some(PartialDatabaseConfiguration {
125137
host: Some("127.0.0.1".to_string()),
126138
port: Some(5432),
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use biome_deserialize_macros::{Merge, Partial};
2+
use bpaf::Bpaf;
3+
use serde::{Deserialize, Serialize};
4+
5+
/// The configuration for type checking.
6+
#[derive(Clone, Debug, Deserialize, Eq, Partial, PartialEq, Serialize)]
7+
#[partial(derive(Bpaf, Clone, Eq, PartialEq, Merge))]
8+
#[partial(cfg_attr(feature = "schema", derive(schemars::JsonSchema)))]
9+
#[partial(serde(rename_all = "camelCase", default, deny_unknown_fields))]
10+
pub struct PlPgSqlCheckConfiguration {
11+
/// if `false`, it disables the feature and pglpgsql_check won't be executed. `true` by default
12+
#[partial(bpaf(hide))]
13+
pub enabled: bool,
14+
}
15+
16+
impl Default for PlPgSqlCheckConfiguration {
17+
fn default() -> Self {
18+
Self { enabled: true }
19+
}
20+
}

crates/pgt_configuration/src/typecheck.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@ use serde::{Deserialize, Serialize};
99
#[partial(cfg_attr(feature = "schema", derive(schemars::JsonSchema)))]
1010
#[partial(serde(rename_all = "camelCase", default, deny_unknown_fields))]
1111
pub struct TypecheckConfiguration {
12+
/// if `false`, it disables the feature and the typechecker won't be executed. `true` by default
13+
#[partial(bpaf(hide))]
14+
pub enabled: bool,
1215
/// Default search path schemas for type checking.
1316
/// Can be a list of schema names or glob patterns like ["public", "app_*"].
1417
/// If not specified, defaults to ["public"].
@@ -19,6 +22,7 @@ pub struct TypecheckConfiguration {
1922
impl Default for TypecheckConfiguration {
2023
fn default() -> Self {
2124
Self {
25+
enabled: true,
2226
search_path: ["public".to_string()].into_iter().collect(),
2327
}
2428
}

crates/pgt_workspace/src/settings.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ use pgt_configuration::{
1717
diagnostics::InvalidIgnorePattern,
1818
files::FilesConfiguration,
1919
migrations::{MigrationsConfiguration, PartialMigrationsConfiguration},
20+
plpgsql_check::PlPgSqlCheckConfiguration,
2021
};
2122
use pgt_fs::PgTPath;
2223

@@ -213,6 +214,9 @@ pub struct Settings {
213214
/// Type checking settings for the workspace
214215
pub typecheck: TypecheckSettings,
215216

217+
/// plpgsql_check settings for the workspace
218+
pub plpgsql_check: PlPgSqlCheckSettings,
219+
216220
/// Migrations settings
217221
pub migrations: Option<MigrationSettings>,
218222
}
@@ -253,6 +257,12 @@ impl Settings {
253257
self.typecheck = to_typecheck_settings(TypecheckConfiguration::from(typecheck));
254258
}
255259

260+
// plpgsql_check part
261+
if let Some(plpgsql_check) = configuration.plpgsql_check {
262+
self.plpgsql_check =
263+
to_plpgsql_check_settings(PlPgSqlCheckConfiguration::from(plpgsql_check));
264+
}
265+
256266
// Migrations settings
257267
if let Some(migrations) = configuration.migrations {
258268
self.migrations = to_migration_settings(
@@ -305,6 +315,13 @@ fn to_linter_settings(
305315
fn to_typecheck_settings(conf: TypecheckConfiguration) -> TypecheckSettings {
306316
TypecheckSettings {
307317
search_path: conf.search_path.into_iter().collect(),
318+
enabled: conf.enabled,
319+
}
320+
}
321+
322+
fn to_plpgsql_check_settings(conf: PlPgSqlCheckConfiguration) -> PlPgSqlCheckSettings {
323+
PlPgSqlCheckSettings {
324+
enabled: conf.enabled,
308325
}
309326
}
310327

@@ -415,16 +432,32 @@ impl Default for LinterSettings {
415432
}
416433
}
417434

435+
/// Type checking settings for the entire workspace
436+
#[derive(Debug)]
437+
pub struct PlPgSqlCheckSettings {
438+
/// Enabled by default
439+
pub enabled: bool,
440+
}
441+
442+
impl Default for PlPgSqlCheckSettings {
443+
fn default() -> Self {
444+
Self { enabled: true }
445+
}
446+
}
447+
418448
/// Type checking settings for the entire workspace
419449
#[derive(Debug)]
420450
pub struct TypecheckSettings {
451+
/// Enabled by default
452+
pub enabled: bool,
421453
/// Default search path schemas for type checking
422454
pub search_path: Vec<String>,
423455
}
424456

425457
impl Default for TypecheckSettings {
426458
fn default() -> Self {
427459
Self {
460+
enabled: true,
428461
search_path: vec!["public".to_string()],
429462
}
430463
}

crates/pgt_workspace/src/workspace/server.rs

Lines changed: 102 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -451,93 +451,111 @@ impl Workspace for WorkspaceServer {
451451
/*
452452
* Type-checking against database connection
453453
*/
454-
if let Some(pool) = self.get_current_connection() {
455-
let path_clone = params.path.clone();
456-
let schema_cache = self.schema_cache.load(pool.clone())?;
457-
let input = doc.iter(TypecheckDiagnosticsMapper).collect::<Vec<_>>();
458-
let search_path_patterns = settings.typecheck.search_path.clone();
459-
460-
// Combined async context for both typecheck and plpgsql_check
461-
let async_results = run_async(async move {
462-
stream::iter(input)
463-
.map(|(id, range, ast, cst, sign)| {
464-
let pool = pool.clone();
465-
let path = path_clone.clone();
466-
let schema_cache = Arc::clone(&schema_cache);
467-
let search_path_patterns = search_path_patterns.clone();
468-
469-
async move {
470-
let mut diagnostics = Vec::new();
471-
472-
if let Some(ast) = ast {
473-
// Type checking
474-
let typecheck_result = pgt_typecheck::check_sql(TypecheckParams {
475-
conn: &pool,
476-
sql: convert_to_positional_params(id.content()).as_str(),
477-
ast: &ast,
478-
tree: &cst,
479-
schema_cache: schema_cache.as_ref(),
480-
search_path_patterns,
481-
identifiers: sign
482-
.map(|s| {
483-
s.args
484-
.iter()
485-
.map(|a| TypedIdentifier {
486-
path: s.name.clone(),
487-
name: a.name.clone(),
488-
type_: IdentifierType {
489-
schema: a.type_.schema.clone(),
490-
name: a.type_.name.clone(),
491-
is_array: a.type_.is_array,
492-
},
493-
})
494-
.collect::<Vec<_>>()
495-
})
496-
.unwrap_or_default(),
497-
})
498-
.await;
499-
500-
if let Ok(Some(diag)) = typecheck_result {
501-
let r = diag.location().span.map(|span| span + range.start());
502-
diagnostics.push(
503-
diag.with_file_path(path.as_path().display().to_string())
504-
.with_file_span(r.unwrap_or(range)),
505-
);
454+
let typecheck_enabled = settings.typecheck.enabled;
455+
let plpgsql_check_enabled = settings.plpgsql_check.enabled;
456+
if typecheck_enabled || plpgsql_check_enabled {
457+
if let Some(pool) = self.get_current_connection() {
458+
let path_clone = params.path.clone();
459+
let schema_cache = self.schema_cache.load(pool.clone())?;
460+
let input = doc.iter(TypecheckDiagnosticsMapper).collect::<Vec<_>>();
461+
let search_path_patterns = settings.typecheck.search_path.clone();
462+
463+
// Combined async context for both typecheck and plpgsql_check
464+
let async_results = run_async(async move {
465+
stream::iter(input)
466+
.map(|(id, range, ast, cst, sign)| {
467+
let pool = pool.clone();
468+
let path = path_clone.clone();
469+
let schema_cache = Arc::clone(&schema_cache);
470+
let search_path_patterns = search_path_patterns.clone();
471+
472+
async move {
473+
let mut diagnostics = Vec::new();
474+
475+
if let Some(ast) = ast {
476+
// Type checking
477+
if typecheck_enabled {
478+
let typecheck_result =
479+
pgt_typecheck::check_sql(TypecheckParams {
480+
conn: &pool,
481+
sql: convert_to_positional_params(id.content())
482+
.as_str(),
483+
ast: &ast,
484+
tree: &cst,
485+
schema_cache: schema_cache.as_ref(),
486+
search_path_patterns,
487+
identifiers: sign
488+
.map(|s| {
489+
s.args
490+
.iter()
491+
.map(|a| TypedIdentifier {
492+
path: s.name.clone(),
493+
name: a.name.clone(),
494+
type_: IdentifierType {
495+
schema: a.type_.schema.clone(),
496+
name: a.type_.name.clone(),
497+
is_array: a.type_.is_array,
498+
},
499+
})
500+
.collect::<Vec<_>>()
501+
})
502+
.unwrap_or_default(),
503+
})
504+
.await;
505+
506+
if let Ok(Some(diag)) = typecheck_result {
507+
let r = diag
508+
.location()
509+
.span
510+
.map(|span| span + range.start());
511+
diagnostics.push(
512+
diag.with_file_path(
513+
path.as_path().display().to_string(),
514+
)
515+
.with_file_span(r.unwrap_or(range)),
516+
);
517+
}
518+
}
519+
520+
// plpgsql_check
521+
if plpgsql_check_enabled {
522+
let plpgsql_check_results =
523+
pgt_plpgsql_check::check_plpgsql(
524+
pgt_plpgsql_check::PlPgSqlCheckParams {
525+
conn: &pool,
526+
sql: id.content(),
527+
ast: &ast,
528+
schema_cache: schema_cache.as_ref(),
529+
},
530+
)
531+
.await
532+
.unwrap_or_else(|_| vec![]);
533+
534+
for d in plpgsql_check_results {
535+
let r = d.span.map(|span| span + range.start());
536+
diagnostics.push(
537+
d.with_file_path(
538+
path.as_path().display().to_string(),
539+
)
540+
.with_file_span(r.unwrap_or(range)),
541+
);
542+
}
543+
}
506544
}
507545

508-
// plpgsql_check
509-
let plpgsql_check_results = pgt_plpgsql_check::check_plpgsql(
510-
pgt_plpgsql_check::PlPgSqlCheckParams {
511-
conn: &pool,
512-
sql: id.content(),
513-
ast: &ast,
514-
schema_cache: schema_cache.as_ref(),
515-
},
516-
)
517-
.await
518-
.unwrap_or_else(|_| vec![]);
519-
520-
for d in plpgsql_check_results {
521-
let r = d.span.map(|span| span + range.start());
522-
diagnostics.push(
523-
d.with_file_path(path.as_path().display().to_string())
524-
.with_file_span(r.unwrap_or(range)),
525-
);
526-
}
546+
Ok::<Vec<pgt_diagnostics::Error>, sqlx::Error>(diagnostics)
527547
}
528-
529-
Ok::<Vec<pgt_diagnostics::Error>, sqlx::Error>(diagnostics)
530-
}
531-
})
532-
.buffer_unordered(10)
533-
.collect::<Vec<_>>()
534-
.await
535-
})?;
536-
537-
for result in async_results.into_iter() {
538-
let diagnostics_batch = result?;
539-
for diag in diagnostics_batch {
540-
diagnostics.push(SDiagnostic::new(diag));
548+
})
549+
.buffer_unordered(10)
550+
.collect::<Vec<_>>()
551+
.await
552+
})?;
553+
554+
for result in async_results.into_iter() {
555+
let diagnostics_batch = result?;
556+
for diag in diagnostics_batch {
557+
diagnostics.push(SDiagnostic::new(diag));
558+
}
541559
}
542560
}
543561
}

0 commit comments

Comments
 (0)