### Nix Development Environment Setup Source: https://github.com/supabase-community/postgres-language-server/blob/main/AGENTS.md Sets up the development environment using Nix and starts the database. This is an alternative for users familiar with Nix. ```bash nix develop && docker-compose up -d ``` -------------------------------- ### Start Database for Schema Introspection Source: https://github.com/supabase-community/postgres-language-server/blob/main/AGENTS.md Starts a Docker Compose setup for the database, which is required for schema introspection during development. ```bash docker-compose up -d ``` -------------------------------- ### Install Development Tools Source: https://github.com/supabase-community/postgres-language-server/blob/main/AGENTS.md Installs necessary development tools for the project. This is a prerequisite for setting up the development environment. ```bash just install-tools ``` -------------------------------- ### Download and Install Postgres Language Server Binary Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/manual_installation.md Provides commands to download the appropriate binary for different platforms (macOS, Linux, Windows) from GitHub releases and set execution permissions. It covers both x86_64 and aarch64 architectures and includes PowerShell commands for Windows. ```shell # macOS arm (M1 or newer) curl -L https://github.com/supabase-community/postgres-language-server/releases/latest/download/postgres-language-server_aarch64-apple-darwin -o postgres-language-server chmod +x postgres-language-server # macOS x86_64 curl -L https://github.com/supabase-community/postgres-language-server/releases/latest/download/postgres-language-server_x86_64-apple-darwin -o postgres-language-server chmod +x postgres-language-server # Linux (x86_64) curl -L https://github.com/supabase-community/postgres-language-server/releases/latest/download/postgres-language-server_x86_64-unknown-linux-gnu -o postgres-language-server chmod +x postgres-language-server # Linux (aarch64) curl -L https://github.com/supabase-community/postgres-language-server/releases/latest/download/postgres-language-server_aarch64-unknown-linux-gnu -o postgres-language-server chmod +x postgres-language-server ``` ```powershell # Windows (x86_64, PowerShell) Invoke-WebRequest -Uri "https://github.com/supabase-community/postgres-language-server/releases/latest/download/postgres-language-server_x86_64-pc-windows-msvc.exe" -OutFile "postgres-language-server.exe" # Windows (aarch64, PowerShell) Invoke-WebRequest -Uri "https://github.com/supabase-community/postgres-language-server/releases/latest/download/postgres-language-server_aarch64-pc-windows-msvc.exe" -OutFile "postgres-language-server.exe" ``` -------------------------------- ### Documenting a Lint Rule with Examples (Rust) Source: https://github.com/supabase-community/postgres-language-server/blob/main/crates/pgls_analyser/CONTRIBUTING.md Provides an example of documenting a lint rule, including a single-line brief description, detailed explanations, and sections for `## Examples` with `### Invalid` and `### Valid` code blocks. It highlights the use of `expect_diagnostic` for invalid snippets and `ignore` for specific cases. ```rust declare_lint_rule! { /// Dropping a column may break existing clients. /// /// Update your application code to no longer read or write the column. /// /// You can leave the column as nullable or delete the column once queries no longer select or modify the column. /// /// ## Examples /// /// ### Invalid /// /// ```sql,expect_diagnostic /// alter table test drop column id; /// ``` /// pub BanDropColumn { version: "next", name: "banDropColumn", recommended: true, severity: Severity::Error, sources: &[RuleSource::Squawk("ban-drop-column")], } } ``` -------------------------------- ### Starting and Using Postgres Language Server Daemon via CLI Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/guides/ide_setup.md This demonstrates how to start the Postgres Language Server daemon and then execute commands through it using the '--use-server' flag. This approach is efficient but requires manual management of the daemon process. ```shell postgres-language-server start ``` ```shell postgres-language-server check --use-server --stdin-file-path=dummy.sql ``` -------------------------------- ### Configure Linter, Typecheck, and PL/pgSQL Check Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/configuration.md Example JSON configuration for enabling the linter, type checker, and PL/pgSQL check extension. This configuration requires the `plpgsql_check` extension to be installed in your database. ```json { "$schema": "https://pg-language-server.com/latest/schema.json", "linter": { "enabled": true, "rules": { "recommended": true } }, "typecheck": { "enabled": true }, "plpgsqlCheck": { "enabled" : true } } ``` -------------------------------- ### Include Specific SQL Files via Configuration Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/configuration.md JSON configuration to specify which files the Postgres Language Server should process, using glob patterns. This example includes only `.sql` files within the `sql/` directory and its subdirectories. ```json { "files": { "includes": ["sql/**/*.sql"] } } ``` -------------------------------- ### Run Doc Tests Source: https://github.com/supabase-community/postgres-language-server/blob/main/AGENTS.md Executes documentation tests to ensure that code examples within the documentation are accurate and functional. ```bash just test-doc ``` -------------------------------- ### Install Postgres Language Server using Homebrew Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/manual_installation.md Installs the Postgres Language Server using the Homebrew package manager. This is a convenient option for macOS and Linux users already utilizing Homebrew. ```shell brew install postgres-language-server ``` -------------------------------- ### Check Specific Files and Folders via CLI Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/configuration.md Example of how to use the `postgres-language-server check` command to specify files and folders for linting and type checking. This command will process `file1.sql` and all files within the `src` directory. ```shell postgres-language-server check file1.js src/ ``` -------------------------------- ### Install Postgres Language Server CLI with PNPM Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/getting_started.md Installs the Postgres Language Server as a development dependency using the PNPM package manager. This command ensures the server is available for local development tooling. ```sh pnpm add -D -E @postgres-language-server/cli ``` -------------------------------- ### Exclude Test Folders via Configuration Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/configuration.md JSON configuration to exclude specific files or folders from processing. This example includes all files except those within any directory named `test`. ```json { "files": { "ignore": [ "**/test", ] } } ``` -------------------------------- ### PostgreSQL AST Node Structure Example Source: https://github.com/supabase-community/postgres-language-server/blob/main/agentic/port_eugene_rules.md Demonstrates the structure of the full PostgreSQL AST using `pgls_query::NodeEnum`, showing how to access nodes like `AlterTableStmt` and its commands. ```rust pgls_query::NodeEnum::AlterTableStmt(stmt) -> stmt.cmds: Vec -> NodeEnum::AlterTableCmd(cmd) -> cmd.subtype: AlterTableType -> cmd.def: Option -> NodeEnum::ColumnDef(col_def) ``` -------------------------------- ### Configure Local Development Database Connection Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/configuration.md JSON configuration for connecting to a local PostgreSQL development database. This includes host, port, credentials, and connection timeout. It also allows specifying which hosts statements can be executed against. ```json { "$schema": "https://pg-language-server.com/latest/schema.json", "db": { "host": "127.0.0.1", "port": 5432, "username": "postgres", "password": "postgres", "database": "postgres", "connTimeoutSecs": 10, "allowStatementExecutionsAgainst": ["127.0.0.1/*", "localhost/*"] } } ``` -------------------------------- ### Eugene Simplified AST Example Source: https://github.com/supabase-community/postgres-language-server/blob/main/agentic/port_eugene_rules.md Shows a simplified AST structure used by Eugene, including `StatementSummary` and `AlterTableAction` enums. ```rust enum StatementSummary { AlterTable { schema: String, name: String, actions: Vec }, CreateIndex { schema: String, idxname: String, concurrently: bool, target: String }, // ... } enum AlterTableAction { AddColumn { column: String, type_name: String, stored_generated: bool, ... }, SetType { column: String, type_name: String }, // ... } ``` -------------------------------- ### Scaffolding a New Lint Rule with 'Just' Source: https://github.com/supabase-community/postgres-language-server/blob/main/crates/pgls_analyser/CONTRIBUTING.md Command to generate the necessary files for a new lint rule. It takes the rule type, name, and an optional severity level as arguments. This simplifies the setup process for new rules. ```shell just new-lintrule safety useMyRuleName (severity) ``` -------------------------------- ### Rust Schema Normalization Example Source: https://github.com/supabase-community/postgres-language-server/blob/main/agentic/port_eugene_rules.md Provides a Rust code snippet for normalizing schema names, defaulting to 'public' if the schema is empty. ```rust let schema_normalized = if schema.is_empty() { "public" } else { schema.as_str() }; ``` -------------------------------- ### Development Workflow Tasks with 'just' Source: https://context7.com/supabase-community/postgres-language-server/llms.txt A collection of command-line tasks defined using the `just` command runner for common development operations. These tasks streamline processes like installing tools, formatting code, running tests, linting, and generating code. ```bash # Install development tools just install-tools # Format all code (Rust, TypeScript, TOML) just format # Run all tests just test # Test specific crate just test-crate pgls_completions # Run linter on entire codebase just lint # Fix all linting issues just lint-fix # Generate linter code and configuration just gen-lint # Create new lint rule just new-lintrule safety banCreateTable error # Run full ready check before committing just ready # Create new crate in workspace just new-crate my_feature # Print tree-sitter AST for SQL file just tree-print example.sql # Start documentation server just serve-docs ``` -------------------------------- ### Advanced CI Linting with PostgreSQL (GitHub Actions) Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/guides/continuous_integration.md A GitHub Actions workflow that sets up a PostgreSQL service alongside the Postgres Language Server CLI for more advanced schema checks. This workflow starts a PostgreSQL container and then runs checks on SQL files. ```yaml jobs: lint: runs-on: ubuntu-latest services: postgres: image: postgres:latest env: POSTGRES_USER: postgres POSTGRES_PASSWORD: postgres POSTGRES_DB: postgres ports: - 5432:5432 steps: - uses: actions/checkout@v4 - uses: supabase-community/pglt-cli-action@main with: version: latest - run: postgres-language-server check sql/ ``` -------------------------------- ### VSCode Extension for Postgres Language Server Integration Source: https://context7.com/supabase-community/postgres-language-server/llms.txt Integrates the Postgres Language Server into VSCode for enhanced SQL editing experience. It starts the language server as a separate process and communicates with it via JSON-RPC. Requires the 'vscode-languageclient' library. ```typescript import { ExtensionContext, window } from 'vscode'; import { LanguageClient, LanguageClientOptions, Executable, ServerOptions } from 'vscode-languageclient/node'; let client: LanguageClient; export function activate(context: ExtensionContext) { // Configure executable const run: Executable = { command: 'postgres-language-server', // or pg_cli args: ['lsp-proxy'] }; const outputChannel = window.createOutputChannel( 'Postgres LSP', { log: true } ); const serverOptions: ServerOptions = { run, debug: run }; // Configure client const clientOptions: LanguageClientOptions = { documentSelector: [{ scheme: 'file', language: 'sql' }], outputChannel, // Optional: pass configuration to server initializationOptions: { database: { host: 'localhost', port: 5432, username: 'postgres', password: 'postgres', database: 'myapp' } } }; // Create and start client client = new LanguageClient( 'postgres_lsp', 'Postgres LSP', serverOptions, clientOptions ); client.start(); } export function deactivate(): Thenable | undefined { return client?.stop(); } ``` -------------------------------- ### Configure Database Connection Using Connection String Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/configuration.md Alternative JSON configuration for database connection using a connection string URI. This method takes precedence over individual field configurations and can include additional Postgres settings like SSL mode. ```json { "db": { "connectionString": "postgres://postgres:postgres@localhost:5432/postgres?sslmode=disable", "allowStatementExecutionsAgainst": ["localhost/*"] } } ``` -------------------------------- ### JSONC Configuration for Rule Options Source: https://github.com/supabase-community/postgres-language-server/blob/main/crates/pgls_analyser/CONTRIBUTING.md Example of how to configure custom rule options within the `postgres-language-server.jsonc` file. This JSONC structure defines the nested path to a specific rule ('safety.myRule') and its associated options, including 'behavior', 'threshold', and 'behaviorExceptions'. ```json { "linter": { "rules": { "safety": { "myRule": { "level": "warn", "options": { "behavior": "A", "threshold": 20, "behaviorExceptions": ["one", "two"] } } } } } } ``` -------------------------------- ### SQL Example: Invalid Transaction Nesting Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/reference/rules/transaction-nesting.md Demonstrates an invalid SQL code snippet where a BEGIN statement is used within a context already managed by a migration tool. This rule is inspired by squawk/transaction-nesting and aims to prevent unexpected behavior in database migrations. ```sql BEGIN; -- Migration tools already manage transactions SELECT 1; ``` -------------------------------- ### SQL: Add Column Safely (Valid) Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/reference/rules/adding-field-with-default.md This example shows the recommended, valid approach to adding a column that will eventually have a default value. It involves adding the column first without a default, then setting the default in a separate statement. This avoids the immediate table rewrite and lock associated with adding a column with a default in a single operation, especially in older PostgreSQL versions. Further steps like backfilling data and adding a NOT NULL constraint can be done subsequently. ```sql ALTER TABLE "core_recipe" ADD COLUMN "foo" integer; ALTER TABLE "core_recipe" ALTER COLUMN "foo" SET DEFAULT 10; -- Then backfill and add NOT NULL constraint if needed ``` -------------------------------- ### Initialize Workspace API in Rust Source: https://context7.com/supabase-community/postgres-language-server/llms.txt Shows how to initialize the `pgls-workspace` API in Rust for local mode operation. It demonstrates creating a workspace server and an application instance with either a default console or a custom filesystem. ```rust use pgls_workspace::{App, workspace}; use pgls_console::EnvConsole; use pgls_fs::OsFileSystem; fn main() { let mut console = EnvConsole::default(); // Create workspace server (local mode) let workspace = workspace::server(); // Create app with console let mut app = App::with_console(&mut console); // Or create with custom filesystem let fs = Box::new(OsFileSystem::default()); let mut app = App::with_filesystem_and_console( pgls_workspace::DynRef::Owned(fs), &mut console ); // Use workspace to open files, run analysis, etc. // This is the main API used by CLI and LSP implementations } ``` -------------------------------- ### Configure Database Connection with Rust Source: https://context7.com/supabase-community/postgres-language-server/llms.txt Demonstrates the `DatabaseConfiguration` structure from `pgls-configuration` for setting up PostgreSQL connection details. It shows initialization using individual fields or a connection string and serialization to JSON. ```rust use pgls_configuration::DatabaseConfiguration; use serde::{Deserialize, Serialize}; fn create_database_config() -> DatabaseConfiguration { // Using individual fields let config = DatabaseConfiguration { connection_string: None, host: "localhost".to_string(), port: 5432, username: "postgres".to_string(), password: "mypassword".to_string(), database: "production".to_string(), allow_statement_executions_against: Default::default(), conn_timeout_secs: 10, disable_connection: false, }; // Or using connection string let config_with_url = DatabaseConfiguration { connection_string: Some( "postgresql://user:pass@localhost:5432/db".to_string() ), ..Default::default() }; // Serialize to JSON for configuration files let json = serde_json::to_string_pretty(&config).unwrap(); println!("{}", json); config } // Example postgrestools.jsonc configuration file: // { // "database": { // "host": "localhost", // "port": 5432, // "username": "postgres", // "password": "postgres", // "database": "myapp", // "connTimeoutSecs": 10 // }, // "lint": { // "rules": { // "recommended": true, // "safety": { // "banDropColumn": "error", // "banDropTable": "error" // } // } // } // } ``` -------------------------------- ### Run LSP Server with Tokio in Rust Source: https://context7.com/supabase-community/postgres-language-server/llms.txt Illustrates how to set up and run the Language Server Protocol (LSP) server using `pgls-lsp` and `tower-lsp` in Rust. The server is configured to run over standard input and output. ```rust use pgls_lsp::{LSPServer, ServerFactory, ServerConnection}; use tower_lsp::LspService; #[tokio::main] async fn main() { // Create LSP service let (service, socket) = LspService::new(|client| { ServerFactory::new(client) }); // Run server on stdio tower_lsp::Server::new(tokio::io::stdin(), tokio::io::stdout(), socket) .serve(service) .await; } ``` -------------------------------- ### Basic CI Linting Workflow (GitHub Actions) Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/guides/continuous_integration.md A simple GitHub Actions workflow to set up the Postgres Language Server CLI and run basic checks on SQL files. It requires the `supabase-community/pglt-cli-action` and checks files in the 'sql/' directory. ```yaml jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: supabase-community/pglt-cli-action@main with: version: latest - run: postgres-language-server check --skip-db sql/ ``` -------------------------------- ### Initialize Postgres Language Server Configuration Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/getting_started.md Initializes a `postgres-language-server.jsonc` configuration file in the project's root directory. This command helps set up default configurations for the language server. ```sh postgres-language-server init ``` -------------------------------- ### Manual Rule Testing with CLI Source: https://github.com/supabase-community/postgres-language-server/blob/main/agentic/port_eugene_rules.md Demonstrates how to manually test a rule by creating a test SQL file and running the CLI command to check it. ```sql -- test.sql ALTER TABLE users ADD COLUMN id serial; ``` ```bash cargo run -p pgls_cli -- check /path/to/test.sql ``` -------------------------------- ### Consolidate Multiple ALTER TABLE Statements (SQL) Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/reference/rules/multiple-alter-table.md This rule flags multiple ALTER TABLE statements affecting the same table, recommending their combination into a single statement to enhance PostgreSQL performance by reducing table scans and lock times. The invalid example shows separate ALTER TABLE commands, while the valid example demonstrates a consolidated statement with comma-separated actions. ```sql ALTER TABLE authors ALTER COLUMN name SET NOT NULL; ALTER TABLE authors ALTER COLUMN email SET NOT NULL; ``` ```sql ALTER TABLE authors ALTER COLUMN name SET NOT NULL, ALTER COLUMN email SET NOT NULL; ``` -------------------------------- ### Build and Verification Commands Source: https://github.com/supabase-community/postgres-language-server/blob/main/agentic/port_eugene_rules.md A sequence of commands to check compilation, generate lint code and documentation, run all tests, and perform a final verification. ```bash # Check compilation cargo check # Generate lint code and documentation just gen-lint # Run all tests cargo test -p pgls_analyser --test rules_tests # Final verification just ready ``` -------------------------------- ### Full Ready Check Source: https://github.com/supabase-community/postgres-language-server/blob/main/AGENTS.md Runs a comprehensive check of the codebase, typically used before committing code to ensure it meets all quality and readiness criteria. ```bash just ready ``` -------------------------------- ### Review Snapshots Source: https://github.com/supabase-community/postgres-language-server/blob/main/AGENTS.md Reviews and updates snapshot tests using the 'insta' crate. This command is used to manage expected test outputs. ```bash cargo insta review ``` -------------------------------- ### Rust Struct Definition for Rule Options Source: https://github.com/supabase-community/postgres-language-server/blob/main/crates/pgls_analyser/CONTRIBUTING.md Example Rust code defining the 'Options' type for a lint rule. This type can be optional and is defined as 'type Options = ()' when not in use. ```rust type Options = (); ``` -------------------------------- ### Run All Tests Source: https://github.com/supabase-community/postgres-language-server/blob/main/AGENTS.md Executes all tests within the project to ensure code quality and functionality. An alternative Cargo command is also provided. ```bash just test # or: cargo test run --no-fail-fast ``` -------------------------------- ### Prevent Dropping NOT NULL Constraint (SQL) Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/reference/rules/ban-drop-not-null.md This SQL example demonstrates an invalid operation where a NOT NULL constraint is dropped from a column. The associated diagnostic message explains the potential risks and suggests alternative approaches. ```sql alter table users alter column email drop not null; ``` -------------------------------- ### Build In-Memory Schema Cache with Rust Source: https://context7.com/supabase-community/postgres-language-server/llms.txt Demonstrates how to build an in-memory schema cache using `pgls-schema-cache` by connecting to a PostgreSQL database and introspecting its schema. It shows how to query tables, columns, and functions. ```rust use pgls_schema_cache::SchemaCache; use sqlx::PgPool; async fn build_schema_cache(database_url: &str) -> Result { // Connect to database let pool = PgPool::connect(database_url).await?; // Build cache by introspecting the database let schema_cache = SchemaCache::load(&pool).await?; // Query tables let tables = schema_cache.find_tables("users", None); for table in tables { println!("Table: {}.{}", table.schema_name, table.name); println!("Kind: {:?}", table.kind); } // Query columns let columns = schema_cache.find_cols("email", Some("users"), Some("public")); for col in columns { println!("Column: {}", col.name); println!("Type: {}", col.data_type); println!("Nullable: {}", col.is_nullable); } // Query functions let functions = schema_cache.find_functions("lower", None); for func in functions { println!("Function: {}", func.name); println!("Returns: {}", func.return_type); println!("Args: {:?}", func.args); } Ok(schema_cache) } #[tokio::main] async fn main() { let cache = build_schema_cache("postgresql://postgres:postgres@localhost:5432/mydb") .await .expect("Failed to build schema cache"); println!("Loaded {} tables", cache.tables.len()); println!("Loaded {} functions", cache.functions.len()); } ``` -------------------------------- ### SQL Snippet with No Diagnostics Expected Source: https://github.com/supabase-community/postgres-language-server/blob/main/crates/pgls_analyser/CONTRIBUTING.md An example of a valid SQL snippet used in documentation, which is not expected to trigger any diagnostics. The `-- expect_no_diagnostics` comment is used to indicate that the rule should not be triggered by this code. ```sql -- Test adding regular column (should be safe) -- expect_no_diagnostics ALTER TABLE prices ADD COLUMN name text; ``` -------------------------------- ### Configuration: Enable 'addingNotNullField' Lint Rule (JSON) Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/reference/rules/adding-not-null-field.md This JSON configuration snippet shows how to enable the 'addingNotNullField' lint rule within a linter setup. Setting the rule to 'error' will flag violations during code analysis. ```json { "linter": { "rules": { "safety": { "addingNotNullField": "error" } } } } ``` -------------------------------- ### Create New Crate Source: https://github.com/supabase-community/postgres-language-server/blob/main/AGENTS.md Generates a new Rust crate within the workspace, identified by its name. ```bash just new-crate ``` -------------------------------- ### Suppress Postgres Linter Diagnostics (SQL) Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/features/linting.md SQL comments are used to ignore specific linter rules for individual lines or blocks of code. This is useful for intentionally violating a rule during maintenance or migrations. Examples show ignoring 'safety/banDropColumn' and 'safety/banDropTable'. ```sql -- pgt-ignore-next-line safety/banDropColumn: Intentionally dropping deprecated column ALTER TABLE users DROP COLUMN deprecated_field; -- pgt-ignore safety/banDropTable: Cleanup during migration DROP TABLE temp_migration_table; ``` -------------------------------- ### Run Migration Linting with Command-Line Arguments (Shell) Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/guides/checking_migrations.md Executes the `check` command with command-line arguments to specify the migrations directory and an 'after' timestamp. This provides an alternative to configuration files for applying specific linting parameters on a per-command basis. ```shell postgres-language-server check supabase/migrations --migrations-dir="supabase/migrations" --after=1740868021 ``` -------------------------------- ### Invalid SQL: Add UNIQUE Constraint Directly Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/reference/rules/disallow-unique-constraint.md Demonstrates SQL statements that directly add a UNIQUE constraint, which is discouraged. This method requires an ACCESS EXCLUSIVE lock, blocking all table operations. The `code-block.sql` examples show how the linter flags these patterns. ```sql ALTER TABLE table_name ADD CONSTRAINT field_name_constraint UNIQUE (field_name); ``` ```sql ALTER TABLE foo ADD COLUMN bar text UNIQUE; ``` -------------------------------- ### SQL Type Checking with Rust and PostgreSQL EXPLAIN Source: https://context7.com/supabase-community/postgres-language-server/llms.txt Validates SQL queries against the actual database schema using PostgreSQL's EXPLAIN command to catch type errors. It requires a database connection pool (`PgPool`), a schema cache, and parsed SQL Abstract Syntax Tree (AST) and tree-sitter parse tree. Dependencies include `pgls-typecheck`, `pgls-schema-cache`, `pgls-query`, `pgls-treesitter`, and `sqlx`. ```rust use pgls_typecheck::{check_sql, TypecheckParams, TypedIdentifier}; use pgls_schema_cache::SchemaCache; use pgls_query::parse; use pgls_treesitter::parse_tree; use sqlx::PgPool; async fn typecheck_query(pool: &PgPool, schema_cache: &SchemaCache) { let sql = "SELECT id, name, invalid_column FROM users WHERE age > 'not a number'"; let ast = parse(sql).expect("Parse failed"); let root = ast.into_root().unwrap(); let tree = parse_tree(sql, None); let params = TypecheckParams { conn: pool, sql, ast: &root, tree: &tree, schema_cache, identifiers: vec![], search_path_patterns: vec!["public".to_string()], }; match check_sql(params).await { Ok(Some(diagnostic)) => { println!("Type Error Found:"); println!("Message: {}", diagnostic.message()); println!("Severity: {:?}", diagnostic.severity()); // Error details from PostgreSQL EXPLAIN } Ok(None) => { println!("No type errors - query is valid!"); } Err(e) => { eprintln!("Typecheck failed: {}", e); } } // Output: // Type Error Found: // Message: column "invalid_column" does not exist // Severity: Error } ``` -------------------------------- ### TypeScript JSON-RPC Client for Workspace Access Source: https://context7.com/supabase-community/postgres-language-server/llms.txt Programmatically access the Postgres Language Server's features, such as analyzing SQL files for diagnostics and getting code completions. This client uses the JSON-RPC protocol and requires the language server backend to be available. ```typescript import { createWorkspace } from '@postgres-language-server/backend-jsonrpc'; async function main() { // Create workspace client connected to daemon const workspace = await createWorkspace(); if (!workspace) { console.error('Failed to create workspace - platform not supported'); return; } // Open a SQL file const fileContent = "\ SELECT * FROM users WHERE id = 1;\n ALTER TABLE products DROP COLUMN price;\n "; // Get diagnostics (lint errors, type errors, etc.) const diagnostics = await workspace.analyze({ path: '/path/to/file.sql', content: fileContent, rules: ['safety/banDropColumn'] }); console.log('Diagnostics:', diagnostics); // Get completions const completions = await workspace.complete({ path: '/path/to/file.sql', content: 'SELECT u. FROM users u', position: { line: 0, character: 9 } }); console.log('Completions:', completions); } main(); ``` -------------------------------- ### SQL Autocompletion Generation with Rust Source: https://context7.com/supabase-community/postgres-language-server/llms.txt Generates context-aware SQL autocompletion suggestions for tables, columns, functions, schemas, and roles. It uses the tree-sitter parser and a schema cache to provide accurate suggestions based on the current SQL context and database schema. Dependencies include `pgls-completions`, `pgls-schema-cache`, `pgls-text-size`, and `pgls-treesitter`. ```rust use pgls_completions::{complete, CompletionParams}; use pgls_schema_cache::SchemaCache; use pgls_text_size::TextSize; use pgls_treesitter::parse_tree; async fn get_completions(schema_cache: &SchemaCache) { let sql = "SELECT u. FROM users u"; let position = TextSize::from(9); // After "u." // Parse with tree-sitter let tree = parse_tree(sql, None); let params = CompletionParams { position, schema: schema_cache, text: sql.to_string(), tree: &tree, }; // Get completion items let items = complete(params); for item in items { println!("Label: {}", item.label); println!("Kind: {:?}", item.kind); if let Some(detail) = item.detail { println!("Detail: {}", detail); } println!("---"); } // Expected output: // Label: id // Kind: Field // Detail: uuid NOT NULL // --- // Label: email // Kind: Field // Detail: text NOT NULL // --- // Label: created_at // Kind: Field // Detail: timestamptz DEFAULT now() } ``` -------------------------------- ### Lint Migrations using CLI (Bash) Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/features/linting.md The Postgres Language Server CLI can be used for linting SQL files, particularly migrations, within a CI/CD pipeline. Commands demonstrate linting all files in a directory, linting with specific rules enabled, and skipping specific rules. ```bash # Lint specific files postgres-language-server check migrations/ # With specific rules postgres-language-server check migrations/ --only safety/banDropColumn # Skip certain rules postgres-language-server check migrations/ --skip safety/banDropTable ``` -------------------------------- ### SQL Snippet with Diagnostic Expectation Source: https://github.com/supabase-community/postgres-language-server/blob/main/crates/pgls_analyser/CONTRIBUTING.md An example of an invalid SQL snippet used in documentation, which is expected to trigger a specific diagnostic. The `expect_diagnostic` property ensures that the documentation generator verifies the rule emits exactly one diagnostic for this code. ```sql -- expect_lint/safety/addSerialColumn -- Test adding serial column to existing table ALTER TABLE prices ADD COLUMN id serial; ``` -------------------------------- ### Rust Accessing File Context for State Tracking Source: https://github.com/supabase-community/postgres-language-server/blob/main/agentic/port_eugene_rules.md Illustrates how to access the file context in Rust to retrieve previous statements, useful for rules that need to track state across multiple statements. ```rust let file_ctx = ctx.file_context(); let previous_stmts = file_ctx.previous_stmts(); ``` -------------------------------- ### Run Migration Linting Command (Shell) Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/guides/checking_migrations.md Executes the `check` command from the `postgres-language-server` to lint a specified migrations directory. This is the basic command to initiate the linting process for all migrations within the given path. ```shell postgres-language-server check supabase/migrations ``` -------------------------------- ### Generate Linter Code and Configuration Source: https://github.com/supabase-community/postgres-language-server/blob/main/AGENTS.md Generates code and configuration files related to the project's linter. This is part of the code generation workflow. ```bash just gen-lint ``` -------------------------------- ### SQL Example: Committing Without Active Transaction Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/reference/rules/transaction-nesting.md Illustrates an invalid SQL scenario where a COMMIT statement is executed without an active transaction. The transactionNesting rule detects this to avoid errors. This is particularly relevant for database migrations where transaction management is often handled automatically. ```sql SELECT 1; COMMIT; -- No transaction to commit ``` -------------------------------- ### Rust Struct for PostgreSQL Lint Rule Source: https://github.com/supabase-community/postgres-language-server/blob/main/agentic/port_eugene_rules.md This Rust code defines a new lint rule for the `postgres-language-server`. It uses the `pgls-analyser` crate to declare a rule with its metadata, severity, sources, and implements the `run` method for rule logic. The example shows how to match on AST nodes and generate diagnostics. ```rust use pgls_analyse::{context::RuleContext, declare_lint_rule, Rule, RuleDiagnostic, RuleSource}; use pgls_console::markup; use pgls_diagnostics::Severity; declare_lint_rule! { /// Brief one-line description (shown in lists). /// /// Detailed explanation of what the rule detects and why it's problematic. /// Explain the PostgreSQL behavior and performance/safety implications. /// /// ## Examples /// /// ### Invalid /// /// ```sql,expect_diagnostic /// -- SQL that should trigger the rule /// ALTER TABLE users ADD COLUMN id serial; /// ``` /// /// ### Valid /// /// ```sql /// -- SQL that should NOT trigger /// CREATE TABLE users (id serial PRIMARY KEY); /// ``` /// pub RuleName { version: "next", name: "ruleName", severity: Severity::Error, // or Warning recommended: true, // or false sources: &[RuleSource::Eugene("")], // e.g., "E11" } } impl Rule for RuleName { type Options = (); fn run(ctx: &RuleContext) -> Vec { let mut diagnostics = Vec::new(); // Pattern match on the statement type if let pgls_query::NodeEnum::AlterTableStmt(stmt) = &ctx.stmt() { // Rule logic here if /* condition */ { diagnostics.push( RuleDiagnostic::new( rule_category!(), None, markup! { "Error message with ""formatting""." }, ) .detail(None, "Additional context about the problem.") .note("Suggested fix or workaround."), ); } } diagnostics } } ``` -------------------------------- ### Generate Rust Tests from SQL Files using tests_macros Source: https://github.com/supabase-community/postgres-language-server/blob/main/crates/pgls_test_macros/README.md Demonstrates how to use the `gen_tests!` macro to define test generation. It accepts a glob pattern for SQL files and a callback function to run each test. An optional `.expected.sql` file can be provided for assertions. The macro generates individual `#[test]` functions for each matched file. ```rust // crate/tests/querytest.rs tests_macros::gen_tests!{ "tests/queries/*.sql", crate::run_test // use `crate::` if the linter complains. } fn run_test( test_path: &str, // absolute path on the machine expected_path: &str, // absolute path of .expected file test_dir: &str // absolute path of the test file's parent ) { // your logic } ``` ```rust #[test] pub fn some_test_abc() { let test_file = "/tests/queries/some_test_abc.sql"; let test_expected_file = "/tests/queries/some_test_abc.expected.sql"; let parent = "/tests/queries"; run_test(test_file, test_expected_file, parent); } ``` -------------------------------- ### SQL: Add Column with Default (Invalid) Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/reference/rules/adding-field-with-default.md This example demonstrates an invalid SQL statement where a column is added with a default value. This is flagged by the 'addingFieldWithDefault' rule because it can cause a table rewrite and an ACCESS EXCLUSIVE lock, blocking all table operations. This behavior is particularly problematic in PostgreSQL versions prior to 11. ```sql ALTER TABLE "core_recipe" ADD COLUMN "foo" integer DEFAULT 10; ``` -------------------------------- ### Lint Entire Codebase Source: https://github.com/supabase-community/postgres-language-server/blob/main/AGENTS.md Performs linting checks on the entire codebase to identify potential issues and enforce coding standards. Alternative commands using Cargo and Bun are also shown. ```bash just lint # or: cargo clippy && cargo run -p rules_check && bun biome lint ``` -------------------------------- ### Valid: Adding NOT NULL Constraint Safely (SQL) Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/reference/rules/adding-not-null-field.md This SQL example shows a safer method for adding a NOT NULL constraint by first creating a CHECK constraint as NOT VALID and then validating it in a separate transaction. This approach avoids blocking reads and writes, mitigating the issue addressed by the 'addingNotNullField' lint rule. ```sql -- First add a CHECK constraint as NOT VALID ALTER TABLE "core_recipe" ADD CONSTRAINT foo_not_null CHECK (foo IS NOT NULL) NOT VALID; -- Then validate it in a separate transaction ALTER TABLE "core_recipe" VALIDATE CONSTRAINT foo_not_null; ``` -------------------------------- ### Invalid SQL: Running statements with ACCESS EXCLUSIVE lock Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/reference/rules/running-statement-while-holding-access-exclusive.md This example demonstrates an invalid SQL pattern where an `ALTER TABLE` statement, which acquires an `ACCESS EXCLUSIVE` lock, is followed by another statement (`SELECT`) within the same transaction. This extends the lock duration, blocking all table access. The rule flags this as an error. ```sql ALTER TABLE authors ADD COLUMN email TEXT; SELECT COUNT(*) FROM authors; ``` -------------------------------- ### Check SQL File with CLI Source: https://github.com/supabase-community/postgres-language-server/blob/main/AGENTS.md Uses the project's command-line interface to check a given SQL file for syntax errors and other issues. ```bash cargo run -p pgls_cli -- check file.sql # or after building: ./target/release/postgres-language-server check file.sql ``` -------------------------------- ### SQL: Avoid Blocking Table Operations with DROP INDEX CONCURRENTLY Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/reference/rules/require-concurrent-index-deletion.md This snippet demonstrates how to correctly drop an index without blocking concurrent read and write operations on the table. Using `DROP INDEX CONCURRENTLY` is crucial for production systems to prevent downtime. The invalid example shows the problematic syntax. ```sql DROP INDEX IF EXISTS users_email_idx; ``` ```sql DROP INDEX CONCURRENTLY IF EXISTS users_email_idx; ``` -------------------------------- ### Use VCS Ignore File - JSON Config Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/guides/vcs_integration.md Configures the Postgres Language Server to respect VCS ignore files (like .gitignore) and a project-level .ignore file. By enabling 'vcs.useIgnoreFile', the server will exclude files and directories listed in these ignore files from analysis, streamlining checks. ```json { "vcs": { "enabled": true, "clientKind": "git", "useIgnoreFile": true } } ``` -------------------------------- ### Ban Concurrent Index Creation in Transaction (SQL) Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/reference/rules/ban-concurrent-index-creation-in-transaction.md This rule prohibits the use of `CREATE INDEX CONCURRENTLY` within a transaction block, as it will result in an error in PostgreSQL. Migration tools typically execute migrations within transactions, making this syntax incompatible. The example demonstrates the invalid SQL syntax. ```sql CREATE INDEX CONCURRENTLY "field_name_idx" ON "table_name" ("field_name"); ``` -------------------------------- ### Valid SQL: Separating statements with ACCESS EXCLUSIVE lock Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/reference/rules/running-statement-while-holding-access-exclusive.md This example shows the valid SQL pattern for managing `ACCESS EXCLUSIVE` locks. The `ALTER TABLE` statement, which acquires the lock, is executed in its own transaction. Subsequent operations, like the commented-out placeholder for other queries, would be run in separate transactions, minimizing the lock duration and impact on table access. ```sql -- Run ALTER TABLE alone, other queries in separate transactions ALTER TABLE authors ADD COLUMN email TEXT; ``` -------------------------------- ### Identify Risky Foreign Key Constraint Addition (SQL) Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/reference/rules/adding-foreign-key-constraint.md Highlights an example of adding a foreign key constraint directly, which is flagged by the linter. This code snippet illustrates the problematic pattern that can lead to table locks and downtime because PostgreSQL needs to scan tables and acquire exclusive locks to verify existing data against the new constraint. ```sql ALTER TABLE "email" ADD CONSTRAINT "fk_user" FOREIGN KEY ("user_id") REFERENCES "user" ("id"); ``` -------------------------------- ### Enable VCS Integration - JSON Config Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/guides/vcs_integration.md Enables the Version Control System (VCS) integration for the Postgres Language Server. This requires setting 'vcs.enabled' to true and specifying the 'vcs.clientKind' (e.g., 'git') in the configuration file. This is the foundational step for all VCS-related features. ```json { "vcs": { "enabled": true, "clientKind": "git" } } ``` -------------------------------- ### Identify Risky Column Addition with Foreign Key (SQL) Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/reference/rules/adding-foreign-key-constraint.md Shows an example of adding a column with an inline foreign key constraint, which is also flagged by the linter. Similar to directly adding a constraint, this approach can cause downtime by requiring a table scan and locks to verify data. The recommended alternative involves adding the column first, then the constraint separately using the 'NOT VALID' method. ```sql ALTER TABLE "emails" ADD COLUMN "user_id" INT REFERENCES "user" ("id"); ``` -------------------------------- ### Create New Lint Rule Source: https://github.com/supabase-community/postgres-language-server/blob/main/AGENTS.md Scaffolds a new lint rule, requiring the group, rule name, and an optional severity level as arguments. ```bash just new-lintrule [severity] ``` -------------------------------- ### Implementing Rule Options in Rust Source: https://github.com/supabase-community/postgres-language-server/blob/main/crates/pgls_analyser/CONTRIBUTING.md Associates the `MyRuleOptions` struct with a specific Rust `Rule` implementation by setting the `Options` associated type. Also shows how to retrieve the rule's options within the context (`ctx`). ```rust impl Rule for MyRule { type Options = MyRuleOptions; } let options = ctx.options(); ``` -------------------------------- ### SQL Object Hover Information with Rust Source: https://context7.com/supabase-community/postgres-language-server/llms.txt Provides markdown-formatted hover information for database objects like tables, columns, and functions. It takes the current SQL statement, the cursor position, and a schema cache as input. Dependencies include `pgls-hover`, `pgls-schema-cache`, `pgls-text-size`, `pgls-treesitter`, and `pgls-query`. ```rust use pgls_hover::{on_hover, OnHoverParams}; use pgls_schema_cache::SchemaCache; use pgls_text_size::TextSize; use pgls_treesitter::parse_tree; use pgls_query::parse; fn get_hover_info(schema_cache: &SchemaCache) { let sql = "SELECT email FROM users"; let position = TextSize::from(7); // Hovering over "email" let ast = parse(sql).ok().and_then(|p| p.into_root().ok()); let tree = parse_tree(sql, None); let params = OnHoverParams { position, schema_cache, stmt_sql: sql, ast: ast.as_ref(), ts_tree: &tree, }; let hover_results = on_hover(params); for markdown in hover_results { println!("{}", markdown); } // Expected output: // ### Column: `email` // **Table:** `public.users` // **Type:** `text NOT NULL` // **Default:** None // // Email address of the user } ``` -------------------------------- ### Rust Clippy Linting Source: https://github.com/supabase-community/postgres-language-server/blob/main/AGENTS.md Runs the Clippy linter on all targets and features to enforce Rust best practices. This command is crucial for maintaining code quality. ```bash cargo clippy --all-targets --all-features ``` -------------------------------- ### Run All Tests for pgls_analyser Crate Source: https://github.com/supabase-community/postgres-language-server/blob/main/agentic/port_eugene_rules.md This command executes all tests within the pgls_analyser crate, ensuring the correctness of the linter rules. It's a crucial step for verifying changes and before merging new code. ```bash cargo test -p pgls_analyser --test rules_tests ``` -------------------------------- ### Cargo Command for Running Tests and Accepting Snapshots Source: https://github.com/supabase-community/postgres-language-server/blob/main/agentic/port_eugene_rules.md Executes tests for the 'pgls_analyser' crate and accepts snapshot updates. Use '--accept' to update expected test output. ```bash cargo insta test -p pgls_analyser --accept ``` -------------------------------- ### SQL: Ensure Robust Concurrent Index Creation Source: https://github.com/supabase-community/postgres-language-server/blob/main/docs/reference/rules/prefer-robust-stmts.md This snippet demonstrates the incorrect and correct SQL syntax for creating indexes concurrently. It highlights the importance of using `IF NOT EXISTS` to make the migration re-runnable if it fails mid-execution. This is crucial for migrations run outside of transactions. ```sql CREATE INDEX CONCURRENTLY users_email_idx ON users (email); ``` ```sql CREATE INDEX CONCURRENTLY IF NOT EXISTS users_email_idx ON users (email); ``` -------------------------------- ### Deriving Serde Traits for Rule Options Source: https://github.com/supabase-community/postgres-language-server/blob/main/crates/pgls_analyser/CONTRIBUTING.md Demonstrates how to use Serde's derive macros (`Serialize`, `Deserialize`) to automatically implement serialization and deserialization for rule options. Includes `rename_all = "camelCase"` for JSON field naming, `deny_unknown_fields` to reject extraneous fields, and `default` to use default values for missing fields. Also shows `skip_serializing_if = "is_default"` for optional fields. ```rust #[derive(Debug, Default, Clone, Serialize, Deserialize)] #[cfg_attr(feature = "schemars", derive(JsonSchema))] #[serde(rename_all = "camelCase", deny_unknown_fields, default)] pub struct MyRuleOptions { #[serde(default, skip_serializing_if = "is_default")] main_behavior: Behavior, #[serde(default, skip_serializing_if = "is_default")] extra_behaviors: Vec, } #[derive(Debug, Default, Clone)] #[cfg_attr(feature = "schemars", derive(JsonSchema))] pub enum Behavior { #[default] A, B, C, } ``` -------------------------------- ### Analyser for SQL Safety and Migration Linting in Rust Source: https://context7.com/supabase-community/postgres-language-server/llms.txt Analyzes SQL statements against configurable safety rules inspired by Squawk for production database migrations. It uses `pgls_analyser`, `pgls_analyse`, and `pgls_query` crates to parse SQL, configure rules, and run analysis, returning diagnostics. ```rust use pgls_analyser::{Analyser, AnalyserConfig, AnalysableStatement, AnalyserParams}; use pgls_analyse::{AnalyserOptions, AnalysisFilter, RuleFilter}; use pgls_query::parse; use pgls_text_size::TextRange; fn main() { let sql = "ALTER TABLE products DROP COLUMN description;"; // Parse the SQL let ast = parse(sql).expect("Failed to parse SQL"); let root = ast.into_root().unwrap(); // Configure which rules to run let rule_filter = RuleFilter::Rule("safety", "banDropColumn"); let filter = AnalysisFilter { enabled_rules: Some(&[rule_filter]), ..Default::default() }; let options = AnalyserOptions::default(); // Create analyser with configuration let analyser = Analyser::new(AnalyserConfig { options: &options, filter, }); // Run analysis let diagnostics = analyser.run(AnalyserParams { stmts: vec![AnalysableStatement { root, range: TextRange::new(0.into(), sql.len().try_into().unwrap()), }], schema_cache: None, }); // Process diagnostics for diagnostic in diagnostics { println!("Rule: {}", diagnostic.rule_name()); println!("Message: {}", diagnostic.message()); println!("Severity: {:?}", diagnostic.severity()); } // Output: // Rule: safety/banDropColumn // Message: Dropping columns can break application code // Severity: Error ```