From f61317697cf7857ad2d5163c66893beed5039105 Mon Sep 17 00:00:00 2001 From: psteinroe Date: Tue, 15 Jul 2025 10:13:56 +0200 Subject: [PATCH 01/29] chore: temp publish --- .github/workflows/publish.reusable.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/publish.reusable.yml b/.github/workflows/publish.reusable.yml index 31e625d7..d2650f33 100644 --- a/.github/workflows/publish.reusable.yml +++ b/.github/workflows/publish.reusable.yml @@ -56,6 +56,18 @@ jobs: env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} # + - name: Temp publish + if: inputs.is-prerelease != 'true' + run: | + npm publish "@postgrestools/cli-aarch64-apple-darwin" --tag latest --access public --provenance + npm publish "@postgrestools/cli-aarch64-windows-msvc" --tag latest --access public --provenance + npm publish "@postgrestools/cli-aarch64-linux-gnu" --tag latest --access public --provenance + npm publish "@postgrestools/cli-x86_64-apple-darwin" --tag latest --access public --provenance + npm publish "@postgrestools/cli-x86_64-windows-msvc" --tag latest --access public --provenance + npm publish "@postgrestools/cli-x86_64-linux-gnu" --tag latest --access public --provenance + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + - name: Publish npm packages as latest if: inputs.is-prerelease != 'true' run: | From 9f8939f16f96c0173ac91add4e43e81027d6b588 Mon Sep 17 00:00:00 2001 From: psteinroe Date: Tue, 15 Jul 2025 10:29:12 +0200 Subject: [PATCH 02/29] chore: temp publish --- .github/workflows/publish.reusable.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/publish.reusable.yml b/.github/workflows/publish.reusable.yml index d2650f33..690c5aa0 100644 --- a/.github/workflows/publish.reusable.yml +++ b/.github/workflows/publish.reusable.yml @@ -47,6 +47,10 @@ jobs: run: | cat packages/@postgrestools/postgrestools/package.json + - name: Print package.json + run: | + cat packages/@postgrestools/cli-aarch64-apple-darwin/package.json + - name: Publish npm packages as nightly if: inputs.is-prerelease == 'true' run: | From 9eb2aac21b4e4ff2a21ed0de9656197e49587714 Mon Sep 17 00:00:00 2001 From: psteinroe Date: Tue, 15 Jul 2025 11:13:52 +0200 Subject: [PATCH 03/29] chore: temp publish --- .github/workflows/publish.reusable.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/publish.reusable.yml b/.github/workflows/publish.reusable.yml index 690c5aa0..ead8a1b8 100644 --- a/.github/workflows/publish.reusable.yml +++ b/.github/workflows/publish.reusable.yml @@ -49,7 +49,7 @@ jobs: - name: Print package.json run: | - cat packages/@postgrestools/cli-aarch64-apple-darwin/package.json + cat packages/@postgrestools/postgrestools_aarch64-apple-darwin/package.json - name: Publish npm packages as nightly if: inputs.is-prerelease == 'true' From 8d776e9b6a60b7c6ca5bc14b814352ee90086942 Mon Sep 17 00:00:00 2001 From: Julian Domke <68325451+juleswritescode@users.noreply.github.com> Date: Tue, 15 Jul 2025 11:45:13 +0200 Subject: [PATCH 04/29] chore: temp publish (#457) --- .github/workflows/publish.reusable.yml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/publish.reusable.yml b/.github/workflows/publish.reusable.yml index ead8a1b8..0d9095ed 100644 --- a/.github/workflows/publish.reusable.yml +++ b/.github/workflows/publish.reusable.yml @@ -60,15 +60,21 @@ jobs: env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} # + - name: Temp LS packages + run: | + cd packages + ls @postgrestools/ + - name: Temp publish if: inputs.is-prerelease != 'true' run: | - npm publish "@postgrestools/cli-aarch64-apple-darwin" --tag latest --access public --provenance - npm publish "@postgrestools/cli-aarch64-windows-msvc" --tag latest --access public --provenance - npm publish "@postgrestools/cli-aarch64-linux-gnu" --tag latest --access public --provenance - npm publish "@postgrestools/cli-x86_64-apple-darwin" --tag latest --access public --provenance - npm publish "@postgrestools/cli-x86_64-windows-msvc" --tag latest --access public --provenance - npm publish "@postgrestools/cli-x86_64-linux-gnu" --tag latest --access public --provenance + cd packages + npm publish "@postgrestools/postgrestools_aarch64-apple-darwin" --tag latest --access public --provenance + npm publish "@postgrestools/postgrestools_aarch64-windows-msvc" --tag latest --access public --provenance + npm publish "@postgrestools/postgrestools_aarch64-linux-gnu" --tag latest --access public --provenance + npm publish "@postgrestools/postgrestools_x86_64-apple-darwin" --tag latest --access public --provenance + npm publish "@postgrestools/postgrestools_x86_64-windows-msvc" --tag latest --access public --provenance + npm publish "@postgrestools/postgrestools_x86_64-linux-gnu" --tag latest --access public --provenance env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} From 5254806d924b42c1cc883fe2f3c1894797585b37 Mon Sep 17 00:00:00 2001 From: psteinroe Date: Tue, 15 Jul 2025 11:50:07 +0200 Subject: [PATCH 05/29] chore: temp publish --- .../@postgrestools/postgrestools/scripts/generate-packages.mjs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/@postgrestools/postgrestools/scripts/generate-packages.mjs b/packages/@postgrestools/postgrestools/scripts/generate-packages.mjs index 34193a92..8d49bd25 100644 --- a/packages/@postgrestools/postgrestools/scripts/generate-packages.mjs +++ b/packages/@postgrestools/postgrestools/scripts/generate-packages.mjs @@ -163,7 +163,7 @@ function copyBinaryToNativePackage(platform, arch, os) { const ext = getBinaryExt(os); const manifestPath = resolve(packageRoot, "package.json"); - console.info(`Update manifest ${manifestPath}`); + console.info(`Update manifest ${manifestPath} to ${JSON.stringify(manifest)}`); fs.writeFileSync(manifestPath, manifest); // Copy the CLI binary From a6d5d13def1103cf94c4b15a09e44df2816f163c Mon Sep 17 00:00:00 2001 From: psteinroe Date: Tue, 15 Jul 2025 11:50:34 +0200 Subject: [PATCH 06/29] chore: temp publish --- .github/workflows/publish.reusable.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/publish.reusable.yml b/.github/workflows/publish.reusable.yml index 0d9095ed..31b00f74 100644 --- a/.github/workflows/publish.reusable.yml +++ b/.github/workflows/publish.reusable.yml @@ -68,7 +68,6 @@ jobs: - name: Temp publish if: inputs.is-prerelease != 'true' run: | - cd packages npm publish "@postgrestools/postgrestools_aarch64-apple-darwin" --tag latest --access public --provenance npm publish "@postgrestools/postgrestools_aarch64-windows-msvc" --tag latest --access public --provenance npm publish "@postgrestools/postgrestools_aarch64-linux-gnu" --tag latest --access public --provenance From 0dc25fdeeed2cab6b37127a9c1770294dbf5a713 Mon Sep 17 00:00:00 2001 From: psteinroe Date: Tue, 15 Jul 2025 11:59:36 +0200 Subject: [PATCH 07/29] chore: temp publish --- .github/workflows/publish.reusable.yml | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/.github/workflows/publish.reusable.yml b/.github/workflows/publish.reusable.yml index 31b00f74..8ab4a298 100644 --- a/.github/workflows/publish.reusable.yml +++ b/.github/workflows/publish.reusable.yml @@ -60,28 +60,13 @@ jobs: env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} # - - name: Temp LS packages - run: | - cd packages - ls @postgrestools/ - - - name: Temp publish - if: inputs.is-prerelease != 'true' - run: | - npm publish "@postgrestools/postgrestools_aarch64-apple-darwin" --tag latest --access public --provenance - npm publish "@postgrestools/postgrestools_aarch64-windows-msvc" --tag latest --access public --provenance - npm publish "@postgrestools/postgrestools_aarch64-linux-gnu" --tag latest --access public --provenance - npm publish "@postgrestools/postgrestools_x86_64-apple-darwin" --tag latest --access public --provenance - npm publish "@postgrestools/postgrestools_x86_64-windows-msvc" --tag latest --access public --provenance - npm publish "@postgrestools/postgrestools_x86_64-linux-gnu" --tag latest --access public --provenance - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - - name: Publish npm packages as latest if: inputs.is-prerelease != 'true' run: | for package in packages/@postgrestools/*; do - npm publish "$package" --tag latest --access public --provenance + if [[ "$package_name" != "backend-jsonrpc" && "$package_name" != "postgrestools" ]]; then + npm publish "$package" --tag latest --access public --provenance + fi done env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} From 3263f34318ea5c06bbe15591a039e1b4aca8ca52 Mon Sep 17 00:00:00 2001 From: psteinroe Date: Tue, 15 Jul 2025 12:02:32 +0200 Subject: [PATCH 08/29] chore: temp publish --- .github/workflows/publish.reusable.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/publish.reusable.yml b/.github/workflows/publish.reusable.yml index 8ab4a298..ce41abf9 100644 --- a/.github/workflows/publish.reusable.yml +++ b/.github/workflows/publish.reusable.yml @@ -64,6 +64,7 @@ jobs: if: inputs.is-prerelease != 'true' run: | for package in packages/@postgrestools/*; do + package_name=$(basename "$package") if [[ "$package_name" != "backend-jsonrpc" && "$package_name" != "postgrestools" ]]; then npm publish "$package" --tag latest --access public --provenance fi From 5dd0981e628b438da45d15b959936aa62d367e27 Mon Sep 17 00:00:00 2001 From: psteinroe Date: Tue, 15 Jul 2025 12:04:32 +0200 Subject: [PATCH 09/29] chore: cleanup --- .github/workflows/publish.reusable.yml | 22 +------------------ .../scripts/generate-packages.mjs | 2 +- 2 files changed, 2 insertions(+), 22 deletions(-) diff --git a/.github/workflows/publish.reusable.yml b/.github/workflows/publish.reusable.yml index ce41abf9..5be2e12f 100644 --- a/.github/workflows/publish.reusable.yml +++ b/.github/workflows/publish.reusable.yml @@ -34,23 +34,6 @@ jobs: RELEASE_TAG: ${{ inputs.release-tag }} PRERELEASE: ${{ inputs.is-prerelease }} - - name: Verify NPM TOKEN exists - run: | - if [ -z "${{ secrets.NPM_TOKEN }}" ]; then - echo "Secret is not defined" - exit 1 - else - echo "Secret is defined" - fi - - - name: Print package.json - run: | - cat packages/@postgrestools/postgrestools/package.json - - - name: Print package.json - run: | - cat packages/@postgrestools/postgrestools_aarch64-apple-darwin/package.json - - name: Publish npm packages as nightly if: inputs.is-prerelease == 'true' run: | @@ -64,10 +47,7 @@ jobs: if: inputs.is-prerelease != 'true' run: | for package in packages/@postgrestools/*; do - package_name=$(basename "$package") - if [[ "$package_name" != "backend-jsonrpc" && "$package_name" != "postgrestools" ]]; then - npm publish "$package" --tag latest --access public --provenance - fi + npm publish "$package" --tag latest --access public --provenance done env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} diff --git a/packages/@postgrestools/postgrestools/scripts/generate-packages.mjs b/packages/@postgrestools/postgrestools/scripts/generate-packages.mjs index 8d49bd25..34193a92 100644 --- a/packages/@postgrestools/postgrestools/scripts/generate-packages.mjs +++ b/packages/@postgrestools/postgrestools/scripts/generate-packages.mjs @@ -163,7 +163,7 @@ function copyBinaryToNativePackage(platform, arch, os) { const ext = getBinaryExt(os); const manifestPath = resolve(packageRoot, "package.json"); - console.info(`Update manifest ${manifestPath} to ${JSON.stringify(manifest)}`); + console.info(`Update manifest ${manifestPath}`); fs.writeFileSync(manifestPath, manifest); // Copy the CLI binary From c323eb03d8c444bcdb08adf96455d432b849f2fc Mon Sep 17 00:00:00 2001 From: Julian Domke <68325451+juleswritescode@users.noreply.github.com> Date: Tue, 15 Jul 2025 12:22:55 +0200 Subject: [PATCH 10/29] chore: expose schema_cache & file_context in lint rules (#449) --- Cargo.lock | 4 + crates/pgt_analyse/Cargo.toml | 9 +- .../pgt_analyse/src/analysed_file_context.rs | 7 ++ crates/pgt_analyse/src/context.rs | 27 ++++- crates/pgt_analyse/src/lib.rs | 2 + crates/pgt_analyse/src/registry.rs | 12 ++- crates/pgt_analyse/src/rule.rs | 9 +- crates/pgt_analyser/Cargo.toml | 12 ++- crates/pgt_analyser/src/lib.rs | 58 ++++++++--- crates/pgt_analyser/tests/rules_tests.rs | 12 ++- crates/pgt_lsp/tests/server.rs | 18 +++- crates/pgt_query_ext/src/diagnostics.rs | 7 +- crates/pgt_workspace/Cargo.toml | 4 +- crates/pgt_workspace/src/workspace/server.rs | 96 +++++++++--------- .../src/workspace/server.tests.rs | 99 +++++++++++++++++++ .../src/workspace/server/document.rs | 52 +++++----- docs/codegen/src/rules_docs.rs | 14 ++- docs/rules/ban-drop-column.md | 6 +- docs/rules/ban-drop-not-null.md | 6 +- docs/rules/ban-drop-table.md | 6 +- .../backend-jsonrpc/src/workspace.ts | 9 +- .../codegen/src/generate_new_analyser_rule.rs | 9 +- xtask/rules_check/src/lib.rs | 14 ++- 23 files changed, 365 insertions(+), 127 deletions(-) create mode 100644 crates/pgt_analyse/src/analysed_file_context.rs create mode 100644 crates/pgt_workspace/src/workspace/server.tests.rs diff --git a/Cargo.lock b/Cargo.lock index 4da985a3..16b1de5e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2693,6 +2693,7 @@ dependencies = [ "pgt_console", "pgt_diagnostics", "pgt_query_ext", + "pgt_schema_cache", "pgt_text_size", "rustc-hash 2.1.0", "schemars", @@ -2708,7 +2709,9 @@ dependencies = [ "pgt_console", "pgt_diagnostics", "pgt_query_ext", + "pgt_schema_cache", "pgt_test_macros", + "pgt_text_size", "serde", "termcolor", ] @@ -3102,6 +3105,7 @@ dependencies = [ "pgt_schema_cache", "pgt_statement_splitter", "pgt_suppressions", + "pgt_test_utils", "pgt_text_size", "pgt_typecheck", "rustc-hash 2.1.0", diff --git a/crates/pgt_analyse/Cargo.toml b/crates/pgt_analyse/Cargo.toml index 75eb0211..4d30784c 100644 --- a/crates/pgt_analyse/Cargo.toml +++ b/crates/pgt_analyse/Cargo.toml @@ -13,10 +13,11 @@ version = "0.0.0" [dependencies] -pgt_console.workspace = true -pgt_diagnostics.workspace = true -pgt_query_ext.workspace = true -rustc-hash = { workspace = true } +pgt_console.workspace = true +pgt_diagnostics.workspace = true +pgt_query_ext.workspace = true +pgt_schema_cache.workspace = true +rustc-hash = { workspace = true } biome_deserialize = { workspace = true, optional = true } biome_deserialize_macros = { workspace = true, optional = true } diff --git a/crates/pgt_analyse/src/analysed_file_context.rs b/crates/pgt_analyse/src/analysed_file_context.rs new file mode 100644 index 00000000..cba53eeb --- /dev/null +++ b/crates/pgt_analyse/src/analysed_file_context.rs @@ -0,0 +1,7 @@ +#[derive(Default)] +pub struct AnalysedFileContext {} + +impl AnalysedFileContext { + #[allow(unused)] + pub fn update_from(&mut self, stmt_root: &pgt_query_ext::NodeEnum) {} +} diff --git a/crates/pgt_analyse/src/context.rs b/crates/pgt_analyse/src/context.rs index cd069657..7447c0bb 100644 --- a/crates/pgt_analyse/src/context.rs +++ b/crates/pgt_analyse/src/context.rs @@ -1,4 +1,7 @@ +use pgt_schema_cache::SchemaCache; + use crate::{ + AnalysedFileContext, categories::RuleCategory, rule::{GroupCategory, Rule, RuleGroup, RuleMetadata}, }; @@ -6,6 +9,8 @@ use crate::{ pub struct RuleContext<'a, R: Rule> { stmt: &'a pgt_query_ext::NodeEnum, options: &'a R::Options, + schema_cache: Option<&'a SchemaCache>, + file_context: &'a AnalysedFileContext, } impl<'a, R> RuleContext<'a, R> @@ -13,8 +18,18 @@ where R: Rule + Sized + 'static, { #[allow(clippy::too_many_arguments)] - pub fn new(stmt: &'a pgt_query_ext::NodeEnum, options: &'a R::Options) -> Self { - Self { stmt, options } + pub fn new( + stmt: &'a pgt_query_ext::NodeEnum, + options: &'a R::Options, + schema_cache: Option<&'a SchemaCache>, + file_context: &'a AnalysedFileContext, + ) -> Self { + Self { + stmt, + options, + schema_cache, + file_context, + } } /// Returns the group that belongs to the current rule @@ -32,6 +47,14 @@ where self.stmt } + pub fn file_context(&self) -> &AnalysedFileContext { + self.file_context + } + + pub fn schema_cache(&self) -> Option<&SchemaCache> { + self.schema_cache + } + /// Returns the metadata of the rule /// /// The metadata contains information about the rule, such as the name, version, language, and whether it is recommended. diff --git a/crates/pgt_analyse/src/lib.rs b/crates/pgt_analyse/src/lib.rs index f312de45..1d4ec6ae 100644 --- a/crates/pgt_analyse/src/lib.rs +++ b/crates/pgt_analyse/src/lib.rs @@ -1,3 +1,4 @@ +mod analysed_file_context; mod categories; pub mod context; mod filter; @@ -9,6 +10,7 @@ mod rule; // Re-exported for use in the `declare_group` macro pub use pgt_diagnostics::category_concat; +pub use crate::analysed_file_context::AnalysedFileContext; pub use crate::categories::{ ActionCategory, RefactorKind, RuleCategories, RuleCategoriesBuilder, RuleCategory, SUPPRESSION_ACTION_CATEGORY, SourceActionKind, diff --git a/crates/pgt_analyse/src/registry.rs b/crates/pgt_analyse/src/registry.rs index 48b73b15..d43d7711 100644 --- a/crates/pgt_analyse/src/registry.rs +++ b/crates/pgt_analyse/src/registry.rs @@ -2,6 +2,7 @@ use std::{borrow, collections::BTreeSet}; use crate::{ AnalyserOptions, + analysed_file_context::AnalysedFileContext, context::RuleContext, filter::{AnalysisFilter, GroupKey, RuleKey}, rule::{GroupCategory, Rule, RuleDiagnostic, RuleGroup}, @@ -158,6 +159,8 @@ impl RuleRegistry { pub struct RegistryRuleParams<'a> { pub root: &'a pgt_query_ext::NodeEnum, pub options: &'a AnalyserOptions, + pub analysed_file_context: &'a AnalysedFileContext, + pub schema_cache: Option<&'a pgt_schema_cache::SchemaCache>, } /// Executor for rule as a generic function pointer @@ -174,7 +177,14 @@ impl RegistryRule { R: Rule + 'static, { let options = params.options.rule_options::().unwrap_or_default(); - let ctx = RuleContext::new(params.root, &options); + + let ctx = RuleContext::new( + params.root, + &options, + params.schema_cache, + params.analysed_file_context, + ); + R::run(&ctx) } diff --git a/crates/pgt_analyse/src/rule.rs b/crates/pgt_analyse/src/rule.rs index fae3cda3..1760ce97 100644 --- a/crates/pgt_analyse/src/rule.rs +++ b/crates/pgt_analyse/src/rule.rs @@ -102,7 +102,8 @@ pub trait GroupCategory { pub trait Rule: RuleMeta + Sized { type Options: Default + Clone + Debug; - fn run(ctx: &RuleContext) -> Vec; + /// `schema_cache` will only be available if the user has a working database connection. + fn run(rule_context: &RuleContext) -> Vec; } /// Diagnostic object returned by a single analysis rule @@ -208,6 +209,12 @@ impl RuleDiagnostic { self } + /// Sets the span of this diagnostic. + pub fn span(mut self, span: TextRange) -> Self { + self.span = Some(span); + self + } + /// Marks this diagnostic as unnecessary code, which will /// be displayed in the language server. /// diff --git a/crates/pgt_analyser/Cargo.toml b/crates/pgt_analyser/Cargo.toml index e77aaa4f..5f65b978 100644 --- a/crates/pgt_analyser/Cargo.toml +++ b/crates/pgt_analyser/Cargo.toml @@ -12,11 +12,13 @@ repository.workspace = true version = "0.0.0" [dependencies] -pgt_analyse = { workspace = true } -pgt_console = { workspace = true } -pgt_diagnostics = { workspace = true } -pgt_query_ext = { workspace = true } -serde = { workspace = true } +pgt_analyse = { workspace = true } +pgt_console = { workspace = true } +pgt_diagnostics = { workspace = true } +pgt_query_ext = { workspace = true } +pgt_schema_cache = { workspace = true } +pgt_text_size = { workspace = true } +serde = { workspace = true } [dev-dependencies] insta = { version = "1.42.1" } diff --git a/crates/pgt_analyser/src/lib.rs b/crates/pgt_analyser/src/lib.rs index 248fe22b..f96b6f6d 100644 --- a/crates/pgt_analyser/src/lib.rs +++ b/crates/pgt_analyser/src/lib.rs @@ -1,8 +1,8 @@ use std::{ops::Deref, sync::LazyLock}; use pgt_analyse::{ - AnalyserOptions, AnalysisFilter, MetadataRegistry, RegistryRuleParams, RuleDiagnostic, - RuleRegistry, + AnalysedFileContext, AnalyserOptions, AnalysisFilter, MetadataRegistry, RegistryRuleParams, + RuleDiagnostic, RuleRegistry, }; pub use registry::visit_registry; @@ -30,8 +30,15 @@ pub struct Analyser<'a> { registry: RuleRegistry, } -pub struct AnalyserContext<'a> { - pub root: &'a pgt_query_ext::NodeEnum, +#[derive(Debug)] +pub struct AnalysableStatement { + pub root: pgt_query_ext::NodeEnum, + pub range: pgt_text_size::TextRange, +} + +pub struct AnalyserParams<'a> { + pub stmts: Vec, + pub schema_cache: Option<&'a pgt_schema_cache::SchemaCache>, } pub struct AnalyserConfig<'a> { @@ -52,17 +59,31 @@ impl<'a> Analyser<'a> { } } - pub fn run(&self, ctx: AnalyserContext) -> Vec { - let params = RegistryRuleParams { - root: ctx.root, - options: self.options, - }; + pub fn run(&self, params: AnalyserParams) -> Vec { + let mut diagnostics = vec![]; + + let mut file_context = AnalysedFileContext::default(); + + for stmt in params.stmts { + let rule_params = RegistryRuleParams { + root: &stmt.root, + options: self.options, + analysed_file_context: &file_context, + schema_cache: params.schema_cache, + }; - self.registry - .rules - .iter() - .flat_map(|rule| (rule.run)(¶ms)) - .collect::>() + diagnostics.extend( + self.registry + .rules + .iter() + .flat_map(|rule| (rule.run)(&rule_params)) + .map(|r| r.span(stmt.range)), + ); + + file_context.update_from(&stmt.root); + } + + diagnostics } } @@ -77,9 +98,10 @@ mod tests { markup, }; use pgt_diagnostics::PrintDiagnostic; + use pgt_text_size::TextRange; use termcolor::NoColor; - use crate::Analyser; + use crate::{AnalysableStatement, Analyser}; #[ignore] #[test] @@ -102,6 +124,7 @@ mod tests { }; let ast = pgt_query_ext::parse(SQL).expect("failed to parse SQL"); + let range = TextRange::new(0.into(), u32::try_from(SQL.len()).unwrap().into()); let options = AnalyserOptions::default(); @@ -110,7 +133,10 @@ mod tests { filter, }); - let results = analyser.run(crate::AnalyserContext { root: &ast }); + let results = analyser.run(crate::AnalyserParams { + stmts: vec![AnalysableStatement { root: ast, range }], + schema_cache: None, + }); println!("*******************"); for result in &results { diff --git a/crates/pgt_analyser/tests/rules_tests.rs b/crates/pgt_analyser/tests/rules_tests.rs index 247c02b0..0a6b47ec 100644 --- a/crates/pgt_analyser/tests/rules_tests.rs +++ b/crates/pgt_analyser/tests/rules_tests.rs @@ -2,7 +2,7 @@ use core::slice; use std::{fmt::Write, fs::read_to_string, path::Path}; use pgt_analyse::{AnalyserOptions, AnalysisFilter, RuleDiagnostic, RuleFilter}; -use pgt_analyser::{Analyser, AnalyserConfig, AnalyserContext}; +use pgt_analyser::{AnalysableStatement, Analyser, AnalyserConfig, AnalyserParams}; use pgt_console::StdDisplay; use pgt_diagnostics::PrintDiagnostic; @@ -32,7 +32,15 @@ fn rule_test(full_path: &'static str, _: &str, _: &str) { filter, }); - let results = analyser.run(AnalyserContext { root: &ast }); + let stmt = AnalysableStatement { + root: ast, + range: pgt_text_size::TextRange::new(0.into(), u32::try_from(query.len()).unwrap().into()), + }; + + let results = analyser.run(AnalyserParams { + stmts: vec![stmt], + schema_cache: None, + }); let mut snapshot = String::new(); write_snapshot(&mut snapshot, query.as_str(), results.as_slice()); diff --git a/crates/pgt_lsp/tests/server.rs b/crates/pgt_lsp/tests/server.rs index 353e80ae..176868f5 100644 --- a/crates/pgt_lsp/tests/server.rs +++ b/crates/pgt_lsp/tests/server.rs @@ -1734,13 +1734,24 @@ $$; server.open_document(initial_content).await?; - let notification = tokio::time::timeout(Duration::from_secs(5), async { + let got_notification = tokio::time::timeout(Duration::from_secs(5), async { loop { match receiver.next().await { Some(ServerNotification::PublishDiagnostics(msg)) => { if msg.diagnostics.iter().any(|d| { d.message .contains("Invalid statement: syntax error at or near \"declre\"") + && d.range + == Range { + start: Position { + line: 5, + character: 9, + }, + end: Position { + line: 11, + character: 0, + }, + } }) { return true; } @@ -1752,7 +1763,10 @@ $$; .await .is_ok(); - assert!(notification, "expected diagnostics for unknown column"); + assert!( + got_notification, + "expected diagnostics for invalid declare statement" + ); server.shutdown().await?; reader.abort(); diff --git a/crates/pgt_query_ext/src/diagnostics.rs b/crates/pgt_query_ext/src/diagnostics.rs index 7e3f0a37..1a068dc0 100644 --- a/crates/pgt_query_ext/src/diagnostics.rs +++ b/crates/pgt_query_ext/src/diagnostics.rs @@ -9,7 +9,7 @@ use pgt_text_size::TextRange; pub struct SyntaxDiagnostic { /// The location where the error is occurred #[location(span)] - span: Option, + pub span: Option, #[message] #[description] pub message: MessageAndDescription, @@ -23,6 +23,11 @@ impl SyntaxDiagnostic { message: MessageAndDescription::from(message.into()), } } + + pub fn span(mut self, span: TextRange) -> Self { + self.span = Some(span); + self + } } impl From for SyntaxDiagnostic { diff --git a/crates/pgt_workspace/Cargo.toml b/crates/pgt_workspace/Cargo.toml index f535e505..3ef4936b 100644 --- a/crates/pgt_workspace/Cargo.toml +++ b/crates/pgt_workspace/Cargo.toml @@ -62,7 +62,9 @@ schema = [ ] [dev-dependencies] -tempfile = "3.15.0" +pgt_test_utils = { workspace = true } +sqlx = { workspace = true } +tempfile = "3.15.0" [lib] doctest = false diff --git a/crates/pgt_workspace/src/workspace/server.rs b/crates/pgt_workspace/src/workspace/server.rs index 399f2ec6..e6456afc 100644 --- a/crates/pgt_workspace/src/workspace/server.rs +++ b/crates/pgt_workspace/src/workspace/server.rs @@ -10,12 +10,12 @@ use analyser::AnalyserVisitorBuilder; use async_helper::run_async; use connection_manager::ConnectionManager; use document::{ - AsyncDiagnosticsMapper, CursorPositionFilter, DefaultMapper, Document, ExecuteStatementMapper, - SyncDiagnosticsMapper, + CursorPositionFilter, DefaultMapper, Document, ExecuteStatementMapper, + TypecheckDiagnosticsMapper, }; use futures::{StreamExt, stream}; use pgt_analyse::{AnalyserOptions, AnalysisFilter}; -use pgt_analyser::{Analyser, AnalyserConfig, AnalyserContext}; +use pgt_analyser::{Analyser, AnalyserConfig, AnalyserParams}; use pgt_diagnostics::{ Diagnostic, DiagnosticExt, Error, Severity, serde::Diagnostic as SDiagnostic, }; @@ -37,6 +37,7 @@ use crate::{ diagnostics::{PullDiagnosticsParams, PullDiagnosticsResult}, }, settings::{WorkspaceSettings, WorkspaceSettingsHandle, WorkspaceSettingsHandleMut}, + workspace::AnalyserDiagnosticsMapper, }; use super::{ @@ -444,7 +445,7 @@ impl Workspace for WorkspaceServer { if let Some(pool) = self.get_current_connection() { let path_clone = params.path.clone(); let schema_cache = self.schema_cache.load(pool.clone())?; - let input = doc.iter(AsyncDiagnosticsMapper).collect::>(); + let input = doc.iter(TypecheckDiagnosticsMapper).collect::>(); // sorry for the ugly code :( let async_results = run_async(async move { stream::iter(input) @@ -527,50 +528,49 @@ impl Workspace for WorkspaceServer { filter, }); + let path = params.path.as_path().display().to_string(); + + let schema_cache = self + .get_current_connection() + .and_then(|pool| self.schema_cache.load(pool.clone()).ok()); + + let mut analysable_stmts = vec![]; + for (stmt_root, diagnostic) in doc.iter(AnalyserDiagnosticsMapper) { + if let Some(node) = stmt_root { + analysable_stmts.push(node); + } + if let Some(diag) = diagnostic { + diagnostics.push(SDiagnostic::new( + diag.with_file_path(path.clone()) + .with_severity(Severity::Error), + )); + } + } + diagnostics.extend( - doc.iter(SyncDiagnosticsMapper) - .flat_map(|(range, ast, diag)| { - let mut errors: Vec = vec![]; - - if let Some(diag) = diag { - errors.push(diag.into()); - } - - if let Some(ast) = ast { - errors.extend( - analyser - .run(AnalyserContext { root: &ast }) - .into_iter() - .map(Error::from) - .collect::>(), - ); - } - - errors - .into_iter() - .map(|d| { - let severity = d - .category() - .filter(|category| category.name().starts_with("lint/")) - .map_or_else( - || d.severity(), - |category| { - settings - .get_severity_from_rule_code(category) - .unwrap_or(Severity::Warning) - }, - ); - - // adjust the span of the diagnostics to the statement (if it has one) - let span = d.location().span.map(|s| s + range.start()); - - SDiagnostic::new( - d.with_file_path(params.path.as_path().display().to_string()) - .with_file_span(span.unwrap_or(range)) - .with_severity(severity), - ) + analyser + .run(AnalyserParams { + stmts: analysable_stmts, + schema_cache: schema_cache.as_deref(), + }) + .into_iter() + .map(Error::from) + .map(|d| { + let severity = d + .category() + .map(|category| { + settings + .get_severity_from_rule_code(category) + .unwrap_or(Severity::Warning) }) - .collect::>() + .unwrap(); + + let span = d.location().span; + SDiagnostic::new( + d.with_file_path(path.clone()) + .with_file_span(span) + .with_severity(severity), + ) }), ); @@ -655,3 +655,7 @@ impl Workspace for WorkspaceServer { fn is_dir(path: &Path) -> bool { path.is_dir() || (path.is_symlink() && fs::read_link(path).is_ok_and(|path| path.is_dir())) } + +#[cfg(test)] +#[path = "server.tests.rs"] +mod tests; diff --git a/crates/pgt_workspace/src/workspace/server.tests.rs b/crates/pgt_workspace/src/workspace/server.tests.rs new file mode 100644 index 00000000..94b02cd5 --- /dev/null +++ b/crates/pgt_workspace/src/workspace/server.tests.rs @@ -0,0 +1,99 @@ +use biome_deserialize::Merge; +use pgt_analyse::RuleCategories; +use pgt_configuration::{PartialConfiguration, database::PartialDatabaseConfiguration}; +use pgt_diagnostics::Diagnostic; +use pgt_fs::PgTPath; +use pgt_text_size::TextRange; +use sqlx::PgPool; + +use crate::{ + Workspace, WorkspaceError, + workspace::{ + OpenFileParams, RegisterProjectFolderParams, UpdateSettingsParams, server::WorkspaceServer, + }, +}; + +fn get_test_workspace( + partial_config: Option, +) -> Result { + let workspace = WorkspaceServer::new(); + + workspace.register_project_folder(RegisterProjectFolderParams { + path: None, + set_as_current_workspace: true, + })?; + + workspace.update_settings(UpdateSettingsParams { + configuration: partial_config.unwrap_or(PartialConfiguration::init()), + gitignore_matches: vec![], + vcs_base_path: None, + workspace_directory: None, + })?; + + Ok(workspace) +} + +#[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] +async fn test_diagnostics(test_db: PgPool) { + let mut conf = PartialConfiguration::init(); + conf.merge_with(PartialConfiguration { + db: Some(PartialDatabaseConfiguration { + database: Some( + test_db + .connect_options() + .get_database() + .unwrap() + .to_string(), + ), + ..Default::default() + }), + ..Default::default() + }); + + let workspace = get_test_workspace(Some(conf)).expect("Unable to create test workspace"); + + let path = PgTPath::new("test.sql"); + let content = r#" + create table users ( + id serial primary key, + name text not null + ); + + drop table non_existing_table; + + select 1; + "#; + + workspace + .open_file(OpenFileParams { + path: path.clone(), + content: content.into(), + version: 1, + }) + .expect("Unable to open test file"); + + let diagnostics = workspace + .pull_diagnostics(crate::workspace::PullDiagnosticsParams { + path: path.clone(), + categories: RuleCategories::all(), + max_diagnostics: 100, + only: vec![], + skip: vec![], + }) + .expect("Unable to pull diagnostics") + .diagnostics; + + assert_eq!(diagnostics.len(), 1, "Expected one diagnostic"); + + let diagnostic = &diagnostics[0]; + + assert_eq!( + diagnostic.category().map(|c| c.name()), + Some("lint/safety/banDropTable") + ); + + assert_eq!( + diagnostic.location().span, + Some(TextRange::new(106.into(), 136.into())) + ); +} diff --git a/crates/pgt_workspace/src/workspace/server/document.rs b/crates/pgt_workspace/src/workspace/server/document.rs index 9d3700df..b5798370 100644 --- a/crates/pgt_workspace/src/workspace/server/document.rs +++ b/crates/pgt_workspace/src/workspace/server/document.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use pgt_analyser::AnalysableStatement; use pgt_diagnostics::{Diagnostic, DiagnosticExt, serde::Diagnostic as SDiagnostic}; use pgt_query_ext::diagnostics::SyntaxDiagnostic; use pgt_suppressions::Suppressions; @@ -208,8 +209,8 @@ impl<'a> StatementMapper<'a> for ExecuteStatementMapper { } } -pub struct AsyncDiagnosticsMapper; -impl<'a> StatementMapper<'a> for AsyncDiagnosticsMapper { +pub struct TypecheckDiagnosticsMapper; +impl<'a> StatementMapper<'a> for TypecheckDiagnosticsMapper { type Output = ( StatementId, TextRange, @@ -240,22 +241,20 @@ impl<'a> StatementMapper<'a> for AsyncDiagnosticsMapper { } } -pub struct SyncDiagnosticsMapper; -impl<'a> StatementMapper<'a> for SyncDiagnosticsMapper { - type Output = ( - TextRange, - Option, - Option, - ); +pub struct AnalyserDiagnosticsMapper; +impl<'a> StatementMapper<'a> for AnalyserDiagnosticsMapper { + type Output = (Option, Option); fn map(&self, parser: &'a Document, id: StatementId, range: TextRange) -> Self::Output { - let ast_result = parser.ast_db.get_or_cache_ast(&id); + let maybe_node = parser.ast_db.get_or_cache_ast(&id); - let (ast_option, diagnostics) = match &*ast_result { + let (ast_option, diagnostics) = match &*maybe_node { Ok(node) => { let plpgsql_result = parser.ast_db.get_or_cache_plpgsql_parse(&id); if let Some(Err(diag)) = plpgsql_result { - (Some(node.clone()), Some(diag.clone())) + // offset the pgpsql diagnostic from the parent statement start + let span = diag.location().span.map(|sp| sp + range.start()); + (Some(node.clone()), Some(diag.span(span.unwrap_or(range)))) } else { (Some(node.clone()), None) } @@ -263,7 +262,10 @@ impl<'a> StatementMapper<'a> for SyncDiagnosticsMapper { Err(diag) => (None, Some(diag.clone())), }; - (range, ast_option, diagnostics) + ( + ast_option.map(|root| AnalysableStatement { range, root }), + diagnostics, + ) } } @@ -401,10 +403,10 @@ END; $$;"; let d = Document::new(input.to_string(), 1); - let results = d.iter(SyncDiagnosticsMapper).collect::>(); + let results = d.iter(AnalyserDiagnosticsMapper).collect::>(); assert_eq!(results.len(), 1); - let (_range, ast, diagnostic) = &results[0]; + let (ast, diagnostic) = &results[0]; // Should have parsed the CREATE FUNCTION statement assert!(ast.is_some()); @@ -432,10 +434,10 @@ END; $$;"; let d = Document::new(input.to_string(), 1); - let results = d.iter(SyncDiagnosticsMapper).collect::>(); + let results = d.iter(AnalyserDiagnosticsMapper).collect::>(); assert_eq!(results.len(), 1); - let (_range, ast, diagnostic) = &results[0]; + let (ast, diagnostic) = &results[0]; // Should have parsed the CREATE FUNCTION statement assert!(ast.is_some()); @@ -458,15 +460,15 @@ $$;"; let d = Document::new(input.to_string(), 1); - let results1 = d.iter(SyncDiagnosticsMapper).collect::>(); + let results1 = d.iter(AnalyserDiagnosticsMapper).collect::>(); assert_eq!(results1.len(), 1); - assert!(results1[0].1.is_some()); - assert!(results1[0].2.is_none()); + assert!(results1[0].0.is_some()); + assert!(results1[0].1.is_none()); - let results2 = d.iter(SyncDiagnosticsMapper).collect::>(); + let results2 = d.iter(AnalyserDiagnosticsMapper).collect::>(); assert_eq!(results2.len(), 1); - assert!(results2[0].1.is_some()); - assert!(results2[0].2.is_none()); + assert!(results2[0].0.is_some()); + assert!(results2[0].1.is_none()); } #[test] @@ -513,7 +515,7 @@ END; $$ LANGUAGE plpgsql;"; let d = Document::new(input.to_string(), 1); - let results = d.iter(AsyncDiagnosticsMapper).collect::>(); + let results = d.iter(TypecheckDiagnosticsMapper).collect::>(); assert_eq!(results.len(), 1); let (_id, _range, ast, cst, sql_fn_sig) = &results[0]; @@ -532,7 +534,7 @@ $$ LANGUAGE plpgsql;"; "CREATE FUNCTION add(a int, b int) RETURNS int AS 'SELECT $1 + $2;' LANGUAGE sql;"; let d = Document::new(input.to_string(), 1); - let results = d.iter(AsyncDiagnosticsMapper).collect::>(); + let results = d.iter(TypecheckDiagnosticsMapper).collect::>(); assert_eq!(results.len(), 2); // Check the function body diff --git a/docs/codegen/src/rules_docs.rs b/docs/codegen/src/rules_docs.rs index 68db53db..1d4b86a9 100644 --- a/docs/codegen/src/rules_docs.rs +++ b/docs/codegen/src/rules_docs.rs @@ -1,7 +1,7 @@ use anyhow::{Result, bail}; use biome_string_case::Case; use pgt_analyse::{AnalyserOptions, AnalysisFilter, RuleFilter, RuleMetadata}; -use pgt_analyser::{Analyser, AnalyserConfig}; +use pgt_analyser::{AnalysableStatement, Analyser, AnalyserConfig}; use pgt_console::StdDisplay; use pgt_diagnostics::{Diagnostic, DiagnosticExt, PrintDiagnostic}; use pgt_query_ext::diagnostics::SyntaxDiagnostic; @@ -443,10 +443,16 @@ fn print_diagnostics( // split and parse each statement let stmts = pgt_statement_splitter::split(code); - for stmt in stmts.ranges { - match pgt_query_ext::parse(&code[stmt]) { + for stmt_range in stmts.ranges { + match pgt_query_ext::parse(&code[stmt_range]) { Ok(ast) => { - for rule_diag in analyser.run(pgt_analyser::AnalyserContext { root: &ast }) { + for rule_diag in analyser.run(pgt_analyser::AnalyserParams { + schema_cache: None, + stmts: vec![AnalysableStatement { + range: stmt_range, + root: ast, + }], + }) { let diag = pgt_diagnostics::serde::Diagnostic::new(rule_diag); let category = diag.category().expect("linter diagnostic has no code"); diff --git a/docs/rules/ban-drop-column.md b/docs/rules/ban-drop-column.md index 0c46d40a..28b3a4b5 100644 --- a/docs/rules/ban-drop-column.md +++ b/docs/rules/ban-drop-column.md @@ -25,10 +25,14 @@ alter table test drop column id; ``` ```sh -code-block.sql lint/safety/banDropColumn ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +code-block.sql:1:1 lint/safety/banDropColumn ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Dropping a column may break existing clients. + > 1 │ alter table test drop column id; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 2 │ + i You can leave the column as nullable or delete the column once queries no longer select or modify the column. diff --git a/docs/rules/ban-drop-not-null.md b/docs/rules/ban-drop-not-null.md index b860c45c..56ec33c7 100644 --- a/docs/rules/ban-drop-not-null.md +++ b/docs/rules/ban-drop-not-null.md @@ -25,10 +25,14 @@ alter table users alter column email drop not null; ``` ```sh -code-block.sql lint/safety/banDropNotNull ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +code-block.sql:1:1 lint/safety/banDropNotNull ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Dropping a NOT NULL constraint may break existing clients. + > 1 │ alter table users alter column email drop not null; + │ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 2 │ + i Consider using a marker value that represents NULL. Alternatively, create a new table allowing NULL values, copy the data from the old table, and create a view that filters NULL values. diff --git a/docs/rules/ban-drop-table.md b/docs/rules/ban-drop-table.md index 4b81755f..8aeb6e2c 100644 --- a/docs/rules/ban-drop-table.md +++ b/docs/rules/ban-drop-table.md @@ -26,10 +26,14 @@ drop table some_table; ``` ```sh -code-block.sql lint/safety/banDropTable ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +code-block.sql:1:1 lint/safety/banDropTable ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ ! Dropping a table may break existing clients. + > 1 │ drop table some_table; + │ ^^^^^^^^^^^^^^^^^^^^^^ + 2 │ + i Update your application code to no longer read or write the table, and only then delete the table. Be sure to create a backup. diff --git a/packages/@postgrestools/backend-jsonrpc/src/workspace.ts b/packages/@postgrestools/backend-jsonrpc/src/workspace.ts index 72b77bc1..971f07ec 100644 --- a/packages/@postgrestools/backend-jsonrpc/src/workspace.ts +++ b/packages/@postgrestools/backend-jsonrpc/src/workspace.ts @@ -435,17 +435,10 @@ export interface OpenFileParams { version: number; } export interface ChangeFileParams { - changes: ChangeParams[]; + content: string; path: PgTPath; version: number; } -export interface ChangeParams { - /** - * The range of the file that changed. If `None`, the whole file changed. - */ - range?: TextRange; - text: string; -} export interface CloseFileParams { path: PgTPath; } diff --git a/xtask/codegen/src/generate_new_analyser_rule.rs b/xtask/codegen/src/generate_new_analyser_rule.rs index fc225712..343b0673 100644 --- a/xtask/codegen/src/generate_new_analyser_rule.rs +++ b/xtask/codegen/src/generate_new_analyser_rule.rs @@ -41,10 +41,11 @@ fn generate_rule_template( format!( r#"use pgt_analyse::{{ - context::RuleContext, {macro_name}, Rule, RuleDiagnostic + AnalysedFileContext, context::RuleContext, {macro_name}, Rule, RuleDiagnostic, }}; use pgt_console::markup; use pgt_diagnostics::Severity; +use pgt_schema_cache::SchemaCache; {macro_name}! {{ /// Succinct description of the rule. @@ -78,7 +79,11 @@ use pgt_diagnostics::Severity; impl Rule for {rule_name_upper_camel} {{ type Options = (); - fn run(ctx: &RuleContext) -> Vec {{ + fn run( + ctx: &RuleContext + _file_context: &AnalysedFileContext, + _schema_cache: Option<&SchemaCache>, + ) -> Vec {{ Vec::new() }} }} diff --git a/xtask/rules_check/src/lib.rs b/xtask/rules_check/src/lib.rs index da4b4c73..0c57d06f 100644 --- a/xtask/rules_check/src/lib.rs +++ b/xtask/rules_check/src/lib.rs @@ -7,7 +7,7 @@ use pgt_analyse::{ AnalyserOptions, AnalysisFilter, GroupCategory, RegistryVisitor, Rule, RuleCategory, RuleFilter, RuleGroup, RuleMetadata, }; -use pgt_analyser::{Analyser, AnalyserConfig}; +use pgt_analyser::{AnalysableStatement, Analyser, AnalyserConfig}; use pgt_console::{markup, Console}; use pgt_diagnostics::{Diagnostic, DiagnosticExt, PrintDiagnostic}; use pgt_query_ext::diagnostics::SyntaxDiagnostic; @@ -127,10 +127,16 @@ fn assert_lint( }); let result = pgt_statement_splitter::split(code); - for stmt in result.ranges { - match pgt_query_ext::parse(&code[stmt]) { + for stmt_range in result.ranges { + match pgt_query_ext::parse(&code[stmt_range]) { Ok(ast) => { - for rule_diag in analyser.run(pgt_analyser::AnalyserContext { root: &ast }) { + for rule_diag in analyser.run(pgt_analyser::AnalyserParams { + schema_cache: None, + stmts: vec![AnalysableStatement { + range: stmt_range, + root: ast, + }], + }) { let diag = pgt_diagnostics::serde::Diagnostic::new(rule_diag); let category = diag.category().expect("linter diagnostic has no code"); From a34f6a3d37bd2d471a9f991bbed19b992153e698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Steinr=C3=B6tter?= Date: Wed, 16 Jul 2025 21:50:45 +0200 Subject: [PATCH 11/29] fix: revoke split (#459) --- crates/pgt_statement_splitter/src/lib.rs | 23 +++++++++++++++++++ .../src/splitter/common.rs | 4 ++++ 2 files changed, 27 insertions(+) diff --git a/crates/pgt_statement_splitter/src/lib.rs b/crates/pgt_statement_splitter/src/lib.rs index de028336..19b2f230 100644 --- a/crates/pgt_statement_splitter/src/lib.rs +++ b/crates/pgt_statement_splitter/src/lib.rs @@ -149,6 +149,29 @@ mod tests { } } + #[test] + fn revoke() { + Tester::from("revoke delete on table \"public\".\"voice_call\" from \"anon\";") + .expect_statements(vec![ + "revoke delete on table \"public\".\"voice_call\" from \"anon\";", + ]); + + Tester::from("revoke select on table \"public\".\"voice_call\" from \"anon\";") + .expect_statements(vec![ + "revoke select on table \"public\".\"voice_call\" from \"anon\";", + ]); + + Tester::from("revoke update on table \"public\".\"voice_call\" from \"anon\";") + .expect_statements(vec![ + "revoke update on table \"public\".\"voice_call\" from \"anon\";", + ]); + + Tester::from("revoke insert on table \"public\".\"voice_call\" from \"anon\";") + .expect_statements(vec![ + "revoke insert on table \"public\".\"voice_call\" from \"anon\";", + ]); + } + #[test] fn double_newlines() { Tester::from("select 1 from contact\n\nselect 1\n\nselect 3").expect_statements(vec![ diff --git a/crates/pgt_statement_splitter/src/splitter/common.rs b/crates/pgt_statement_splitter/src/splitter/common.rs index 4f2cd069..9c3dea48 100644 --- a/crates/pgt_statement_splitter/src/splitter/common.rs +++ b/crates/pgt_statement_splitter/src/splitter/common.rs @@ -183,6 +183,8 @@ pub(crate) fn unknown(p: &mut Splitter, exclude: &[SyntaxKind]) { SyntaxKind::EXCEPT_KW, // for grant SyntaxKind::GRANT_KW, + // for revoke + SyntaxKind::REVOKE_KW, SyntaxKind::COMMA, ] .iter() @@ -215,6 +217,8 @@ pub(crate) fn unknown(p: &mut Splitter, exclude: &[SyntaxKind]) { SyntaxKind::INSTEAD_KW, // for grant SyntaxKind::GRANT_KW, + // for revoke + SyntaxKind::REVOKE_KW, SyntaxKind::COMMA, // Do update in INSERT stmt SyntaxKind::DO_KW, From 544b6b08feb17657bf8eeff00d71457658b7826f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Steinr=C3=B6tter?= Date: Wed, 16 Jul 2025 22:03:20 +0200 Subject: [PATCH 12/29] feat: add support for named params (#458) --- crates/pgt_lexer/src/lexer.rs | 12 ++++ crates/pgt_lexer/src/lib.rs | 30 ++++++++ crates/pgt_lexer_codegen/src/syntax_kind.rs | 2 +- crates/pgt_tokenizer/src/lib.rs | 69 ++++++++++++++++++- .../pgt_tokenizer__tests__named_param_at.snap | 23 +++++++ ...__tests__named_param_colon_identifier.snap | 23 +++++++ ...kenizer__tests__named_param_colon_raw.snap | 23 +++++++ ...izer__tests__named_param_colon_string.snap | 23 +++++++ crates/pgt_tokenizer/src/token.rs | 30 ++++++++ 9 files changed, 231 insertions(+), 4 deletions(-) create mode 100644 crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_at.snap create mode 100644 crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_colon_identifier.snap create mode 100644 crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_colon_raw.snap create mode 100644 crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_colon_string.snap diff --git a/crates/pgt_lexer/src/lexer.rs b/crates/pgt_lexer/src/lexer.rs index db4b4ae2..ad6db297 100644 --- a/crates/pgt_lexer/src/lexer.rs +++ b/crates/pgt_lexer/src/lexer.rs @@ -132,6 +132,18 @@ impl<'a> Lexer<'a> { pgt_tokenizer::TokenKind::Eof => SyntaxKind::EOF, pgt_tokenizer::TokenKind::Backtick => SyntaxKind::BACKTICK, pgt_tokenizer::TokenKind::PositionalParam => SyntaxKind::POSITIONAL_PARAM, + pgt_tokenizer::TokenKind::NamedParam { kind } => { + match kind { + pgt_tokenizer::NamedParamKind::ColonIdentifier { terminated: false } => { + err = "Missing trailing \" to terminate the named parameter"; + } + pgt_tokenizer::NamedParamKind::ColonString { terminated: false } => { + err = "Missing trailing ' to terminate the named parameter"; + } + _ => {} + }; + SyntaxKind::POSITIONAL_PARAM + } pgt_tokenizer::TokenKind::QuotedIdent { terminated } => { if !terminated { err = "Missing trailing \" to terminate the quoted identifier" diff --git a/crates/pgt_lexer/src/lib.rs b/crates/pgt_lexer/src/lib.rs index 2d8779a7..45fa6c6b 100644 --- a/crates/pgt_lexer/src/lib.rs +++ b/crates/pgt_lexer/src/lib.rs @@ -50,6 +50,36 @@ mod tests { assert!(!errors[0].message.to_string().is_empty()); } + #[test] + fn test_lexing_string_params_with_errors() { + let input = "SELECT :'unterminated string"; + let lexed = lex(input); + + // Should have tokens + assert!(!lexed.is_empty()); + + // Should have an error for unterminated string + let errors = lexed.errors(); + assert!(!errors.is_empty()); + // Check the error message exists + assert!(!errors[0].message.to_string().is_empty()); + } + + #[test] + fn test_lexing_identifier_params_with_errors() { + let input = "SELECT :\"unterminated string"; + let lexed = lex(input); + + // Should have tokens + assert!(!lexed.is_empty()); + + // Should have an error for unterminated string + let errors = lexed.errors(); + assert!(!errors.is_empty()); + // Check the error message exists + assert!(!errors[0].message.to_string().is_empty()); + } + #[test] fn test_token_ranges() { let input = "SELECT id"; diff --git a/crates/pgt_lexer_codegen/src/syntax_kind.rs b/crates/pgt_lexer_codegen/src/syntax_kind.rs index 07b7a419..c671e451 100644 --- a/crates/pgt_lexer_codegen/src/syntax_kind.rs +++ b/crates/pgt_lexer_codegen/src/syntax_kind.rs @@ -43,7 +43,7 @@ const PUNCT: &[(&str, &str)] = &[ ("`", "BACKTICK"), ]; -const EXTRA: &[&str] = &["POSITIONAL_PARAM", "ERROR", "COMMENT", "EOF"]; +const EXTRA: &[&str] = &["POSITIONAL_PARAM", "NAMED_PARAM", "ERROR", "COMMENT", "EOF"]; const LITERALS: &[&str] = &[ "BIT_STRING", diff --git a/crates/pgt_tokenizer/src/lib.rs b/crates/pgt_tokenizer/src/lib.rs index 787adcaa..80b66363 100644 --- a/crates/pgt_tokenizer/src/lib.rs +++ b/crates/pgt_tokenizer/src/lib.rs @@ -1,7 +1,7 @@ mod cursor; mod token; use cursor::{Cursor, EOF_CHAR}; -pub use token::{Base, LiteralKind, Token, TokenKind}; +pub use token::{Base, LiteralKind, NamedParamKind, Token, TokenKind}; // via: https://github.com/postgres/postgres/blob/db0c96cc18aec417101e37e59fcc53d4bf647915/src/backend/parser/scan.l#L346 // ident_start [A-Za-z\200-\377_] @@ -132,6 +132,46 @@ impl Cursor<'_> { } _ => TokenKind::Dot, }, + '@' => { + if is_ident_start(self.first()) { + // Named parameter with @ prefix. + self.eat_while(is_ident_cont); + TokenKind::NamedParam { + kind: NamedParamKind::AtPrefix, + } + } else { + TokenKind::At + } + } + ':' => { + // Named parameters in psql with different substitution styles. + // + // https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-INTERPOLATION + match self.first() { + '\'' => { + // Named parameter with colon prefix and single quotes. + self.bump(); + let terminated = self.single_quoted_string(); + let kind = NamedParamKind::ColonString { terminated }; + TokenKind::NamedParam { kind } + } + '"' => { + // Named parameter with colon prefix and double quotes. + self.bump(); + let terminated = self.double_quoted_string(); + let kind = NamedParamKind::ColonIdentifier { terminated }; + TokenKind::NamedParam { kind } + } + c if is_ident_start(c) => { + // Named parameter with colon prefix. + self.eat_while(is_ident_cont); + TokenKind::NamedParam { + kind: NamedParamKind::ColonRaw, + } + } + _ => TokenKind::Colon, + } + } // One-symbol tokens. ';' => TokenKind::Semi, '\\' => TokenKind::Backslash, @@ -140,11 +180,9 @@ impl Cursor<'_> { ')' => TokenKind::CloseParen, '[' => TokenKind::OpenBracket, ']' => TokenKind::CloseBracket, - '@' => TokenKind::At, '#' => TokenKind::Pound, '~' => TokenKind::Tilde, '?' => TokenKind::Question, - ':' => TokenKind::Colon, '$' => { // Dollar quoted strings if is_ident_start(self.first()) || self.first() == '$' { @@ -613,6 +651,31 @@ mod tests { } tokens } + + #[test] + fn named_param_at() { + let result = lex("select 1 from c where id = @id;"); + assert_debug_snapshot!(result); + } + + #[test] + fn named_param_colon_raw() { + let result = lex("select 1 from c where id = :id;"); + assert_debug_snapshot!(result); + } + + #[test] + fn named_param_colon_string() { + let result = lex("select 1 from c where id = :'id';"); + assert_debug_snapshot!(result); + } + + #[test] + fn named_param_colon_identifier() { + let result = lex("select 1 from c where id = :\"id\";"); + assert_debug_snapshot!(result); + } + #[test] fn lex_statement() { let result = lex("select 1;"); diff --git a/crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_at.snap b/crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_at.snap new file mode 100644 index 00000000..30bbe87f --- /dev/null +++ b/crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_at.snap @@ -0,0 +1,23 @@ +--- +source: crates/pgt_tokenizer/src/lib.rs +expression: result +snapshot_kind: text +--- +[ + "select" @ Ident, + " " @ Space, + "1" @ Literal { kind: Int { base: Decimal, empty_int: false } }, + " " @ Space, + "from" @ Ident, + " " @ Space, + "c" @ Ident, + " " @ Space, + "where" @ Ident, + " " @ Space, + "id" @ Ident, + " " @ Space, + "=" @ Eq, + " " @ Space, + "@id" @ NamedParam { kind: AtPrefix }, + ";" @ Semi, +] diff --git a/crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_colon_identifier.snap b/crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_colon_identifier.snap new file mode 100644 index 00000000..6986ab0e --- /dev/null +++ b/crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_colon_identifier.snap @@ -0,0 +1,23 @@ +--- +source: crates/pgt_tokenizer/src/lib.rs +expression: result +snapshot_kind: text +--- +[ + "select" @ Ident, + " " @ Space, + "1" @ Literal { kind: Int { base: Decimal, empty_int: false } }, + " " @ Space, + "from" @ Ident, + " " @ Space, + "c" @ Ident, + " " @ Space, + "where" @ Ident, + " " @ Space, + "id" @ Ident, + " " @ Space, + "=" @ Eq, + " " @ Space, + ":\"id\"" @ NamedParam { kind: ColonIdentifier { terminated: true } }, + ";" @ Semi, +] diff --git a/crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_colon_raw.snap b/crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_colon_raw.snap new file mode 100644 index 00000000..f6db199d --- /dev/null +++ b/crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_colon_raw.snap @@ -0,0 +1,23 @@ +--- +source: crates/pgt_tokenizer/src/lib.rs +expression: result +snapshot_kind: text +--- +[ + "select" @ Ident, + " " @ Space, + "1" @ Literal { kind: Int { base: Decimal, empty_int: false } }, + " " @ Space, + "from" @ Ident, + " " @ Space, + "c" @ Ident, + " " @ Space, + "where" @ Ident, + " " @ Space, + "id" @ Ident, + " " @ Space, + "=" @ Eq, + " " @ Space, + ":id" @ NamedParam { kind: ColonRaw }, + ";" @ Semi, +] diff --git a/crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_colon_string.snap b/crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_colon_string.snap new file mode 100644 index 00000000..d9150083 --- /dev/null +++ b/crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_colon_string.snap @@ -0,0 +1,23 @@ +--- +source: crates/pgt_tokenizer/src/lib.rs +expression: result +snapshot_kind: text +--- +[ + "select" @ Ident, + " " @ Space, + "1" @ Literal { kind: Int { base: Decimal, empty_int: false } }, + " " @ Space, + "from" @ Ident, + " " @ Space, + "c" @ Ident, + " " @ Space, + "where" @ Ident, + " " @ Space, + "id" @ Ident, + " " @ Space, + "=" @ Eq, + " " @ Space, + ":'id'" @ NamedParam { kind: ColonString { terminated: true } }, + ";" @ Semi, +] diff --git a/crates/pgt_tokenizer/src/token.rs b/crates/pgt_tokenizer/src/token.rs index 50a7d12a..e3dbaee2 100644 --- a/crates/pgt_tokenizer/src/token.rs +++ b/crates/pgt_tokenizer/src/token.rs @@ -94,6 +94,12 @@ pub enum TokenKind { /// /// see: PositionalParam, + /// Named Parameter, e.g., `@name` + /// + /// This is used in some ORMs and query builders, like sqlc. + NamedParam { + kind: NamedParamKind, + }, /// Quoted Identifier, e.g., `"update"` in `update "my_table" set "a" = 5;` /// /// These are case-sensitive, unlike [`TokenKind::Ident`] @@ -104,6 +110,30 @@ pub enum TokenKind { }, } +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum NamedParamKind { + /// e.g. `@name` + /// + /// Used in: + /// - sqlc: https://docs.sqlc.dev/en/latest/howto/named_parameters.html + AtPrefix, + + /// e.g. `:name` (raw substitution) + /// + /// Used in: psql + ColonRaw, + + /// e.g. `:'name'` (quoted string substitution) + /// + /// Used in: psql + ColonString { terminated: bool }, + + /// e.g. `:"name"` (quoted identifier substitution) + /// + /// Used in: psql + ColonIdentifier { terminated: bool }, +} + /// Parsed token. /// It doesn't contain information about data that has been parsed, /// only the type of the token and its size. From fdfbd2c57b2ebddbe53a4854ac9328de8cf71945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Steinr=C3=B6tter?= Date: Thu, 17 Jul 2025 13:35:02 +0200 Subject: [PATCH 13/29] fix: deadlock (#460) after using it for a day, i saw two deadlocks. not exactly sure where they occur, but this is the only place i see potential issues. this must be coming from the dashmap replacement... Mutex requires us to be more careful. --- .../workspace/server/connection_manager.rs | 50 ++++++++----------- .../src/workspace/server/tree_sitter.rs | 30 +++++------ 2 files changed, 36 insertions(+), 44 deletions(-) diff --git a/crates/pgt_workspace/src/workspace/server/connection_manager.rs b/crates/pgt_workspace/src/workspace/server/connection_manager.rs index 8955b378..145b6fa0 100644 --- a/crates/pgt_workspace/src/workspace/server/connection_manager.rs +++ b/crates/pgt_workspace/src/workspace/server/connection_manager.rs @@ -34,23 +34,19 @@ impl ConnectionManager { pub(crate) fn get_pool(&self, settings: &DatabaseSettings) -> Option { let key = ConnectionKey::from(settings); - // Cleanup idle connections first - self.cleanup_idle_pools(&key); - if !settings.enable_connection { tracing::info!("Database connection disabled."); return None; } - // Try read lock first for cache hit - if let Ok(pools) = self.pools.read() { - if let Some(cached_pool) = pools.get(&key) { - // Can't update last_accessed with read lock, but that's okay for occasional misses - return Some(cached_pool.pool.clone()); + { + if let Ok(pools) = self.pools.read() { + if let Some(cached_pool) = pools.get(&key) { + return Some(cached_pool.pool.clone()); + } } } - // Cache miss or need to update timestamp - use write lock let mut pools = self.pools.write().unwrap(); // Double-check after acquiring write lock @@ -59,6 +55,21 @@ impl ConnectionManager { return Some(cached_pool.pool.clone()); } + // Clean up idle connections before creating new ones to avoid unbounded growth + let now = Instant::now(); + pools.retain(|k, cached_pool| { + let idle_duration = now.duration_since(cached_pool.last_accessed); + if idle_duration > cached_pool.idle_timeout && k != &key { + tracing::debug!( + "Removing idle database connection (idle for {:?})", + idle_duration + ); + false + } else { + true + } + }); + // Create a new pool let config = PgConnectOptions::new() .host(&settings.host) @@ -85,25 +96,4 @@ impl ConnectionManager { Some(pool) } - - /// Remove pools that haven't been accessed for longer than the idle timeout - fn cleanup_idle_pools(&self, ignore_key: &ConnectionKey) { - let now = Instant::now(); - - let mut pools = self.pools.write().unwrap(); - - // Use retain to keep only non-idle connections - pools.retain(|key, cached_pool| { - let idle_duration = now.duration_since(cached_pool.last_accessed); - if idle_duration > cached_pool.idle_timeout && key != ignore_key { - tracing::debug!( - "Removing idle database connection (idle for {:?})", - idle_duration - ); - false - } else { - true - } - }); - } } diff --git a/crates/pgt_workspace/src/workspace/server/tree_sitter.rs b/crates/pgt_workspace/src/workspace/server/tree_sitter.rs index b8f62b63..71411d27 100644 --- a/crates/pgt_workspace/src/workspace/server/tree_sitter.rs +++ b/crates/pgt_workspace/src/workspace/server/tree_sitter.rs @@ -28,27 +28,29 @@ impl TreeSitterStore { } pub fn get_or_cache_tree(&self, statement: &StatementId) -> Arc { - let mut cache = self.db.lock().expect("Failed to lock cache"); - - if let Some(existing) = cache.get(statement) { - return existing.clone(); + // First check cache + { + let mut cache = self.db.lock().unwrap(); + if let Some(existing) = cache.get(statement) { + return existing.clone(); + } } - // Cache miss - drop cache lock, parse, then re-acquire to insert - drop(cache); - - let mut parser = self.parser.lock().expect("Failed to lock parser"); + // Cache miss - parse outside of cache lock to avoid deadlock + let mut parser = self.parser.lock().unwrap(); let tree = Arc::new(parser.parse(statement.content(), None).unwrap()); drop(parser); - let mut cache = self.db.lock().expect("Failed to lock cache"); - - // Double-check after re-acquiring lock - if let Some(existing) = cache.get(statement) { - return existing.clone(); + // Insert into cache + { + let mut cache = self.db.lock().unwrap(); + // Double-check in case another thread inserted while we were parsing + if let Some(existing) = cache.get(statement) { + return existing.clone(); + } + cache.put(statement.clone(), tree.clone()); } - cache.put(statement.clone(), tree.clone()); tree } } From 36a3c522fc44712752a75ef5a42d01c51064ab95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Steinr=C3=B6tter?= Date: Fri, 18 Jul 2025 08:11:20 +0200 Subject: [PATCH 14/29] fix: syntax error regression (#461) syntax errors were not shown anymore due to a missing range. added a test to catch this regression next time. --- .../src/workspace/server.tests.rs | 55 +++++++++++++++++++ .../src/workspace/server/document.rs | 2 +- 2 files changed, 56 insertions(+), 1 deletion(-) diff --git a/crates/pgt_workspace/src/workspace/server.tests.rs b/crates/pgt_workspace/src/workspace/server.tests.rs index 94b02cd5..c3fbf723 100644 --- a/crates/pgt_workspace/src/workspace/server.tests.rs +++ b/crates/pgt_workspace/src/workspace/server.tests.rs @@ -97,3 +97,58 @@ async fn test_diagnostics(test_db: PgPool) { Some(TextRange::new(106.into(), 136.into())) ); } + +#[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] +async fn test_syntax_error(test_db: PgPool) { + let mut conf = PartialConfiguration::init(); + conf.merge_with(PartialConfiguration { + db: Some(PartialDatabaseConfiguration { + database: Some( + test_db + .connect_options() + .get_database() + .unwrap() + .to_string(), + ), + ..Default::default() + }), + ..Default::default() + }); + + let workspace = get_test_workspace(Some(conf)).expect("Unable to create test workspace"); + + let path = PgTPath::new("test.sql"); + let content = r#" + seect 1; + "#; + + workspace + .open_file(OpenFileParams { + path: path.clone(), + content: content.into(), + version: 1, + }) + .expect("Unable to open test file"); + + let diagnostics = workspace + .pull_diagnostics(crate::workspace::PullDiagnosticsParams { + path: path.clone(), + categories: RuleCategories::all(), + max_diagnostics: 100, + only: vec![], + skip: vec![], + }) + .expect("Unable to pull diagnostics") + .diagnostics; + + assert_eq!(diagnostics.len(), 1, "Expected one diagnostic"); + + let diagnostic = &diagnostics[0]; + + assert_eq!(diagnostic.category().map(|c| c.name()), Some("syntax")); + + assert_eq!( + diagnostic.location().span, + Some(TextRange::new(7.into(), 15.into())) + ); +} diff --git a/crates/pgt_workspace/src/workspace/server/document.rs b/crates/pgt_workspace/src/workspace/server/document.rs index b5798370..c9f880ec 100644 --- a/crates/pgt_workspace/src/workspace/server/document.rs +++ b/crates/pgt_workspace/src/workspace/server/document.rs @@ -259,7 +259,7 @@ impl<'a> StatementMapper<'a> for AnalyserDiagnosticsMapper { (Some(node.clone()), None) } } - Err(diag) => (None, Some(diag.clone())), + Err(diag) => (None, Some(diag.clone().span(range))), }; ( From c0567ec08ce42cec8b12c399009718b667684591 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Steinr=C3=B6tter?= Date: Fri, 18 Jul 2025 23:26:11 +0200 Subject: [PATCH 15/29] fix: splitter crash (#464) fixes a crash in the splitter when the last char is a closing parentheses. --- crates/pgt_lsp/src/handlers/text_document.rs | 14 +- crates/pgt_lsp/src/utils.rs | 14 +- crates/pgt_lsp/tests/server.rs | 487 ++++++++++++++++++ crates/pgt_statement_splitter/src/lib.rs | 8 + .../src/splitter/common.rs | 4 +- 5 files changed, 511 insertions(+), 16 deletions(-) diff --git a/crates/pgt_lsp/src/handlers/text_document.rs b/crates/pgt_lsp/src/handlers/text_document.rs index cc2efb4b..1c5a9a11 100644 --- a/crates/pgt_lsp/src/handlers/text_document.rs +++ b/crates/pgt_lsp/src/handlers/text_document.rs @@ -1,12 +1,10 @@ -use crate::{ - diagnostics::LspError, documents::Document, session::Session, utils::apply_document_changes, -}; +use crate::{documents::Document, session::Session, utils::apply_document_changes}; use anyhow::Result; use pgt_workspace::workspace::{ ChangeFileParams, CloseFileParams, GetFileContentParams, OpenFileParams, }; use tower_lsp::lsp_types; -use tracing::error; +use tracing::{error, field}; /// Handler for `textDocument/didOpen` LSP notification #[tracing::instrument(level = "debug", skip(session), err)] @@ -36,12 +34,12 @@ pub(crate) async fn did_open( Ok(()) } -// Handler for `textDocument/didChange` LSP notification -#[tracing::instrument(level = "debug", skip(session), err)] +/// Handler for `textDocument/didChange` LSP notification +#[tracing::instrument(level = "debug", skip_all, fields(url = field::display(¶ms.text_document.uri), version = params.text_document.version), err)] pub(crate) async fn did_change( session: &Session, params: lsp_types::DidChangeTextDocumentParams, -) -> Result<(), LspError> { +) -> Result<()> { let url = params.text_document.uri; let version = params.text_document.version; @@ -56,7 +54,7 @@ pub(crate) async fn did_change( let text = apply_document_changes( session.position_encoding(), old_text, - ¶ms.content_changes, + params.content_changes, ); tracing::trace!("new document: {:?}", text); diff --git a/crates/pgt_lsp/src/utils.rs b/crates/pgt_lsp/src/utils.rs index 92059b66..8361cf08 100644 --- a/crates/pgt_lsp/src/utils.rs +++ b/crates/pgt_lsp/src/utils.rs @@ -1,5 +1,6 @@ +use crate::adapters::from_lsp::text_range; use crate::adapters::line_index::LineIndex; -use crate::adapters::{PositionEncoding, from_lsp, to_lsp}; +use crate::adapters::{PositionEncoding, to_lsp}; use anyhow::{Context, Result, ensure}; use pgt_console::MarkupBuf; use pgt_console::fmt::Termcolor; @@ -10,8 +11,8 @@ use pgt_text_size::{TextRange, TextSize}; use std::any::Any; use std::borrow::Cow; use std::fmt::{Debug, Display}; -use std::io; use std::ops::{Add, Range}; +use std::{io, mem}; use tower_lsp::jsonrpc::Error as LspError; use tower_lsp::lsp_types; use tower_lsp::lsp_types::{self as lsp, CodeDescription, Url}; @@ -183,7 +184,7 @@ pub(crate) fn panic_to_lsp_error(err: Box) -> LspError { pub(crate) fn apply_document_changes( position_encoding: PositionEncoding, current_content: String, - content_changes: &[lsp_types::TextDocumentContentChangeEvent], + mut content_changes: Vec, ) -> String { // Skip to the last full document change, as it invalidates all previous changes anyways. let mut start = content_changes @@ -192,12 +193,12 @@ pub(crate) fn apply_document_changes( .position(|change| change.range.is_none()) .map_or(0, |idx| content_changes.len() - idx - 1); - let mut text: String = match content_changes.get(start) { + let mut text: String = match content_changes.get_mut(start) { // peek at the first content change as an optimization Some(lsp_types::TextDocumentContentChangeEvent { range: None, text, .. }) => { - let text = text.clone(); + let text = mem::take(text); start += 1; // The only change is a full document update @@ -225,12 +226,11 @@ pub(crate) fn apply_document_changes( line_index = LineIndex::new(&text); } index_valid = range.start.line; - if let Ok(range) = from_lsp::text_range(&line_index, range, position_encoding) { + if let Ok(range) = text_range(&line_index, range, position_encoding) { text.replace_range(Range::::from(range), &change.text); } } } - text } diff --git a/crates/pgt_lsp/tests/server.rs b/crates/pgt_lsp/tests/server.rs index 176868f5..63953590 100644 --- a/crates/pgt_lsp/tests/server.rs +++ b/crates/pgt_lsp/tests/server.rs @@ -1773,3 +1773,490 @@ $$; Ok(()) } + +#[tokio::test] +async fn test_crash_on_delete_character() -> Result<()> { + let factory = ServerFactory::default(); + let (service, client) = factory.create(None).into_inner(); + let (stream, sink) = client.split(); + let mut server = Server::new(service); + + let (sender, _) = channel(CHANNEL_BUFFER_SIZE); + let reader = tokio::spawn(client_handler(stream, sink, sender)); + + server.initialize().await?; + server.initialized().await?; + + // Open document with initial CREATE INDEX statement - exactly as in log + let initial_content = "\n\n\n\nCREATE INDEX \"idx_analytics_read_ratio\" ON \"public\".\"message\" USING \"btree\" (\"inbox_id\", \"timestamp\") INCLUDE (\"status\") WHERE (\"is_inbound\" = false);\n"; + + server.open_document(initial_content).await?; + + // Add a space after false (position 148 from the log) + server + .change_document( + 3, + vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 148, + }, + end: Position { + line: 4, + character: 148, + }, + }), + range_length: Some(0), + text: " ".to_string(), + }], + ) + .await?; + + // Follow the exact sequence from the logfile + // Type character by character in exact order + + // Version 4: "a" at 149 + server + .change_document( + 4, + vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 149, + }, + end: Position { + line: 4, + character: 149, + }, + }), + range_length: Some(0), + text: "a".to_string(), + }], + ) + .await?; + + // Version 5: "n" at 150 + server + .change_document( + 5, + vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 150, + }, + end: Position { + line: 4, + character: 150, + }, + }), + range_length: Some(0), + text: "n".to_string(), + }], + ) + .await?; + + // Version 6: "d" at 151 + server + .change_document( + 6, + vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 151, + }, + end: Position { + line: 4, + character: 151, + }, + }), + range_length: Some(0), + text: "d".to_string(), + }], + ) + .await?; + + // Version 7: " " at 152 + server + .change_document( + 7, + vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 152, + }, + end: Position { + line: 4, + character: 152, + }, + }), + range_length: Some(0), + text: " ".to_string(), + }], + ) + .await?; + + // Version 8: "c" at 153 + server + .change_document( + 8, + vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 153, + }, + end: Position { + line: 4, + character: 153, + }, + }), + range_length: Some(0), + text: "c".to_string(), + }], + ) + .await?; + + // Version 10: "h" at 154 and "a" at 155 (two changes in one version) + server + .change_document( + 10, + vec![ + TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 154, + }, + end: Position { + line: 4, + character: 154, + }, + }), + range_length: Some(0), + text: "h".to_string(), + }, + TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 155, + }, + end: Position { + line: 4, + character: 155, + }, + }), + range_length: Some(0), + text: "a".to_string(), + }, + ], + ) + .await?; + + // Version 11: "n" at 156 + server + .change_document( + 11, + vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 156, + }, + end: Position { + line: 4, + character: 156, + }, + }), + range_length: Some(0), + text: "n".to_string(), + }], + ) + .await?; + + // Version 12: "n" at 157 + server + .change_document( + 12, + vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 157, + }, + end: Position { + line: 4, + character: 157, + }, + }), + range_length: Some(0), + text: "n".to_string(), + }], + ) + .await?; + + // Version 13: "e" at 158 + server + .change_document( + 13, + vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 158, + }, + end: Position { + line: 4, + character: 158, + }, + }), + range_length: Some(0), + text: "e".to_string(), + }], + ) + .await?; + + // Version 14: "l" at 159 + server + .change_document( + 14, + vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 159, + }, + end: Position { + line: 4, + character: 159, + }, + }), + range_length: Some(0), + text: "l".to_string(), + }], + ) + .await?; + + // Version 15: "_" at 160 + server + .change_document( + 15, + vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 160, + }, + end: Position { + line: 4, + character: 160, + }, + }), + range_length: Some(0), + text: "_".to_string(), + }], + ) + .await?; + + // Version 16: "t" at 161 + server + .change_document( + 16, + vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 161, + }, + end: Position { + line: 4, + character: 161, + }, + }), + range_length: Some(0), + text: "t".to_string(), + }], + ) + .await?; + + // Version 17: "y" at 162 + server + .change_document( + 17, + vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 162, + }, + end: Position { + line: 4, + character: 162, + }, + }), + range_length: Some(0), + text: "y".to_string(), + }], + ) + .await?; + + // Version 18: "p" at 163 + server + .change_document( + 18, + vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 163, + }, + end: Position { + line: 4, + character: 163, + }, + }), + range_length: Some(0), + text: "p".to_string(), + }], + ) + .await?; + + // Version 19: "e" at 164 + server + .change_document( + 19, + vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 164, + }, + end: Position { + line: 4, + character: 164, + }, + }), + range_length: Some(0), + text: "e".to_string(), + }], + ) + .await?; + + // Version 20: " " at 165 + server + .change_document( + 20, + vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 165, + }, + end: Position { + line: 4, + character: 165, + }, + }), + range_length: Some(0), + text: " ".to_string(), + }], + ) + .await?; + + // Now we should have: "WHERE ("is_inbound" = false and channel_type )" + + // Version 21: Paste the problematic text with double single quotes + server + .change_document( + 21, + vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 166, + }, + end: Position { + line: 4, + character: 166, + }, + }), + range_length: Some(0), + text: "channel_type not in (''postal'', ''sms'')".to_string(), + }], + ) + .await?; + + // Delete "channel_type" + server + .change_document( + 22, + vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 166, + }, + end: Position { + line: 4, + character: 178, + }, + }), + range_length: Some(12), + text: "".to_string(), + }], + ) + .await?; + + // Delete one more character + server + .change_document( + 23, + vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 166, + }, + end: Position { + line: 4, + character: 167, + }, + }), + range_length: Some(1), + text: "".to_string(), + }], + ) + .await?; + + // This final delete should trigger the panic + let result = server + .change_document( + 24, + vec![TextDocumentContentChangeEvent { + range: Some(Range { + start: Position { + line: 4, + character: 175, + }, + end: Position { + line: 4, + character: 176, + }, + }), + range_length: Some(1), + text: "".to_string(), + }], + ) + .await; + + assert!(result.is_ok()); + + reader.abort(); + + Ok(()) +} diff --git a/crates/pgt_statement_splitter/src/lib.rs b/crates/pgt_statement_splitter/src/lib.rs index 19b2f230..02ca1b30 100644 --- a/crates/pgt_statement_splitter/src/lib.rs +++ b/crates/pgt_statement_splitter/src/lib.rs @@ -114,6 +114,14 @@ mod tests { ); } + #[test] + fn test_crash_eof() { + Tester::from("CREATE INDEX \"idx_analytics_read_ratio\" ON \"public\".\"message\" USING \"btree\" (\"inbox_id\", \"timestamp\") INCLUDE (\"status\") WHERE (\"is_inbound\" = false and channel_type not in ('postal'', 'sms'));") + .expect_statements(vec![ + "CREATE INDEX \"idx_analytics_read_ratio\" ON \"public\".\"message\" USING \"btree\" (\"inbox_id\", \"timestamp\") INCLUDE (\"status\") WHERE (\"is_inbound\" = false and channel_type not in ('postal'', 'sms'));", + ]); + } + #[test] #[timeout(1000)] fn basic() { diff --git a/crates/pgt_statement_splitter/src/splitter/common.rs b/crates/pgt_statement_splitter/src/splitter/common.rs index 9c3dea48..786c2478 100644 --- a/crates/pgt_statement_splitter/src/splitter/common.rs +++ b/crates/pgt_statement_splitter/src/splitter/common.rs @@ -70,7 +70,9 @@ pub(crate) fn parenthesis(p: &mut Splitter) { depth += 1; } SyntaxKind::R_PAREN | SyntaxKind::EOF => { - p.advance(); + if p.current() == SyntaxKind::R_PAREN { + p.advance(); + } depth -= 1; if depth == 0 { break; From fa18370c590e007f40dac1d7c0b96d377fc24653 Mon Sep 17 00:00:00 2001 From: Julian Domke <68325451+juleswritescode@users.noreply.github.com> Date: Sat, 19 Jul 2025 09:38:49 +0200 Subject: [PATCH 16/29] fix: `files.ignore` setting should work (#462) --- Cargo.lock | 14 +- Cargo.toml | 3 +- crates/pgt_diagnostics/src/serde.rs | 3 +- crates/pgt_workspace/Cargo.toml | 1 + .../src/features/code_actions.rs | 4 +- .../pgt_workspace/src/features/diagnostics.rs | 2 +- crates/pgt_workspace/src/workspace/server.rs | 15 ++- .../src/workspace/server.tests.rs | 60 ++++++++- crates/pgt_workspace_macros/Cargo.toml | 19 +++ crates/pgt_workspace_macros/src/lib.rs | 123 ++++++++++++++++++ 10 files changed, 231 insertions(+), 13 deletions(-) create mode 100644 crates/pgt_workspace_macros/Cargo.toml create mode 100644 crates/pgt_workspace_macros/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 16b1de5e..d5c626e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3108,6 +3108,7 @@ dependencies = [ "pgt_test_utils", "pgt_text_size", "pgt_typecheck", + "pgt_workspace_macros", "rustc-hash 2.1.0", "schemars", "serde", @@ -3122,6 +3123,15 @@ dependencies = [ "tree_sitter_sql", ] +[[package]] +name = "pgt_workspace_macros" +version = "0.0.0" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "pin-project" version = "1.1.7" @@ -3341,9 +3351,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.93" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] diff --git a/Cargo.toml b/Cargo.toml index 15c6f02f..4a3454c6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,7 @@ convert_case = "0.6.0" prost-reflect = "0.15.3" protox = "0.8.0" sqlx = { version = "0.8.2", features = ["runtime-tokio", "runtime-async-std", "postgres", "json"] } -syn = "1.0.109" +syn = { version = "1.0.109", features = ["full"] } termcolor = "1.4.1" test-log = "0.2.17" tokio = { version = "1.40.0", features = ["full"] } @@ -85,6 +85,7 @@ pgt_tokenizer = { path = "./crates/pgt_tokenizer", version = "0.0.0 pgt_treesitter_queries = { path = "./crates/pgt_treesitter_queries", version = "0.0.0" } pgt_typecheck = { path = "./crates/pgt_typecheck", version = "0.0.0" } pgt_workspace = { path = "./crates/pgt_workspace", version = "0.0.0" } +pgt_workspace_macros = { path = "./crates/pgt_workspace_macros", version = "0.0.0" } pgt_test_macros = { path = "./crates/pgt_test_macros" } pgt_test_utils = { path = "./crates/pgt_test_utils" } diff --git a/crates/pgt_diagnostics/src/serde.rs b/crates/pgt_diagnostics/src/serde.rs index 334bd4e9..57ed3e28 100644 --- a/crates/pgt_diagnostics/src/serde.rs +++ b/crates/pgt_diagnostics/src/serde.rs @@ -164,6 +164,7 @@ impl From> for Location { #[serde(rename_all = "camelCase")] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] #[cfg_attr(test, derive(Eq, PartialEq))] + struct Advices { advices: Vec, } @@ -250,7 +251,7 @@ impl super::Advices for Advices { #[derive(Clone, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] -#[cfg_attr(test, derive(Eq, PartialEq))] +#[cfg_attr(test, derive(PartialEq, Eq))] enum Advice { Log(LogCategory, MarkupBuf), List(Vec), diff --git a/crates/pgt_workspace/Cargo.toml b/crates/pgt_workspace/Cargo.toml index 3ef4936b..e78f4391 100644 --- a/crates/pgt_workspace/Cargo.toml +++ b/crates/pgt_workspace/Cargo.toml @@ -32,6 +32,7 @@ pgt_statement_splitter = { workspace = true } pgt_suppressions = { workspace = true } pgt_text_size.workspace = true pgt_typecheck = { workspace = true } +pgt_workspace_macros = { workspace = true } rustc-hash = { workspace = true } schemars = { workspace = true, optional = true } serde = { workspace = true, features = ["derive"] } diff --git a/crates/pgt_workspace/src/features/code_actions.rs b/crates/pgt_workspace/src/features/code_actions.rs index 22223dd3..cd1706d3 100644 --- a/crates/pgt_workspace/src/features/code_actions.rs +++ b/crates/pgt_workspace/src/features/code_actions.rs @@ -12,7 +12,7 @@ pub struct CodeActionsParams { pub skip: Vec, } -#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[derive(Debug, serde::Serialize, serde::Deserialize, Default)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct CodeActionsResult { pub actions: Vec, @@ -57,7 +57,7 @@ pub struct ExecuteStatementParams { pub path: PgTPath, } -#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[derive(Debug, serde::Serialize, serde::Deserialize, Default, PartialEq, Eq)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct ExecuteStatementResult { pub message: String, diff --git a/crates/pgt_workspace/src/features/diagnostics.rs b/crates/pgt_workspace/src/features/diagnostics.rs index ff60e142..a697641e 100644 --- a/crates/pgt_workspace/src/features/diagnostics.rs +++ b/crates/pgt_workspace/src/features/diagnostics.rs @@ -12,7 +12,7 @@ pub struct PullDiagnosticsParams { pub skip: Vec, } -#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[derive(Debug, serde::Serialize, serde::Deserialize, Default)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] pub struct PullDiagnosticsResult { pub diagnostics: Vec, diff --git a/crates/pgt_workspace/src/workspace/server.rs b/crates/pgt_workspace/src/workspace/server.rs index e6456afc..c6ed0827 100644 --- a/crates/pgt_workspace/src/workspace/server.rs +++ b/crates/pgt_workspace/src/workspace/server.rs @@ -21,6 +21,7 @@ use pgt_diagnostics::{ }; use pgt_fs::{ConfigName, PgTPath}; use pgt_typecheck::{IdentifierType, TypecheckParams, TypedIdentifier}; +use pgt_workspace_macros::ignored_path; use schema_cache_manager::SchemaCacheManager; use sqlx::{Executor, PgPool}; use tracing::{debug, info}; @@ -30,7 +31,7 @@ use crate::{ configuration::to_analyser_rules, features::{ code_actions::{ - self, CodeAction, CodeActionKind, CodeActionsResult, CommandAction, + CodeAction, CodeActionKind, CodeActionsParams, CodeActionsResult, CommandAction, CommandActionCategory, ExecuteStatementParams, ExecuteStatementResult, }, completions::{CompletionsResult, GetCompletionsParams, get_statement_for_completions}, @@ -262,6 +263,7 @@ impl Workspace for WorkspaceServer { } /// Add a new file to the workspace + #[ignored_path(path=¶ms.path)] #[tracing::instrument(level = "info", skip_all, fields(path = params.path.as_path().as_os_str().to_str()), err)] fn open_file(&self, params: OpenFileParams) -> Result<(), WorkspaceError> { let mut documents = self.documents.write().unwrap(); @@ -277,6 +279,7 @@ impl Workspace for WorkspaceServer { } /// Remove a file from the workspace + #[ignored_path(path=¶ms.path)] fn close_file(&self, params: super::CloseFileParams) -> Result<(), WorkspaceError> { let mut documents = self.documents.write().unwrap(); documents @@ -291,6 +294,7 @@ impl Workspace for WorkspaceServer { path = params.path.as_os_str().to_str(), version = params.version ), err)] + #[ignored_path(path=¶ms.path)] fn change_file(&self, params: super::ChangeFileParams) -> Result<(), WorkspaceError> { let mut documents = self.documents.write().unwrap(); @@ -312,6 +316,7 @@ impl Workspace for WorkspaceServer { None } + #[ignored_path(path=¶ms.path)] fn get_file_content(&self, params: GetFileContentParams) -> Result { let documents = self.documents.read().unwrap(); let document = documents @@ -324,10 +329,11 @@ impl Workspace for WorkspaceServer { Ok(self.is_ignored(params.pgt_path.as_path())) } + #[ignored_path(path=¶ms.path)] fn pull_code_actions( &self, - params: code_actions::CodeActionsParams, - ) -> Result { + params: CodeActionsParams, + ) -> Result { let documents = self.documents.read().unwrap(); let parser = documents .get(¶ms.path) @@ -366,6 +372,7 @@ impl Workspace for WorkspaceServer { Ok(CodeActionsResult { actions }) } + #[ignored_path(path=¶ms.path)] fn execute_statement( &self, params: ExecuteStatementParams, @@ -409,6 +416,7 @@ impl Workspace for WorkspaceServer { }) } + #[ignored_path(path=¶ms.path)] fn pull_diagnostics( &self, params: PullDiagnosticsParams, @@ -607,6 +615,7 @@ impl Workspace for WorkspaceServer { }) } + #[ignored_path(path=¶ms.path)] #[tracing::instrument(level = "debug", skip_all, fields( path = params.path.as_os_str().to_str(), position = params.position.to_string() diff --git a/crates/pgt_workspace/src/workspace/server.tests.rs b/crates/pgt_workspace/src/workspace/server.tests.rs index c3fbf723..0578f90f 100644 --- a/crates/pgt_workspace/src/workspace/server.tests.rs +++ b/crates/pgt_workspace/src/workspace/server.tests.rs @@ -1,6 +1,10 @@ -use biome_deserialize::Merge; +use std::sync::Arc; + +use biome_deserialize::{Merge, StringSet}; use pgt_analyse::RuleCategories; -use pgt_configuration::{PartialConfiguration, database::PartialDatabaseConfiguration}; +use pgt_configuration::{ + PartialConfiguration, database::PartialDatabaseConfiguration, files::PartialFilesConfiguration, +}; use pgt_diagnostics::Diagnostic; use pgt_fs::PgTPath; use pgt_text_size::TextRange; @@ -8,8 +12,10 @@ use sqlx::PgPool; use crate::{ Workspace, WorkspaceError, + features::code_actions::ExecuteStatementResult, workspace::{ - OpenFileParams, RegisterProjectFolderParams, UpdateSettingsParams, server::WorkspaceServer, + OpenFileParams, RegisterProjectFolderParams, StatementId, UpdateSettingsParams, + server::WorkspaceServer, }, }; @@ -152,3 +158,51 @@ async fn test_syntax_error(test_db: PgPool) { Some(TextRange::new(7.into(), 15.into())) ); } + +#[tokio::test] +async fn correctly_ignores_files() { + let mut conf = PartialConfiguration::init(); + conf.merge_with(PartialConfiguration { + files: Some(PartialFilesConfiguration { + ignore: Some(StringSet::from_iter(["test.sql".to_string()])), + ..Default::default() + }), + ..Default::default() + }); + + let workspace = get_test_workspace(Some(conf)).expect("Unable to create test workspace"); + + let path = PgTPath::new("test.sql"); + let content = r#" + seect 1; + "#; + + let diagnostics_result = workspace.pull_diagnostics(crate::workspace::PullDiagnosticsParams { + path: path.clone(), + categories: RuleCategories::all(), + max_diagnostics: 100, + only: vec![], + skip: vec![], + }); + + assert!( + diagnostics_result.is_ok_and(|res| res.diagnostics.is_empty() + && res.errors == 0 + && res.skipped_diagnostics == 0) + ); + + let close_file_result = + workspace.close_file(crate::workspace::CloseFileParams { path: path.clone() }); + + assert!(close_file_result.is_ok()); + + let execute_statement_result = + workspace.execute_statement(crate::workspace::ExecuteStatementParams { + path: path.clone(), + statement_id: StatementId::Root { + content: Arc::from(content), + }, + }); + + assert!(execute_statement_result.is_ok_and(|res| res == ExecuteStatementResult::default())); +} diff --git a/crates/pgt_workspace_macros/Cargo.toml b/crates/pgt_workspace_macros/Cargo.toml new file mode 100644 index 00000000..c192db04 --- /dev/null +++ b/crates/pgt_workspace_macros/Cargo.toml @@ -0,0 +1,19 @@ +[package] +authors.workspace = true +categories.workspace = true +description = "" +edition.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +name = "pgt_workspace_macros" +repository.workspace = true +version = "0.0.0" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = { version = "1.0.95" } +quote = { workspace = true } +syn = { workspace = true } diff --git a/crates/pgt_workspace_macros/src/lib.rs b/crates/pgt_workspace_macros/src/lib.rs new file mode 100644 index 00000000..d46f484d --- /dev/null +++ b/crates/pgt_workspace_macros/src/lib.rs @@ -0,0 +1,123 @@ +use std::ops::Deref; + +use proc_macro::TokenStream; +use quote::quote; +use syn::{TypePath, TypeTuple, parse_macro_input}; + +struct IgnoredPath { + path: syn::Expr, +} + +impl syn::parse::Parse for IgnoredPath { + fn parse(input: syn::parse::ParseStream) -> syn::Result { + let arg_name: syn::Ident = input.parse()?; + + if arg_name != "path" { + return Err(syn::Error::new_spanned( + arg_name, + "Expected 'path' argument.", + )); + } + + let _: syn::Token!(=) = input.parse()?; + let path: syn::Expr = input.parse()?; + + Ok(Self { path }) + } +} + +#[proc_macro_attribute] +/// You can use this on a workspace server function to return a default if the specified path +/// is ignored by the user's settings. +/// +/// This will work for any function where &self is in scope and that returns `Result`, `Result<(), E>`, or `T`, where `T: Default`. +/// `path` needs to point at a `&PgTPath`. +/// +/// ### Usage +/// +/// ```ignore +/// impl WorkspaceServer { +/// #[ignore_path(path=¶ms.path)] +/// fn foo(&self, params: FooParams) -> Result { +/// ... codeblock +/// } +/// } +/// +/// // …expands to… +/// +/// impl WorkspaceServer { +/// fn foo(&self, params: FooParams) -> Result { +/// if self.is_ignored(¶ms.path) { +/// return Ok(FooResult::default()); +/// } +/// ... codeblock +/// } +/// } +/// ``` +pub fn ignored_path(args: TokenStream, input: TokenStream) -> TokenStream { + let ignored_path = parse_macro_input!(args as IgnoredPath); + let input_fn = parse_macro_input!(input as syn::ItemFn); + + let macro_specified_path = ignored_path.path; + + let vis = &input_fn.vis; + let sig = &input_fn.sig; + let block = &input_fn.block; + let attrs = &input_fn.attrs; + + // handles cases `fn foo() -> Result` and `fn foo() -> Result<(), E>` + // T needs to implement default + if let syn::ReturnType::Type(_, ty) = &sig.output { + if let syn::Type::Path(TypePath { path, .. }) = ty.deref() { + if let Some(seg) = path.segments.last() { + if seg.ident == "Result" { + if let syn::PathArguments::AngleBracketed(type_args) = &seg.arguments { + if let Some(syn::GenericArgument::Type(t)) = type_args.args.first() { + if let syn::Type::Tuple(TypeTuple { elems, .. }) = t { + // case: Result<(), E> + if elems.is_empty() { + return TokenStream::from(quote! { + #(#attrs)* + #vis #sig { + if self.is_ignored(#macro_specified_path) { + return Ok(()); + }; + #block + } + }); + } + } + if let syn::Type::Path(TypePath { path, .. }) = t { + if let Some(seg) = path.segments.first() { + let ident = &seg.ident; + return TokenStream::from(quote! { + #(#attrs)* + #vis #sig { + if self.is_ignored(#macro_specified_path) { + return Ok(#ident::default()); + }; + #block + } + }); + } + } + }; + }; + }; + }; + }; + }; + + // case fn foo() -> T {} + // handles all other T's + // T needs to implement Default + TokenStream::from(quote! { + #(#attrs)* + #vis #sig { + if self.is_ignored(#macro_specified_path) { + return Default::default(); + } + #block + } + }) +} From be2cd02de82b0c1aeaeb8aa9485645b3d7319c54 Mon Sep 17 00:00:00 2001 From: Julian Domke <68325451+juleswritescode@users.noreply.github.com> Date: Tue, 22 Jul 2025 07:50:56 +0200 Subject: [PATCH 17/29] docs: fix links to rules (#467) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit we'll have to deploy to see if this actually works – using `mike serve` apparently doesn't deploy the local `docs/` directory, but uses a git branch. --- crates/pgt_diagnostics_categories/src/categories.rs | 12 ++++++------ docs/codegen/src/rules_index.rs | 2 +- docs/codegen/src/rules_sources.rs | 4 ++-- docs/rule_sources.md | 12 ++++++------ docs/rules.md | 12 ++++++------ xtask/codegen/src/generate_new_analyser_rule.rs | 2 +- 6 files changed, 22 insertions(+), 22 deletions(-) diff --git a/crates/pgt_diagnostics_categories/src/categories.rs b/crates/pgt_diagnostics_categories/src/categories.rs index d5ce48d7..b9d29698 100644 --- a/crates/pgt_diagnostics_categories/src/categories.rs +++ b/crates/pgt_diagnostics_categories/src/categories.rs @@ -13,12 +13,12 @@ // must be between `define_categories! {\n` and `\n ;\n`. define_categories! { - "lint/safety/addingRequiredField": "https://pglt.dev/linter/rules/adding-required-field", - "lint/safety/banDropColumn": "https://pglt.dev/linter/rules/ban-drop-column", - "lint/safety/banDropDatabase": "https://pgtools.dev/linter/rules/ban-drop-database", - "lint/safety/banDropNotNull": "https://pglt.dev/linter/rules/ban-drop-not-null", - "lint/safety/banDropTable": "https://pglt.dev/linter/rules/ban-drop-table", - "lint/safety/banTruncateCascade": "https://pgtools.dev/linter/rules/ban-truncate-cascade", + "lint/safety/addingRequiredField": "https://pgtools.dev/latest/rules/adding-required-field", + "lint/safety/banDropColumn": "https://pgtools.dev/latest/rules/ban-drop-column", + "lint/safety/banDropDatabase": "https://pgtools.dev/latest/rules/ban-drop-database", + "lint/safety/banDropNotNull": "https://pgtools.dev/latest/rules/ban-drop-not-null", + "lint/safety/banDropTable": "https://pgtools.dev/latest/rules/ban-drop-table", + "lint/safety/banTruncateCascade": "https://pgtools.dev/latest/rules/ban-truncate-cascade", // end lint rules ; // General categories diff --git a/docs/codegen/src/rules_index.rs b/docs/codegen/src/rules_index.rs index 655e4bdb..99d22580 100644 --- a/docs/codegen/src/rules_index.rs +++ b/docs/codegen/src/rules_index.rs @@ -69,7 +69,7 @@ fn generate_group( write!( content, - "| [{rule_name}](/rules/{dashed_rule}) | {summary} | {properties} |" + "| [{rule_name}](./{dashed_rule}) | {summary} | {properties} |" )?; writeln!(content)?; diff --git a/docs/codegen/src/rules_sources.rs b/docs/codegen/src/rules_sources.rs index b8fac23d..5b21ea93 100644 --- a/docs/codegen/src/rules_sources.rs +++ b/docs/codegen/src/rules_sources.rs @@ -48,12 +48,12 @@ pub fn generate_rule_sources(docs_dir: &Path) -> anyhow::Result<()> { for (rule_name, metadata) in rules { let kebab_rule_name = Case::Kebab.convert(rule_name); if metadata.sources.is_empty() { - exclusive_rules.insert((rule_name.to_string(), format!("./rules/{kebab_rule_name}"))); + exclusive_rules.insert((rule_name.to_string(), format!("../rules/{kebab_rule_name}"))); } else { for source in metadata.sources { let source_set = SourceSet { rule_name: rule_name.to_string(), - link: format!("./rules/{kebab_rule_name}"), + link: format!("../rules/{kebab_rule_name}"), source_link: source.to_rule_url(), source_rule_name: source.as_rule_name().to_string(), }; diff --git a/docs/rule_sources.md b/docs/rule_sources.md index 8c0f085d..679448cd 100644 --- a/docs/rule_sources.md +++ b/docs/rule_sources.md @@ -3,9 +3,9 @@ ### Squawk | Squawk Rule Name | Rule Name | | ---- | ---- | -| [adding-required-field](https://squawkhq.com/docs/adding-required-field) |[addingRequiredField](./rules/adding-required-field) | -| [ban-drop-column](https://squawkhq.com/docs/ban-drop-column) |[banDropColumn](./rules/ban-drop-column) | -| [ban-drop-database](https://squawkhq.com/docs/ban-drop-database) |[banDropDatabase](./rules/ban-drop-database) | -| [ban-drop-not-null](https://squawkhq.com/docs/ban-drop-not-null) |[banDropNotNull](./rules/ban-drop-not-null) | -| [ban-drop-table](https://squawkhq.com/docs/ban-drop-table) |[banDropTable](./rules/ban-drop-table) | -| [ban-truncate-cascade](https://squawkhq.com/docs/ban-truncate-cascade) |[banTruncateCascade](./rules/ban-truncate-cascade) | +| [adding-required-field](https://squawkhq.com/docs/adding-required-field) |[addingRequiredField](../rules/adding-required-field) | +| [ban-drop-column](https://squawkhq.com/docs/ban-drop-column) |[banDropColumn](../rules/ban-drop-column) | +| [ban-drop-database](https://squawkhq.com/docs/ban-drop-database) |[banDropDatabase](../rules/ban-drop-database) | +| [ban-drop-not-null](https://squawkhq.com/docs/ban-drop-not-null) |[banDropNotNull](../rules/ban-drop-not-null) | +| [ban-drop-table](https://squawkhq.com/docs/ban-drop-table) |[banDropTable](../rules/ban-drop-table) | +| [ban-truncate-cascade](https://squawkhq.com/docs/ban-truncate-cascade) |[banTruncateCascade](../rules/ban-truncate-cascade) | diff --git a/docs/rules.md b/docs/rules.md index 19e110c6..d74b67e8 100644 --- a/docs/rules.md +++ b/docs/rules.md @@ -12,12 +12,12 @@ Rules that detect potential safety issues in your code. | Rule name | Description | Properties | | --- | --- | --- | -| [addingRequiredField](/rules/adding-required-field) | Adding a new column that is NOT NULL and has no default value to an existing table effectively makes it required. | | -| [banDropColumn](/rules/ban-drop-column) | Dropping a column may break existing clients. | ✅ | -| [banDropDatabase](/rules/ban-drop-database) | Dropping a database may break existing clients (and everything else, really). | | -| [banDropNotNull](/rules/ban-drop-not-null) | Dropping a NOT NULL constraint may break existing clients. | ✅ | -| [banDropTable](/rules/ban-drop-table) | Dropping a table may break existing clients. | ✅ | -| [banTruncateCascade](/rules/ban-truncate-cascade) | Using `TRUNCATE`'s `CASCADE` option will truncate any tables that are also foreign-keyed to the specified tables. | | +| [addingRequiredField](./adding-required-field) | Adding a new column that is NOT NULL and has no default value to an existing table effectively makes it required. | | +| [banDropColumn](./ban-drop-column) | Dropping a column may break existing clients. | ✅ | +| [banDropDatabase](./ban-drop-database) | Dropping a database may break existing clients (and everything else, really). | | +| [banDropNotNull](./ban-drop-not-null) | Dropping a NOT NULL constraint may break existing clients. | ✅ | +| [banDropTable](./ban-drop-table) | Dropping a table may break existing clients. | ✅ | +| [banTruncateCascade](./ban-truncate-cascade) | Using `TRUNCATE`'s `CASCADE` option will truncate any tables that are also foreign-keyed to the specified tables. | | [//]: # (END RULES_INDEX) diff --git a/xtask/codegen/src/generate_new_analyser_rule.rs b/xtask/codegen/src/generate_new_analyser_rule.rs index 343b0673..4c4bcc69 100644 --- a/xtask/codegen/src/generate_new_analyser_rule.rs +++ b/xtask/codegen/src/generate_new_analyser_rule.rs @@ -132,7 +132,7 @@ pub fn generate_new_analyser_rule( // We sort rules to reduce conflicts between contributions made in parallel. let rule_line = match category { Category::Lint => format!( - r#" "lint/{group}/{rule_name_camel}": "https://pgtools.dev/linter/rules/{kebab_case_rule}","# + r#" "lint/{group}/{rule_name_camel}": "https://pgtools.dev/latest/rules/{kebab_case_rule}","# ), }; let lint_start = match category { From 51abce1c1a6b73699746090978c0c3f3ac87beb3 Mon Sep 17 00:00:00 2001 From: Julian Domke <68325451+juleswritescode@users.noreply.github.com> Date: Tue, 22 Jul 2025 07:52:18 +0200 Subject: [PATCH 18/29] refactor: extract completions context into `pgt_treesitter` crate (#466) I wanted to extract the treesitter-context that's used in completions, so it can also be used for the on-hover feature. I've also tried to clean up the APIs, detangled completion-context and sanitization, and extracted the general test helpers into the `pgt_test_utils` crate. --- Cargo.lock | 9 +- Cargo.toml | 2 +- crates/pgt_completions/Cargo.toml | 23 +- crates/pgt_completions/src/builder.rs | 7 +- crates/pgt_completions/src/complete.rs | 21 +- crates/pgt_completions/src/lib.rs | 1 - .../pgt_completions/src/providers/columns.rs | 157 +++++--- .../src/providers/functions.rs | 35 +- .../pgt_completions/src/providers/helper.rs | 9 +- .../pgt_completions/src/providers/policies.rs | 26 +- crates/pgt_completions/src/providers/roles.rs | 71 +++- .../pgt_completions/src/providers/schemas.rs | 23 +- .../pgt_completions/src/providers/tables.rs | 146 +++++-- .../pgt_completions/src/providers/triggers.rs | 169 --------- .../src/relevance/filtering.rs | 38 +- .../pgt_completions/src/relevance/scoring.rs | 28 +- crates/pgt_completions/src/sanitization.rs | 8 +- crates/pgt_completions/src/test_helper.rs | 82 +--- crates/pgt_test_utils/src/lib.rs | 84 +++++ .../Cargo.toml | 11 +- .../src/context/base_parser.rs | 0 .../src/context/grant_parser.rs | 27 +- .../src/context/mod.rs | 356 ++++++++++-------- .../src/context/policy_parser.rs | 39 +- .../src/context/revoke_parser.rs | 21 +- crates/pgt_treesitter/src/lib.rs | 5 + .../src/queries/insert_columns.rs | 6 +- .../src/queries/mod.rs} | 92 ++++- .../src/queries/parameters.rs | 4 +- .../src/queries/relations.rs | 7 +- .../src/queries/select_columns.rs | 6 +- .../src/queries/table_aliases.rs | 4 +- .../src/queries/where_columns.rs | 4 +- .../pgt_treesitter_queries/src/queries/mod.rs | 86 ----- crates/pgt_typecheck/Cargo.toml | 20 +- crates/pgt_typecheck/src/typed_identifier.rs | 2 +- .../pgt_workspace/src/features/completions.rs | 34 +- 37 files changed, 905 insertions(+), 758 deletions(-) delete mode 100644 crates/pgt_completions/src/providers/triggers.rs rename crates/{pgt_treesitter_queries => pgt_treesitter}/Cargo.toml (54%) rename crates/{pgt_completions => pgt_treesitter}/src/context/base_parser.rs (100%) rename crates/{pgt_completions => pgt_treesitter}/src/context/grant_parser.rs (94%) rename crates/{pgt_completions => pgt_treesitter}/src/context/mod.rs (78%) rename crates/{pgt_completions => pgt_treesitter}/src/context/policy_parser.rs (95%) rename crates/{pgt_completions => pgt_treesitter}/src/context/revoke_parser.rs (94%) create mode 100644 crates/pgt_treesitter/src/lib.rs rename crates/{pgt_treesitter_queries => pgt_treesitter}/src/queries/insert_columns.rs (97%) rename crates/{pgt_treesitter_queries/src/lib.rs => pgt_treesitter/src/queries/mod.rs} (72%) rename crates/{pgt_treesitter_queries => pgt_treesitter}/src/queries/parameters.rs (96%) rename crates/{pgt_treesitter_queries => pgt_treesitter}/src/queries/relations.rs (98%) rename crates/{pgt_treesitter_queries => pgt_treesitter}/src/queries/select_columns.rs (97%) rename crates/{pgt_treesitter_queries => pgt_treesitter}/src/queries/table_aliases.rs (97%) rename crates/{pgt_treesitter_queries => pgt_treesitter}/src/queries/where_columns.rs (97%) delete mode 100644 crates/pgt_treesitter_queries/src/queries/mod.rs diff --git a/Cargo.lock b/Cargo.lock index d5c626e1..1bf796b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2765,7 +2765,7 @@ dependencies = [ "pgt_schema_cache", "pgt_test_utils", "pgt_text_size", - "pgt_treesitter_queries", + "pgt_treesitter", "schemars", "serde", "serde_json", @@ -3047,10 +3047,13 @@ dependencies = [ ] [[package]] -name = "pgt_treesitter_queries" +name = "pgt_treesitter" version = "0.0.0" dependencies = [ "clap 4.5.23", + "pgt_schema_cache", + "pgt_test_utils", + "pgt_text_size", "tree-sitter", "tree_sitter_sql", ] @@ -3074,7 +3077,7 @@ dependencies = [ "pgt_schema_cache", "pgt_test_utils", "pgt_text_size", - "pgt_treesitter_queries", + "pgt_treesitter", "sqlx", "tokio", "tree-sitter", diff --git a/Cargo.toml b/Cargo.toml index 4a3454c6..23e21889 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -82,7 +82,7 @@ pgt_suppressions = { path = "./crates/pgt_suppressions", version = "0. pgt_text_edit = { path = "./crates/pgt_text_edit", version = "0.0.0" } pgt_text_size = { path = "./crates/pgt_text_size", version = "0.0.0" } pgt_tokenizer = { path = "./crates/pgt_tokenizer", version = "0.0.0" } -pgt_treesitter_queries = { path = "./crates/pgt_treesitter_queries", version = "0.0.0" } +pgt_treesitter = { path = "./crates/pgt_treesitter", version = "0.0.0" } pgt_typecheck = { path = "./crates/pgt_typecheck", version = "0.0.0" } pgt_workspace = { path = "./crates/pgt_workspace", version = "0.0.0" } pgt_workspace_macros = { path = "./crates/pgt_workspace_macros", version = "0.0.0" } diff --git a/crates/pgt_completions/Cargo.toml b/crates/pgt_completions/Cargo.toml index 916a0020..0ebb8e56 100644 --- a/crates/pgt_completions/Cargo.toml +++ b/crates/pgt_completions/Cargo.toml @@ -14,18 +14,17 @@ version = "0.0.0" [dependencies] async-std = "1.12.0" -pgt_text_size.workspace = true - - -fuzzy-matcher = "0.3.7" -pgt_schema_cache.workspace = true -pgt_treesitter_queries.workspace = true -schemars = { workspace = true, optional = true } -serde = { workspace = true, features = ["derive"] } -serde_json = { workspace = true } -tracing = { workspace = true } -tree-sitter.workspace = true -tree_sitter_sql.workspace = true +pgt_schema_cache.workspace = true +pgt_text_size.workspace = true +pgt_treesitter.workspace = true + +fuzzy-matcher = "0.3.7" +schemars = { workspace = true, optional = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +tracing = { workspace = true } +tree-sitter.workspace = true +tree_sitter_sql.workspace = true sqlx.workspace = true diff --git a/crates/pgt_completions/src/builder.rs b/crates/pgt_completions/src/builder.rs index 96576053..bf8eb66a 100644 --- a/crates/pgt_completions/src/builder.rs +++ b/crates/pgt_completions/src/builder.rs @@ -1,10 +1,11 @@ use crate::{ CompletionItemKind, CompletionText, - context::CompletionContext, item::CompletionItem, relevance::{filtering::CompletionFilter, scoring::CompletionScore}, }; +use pgt_treesitter::TreesitterContext; + pub(crate) struct PossibleCompletionItem<'a> { pub label: String, pub description: String, @@ -17,11 +18,11 @@ pub(crate) struct PossibleCompletionItem<'a> { pub(crate) struct CompletionBuilder<'a> { items: Vec>, - ctx: &'a CompletionContext<'a>, + ctx: &'a TreesitterContext<'a>, } impl<'a> CompletionBuilder<'a> { - pub fn new(ctx: &'a CompletionContext) -> Self { + pub fn new(ctx: &'a TreesitterContext) -> Self { CompletionBuilder { items: vec![], ctx } } diff --git a/crates/pgt_completions/src/complete.rs b/crates/pgt_completions/src/complete.rs index bd5efd19..e18589af 100644 --- a/crates/pgt_completions/src/complete.rs +++ b/crates/pgt_completions/src/complete.rs @@ -1,8 +1,9 @@ use pgt_text_size::TextSize; +use pgt_treesitter::{TreeSitterContextParams, context::TreesitterContext}; + use crate::{ builder::CompletionBuilder, - context::CompletionContext, item::CompletionItem, providers::{ complete_columns, complete_functions, complete_policies, complete_roles, complete_schemas, @@ -28,16 +29,20 @@ pub struct CompletionParams<'a> { pub fn complete(params: CompletionParams) -> Vec { let sanitized_params = SanitizedCompletionParams::from(params); - let ctx = CompletionContext::new(&sanitized_params); + let ctx = TreesitterContext::new(TreeSitterContextParams { + position: sanitized_params.position, + text: &sanitized_params.text, + tree: &sanitized_params.tree, + }); let mut builder = CompletionBuilder::new(&ctx); - complete_tables(&ctx, &mut builder); - complete_functions(&ctx, &mut builder); - complete_columns(&ctx, &mut builder); - complete_schemas(&ctx, &mut builder); - complete_policies(&ctx, &mut builder); - complete_roles(&ctx, &mut builder); + complete_tables(&ctx, sanitized_params.schema, &mut builder); + complete_functions(&ctx, sanitized_params.schema, &mut builder); + complete_columns(&ctx, sanitized_params.schema, &mut builder); + complete_schemas(&ctx, sanitized_params.schema, &mut builder); + complete_policies(&ctx, sanitized_params.schema, &mut builder); + complete_roles(&ctx, sanitized_params.schema, &mut builder); builder.finish() } diff --git a/crates/pgt_completions/src/lib.rs b/crates/pgt_completions/src/lib.rs index f8ca1a55..c4e592ee 100644 --- a/crates/pgt_completions/src/lib.rs +++ b/crates/pgt_completions/src/lib.rs @@ -1,6 +1,5 @@ mod builder; mod complete; -mod context; mod item; mod providers; mod relevance; diff --git a/crates/pgt_completions/src/providers/columns.rs b/crates/pgt_completions/src/providers/columns.rs index 04d0af65..ba3b2481 100644 --- a/crates/pgt_completions/src/providers/columns.rs +++ b/crates/pgt_completions/src/providers/columns.rs @@ -1,14 +1,20 @@ +use pgt_schema_cache::SchemaCache; +use pgt_treesitter::{TreesitterContext, WrappingClause}; + use crate::{ CompletionItemKind, builder::{CompletionBuilder, PossibleCompletionItem}, - context::{CompletionContext, WrappingClause}, relevance::{CompletionRelevanceData, filtering::CompletionFilter, scoring::CompletionScore}, }; use super::helper::{find_matching_alias_for_table, get_completion_text_with_schema_or_alias}; -pub fn complete_columns<'a>(ctx: &CompletionContext<'a>, builder: &mut CompletionBuilder<'a>) { - let available_columns = &ctx.schema_cache.columns; +pub fn complete_columns<'a>( + ctx: &TreesitterContext<'a>, + schema_cache: &'a SchemaCache, + builder: &mut CompletionBuilder<'a>, +) { + let available_columns = &schema_cache.columns; for col in available_columns { let relevance = CompletionRelevanceData::Column(col); @@ -49,11 +55,13 @@ mod tests { use crate::{ CompletionItem, CompletionItemKind, complete, test_helper::{ - CURSOR_POS, CompletionAssertion, InputQuery, assert_complete_results, - assert_no_complete_results, get_test_deps, get_test_params, + CompletionAssertion, assert_complete_results, assert_no_complete_results, + get_test_deps, get_test_params, }, }; + use pgt_test_utils::QueryWithCursorPosition; + struct TestCase { query: String, message: &'static str, @@ -62,7 +70,7 @@ mod tests { } impl TestCase { - fn get_input_query(&self) -> InputQuery { + fn get_input_query(&self) -> QueryWithCursorPosition { let strs: Vec<&str> = self.query.split_whitespace().collect(); strs.join(" ").as_str().into() } @@ -94,7 +102,10 @@ mod tests { let queries: Vec = vec![ TestCase { message: "correctly prefers the columns of present tables", - query: format!(r#"select na{} from public.audio_books;"#, CURSOR_POS), + query: format!( + r#"select na{} from public.audio_books;"#, + QueryWithCursorPosition::cursor_marker() + ), label: "narrator", description: "public.audio_books", }, @@ -111,14 +122,17 @@ mod tests { join public.users u on u.id = subquery.id; "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ), label: "narrator_id", description: "private.audio_books", }, TestCase { message: "works without a schema", - query: format!(r#"select na{} from users;"#, CURSOR_POS), + query: format!( + r#"select na{} from users;"#, + QueryWithCursorPosition::cursor_marker() + ), label: "name", description: "public.users", }, @@ -165,7 +179,7 @@ mod tests { pool.execute(setup).await.unwrap(); let case = TestCase { - query: format!(r#"select n{};"#, CURSOR_POS), + query: format!(r#"select n{};"#, QueryWithCursorPosition::cursor_marker()), description: "", label: "", message: "", @@ -220,7 +234,10 @@ mod tests { let test_case = TestCase { message: "suggests user created tables first", - query: format!(r#"select {} from users"#, CURSOR_POS), + query: format!( + r#"select {} from users"#, + QueryWithCursorPosition::cursor_marker() + ), label: "", description: "", }; @@ -270,7 +287,10 @@ mod tests { let test_case = TestCase { message: "suggests user created tables first", - query: format!(r#"select * from private.{}"#, CURSOR_POS), + query: format!( + r#"select * from private.{}"#, + QueryWithCursorPosition::cursor_marker() + ), label: "", description: "", }; @@ -311,7 +331,11 @@ mod tests { pool.execute(setup).await.unwrap(); assert_complete_results( - format!(r#"select {} from users"#, CURSOR_POS).as_str(), + format!( + r#"select {} from users"#, + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), vec![ CompletionAssertion::Label("address2".into()), CompletionAssertion::Label("email2".into()), @@ -324,7 +348,11 @@ mod tests { .await; assert_complete_results( - format!(r#"select {} from private.users"#, CURSOR_POS).as_str(), + format!( + r#"select {} from private.users"#, + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), vec![ CompletionAssertion::Label("address1".into()), CompletionAssertion::Label("email1".into()), @@ -338,7 +366,11 @@ mod tests { // asserts fuzzy finding for "settings" assert_complete_results( - format!(r#"select sett{} from private.users"#, CURSOR_POS).as_str(), + format!( + r#"select sett{} from private.users"#, + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), vec![CompletionAssertion::Label("user_settings".into())], None, &pool, @@ -372,7 +404,7 @@ mod tests { assert_complete_results( format!( "select u.id, p.{} from auth.users u join auth.posts p on u.id = p.user_id;", - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ) .as_str(), vec![ @@ -394,7 +426,7 @@ mod tests { assert_complete_results( format!( "select u.id, p.content from auth.users u join auth.posts p on u.id = p.{};", - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ) .as_str(), vec![ @@ -440,7 +472,7 @@ mod tests { assert_complete_results( format!( "select u.id, p.content from auth.users u join auth.{}", - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ) .as_str(), vec![ @@ -479,7 +511,7 @@ mod tests { assert_complete_results( format!( "select u.id, auth.posts.content from auth.users u join auth.posts on u.{}", - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ) .as_str(), vec![ @@ -496,7 +528,7 @@ mod tests { assert_complete_results( format!( "select u.id, p.content from auth.users u join auth.posts p on p.user_id = u.{}", - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ) .as_str(), vec![ @@ -536,7 +568,7 @@ mod tests { assert_complete_results( format!( "select {} from public.one o join public.two on o.id = t.id;", - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ) .as_str(), vec![ @@ -555,7 +587,7 @@ mod tests { assert_complete_results( format!( "select a, {} from public.one o join public.two on o.id = t.id;", - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ) .as_str(), vec![ @@ -577,7 +609,7 @@ mod tests { assert_complete_results( format!( "select o.id, a, b, c, d, e, {} from public.one o join public.two on o.id = t.id;", - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ) .as_str(), vec![ @@ -593,7 +625,7 @@ mod tests { assert_complete_results( format!( "select id, a, b, c, d, e, {} from public.one o join public.two on o.id = t.id;", - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ) .as_str(), vec![CompletionAssertion::Label("z".to_string())], @@ -625,7 +657,11 @@ mod tests { // are lower in the alphabet assert_complete_results( - format!("insert into instruments ({})", CURSOR_POS).as_str(), + format!( + "insert into instruments ({})", + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), vec![ CompletionAssertion::Label("id".to_string()), CompletionAssertion::Label("name".to_string()), @@ -637,7 +673,11 @@ mod tests { .await; assert_complete_results( - format!("insert into instruments (id, {})", CURSOR_POS).as_str(), + format!( + "insert into instruments (id, {})", + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), vec![ CompletionAssertion::Label("name".to_string()), CompletionAssertion::Label("z".to_string()), @@ -648,7 +688,11 @@ mod tests { .await; assert_complete_results( - format!("insert into instruments (id, {}, name)", CURSOR_POS).as_str(), + format!( + "insert into instruments (id, {}, name)", + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), vec![CompletionAssertion::Label("z".to_string())], None, &pool, @@ -659,7 +703,7 @@ mod tests { assert_complete_results( format!( "insert into instruments (name, {}) values ('my_bass');", - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ) .as_str(), vec![ @@ -673,7 +717,11 @@ mod tests { // no completions in the values list! assert_no_complete_results( - format!("insert into instruments (id, name) values ({})", CURSOR_POS).as_str(), + format!( + "insert into instruments (id, name) values ({})", + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), None, &pool, ) @@ -700,7 +748,11 @@ mod tests { pool.execute(setup).await.unwrap(); assert_complete_results( - format!("select name from instruments where {} ", CURSOR_POS).as_str(), + format!( + "select name from instruments where {} ", + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), vec![ CompletionAssertion::Label("created_at".into()), CompletionAssertion::Label("id".into()), @@ -715,7 +767,7 @@ mod tests { assert_complete_results( format!( "select name from instruments where z = 'something' and created_at > {}", - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ) .as_str(), // simply do not complete columns + schemas; functions etc. are ok @@ -732,7 +784,7 @@ mod tests { assert_complete_results( format!( "select name from instruments where id = 'something' and {}", - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ) .as_str(), vec![ @@ -749,7 +801,7 @@ mod tests { assert_complete_results( format!( "select name from instruments i join others o on i.z = o.a where i.{}", - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ) .as_str(), vec![ @@ -783,22 +835,37 @@ mod tests { pool.execute(setup).await.unwrap(); let queries = vec![ - format!("alter table instruments drop column {}", CURSOR_POS), + format!( + "alter table instruments drop column {}", + QueryWithCursorPosition::cursor_marker() + ), format!( "alter table instruments drop column if exists {}", - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ), format!( "alter table instruments alter column {} set default", - CURSOR_POS + QueryWithCursorPosition::cursor_marker() + ), + format!( + "alter table instruments alter {} set default", + QueryWithCursorPosition::cursor_marker() + ), + format!( + "alter table public.instruments alter column {}", + QueryWithCursorPosition::cursor_marker() + ), + format!( + "alter table instruments alter {}", + QueryWithCursorPosition::cursor_marker() + ), + format!( + "alter table instruments rename {} to new_col", + QueryWithCursorPosition::cursor_marker() ), - format!("alter table instruments alter {} set default", CURSOR_POS), - format!("alter table public.instruments alter column {}", CURSOR_POS), - format!("alter table instruments alter {}", CURSOR_POS), - format!("alter table instruments rename {} to new_col", CURSOR_POS), format!( "alter table public.instruments rename column {} to new_col", - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ), ]; @@ -834,19 +901,19 @@ mod tests { let col_queries = vec![ format!( r#"create policy "my_pol" on public.instruments for select using ({})"#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ), format!( r#"create policy "my_pol" on public.instruments for insert with check ({})"#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ), format!( r#"create policy "my_pol" on public.instruments for update using (id = 1 and {})"#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ), format!( r#"create policy "my_pol" on public.instruments for insert with check (id = 1 and {})"#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ), ]; diff --git a/crates/pgt_completions/src/providers/functions.rs b/crates/pgt_completions/src/providers/functions.rs index 615e4f95..b2ac2fae 100644 --- a/crates/pgt_completions/src/providers/functions.rs +++ b/crates/pgt_completions/src/providers/functions.rs @@ -1,17 +1,21 @@ -use pgt_schema_cache::Function; +use pgt_schema_cache::{Function, SchemaCache}; +use pgt_treesitter::TreesitterContext; use crate::{ CompletionItemKind, CompletionText, builder::{CompletionBuilder, PossibleCompletionItem}, - context::CompletionContext, providers::helper::get_range_to_replace, relevance::{CompletionRelevanceData, filtering::CompletionFilter, scoring::CompletionScore}, }; use super::helper::get_completion_text_with_schema_or_alias; -pub fn complete_functions<'a>(ctx: &'a CompletionContext, builder: &mut CompletionBuilder<'a>) { - let available_functions = &ctx.schema_cache.functions; +pub fn complete_functions<'a>( + ctx: &'a TreesitterContext, + schema_cache: &'a SchemaCache, + builder: &mut CompletionBuilder<'a>, +) { + let available_functions = &schema_cache.functions; for func in available_functions { let relevance = CompletionRelevanceData::Function(func); @@ -30,7 +34,7 @@ pub fn complete_functions<'a>(ctx: &'a CompletionContext, builder: &mut Completi } } -fn get_completion_text(ctx: &CompletionContext, func: &Function) -> CompletionText { +fn get_completion_text(ctx: &TreesitterContext, func: &Function) -> CompletionText { let range = get_range_to_replace(ctx); let mut text = get_completion_text_with_schema_or_alias(ctx, &func.name, &func.schema) .map(|ct| ct.text) @@ -70,11 +74,12 @@ mod tests { use crate::{ CompletionItem, CompletionItemKind, complete, test_helper::{ - CURSOR_POS, CompletionAssertion, assert_complete_results, get_test_deps, - get_test_params, + CompletionAssertion, assert_complete_results, get_test_deps, get_test_params, }, }; + use pgt_test_utils::QueryWithCursorPosition; + #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] async fn completes_fn(pool: PgPool) { let setup = r#" @@ -89,7 +94,7 @@ mod tests { $$; "#; - let query = format!("select coo{}", CURSOR_POS); + let query = format!("select coo{}", QueryWithCursorPosition::cursor_marker()); let (tree, cache) = get_test_deps(Some(setup), query.as_str().into(), &pool).await; let params = get_test_params(&tree, &cache, query.as_str().into()); @@ -122,7 +127,10 @@ mod tests { $$; "#; - let query = format!(r#"select * from coo{}()"#, CURSOR_POS); + let query = format!( + r#"select * from coo{}()"#, + QueryWithCursorPosition::cursor_marker() + ); let (tree, cache) = get_test_deps(Some(setup), query.as_str().into(), &pool).await; let params = get_test_params(&tree, &cache, query.as_str().into()); @@ -156,7 +164,7 @@ mod tests { $$; "#; - let query = format!(r#"select coo{}"#, CURSOR_POS); + let query = format!(r#"select coo{}"#, QueryWithCursorPosition::cursor_marker()); let (tree, cache) = get_test_deps(Some(setup), query.as_str().into(), &pool).await; let params = get_test_params(&tree, &cache, query.as_str().into()); @@ -190,7 +198,10 @@ mod tests { $$; "#; - let query = format!(r#"select * from coo{}()"#, CURSOR_POS); + let query = format!( + r#"select * from coo{}()"#, + QueryWithCursorPosition::cursor_marker() + ); let (tree, cache) = get_test_deps(Some(setup), query.as_str().into(), &pool).await; let params = get_test_params(&tree, &cache, query.as_str().into()); @@ -259,7 +270,7 @@ mod tests { let query = format!( r#"create policy "my_pol" on public.instruments for insert with check (id = {})"#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ); assert_complete_results( diff --git a/crates/pgt_completions/src/providers/helper.rs b/crates/pgt_completions/src/providers/helper.rs index 811125bd..cd1046f1 100644 --- a/crates/pgt_completions/src/providers/helper.rs +++ b/crates/pgt_completions/src/providers/helper.rs @@ -1,9 +1,10 @@ use pgt_text_size::{TextRange, TextSize}; +use pgt_treesitter::TreesitterContext; -use crate::{CompletionText, context::CompletionContext, remove_sanitized_token}; +use crate::{CompletionText, remove_sanitized_token}; pub(crate) fn find_matching_alias_for_table( - ctx: &CompletionContext, + ctx: &TreesitterContext, table_name: &str, ) -> Option { for (alias, table) in ctx.mentioned_table_aliases.iter() { @@ -14,7 +15,7 @@ pub(crate) fn find_matching_alias_for_table( None } -pub(crate) fn get_range_to_replace(ctx: &CompletionContext) -> TextRange { +pub(crate) fn get_range_to_replace(ctx: &TreesitterContext) -> TextRange { match ctx.node_under_cursor.as_ref() { Some(node) => { let content = ctx.get_node_under_cursor_content().unwrap_or("".into()); @@ -30,7 +31,7 @@ pub(crate) fn get_range_to_replace(ctx: &CompletionContext) -> TextRange { } pub(crate) fn get_completion_text_with_schema_or_alias( - ctx: &CompletionContext, + ctx: &TreesitterContext, item_name: &str, schema_or_alias_name: &str, ) -> Option { diff --git a/crates/pgt_completions/src/providers/policies.rs b/crates/pgt_completions/src/providers/policies.rs index 216fcefa..a5ffdb43 100644 --- a/crates/pgt_completions/src/providers/policies.rs +++ b/crates/pgt_completions/src/providers/policies.rs @@ -1,16 +1,21 @@ +use pgt_schema_cache::SchemaCache; use pgt_text_size::{TextRange, TextSize}; +use pgt_treesitter::TreesitterContext; use crate::{ CompletionItemKind, CompletionText, builder::{CompletionBuilder, PossibleCompletionItem}, - context::CompletionContext, relevance::{CompletionRelevanceData, filtering::CompletionFilter, scoring::CompletionScore}, }; use super::helper::get_range_to_replace; -pub fn complete_policies<'a>(ctx: &CompletionContext<'a>, builder: &mut CompletionBuilder<'a>) { - let available_policies = &ctx.schema_cache.policies; +pub fn complete_policies<'a>( + ctx: &TreesitterContext<'a>, + schema_cache: &'a SchemaCache, + builder: &mut CompletionBuilder<'a>, +) { + let available_policies = &schema_cache.policies; let surrounded_by_quotes = ctx .get_node_under_cursor_content() @@ -61,7 +66,8 @@ pub fn complete_policies<'a>(ctx: &CompletionContext<'a>, builder: &mut Completi mod tests { use sqlx::{Executor, PgPool}; - use crate::test_helper::{CURSOR_POS, CompletionAssertion, assert_complete_results}; + use crate::test_helper::{CompletionAssertion, assert_complete_results}; + use pgt_test_utils::QueryWithCursorPosition; #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] async fn completes_within_quotation_marks(pool: PgPool) { @@ -89,7 +95,11 @@ mod tests { pool.execute(setup).await.unwrap(); assert_complete_results( - format!("alter policy \"{}\" on private.users;", CURSOR_POS).as_str(), + format!( + "alter policy \"{}\" on private.users;", + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), vec![ CompletionAssertion::Label("read for public users disallowed".into()), CompletionAssertion::Label("write for public users allowed".into()), @@ -100,7 +110,11 @@ mod tests { .await; assert_complete_results( - format!("alter policy \"w{}\" on private.users;", CURSOR_POS).as_str(), + format!( + "alter policy \"w{}\" on private.users;", + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), vec![CompletionAssertion::Label( "write for public users allowed".into(), )], diff --git a/crates/pgt_completions/src/providers/roles.rs b/crates/pgt_completions/src/providers/roles.rs index 01641543..b7664349 100644 --- a/crates/pgt_completions/src/providers/roles.rs +++ b/crates/pgt_completions/src/providers/roles.rs @@ -1,12 +1,17 @@ use crate::{ CompletionItemKind, builder::{CompletionBuilder, PossibleCompletionItem}, - context::CompletionContext, relevance::{CompletionRelevanceData, filtering::CompletionFilter, scoring::CompletionScore}, }; +use pgt_schema_cache::SchemaCache; +use pgt_treesitter::TreesitterContext; -pub fn complete_roles<'a>(ctx: &CompletionContext<'a>, builder: &mut CompletionBuilder<'a>) { - let available_roles = &ctx.schema_cache.roles; +pub fn complete_roles<'a>( + _ctx: &TreesitterContext<'a>, + schema_cache: &'a SchemaCache, + builder: &mut CompletionBuilder<'a>, +) { + let available_roles = &schema_cache.roles; for role in available_roles { let relevance = CompletionRelevanceData::Role(role); @@ -29,7 +34,9 @@ pub fn complete_roles<'a>(ctx: &CompletionContext<'a>, builder: &mut CompletionB mod tests { use sqlx::{Executor, PgPool}; - use crate::test_helper::{CURSOR_POS, CompletionAssertion, assert_complete_results}; + use crate::test_helper::{CompletionAssertion, assert_complete_results}; + + use pgt_test_utils::QueryWithCursorPosition; const SETUP: &str = r#" create table users ( @@ -42,7 +49,7 @@ mod tests { #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] async fn works_in_drop_role(pool: PgPool) { assert_complete_results( - format!("drop role {}", CURSOR_POS).as_str(), + format!("drop role {}", QueryWithCursorPosition::cursor_marker()).as_str(), vec![ CompletionAssertion::LabelAndKind("owner".into(), crate::CompletionItemKind::Role), CompletionAssertion::LabelAndKind( @@ -63,7 +70,7 @@ mod tests { #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] async fn works_in_alter_role(pool: PgPool) { assert_complete_results( - format!("alter role {}", CURSOR_POS).as_str(), + format!("alter role {}", QueryWithCursorPosition::cursor_marker()).as_str(), vec![ CompletionAssertion::LabelAndKind("owner".into(), crate::CompletionItemKind::Role), CompletionAssertion::LabelAndKind( @@ -86,7 +93,7 @@ mod tests { pool.execute(SETUP).await.unwrap(); assert_complete_results( - format!("set role {}", CURSOR_POS).as_str(), + format!("set role {}", QueryWithCursorPosition::cursor_marker()).as_str(), vec![ CompletionAssertion::LabelAndKind("owner".into(), crate::CompletionItemKind::Role), CompletionAssertion::LabelAndKind( @@ -104,7 +111,11 @@ mod tests { .await; assert_complete_results( - format!("set session authorization {}", CURSOR_POS).as_str(), + format!( + "set session authorization {}", + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), vec![ CompletionAssertion::LabelAndKind("owner".into(), crate::CompletionItemKind::Role), CompletionAssertion::LabelAndKind( @@ -133,7 +144,7 @@ mod tests { for all to {} using (true);"#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ) .as_str(), vec![ @@ -157,7 +168,7 @@ mod tests { r#"create policy "my cool policy" on public.users for select to {}"#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ) .as_str(), vec![ @@ -186,7 +197,7 @@ mod tests { r#"grant select on table public.users to {}"#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ) .as_str(), vec![ @@ -211,7 +222,7 @@ mod tests { r#"grant select on table public.users to owner, {}"#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ) .as_str(), vec![ @@ -232,7 +243,11 @@ mod tests { .await; assert_complete_results( - format!(r#"grant {} to owner"#, CURSOR_POS).as_str(), + format!( + r#"grant {} to owner"#, + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), vec![ // recognizing already mentioned roles is not supported for now CompletionAssertion::LabelAndKind("owner".into(), crate::CompletionItemKind::Role), @@ -256,12 +271,30 @@ mod tests { pool.execute(SETUP).await.unwrap(); let queries = vec![ - format!("revoke {} from owner", CURSOR_POS), - format!("revoke admin option for {} from owner", CURSOR_POS), - format!("revoke owner from {}", CURSOR_POS), - format!("revoke all on schema public from {} granted by", CURSOR_POS), - format!("revoke all on schema public from owner, {}", CURSOR_POS), - format!("revoke all on table userse from owner, {}", CURSOR_POS), + format!( + "revoke {} from owner", + QueryWithCursorPosition::cursor_marker() + ), + format!( + "revoke admin option for {} from owner", + QueryWithCursorPosition::cursor_marker() + ), + format!( + "revoke owner from {}", + QueryWithCursorPosition::cursor_marker() + ), + format!( + "revoke all on schema public from {} granted by", + QueryWithCursorPosition::cursor_marker() + ), + format!( + "revoke all on schema public from owner, {}", + QueryWithCursorPosition::cursor_marker() + ), + format!( + "revoke all on table userse from owner, {}", + QueryWithCursorPosition::cursor_marker() + ), ]; for query in queries { diff --git a/crates/pgt_completions/src/providers/schemas.rs b/crates/pgt_completions/src/providers/schemas.rs index 561da0f8..43c52387 100644 --- a/crates/pgt_completions/src/providers/schemas.rs +++ b/crates/pgt_completions/src/providers/schemas.rs @@ -1,11 +1,16 @@ use crate::{ builder::{CompletionBuilder, PossibleCompletionItem}, - context::CompletionContext, relevance::{CompletionRelevanceData, filtering::CompletionFilter, scoring::CompletionScore}, }; +use pgt_schema_cache::SchemaCache; +use pgt_treesitter::TreesitterContext; -pub fn complete_schemas<'a>(ctx: &'a CompletionContext, builder: &mut CompletionBuilder<'a>) { - let available_schemas = &ctx.schema_cache.schemas; +pub fn complete_schemas<'a>( + _ctx: &'a TreesitterContext, + schema_cache: &'a SchemaCache, + builder: &mut CompletionBuilder<'a>, +) { + let available_schemas = &schema_cache.schemas; for schema in available_schemas { let relevance = CompletionRelevanceData::Schema(schema); @@ -31,9 +36,11 @@ mod tests { use crate::{ CompletionItemKind, - test_helper::{CURSOR_POS, CompletionAssertion, assert_complete_results}, + test_helper::{CompletionAssertion, assert_complete_results}, }; + use pgt_test_utils::QueryWithCursorPosition; + #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] async fn autocompletes_schemas(pool: PgPool) { let setup = r#" @@ -50,7 +57,7 @@ mod tests { "#; assert_complete_results( - format!("select * from {}", CURSOR_POS).as_str(), + format!("select * from {}", QueryWithCursorPosition::cursor_marker()).as_str(), vec![ CompletionAssertion::LabelAndKind("public".to_string(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("auth".to_string(), CompletionItemKind::Schema), @@ -97,7 +104,11 @@ mod tests { "#; assert_complete_results( - format!("select * from u{}", CURSOR_POS).as_str(), + format!( + "select * from u{}", + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), vec![ CompletionAssertion::LabelAndKind("users".into(), CompletionItemKind::Table), CompletionAssertion::LabelAndKind("ultimate".into(), CompletionItemKind::Schema), diff --git a/crates/pgt_completions/src/providers/tables.rs b/crates/pgt_completions/src/providers/tables.rs index 3fbee8f1..f78b697c 100644 --- a/crates/pgt_completions/src/providers/tables.rs +++ b/crates/pgt_completions/src/providers/tables.rs @@ -1,14 +1,20 @@ +use pgt_schema_cache::SchemaCache; +use pgt_treesitter::TreesitterContext; + use crate::{ builder::{CompletionBuilder, PossibleCompletionItem}, - context::CompletionContext, item::CompletionItemKind, relevance::{CompletionRelevanceData, filtering::CompletionFilter, scoring::CompletionScore}, }; use super::helper::get_completion_text_with_schema_or_alias; -pub fn complete_tables<'a>(ctx: &'a CompletionContext, builder: &mut CompletionBuilder<'a>) { - let available_tables = &ctx.schema_cache.tables; +pub fn complete_tables<'a>( + ctx: &'a TreesitterContext, + schema_cache: &'a SchemaCache, + builder: &mut CompletionBuilder<'a>, +) { + let available_tables = &schema_cache.tables; for table in available_tables { let relevance = CompletionRelevanceData::Table(table); @@ -47,11 +53,13 @@ mod tests { use crate::{ CompletionItem, CompletionItemKind, complete, test_helper::{ - CURSOR_POS, CompletionAssertion, assert_complete_results, assert_no_complete_results, + CompletionAssertion, assert_complete_results, assert_no_complete_results, get_test_deps, get_test_params, }, }; + use pgt_test_utils::QueryWithCursorPosition; + #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] async fn autocompletes_simple_table(pool: PgPool) { let setup = r#" @@ -62,7 +70,10 @@ mod tests { ); "#; - let query = format!("select * from u{}", CURSOR_POS); + let query = format!( + "select * from u{}", + QueryWithCursorPosition::cursor_marker() + ); let (tree, cache) = get_test_deps(Some(setup), query.as_str().into(), &pool).await; let params = get_test_params(&tree, &cache, query.as_str().into()); @@ -98,9 +109,27 @@ mod tests { pool.execute(setup).await.unwrap(); let test_cases = vec![ - (format!("select * from u{}", CURSOR_POS), "users"), - (format!("select * from e{}", CURSOR_POS), "emails"), - (format!("select * from a{}", CURSOR_POS), "addresses"), + ( + format!( + "select * from u{}", + QueryWithCursorPosition::cursor_marker() + ), + "users", + ), + ( + format!( + "select * from e{}", + QueryWithCursorPosition::cursor_marker() + ), + "emails", + ), + ( + format!( + "select * from a{}", + QueryWithCursorPosition::cursor_marker() + ), + "addresses", + ), ]; for (query, expected_label) in test_cases { @@ -142,10 +171,25 @@ mod tests { pool.execute(setup).await.unwrap(); let test_cases = vec![ - (format!("select * from u{}", CURSOR_POS), "user_y"), // user_y is preferred alphanumerically - (format!("select * from private.u{}", CURSOR_POS), "user_z"), ( - format!("select * from customer_support.u{}", CURSOR_POS), + format!( + "select * from u{}", + QueryWithCursorPosition::cursor_marker() + ), + "user_y", + ), // user_y is preferred alphanumerically + ( + format!( + "select * from private.u{}", + QueryWithCursorPosition::cursor_marker() + ), + "user_z", + ), + ( + format!( + "select * from customer_support.u{}", + QueryWithCursorPosition::cursor_marker() + ), "user_y", ), ]; @@ -186,7 +230,10 @@ mod tests { $$; "#; - let query = format!(r#"select * from coo{}"#, CURSOR_POS); + let query = format!( + r#"select * from coo{}"#, + QueryWithCursorPosition::cursor_marker() + ); let (tree, cache) = get_test_deps(Some(setup), query.as_str().into(), &pool).await; let params = get_test_params(&tree, &cache, query.as_str().into()); @@ -213,7 +260,7 @@ mod tests { pool.execute(setup).await.unwrap(); assert_complete_results( - format!("update {}", CURSOR_POS).as_str(), + format!("update {}", QueryWithCursorPosition::cursor_marker()).as_str(), vec![CompletionAssertion::LabelAndKind( "public".into(), CompletionItemKind::Schema, @@ -224,7 +271,7 @@ mod tests { .await; assert_complete_results( - format!("update public.{}", CURSOR_POS).as_str(), + format!("update public.{}", QueryWithCursorPosition::cursor_marker()).as_str(), vec![CompletionAssertion::LabelAndKind( "coos".into(), CompletionItemKind::Table, @@ -235,14 +282,22 @@ mod tests { .await; assert_no_complete_results( - format!("update public.coos {}", CURSOR_POS).as_str(), + format!( + "update public.coos {}", + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), None, &pool, ) .await; assert_complete_results( - format!("update coos set {}", CURSOR_POS).as_str(), + format!( + "update coos set {}", + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), vec![ CompletionAssertion::Label("id".into()), CompletionAssertion::Label("name".into()), @@ -253,7 +308,11 @@ mod tests { .await; assert_complete_results( - format!("update coos set name = 'cool' where {}", CURSOR_POS).as_str(), + format!( + "update coos set name = 'cool' where {}", + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), vec![ CompletionAssertion::Label("id".into()), CompletionAssertion::Label("name".into()), @@ -275,10 +334,15 @@ mod tests { pool.execute(setup).await.unwrap(); - assert_no_complete_results(format!("delete {}", CURSOR_POS).as_str(), None, &pool).await; + assert_no_complete_results( + format!("delete {}", QueryWithCursorPosition::cursor_marker()).as_str(), + None, + &pool, + ) + .await; assert_complete_results( - format!("delete from {}", CURSOR_POS).as_str(), + format!("delete from {}", QueryWithCursorPosition::cursor_marker()).as_str(), vec![ CompletionAssertion::LabelAndKind("public".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("coos".into(), CompletionItemKind::Table), @@ -289,7 +353,11 @@ mod tests { .await; assert_complete_results( - format!("delete from public.{}", CURSOR_POS).as_str(), + format!( + "delete from public.{}", + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), vec![CompletionAssertion::Label("coos".into())], None, &pool, @@ -297,7 +365,11 @@ mod tests { .await; assert_complete_results( - format!("delete from public.coos where {}", CURSOR_POS).as_str(), + format!( + "delete from public.coos where {}", + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), vec![ CompletionAssertion::Label("id".into()), CompletionAssertion::Label("name".into()), @@ -329,7 +401,11 @@ mod tests { "#; assert_complete_results( - format!("select * from auth.users u join {}", CURSOR_POS).as_str(), + format!( + "select * from auth.users u join {}", + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), vec![ CompletionAssertion::LabelAndKind("public".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("auth".into(), CompletionItemKind::Schema), @@ -365,7 +441,7 @@ mod tests { pool.execute(setup).await.unwrap(); assert_complete_results( - format!("alter table {}", CURSOR_POS).as_str(), + format!("alter table {}", QueryWithCursorPosition::cursor_marker()).as_str(), vec![ CompletionAssertion::LabelAndKind("public".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("auth".into(), CompletionItemKind::Schema), @@ -378,7 +454,11 @@ mod tests { .await; assert_complete_results( - format!("alter table if exists {}", CURSOR_POS).as_str(), + format!( + "alter table if exists {}", + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), vec![ CompletionAssertion::LabelAndKind("public".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("auth".into(), CompletionItemKind::Schema), @@ -391,7 +471,7 @@ mod tests { .await; assert_complete_results( - format!("drop table {}", CURSOR_POS).as_str(), + format!("drop table {}", QueryWithCursorPosition::cursor_marker()).as_str(), vec![ CompletionAssertion::LabelAndKind("public".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("auth".into(), CompletionItemKind::Schema), @@ -404,7 +484,11 @@ mod tests { .await; assert_complete_results( - format!("drop table if exists {}", CURSOR_POS).as_str(), + format!( + "drop table if exists {}", + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), vec![ CompletionAssertion::LabelAndKind("public".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("auth".into(), CompletionItemKind::Schema), @@ -432,7 +516,7 @@ mod tests { pool.execute(setup).await.unwrap(); assert_complete_results( - format!("insert into {}", CURSOR_POS).as_str(), + format!("insert into {}", QueryWithCursorPosition::cursor_marker()).as_str(), vec![ CompletionAssertion::LabelAndKind("public".into(), CompletionItemKind::Schema), CompletionAssertion::LabelAndKind("auth".into(), CompletionItemKind::Schema), @@ -444,7 +528,11 @@ mod tests { .await; assert_complete_results( - format!("insert into auth.{}", CURSOR_POS).as_str(), + format!( + "insert into auth.{}", + QueryWithCursorPosition::cursor_marker() + ) + .as_str(), vec![CompletionAssertion::LabelAndKind( "users".into(), CompletionItemKind::Table, @@ -458,7 +546,7 @@ mod tests { assert_complete_results( format!( "insert into {} (name, email) values ('jules', 'a@b.com');", - CURSOR_POS + QueryWithCursorPosition::cursor_marker() ) .as_str(), vec![ diff --git a/crates/pgt_completions/src/providers/triggers.rs b/crates/pgt_completions/src/providers/triggers.rs deleted file mode 100644 index 6bc04deb..00000000 --- a/crates/pgt_completions/src/providers/triggers.rs +++ /dev/null @@ -1,169 +0,0 @@ -use crate::{ - CompletionItemKind, - builder::{CompletionBuilder, PossibleCompletionItem}, - context::CompletionContext, - relevance::{CompletionRelevanceData, filtering::CompletionFilter, scoring::CompletionScore}, -}; - -use super::helper::get_completion_text_with_schema_or_alias; - -pub fn complete_functions<'a>(ctx: &'a CompletionContext, builder: &mut CompletionBuilder<'a>) { - let available_functions = &ctx.schema_cache.functions; - - for func in available_functions { - let relevance = CompletionRelevanceData::Function(func); - - let item = PossibleCompletionItem { - label: func.name.clone(), - score: CompletionScore::from(relevance.clone()), - filter: CompletionFilter::from(relevance), - description: format!("Schema: {}", func.schema), - kind: CompletionItemKind::Function, - completion_text: get_completion_text_with_schema_or_alias( - ctx, - &func.name, - &func.schema, - ), - }; - - builder.add_item(item); - } -} - -#[cfg(test)] -mod tests { - use crate::{ - CompletionItem, CompletionItemKind, complete, - test_helper::{CURSOR_POS, get_test_deps, get_test_params}, - }; - - #[tokio::test] - async fn completes_fn() { - let setup = r#" - create or replace function cool() - returns trigger - language plpgsql - security invoker - as $$ - begin - raise exception 'dont matter'; - end; - $$; - "#; - - let query = format!("select coo{}", CURSOR_POS); - - let (tree, cache) = get_test_deps(setup, query.as_str().into()).await; - let params = get_test_params(&tree, &cache, query.as_str().into()); - let results = complete(params); - - let CompletionItem { label, .. } = results - .into_iter() - .next() - .expect("Should return at least one completion item"); - - assert_eq!(label, "cool"); - } - - #[tokio::test] - async fn prefers_fn_if_invocation() { - let setup = r#" - create table coos ( - id serial primary key, - name text - ); - - create or replace function cool() - returns trigger - language plpgsql - security invoker - as $$ - begin - raise exception 'dont matter'; - end; - $$; - "#; - - let query = format!(r#"select * from coo{}()"#, CURSOR_POS); - - let (tree, cache) = get_test_deps(setup, query.as_str().into()).await; - let params = get_test_params(&tree, &cache, query.as_str().into()); - let results = complete(params); - - let CompletionItem { label, kind, .. } = results - .into_iter() - .next() - .expect("Should return at least one completion item"); - - assert_eq!(label, "cool"); - assert_eq!(kind, CompletionItemKind::Function); - } - - #[tokio::test] - async fn prefers_fn_in_select_clause() { - let setup = r#" - create table coos ( - id serial primary key, - name text - ); - - create or replace function cool() - returns trigger - language plpgsql - security invoker - as $$ - begin - raise exception 'dont matter'; - end; - $$; - "#; - - let query = format!(r#"select coo{}"#, CURSOR_POS); - - let (tree, cache) = get_test_deps(setup, query.as_str().into()).await; - let params = get_test_params(&tree, &cache, query.as_str().into()); - let results = complete(params); - - let CompletionItem { label, kind, .. } = results - .into_iter() - .next() - .expect("Should return at least one completion item"); - - assert_eq!(label, "cool"); - assert_eq!(kind, CompletionItemKind::Function); - } - - #[tokio::test] - async fn prefers_function_in_from_clause_if_invocation() { - let setup = r#" - create table coos ( - id serial primary key, - name text - ); - - create or replace function cool() - returns trigger - language plpgsql - security invoker - as $$ - begin - raise exception 'dont matter'; - end; - $$; - "#; - - let query = format!(r#"select * from coo{}()"#, CURSOR_POS); - - let (tree, cache) = get_test_deps(setup, query.as_str().into()).await; - let params = get_test_params(&tree, &cache, query.as_str().into()); - let results = complete(params); - - let CompletionItem { label, kind, .. } = results - .into_iter() - .next() - .expect("Should return at least one completion item"); - - assert_eq!(label, "cool"); - assert_eq!(kind, CompletionItemKind::Function); - } -} diff --git a/crates/pgt_completions/src/relevance/filtering.rs b/crates/pgt_completions/src/relevance/filtering.rs index beea6ddb..18e3d7ce 100644 --- a/crates/pgt_completions/src/relevance/filtering.rs +++ b/crates/pgt_completions/src/relevance/filtering.rs @@ -1,6 +1,6 @@ use pgt_schema_cache::ProcKind; -use crate::context::{CompletionContext, NodeUnderCursor, WrappingClause, WrappingNode}; +use pgt_treesitter::context::{NodeUnderCursor, TreesitterContext, WrappingClause, WrappingNode}; use super::CompletionRelevanceData; @@ -16,7 +16,7 @@ impl<'a> From> for CompletionFilter<'a> { } impl CompletionFilter<'_> { - pub fn is_relevant(&self, ctx: &CompletionContext) -> Option<()> { + pub fn is_relevant(&self, ctx: &TreesitterContext) -> Option<()> { self.completable_context(ctx)?; self.check_clause(ctx)?; self.check_invocation(ctx)?; @@ -25,7 +25,7 @@ impl CompletionFilter<'_> { Some(()) } - fn completable_context(&self, ctx: &CompletionContext) -> Option<()> { + fn completable_context(&self, ctx: &TreesitterContext) -> Option<()> { if ctx.wrapping_node_kind.is_none() && ctx.wrapping_clause_type.is_none() { return None; } @@ -70,7 +70,7 @@ impl CompletionFilter<'_> { Some(()) } - fn check_clause(&self, ctx: &CompletionContext) -> Option<()> { + fn check_clause(&self, ctx: &TreesitterContext) -> Option<()> { ctx.wrapping_clause_type .as_ref() .map(|clause| { @@ -208,7 +208,7 @@ impl CompletionFilter<'_> { .and_then(|is_ok| if is_ok { Some(()) } else { None }) } - fn check_invocation(&self, ctx: &CompletionContext) -> Option<()> { + fn check_invocation(&self, ctx: &TreesitterContext) -> Option<()> { if !ctx.is_invocation { return Some(()); } @@ -221,7 +221,7 @@ impl CompletionFilter<'_> { Some(()) } - fn check_mentioned_schema_or_alias(&self, ctx: &CompletionContext) -> Option<()> { + fn check_mentioned_schema_or_alias(&self, ctx: &TreesitterContext) -> Option<()> { if ctx.schema_or_alias_name.is_none() { return Some(()); } @@ -255,9 +255,11 @@ mod tests { use sqlx::{Executor, PgPool}; use crate::test_helper::{ - CURSOR_POS, CompletionAssertion, assert_complete_results, assert_no_complete_results, + CompletionAssertion, assert_complete_results, assert_no_complete_results, }; + use pgt_test_utils::QueryWithCursorPosition; + #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] async fn completion_after_asterisk(pool: PgPool) { let setup = r#" @@ -270,11 +272,16 @@ mod tests { pool.execute(setup).await.unwrap(); - assert_no_complete_results(format!("select * {}", CURSOR_POS).as_str(), None, &pool).await; + assert_no_complete_results( + format!("select * {}", QueryWithCursorPosition::cursor_marker()).as_str(), + None, + &pool, + ) + .await; // if there s a COMMA after the asterisk, we're good assert_complete_results( - format!("select *, {}", CURSOR_POS).as_str(), + format!("select *, {}", QueryWithCursorPosition::cursor_marker()).as_str(), vec![ CompletionAssertion::Label("address".into()), CompletionAssertion::Label("email".into()), @@ -288,13 +295,20 @@ mod tests { #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] async fn completion_after_create_table(pool: PgPool) { - assert_no_complete_results(format!("create table {}", CURSOR_POS).as_str(), None, &pool) - .await; + assert_no_complete_results( + format!("create table {}", QueryWithCursorPosition::cursor_marker()).as_str(), + None, + &pool, + ) + .await; } #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] async fn completion_in_column_definitions(pool: PgPool) { - let query = format!(r#"create table instruments ( {} )"#, CURSOR_POS); + let query = format!( + r#"create table instruments ( {} )"#, + QueryWithCursorPosition::cursor_marker() + ); assert_no_complete_results(query.as_str(), None, &pool).await; } } diff --git a/crates/pgt_completions/src/relevance/scoring.rs b/crates/pgt_completions/src/relevance/scoring.rs index a0b5efa5..4bbf325f 100644 --- a/crates/pgt_completions/src/relevance/scoring.rs +++ b/crates/pgt_completions/src/relevance/scoring.rs @@ -1,6 +1,8 @@ use fuzzy_matcher::{FuzzyMatcher, skim::SkimMatcherV2}; -use crate::context::{CompletionContext, WrappingClause, WrappingNode}; +use pgt_treesitter::context::{TreesitterContext, WrappingClause, WrappingNode}; + +use crate::sanitization; use super::CompletionRelevanceData; @@ -24,7 +26,7 @@ impl CompletionScore<'_> { self.score } - pub fn calc_score(&mut self, ctx: &CompletionContext) { + pub fn calc_score(&mut self, ctx: &TreesitterContext) { self.check_is_user_defined(); self.check_matches_schema(ctx); self.check_matches_query_input(ctx); @@ -35,10 +37,10 @@ impl CompletionScore<'_> { self.check_columns_in_stmt(ctx); } - fn check_matches_query_input(&mut self, ctx: &CompletionContext) { + fn check_matches_query_input(&mut self, ctx: &TreesitterContext) { let content = match ctx.get_node_under_cursor_content() { - Some(c) => c.replace('"', ""), - None => return, + Some(c) if !sanitization::is_sanitized_token(c.as_str()) => c.replace('"', ""), + _ => return, }; let name = match self.data { @@ -69,7 +71,7 @@ impl CompletionScore<'_> { } } - fn check_matching_clause_type(&mut self, ctx: &CompletionContext) { + fn check_matching_clause_type(&mut self, ctx: &TreesitterContext) { let clause_type = match ctx.wrapping_clause_type.as_ref() { None => return, Some(ct) => ct, @@ -135,14 +137,16 @@ impl CompletionScore<'_> { } } - fn check_matching_wrapping_node(&mut self, ctx: &CompletionContext) { + fn check_matching_wrapping_node(&mut self, ctx: &TreesitterContext) { let wrapping_node = match ctx.wrapping_node_kind.as_ref() { None => return, Some(wn) => wn, }; let has_mentioned_schema = ctx.schema_or_alias_name.is_some(); - let has_node_text = ctx.get_node_under_cursor_content().is_some(); + let has_node_text = ctx + .get_node_under_cursor_content() + .is_some_and(|txt| !sanitization::is_sanitized_token(txt.as_str())); self.score += match self.data { CompletionRelevanceData::Table(_) => match wrapping_node { @@ -170,7 +174,7 @@ impl CompletionScore<'_> { } } - fn check_is_invocation(&mut self, ctx: &CompletionContext) { + fn check_is_invocation(&mut self, ctx: &TreesitterContext) { self.score += match self.data { CompletionRelevanceData::Function(_) if ctx.is_invocation => 30, CompletionRelevanceData::Function(_) if !ctx.is_invocation => -10, @@ -179,7 +183,7 @@ impl CompletionScore<'_> { }; } - fn check_matches_schema(&mut self, ctx: &CompletionContext) { + fn check_matches_schema(&mut self, ctx: &TreesitterContext) { let schema_name = match ctx.schema_or_alias_name.as_ref() { None => return, Some(n) => n, @@ -228,7 +232,7 @@ impl CompletionScore<'_> { } } - fn check_relations_in_stmt(&mut self, ctx: &CompletionContext) { + fn check_relations_in_stmt(&mut self, ctx: &TreesitterContext) { match self.data { CompletionRelevanceData::Table(_) | CompletionRelevanceData::Function(_) => return, _ => {} @@ -312,7 +316,7 @@ impl CompletionScore<'_> { } } - fn check_columns_in_stmt(&mut self, ctx: &CompletionContext) { + fn check_columns_in_stmt(&mut self, ctx: &TreesitterContext) { if let CompletionRelevanceData::Column(column) = self.data { /* * Columns can be mentioned in one of two ways: diff --git a/crates/pgt_completions/src/sanitization.rs b/crates/pgt_completions/src/sanitization.rs index bf4d9816..155256c8 100644 --- a/crates/pgt_completions/src/sanitization.rs +++ b/crates/pgt_completions/src/sanitization.rs @@ -23,6 +23,10 @@ pub(crate) fn remove_sanitized_token(it: &str) -> String { it.replace(SANITIZED_TOKEN, "") } +pub(crate) fn is_sanitized_token(txt: &str) -> bool { + txt == SANITIZED_TOKEN +} + #[derive(PartialEq, Eq, Debug)] pub(crate) enum NodeText { Replaced, @@ -118,10 +122,6 @@ where tree: Cow::Borrowed(params.tree), } } - - pub fn is_sanitized_token(txt: &str) -> bool { - txt == SANITIZED_TOKEN - } } /// Checks if the cursor is positioned inbetween two SQL nodes. diff --git a/crates/pgt_completions/src/test_helper.rs b/crates/pgt_completions/src/test_helper.rs index 1bd5229c..e6c34761 100644 --- a/crates/pgt_completions/src/test_helper.rs +++ b/crates/pgt_completions/src/test_helper.rs @@ -1,40 +1,12 @@ -use std::fmt::Display; - use pgt_schema_cache::SchemaCache; +use pgt_test_utils::QueryWithCursorPosition; use sqlx::{Executor, PgPool}; use crate::{CompletionItem, CompletionItemKind, CompletionParams, complete}; -pub static CURSOR_POS: char = '€'; - -#[derive(Clone)] -pub struct InputQuery { - sql: String, - position: usize, -} - -impl From<&str> for InputQuery { - fn from(value: &str) -> Self { - let position = value - .find(CURSOR_POS) - .expect("Insert Cursor Position into your Query."); - - InputQuery { - sql: value.replace(CURSOR_POS, "").trim().to_string(), - position, - } - } -} - -impl Display for InputQuery { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.sql) - } -} - pub(crate) async fn get_test_deps( setup: Option<&str>, - input: InputQuery, + input: QueryWithCursorPosition, test_db: &PgPool, ) -> (tree_sitter::Tree, pgt_schema_cache::SchemaCache) { if let Some(setup) = setup { @@ -63,7 +35,7 @@ pub(crate) async fn get_test_deps( #[allow(dead_code)] pub(crate) async fn test_against_connection_string( conn_str: &str, - input: InputQuery, + input: QueryWithCursorPosition, ) -> (tree_sitter::Tree, pgt_schema_cache::SchemaCache) { let pool = sqlx::PgPool::connect(conn_str) .await @@ -83,16 +55,12 @@ pub(crate) async fn test_against_connection_string( (tree, schema_cache) } -pub(crate) fn get_text_and_position(q: InputQuery) -> (usize, String) { - (q.position, q.sql) -} - pub(crate) fn get_test_params<'a>( tree: &'a tree_sitter::Tree, schema_cache: &'a pgt_schema_cache::SchemaCache, - sql: InputQuery, + sql: QueryWithCursorPosition, ) -> CompletionParams<'a> { - let (position, text) = get_text_and_position(sql); + let (position, text) = sql.get_text_and_position(); CompletionParams { position: (position as u32).into(), @@ -102,46 +70,6 @@ pub(crate) fn get_test_params<'a>( } } -#[cfg(test)] -mod tests { - use crate::test_helper::CURSOR_POS; - - use super::InputQuery; - - #[test] - fn input_query_should_extract_correct_position() { - struct TestCase { - query: String, - expected_pos: usize, - expected_sql_len: usize, - } - - let cases = vec![ - TestCase { - query: format!("select * from{}", CURSOR_POS), - expected_pos: 13, - expected_sql_len: 13, - }, - TestCase { - query: format!("{}select * from", CURSOR_POS), - expected_pos: 0, - expected_sql_len: 13, - }, - TestCase { - query: format!("select {} from", CURSOR_POS), - expected_pos: 7, - expected_sql_len: 12, - }, - ]; - - for case in cases { - let query = InputQuery::from(case.query.as_str()); - assert_eq!(query.position, case.expected_pos); - assert_eq!(query.sql.len(), case.expected_sql_len); - } - } -} - #[derive(Debug, PartialEq, Eq)] pub(crate) enum CompletionAssertion { Label(String), diff --git a/crates/pgt_test_utils/src/lib.rs b/crates/pgt_test_utils/src/lib.rs index e21c6ce4..11bb1aeb 100644 --- a/crates/pgt_test_utils/src/lib.rs +++ b/crates/pgt_test_utils/src/lib.rs @@ -1 +1,85 @@ +use std::fmt::Display; + pub static MIGRATIONS: sqlx::migrate::Migrator = sqlx::migrate!("./testdb_migrations"); + +static CURSOR_POS: char = '€'; + +#[derive(Clone)] +pub struct QueryWithCursorPosition { + sql: String, + position: usize, +} + +impl QueryWithCursorPosition { + pub fn cursor_marker() -> char { + CURSOR_POS + } + + pub fn get_text_and_position(&self) -> (usize, String) { + (self.position, self.sql.clone()) + } +} + +impl From for QueryWithCursorPosition { + fn from(value: String) -> Self { + value.as_str().into() + } +} + +impl From<&str> for QueryWithCursorPosition { + fn from(value: &str) -> Self { + let position = value + .find(CURSOR_POS) + .expect("Use `QueryWithCursorPosition::cursor_marker()` to insert cursor position into your Query."); + + QueryWithCursorPosition { + sql: value.replace(CURSOR_POS, "").trim().to_string(), + position, + } + } +} + +impl Display for QueryWithCursorPosition { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.sql) + } +} + +#[cfg(test)] +mod tests { + + use super::QueryWithCursorPosition; + + #[test] + fn input_query_should_extract_correct_position() { + struct TestCase { + query: String, + expected_pos: usize, + expected_sql_len: usize, + } + + let cases = vec![ + TestCase { + query: format!("select * from{}", QueryWithCursorPosition::cursor_marker()), + expected_pos: 13, + expected_sql_len: 13, + }, + TestCase { + query: format!("{}select * from", QueryWithCursorPosition::cursor_marker()), + expected_pos: 0, + expected_sql_len: 13, + }, + TestCase { + query: format!("select {} from", QueryWithCursorPosition::cursor_marker()), + expected_pos: 7, + expected_sql_len: 12, + }, + ]; + + for case in cases { + let query = QueryWithCursorPosition::from(case.query.as_str()); + assert_eq!(query.position, case.expected_pos); + assert_eq!(query.sql.len(), case.expected_sql_len); + } + } +} diff --git a/crates/pgt_treesitter_queries/Cargo.toml b/crates/pgt_treesitter/Cargo.toml similarity index 54% rename from crates/pgt_treesitter_queries/Cargo.toml rename to crates/pgt_treesitter/Cargo.toml index 5806861f..f2d8b46e 100644 --- a/crates/pgt_treesitter_queries/Cargo.toml +++ b/crates/pgt_treesitter/Cargo.toml @@ -6,17 +6,20 @@ edition.workspace = true homepage.workspace = true keywords.workspace = true license.workspace = true -name = "pgt_treesitter_queries" +name = "pgt_treesitter" repository.workspace = true version = "0.0.0" [dependencies] -clap = { version = "4.5.23", features = ["derive"] } -tree-sitter.workspace = true -tree_sitter_sql.workspace = true +clap = { version = "4.5.23", features = ["derive"] } +pgt_schema_cache.workspace = true +pgt_text_size.workspace = true +tree-sitter.workspace = true +tree_sitter_sql.workspace = true [dev-dependencies] +pgt_test_utils.workspace = true [lib] doctest = false diff --git a/crates/pgt_completions/src/context/base_parser.rs b/crates/pgt_treesitter/src/context/base_parser.rs similarity index 100% rename from crates/pgt_completions/src/context/base_parser.rs rename to crates/pgt_treesitter/src/context/base_parser.rs diff --git a/crates/pgt_completions/src/context/grant_parser.rs b/crates/pgt_treesitter/src/context/grant_parser.rs similarity index 94% rename from crates/pgt_completions/src/context/grant_parser.rs rename to crates/pgt_treesitter/src/context/grant_parser.rs index 14ba882a..c9aebc33 100644 --- a/crates/pgt_completions/src/context/grant_parser.rs +++ b/crates/pgt_treesitter/src/context/grant_parser.rs @@ -187,14 +187,15 @@ mod tests { use crate::{ context::base_parser::CompletionStatementParser, context::grant_parser::{GrantContext, GrantParser}, - test_helper::CURSOR_POS, }; + use pgt_test_utils::QueryWithCursorPosition; + fn with_pos(query: String) -> (usize, String) { let mut pos: Option = None; for (p, c) in query.char_indices() { - if c == CURSOR_POS { + if c == QueryWithCursorPosition::cursor_marker() { pos = Some(p); break; } @@ -202,7 +203,9 @@ mod tests { ( pos.expect("Please add cursor position!"), - query.replace(CURSOR_POS, "REPLACED_TOKEN").to_string(), + query + .replace(QueryWithCursorPosition::cursor_marker(), "REPLACED_TOKEN") + .to_string(), ) } @@ -212,7 +215,7 @@ mod tests { r#" grant {} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = GrantParser::get_context(query.as_str(), pos); @@ -235,7 +238,7 @@ mod tests { r#" grant select on {} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = GrantParser::get_context(query.as_str(), pos); @@ -258,7 +261,7 @@ mod tests { r#" grant select on table {} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = GrantParser::get_context(query.as_str(), pos); @@ -281,7 +284,7 @@ mod tests { r#" grant select on public.{} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = GrantParser::get_context(query.as_str(), pos); @@ -304,7 +307,7 @@ mod tests { r#" grant select on table public.{} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = GrantParser::get_context(query.as_str(), pos); @@ -327,7 +330,7 @@ mod tests { r#" grant select on public.users to {} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = GrantParser::get_context(query.as_str(), pos); @@ -350,7 +353,7 @@ mod tests { r#" grant select on public.{} to test_role "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = GrantParser::get_context(query.as_str(), pos); @@ -373,7 +376,7 @@ mod tests { r#" grant select on "MySchema"."MyTable" to {} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = GrantParser::get_context(query.as_str(), pos); @@ -396,7 +399,7 @@ mod tests { r#" grant select on public.users to alice, {} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = GrantParser::get_context(query.as_str(), pos); diff --git a/crates/pgt_completions/src/context/mod.rs b/crates/pgt_treesitter/src/context/mod.rs similarity index 78% rename from crates/pgt_completions/src/context/mod.rs rename to crates/pgt_treesitter/src/context/mod.rs index 01e563b0..9cfaadea 100644 --- a/crates/pgt_completions/src/context/mod.rs +++ b/crates/pgt_treesitter/src/context/mod.rs @@ -7,22 +7,14 @@ mod grant_parser; mod policy_parser; mod revoke_parser; -use pgt_schema_cache::SchemaCache; -use pgt_text_size::TextRange; -use pgt_treesitter_queries::{ - TreeSitterQueriesExecutor, - queries::{self, QueryResult}, -}; - -use crate::{ - NodeText, - context::{ - base_parser::CompletionStatementParser, - grant_parser::GrantParser, - policy_parser::{PolicyParser, PolicyStmtKind}, - revoke_parser::RevokeParser, - }, - sanitization::SanitizedCompletionParams, +use crate::queries::{self, QueryResult, TreeSitterQueriesExecutor}; +use pgt_text_size::{TextRange, TextSize}; + +use crate::context::{ + base_parser::CompletionStatementParser, + grant_parser::GrantParser, + policy_parser::{PolicyParser, PolicyStmtKind}, + revoke_parser::RevokeParser, }; #[derive(Debug, PartialEq, Eq, Hash, Clone)] @@ -59,9 +51,9 @@ pub enum WrappingClause<'a> { } #[derive(PartialEq, Eq, Hash, Debug, Clone)] -pub(crate) struct MentionedColumn { - pub(crate) column: String, - pub(crate) alias: Option, +pub struct MentionedColumn { + pub column: String, + pub alias: Option, } /// We can map a few nodes, such as the "update" node, to actual SQL clauses. @@ -81,10 +73,10 @@ pub enum WrappingNode { } #[derive(Debug)] -pub(crate) enum NodeUnderCursor<'a> { +pub enum NodeUnderCursor<'a> { TsNode(tree_sitter::Node<'a>), CustomNode { - text: NodeText, + text: String, range: TextRange, kind: String, previous_node_kind: Option, @@ -150,13 +142,18 @@ impl TryFrom for WrappingNode { } } +pub struct TreeSitterContextParams<'a> { + pub position: TextSize, + pub text: &'a str, + pub tree: &'a tree_sitter::Tree, +} + #[derive(Debug)] -pub(crate) struct CompletionContext<'a> { +pub struct TreesitterContext<'a> { pub node_under_cursor: Option>, pub tree: &'a tree_sitter::Tree, pub text: &'a str, - pub schema_cache: &'a SchemaCache, pub position: usize, /// If the cursor is on a node that uses dot notation @@ -178,6 +175,7 @@ pub(crate) struct CompletionContext<'a> { /// on u.id = i.user_id; /// ``` pub schema_or_alias_name: Option, + pub wrapping_clause_type: Option>, pub wrapping_node_kind: Option, @@ -190,12 +188,11 @@ pub(crate) struct CompletionContext<'a> { pub mentioned_columns: HashMap>, HashSet>, } -impl<'a> CompletionContext<'a> { - pub fn new(params: &'a SanitizedCompletionParams) -> Self { +impl<'a> TreesitterContext<'a> { + pub fn new(params: TreeSitterContextParams<'a>) -> Self { let mut ctx = Self { - tree: params.tree.as_ref(), - text: ¶ms.text, - schema_cache: params.schema, + tree: params.tree, + text: params.text, position: usize::from(params.position), node_under_cursor: None, schema_or_alias_name: None, @@ -211,11 +208,11 @@ impl<'a> CompletionContext<'a> { // policy handling is important to Supabase, but they are a PostgreSQL specific extension, // so the tree_sitter_sql language does not support it. // We infer the context manually. - if PolicyParser::looks_like_matching_stmt(¶ms.text) { + if PolicyParser::looks_like_matching_stmt(params.text) { ctx.gather_policy_context(); - } else if GrantParser::looks_like_matching_stmt(¶ms.text) { + } else if GrantParser::looks_like_matching_stmt(params.text) { ctx.gather_grant_context(); - } else if RevokeParser::looks_like_matching_stmt(¶ms.text) { + } else if RevokeParser::looks_like_matching_stmt(params.text) { ctx.gather_revoke_context(); } else { ctx.gather_tree_context(); @@ -229,7 +226,7 @@ impl<'a> CompletionContext<'a> { let revoke_context = RevokeParser::get_context(self.text, self.position); self.node_under_cursor = Some(NodeUnderCursor::CustomNode { - text: revoke_context.node_text.into(), + text: revoke_context.node_text, range: revoke_context.node_range, kind: revoke_context.node_kind.clone(), previous_node_kind: None, @@ -257,7 +254,7 @@ impl<'a> CompletionContext<'a> { let grant_context = GrantParser::get_context(self.text, self.position); self.node_under_cursor = Some(NodeUnderCursor::CustomNode { - text: grant_context.node_text.into(), + text: grant_context.node_text, range: grant_context.node_range, kind: grant_context.node_kind.clone(), previous_node_kind: None, @@ -285,7 +282,7 @@ impl<'a> CompletionContext<'a> { let policy_context = PolicyParser::get_context(self.text, self.position); self.node_under_cursor = Some(NodeUnderCursor::CustomNode { - text: policy_context.node_text.into(), + text: policy_context.node_text, range: policy_context.node_range, kind: policy_context.node_kind.clone(), previous_node_kind: Some(policy_context.previous_node_kind), @@ -397,29 +394,18 @@ impl<'a> CompletionContext<'a> { } } - fn get_ts_node_content(&self, ts_node: &tree_sitter::Node<'a>) -> Option { + fn get_ts_node_content(&self, ts_node: &tree_sitter::Node<'a>) -> Option { let source = self.text; - ts_node.utf8_text(source.as_bytes()).ok().map(|txt| { - if SanitizedCompletionParams::is_sanitized_token(txt) { - NodeText::Replaced - } else { - NodeText::Original(txt.into()) - } - }) + ts_node + .utf8_text(source.as_bytes()) + .ok() + .map(|txt| txt.into()) } pub fn get_node_under_cursor_content(&self) -> Option { match self.node_under_cursor.as_ref()? { - NodeUnderCursor::TsNode(node) => { - self.get_ts_node_content(node).and_then(|nt| match nt { - NodeText::Replaced => None, - NodeText::Original(c) => Some(c.to_string()), - }) - } - NodeUnderCursor::CustomNode { text, .. } => match text { - NodeText::Replaced => None, - NodeText::Original(c) => Some(c.to_string()), - }, + NodeUnderCursor::TsNode(node) => self.get_ts_node_content(node), + NodeUnderCursor::CustomNode { text, .. } => Some(text.clone()), } } @@ -501,15 +487,10 @@ impl<'a> CompletionContext<'a> { match current_node_kind { "object_reference" | "field" => { let content = self.get_ts_node_content(¤t_node); - if let Some(node_txt) = content { - match node_txt { - NodeText::Original(txt) => { - let parts: Vec<&str> = txt.split('.').collect(); - if parts.len() == 2 { - self.schema_or_alias_name = Some(parts[0].to_string()); - } - } - NodeText::Replaced => {} + if let Some(txt) = content { + let parts: Vec<&str> = txt.split('.').collect(); + if parts.len() == 2 { + self.schema_or_alias_name = Some(parts[0].to_string()); } } } @@ -638,12 +619,7 @@ impl<'a> CompletionContext<'a> { break; } - if let Some(sibling_content) = - self.get_ts_node_content(&sib).and_then(|txt| match txt { - NodeText::Original(txt) => Some(txt), - NodeText::Replaced => None, - }) - { + if let Some(sibling_content) = self.get_ts_node_content(&sib) { if sibling_content == tokens[idx] { idx += 1; } @@ -674,9 +650,7 @@ impl<'a> CompletionContext<'a> { while let Some(sib) = first_sibling.next_sibling() { match sib.kind() { "object_reference" => { - if let Some(NodeText::Original(txt)) = - self.get_ts_node_content(&sib) - { + if let Some(txt) = self.get_ts_node_content(&sib) { let mut iter = txt.split('.').rev(); let table = iter.next().unwrap().to_string(); let schema = iter.next().map(|s| s.to_string()); @@ -690,9 +664,7 @@ impl<'a> CompletionContext<'a> { } "column" => { - if let Some(NodeText::Original(txt)) = - self.get_ts_node_content(&sib) - { + if let Some(txt) = self.get_ts_node_content(&sib) { let entry = MentionedColumn { column: txt, alias: None, @@ -717,7 +689,7 @@ impl<'a> CompletionContext<'a> { WrappingClause::AlterColumn => { while let Some(sib) = first_sibling.next_sibling() { if sib.kind() == "object_reference" { - if let Some(NodeText::Original(txt)) = self.get_ts_node_content(&sib) { + if let Some(txt) = self.get_ts_node_content(&sib) { let mut iter = txt.split('.').rev(); let table = iter.next().unwrap().to_string(); let schema = iter.next().map(|s| s.to_string()); @@ -777,7 +749,7 @@ impl<'a> CompletionContext<'a> { } } - pub(crate) fn parent_matches_one_of_kind(&self, kinds: &[&'static str]) -> bool { + pub fn parent_matches_one_of_kind(&self, kinds: &[&'static str]) -> bool { self.node_under_cursor .as_ref() .is_some_and(|under_cursor| match under_cursor { @@ -788,7 +760,7 @@ impl<'a> CompletionContext<'a> { NodeUnderCursor::CustomNode { .. } => false, }) } - pub(crate) fn before_cursor_matches_kind(&self, kinds: &[&'static str]) -> bool { + pub fn before_cursor_matches_kind(&self, kinds: &[&'static str]) -> bool { self.node_under_cursor.as_ref().is_some_and(|under_cursor| { match under_cursor { NodeUnderCursor::TsNode(node) => { @@ -816,12 +788,9 @@ impl<'a> CompletionContext<'a> { #[cfg(test)] mod tests { - use crate::{ - NodeText, - context::{CompletionContext, WrappingClause}, - sanitization::SanitizedCompletionParams, - test_helper::{CURSOR_POS, get_text_and_position}, - }; + use crate::context::{TreeSitterContextParams, TreesitterContext, WrappingClause}; + + use pgt_test_utils::QueryWithCursorPosition; use super::NodeUnderCursor; @@ -838,56 +807,82 @@ mod tests { fn identifies_clauses() { let test_cases = vec![ ( - format!("Select {}* from users;", CURSOR_POS), + format!( + "Select {}* from users;", + QueryWithCursorPosition::cursor_marker() + ), WrappingClause::Select, ), ( - format!("Select * from u{};", CURSOR_POS), + format!( + "Select * from u{};", + QueryWithCursorPosition::cursor_marker() + ), WrappingClause::From, ), ( - format!("Select {}* from users where n = 1;", CURSOR_POS), + format!( + "Select {}* from users where n = 1;", + QueryWithCursorPosition::cursor_marker() + ), WrappingClause::Select, ), ( - format!("Select * from users where {}n = 1;", CURSOR_POS), + format!( + "Select * from users where {}n = 1;", + QueryWithCursorPosition::cursor_marker() + ), WrappingClause::Where, ), ( - format!("update users set u{} = 1 where n = 2;", CURSOR_POS), + format!( + "update users set u{} = 1 where n = 2;", + QueryWithCursorPosition::cursor_marker() + ), WrappingClause::Update, ), ( - format!("update users set u = 1 where n{} = 2;", CURSOR_POS), + format!( + "update users set u = 1 where n{} = 2;", + QueryWithCursorPosition::cursor_marker() + ), WrappingClause::Where, ), ( - format!("delete{} from users;", CURSOR_POS), + format!( + "delete{} from users;", + QueryWithCursorPosition::cursor_marker() + ), WrappingClause::Delete, ), ( - format!("delete from {}users;", CURSOR_POS), + format!( + "delete from {}users;", + QueryWithCursorPosition::cursor_marker() + ), WrappingClause::From, ), ( - format!("select name, age, location from public.u{}sers", CURSOR_POS), + format!( + "select name, age, location from public.u{}sers", + QueryWithCursorPosition::cursor_marker() + ), WrappingClause::From, ), ]; for (query, expected_clause) in test_cases { - let (position, text) = get_text_and_position(query.as_str().into()); + let (position, text) = QueryWithCursorPosition::from(query).get_text_and_position(); let tree = get_tree(text.as_str()); - let params = SanitizedCompletionParams { + let params = TreeSitterContextParams { position: (position as u32).into(), - text, - tree: std::borrow::Cow::Owned(tree), - schema: &pgt_schema_cache::SchemaCache::default(), + text: &text, + tree: &tree, }; - let ctx = CompletionContext::new(¶ms); + let ctx = TreesitterContext::new(params); assert_eq!(ctx.wrapping_clause_type, Some(expected_clause)); } @@ -897,29 +892,46 @@ mod tests { fn identifies_schema() { let test_cases = vec![ ( - format!("Select * from private.u{}", CURSOR_POS), + format!( + "Select * from private.u{}", + QueryWithCursorPosition::cursor_marker() + ), Some("private"), ), ( - format!("Select * from private.u{}sers()", CURSOR_POS), + format!( + "Select * from private.u{}sers()", + QueryWithCursorPosition::cursor_marker() + ), Some("private"), ), - (format!("Select * from u{}sers", CURSOR_POS), None), - (format!("Select * from u{}sers()", CURSOR_POS), None), + ( + format!( + "Select * from u{}sers", + QueryWithCursorPosition::cursor_marker() + ), + None, + ), + ( + format!( + "Select * from u{}sers()", + QueryWithCursorPosition::cursor_marker() + ), + None, + ), ]; for (query, expected_schema) in test_cases { - let (position, text) = get_text_and_position(query.as_str().into()); + let (position, text) = QueryWithCursorPosition::from(query).get_text_and_position(); let tree = get_tree(text.as_str()); - let params = SanitizedCompletionParams { + let params = TreeSitterContextParams { position: (position as u32).into(), - text, - tree: std::borrow::Cow::Owned(tree), - schema: &pgt_schema_cache::SchemaCache::default(), + text: &text, + tree: &tree, }; - let ctx = CompletionContext::new(¶ms); + let ctx = TreesitterContext::new(params); assert_eq!( ctx.schema_or_alias_name, @@ -931,32 +943,55 @@ mod tests { #[test] fn identifies_invocation() { let test_cases = vec![ - (format!("Select * from u{}sers", CURSOR_POS), false), - (format!("Select * from u{}sers()", CURSOR_POS), true), - (format!("Select cool{};", CURSOR_POS), false), - (format!("Select cool{}();", CURSOR_POS), true), ( - format!("Select upp{}ercase as title from users;", CURSOR_POS), + format!( + "Select * from u{}sers", + QueryWithCursorPosition::cursor_marker() + ), + false, + ), + ( + format!( + "Select * from u{}sers()", + QueryWithCursorPosition::cursor_marker() + ), + true, + ), + ( + format!("Select cool{};", QueryWithCursorPosition::cursor_marker()), + false, + ), + ( + format!("Select cool{}();", QueryWithCursorPosition::cursor_marker()), + true, + ), + ( + format!( + "Select upp{}ercase as title from users;", + QueryWithCursorPosition::cursor_marker() + ), false, ), ( - format!("Select upp{}ercase(name) as title from users;", CURSOR_POS), + format!( + "Select upp{}ercase(name) as title from users;", + QueryWithCursorPosition::cursor_marker() + ), true, ), ]; for (query, is_invocation) in test_cases { - let (position, text) = get_text_and_position(query.as_str().into()); + let (position, text) = QueryWithCursorPosition::from(query).get_text_and_position(); let tree = get_tree(text.as_str()); - let params = SanitizedCompletionParams { + let params = TreeSitterContextParams { position: (position as u32).into(), - text, - tree: std::borrow::Cow::Owned(tree), - schema: &pgt_schema_cache::SchemaCache::default(), + text: text.as_str(), + tree: &tree, }; - let ctx = CompletionContext::new(¶ms); + let ctx = TreesitterContext::new(params); assert_eq!(ctx.is_invocation, is_invocation); } @@ -965,32 +1000,34 @@ mod tests { #[test] fn does_not_fail_on_leading_whitespace() { let cases = vec![ - format!("{} select * from", CURSOR_POS), - format!(" {} select * from", CURSOR_POS), + format!( + "{} select * from", + QueryWithCursorPosition::cursor_marker() + ), + format!( + " {} select * from", + QueryWithCursorPosition::cursor_marker() + ), ]; for query in cases { - let (position, text) = get_text_and_position(query.as_str().into()); + let (position, text) = QueryWithCursorPosition::from(query).get_text_and_position(); let tree = get_tree(text.as_str()); - let params = SanitizedCompletionParams { + let params = TreeSitterContextParams { position: (position as u32).into(), - text, - tree: std::borrow::Cow::Owned(tree), - schema: &pgt_schema_cache::SchemaCache::default(), + text: &text, + tree: &tree, }; - let ctx = CompletionContext::new(¶ms); + let ctx = TreesitterContext::new(params); let node = ctx.node_under_cursor.as_ref().unwrap(); match node { NodeUnderCursor::TsNode(node) => { - assert_eq!( - ctx.get_ts_node_content(node), - Some(NodeText::Original("select".into())) - ); + assert_eq!(ctx.get_ts_node_content(node), Some("select".into())); assert_eq!( ctx.wrapping_clause_type, @@ -1004,29 +1041,28 @@ mod tests { #[test] fn does_not_fail_on_trailing_whitespace() { - let query = format!("select * from {}", CURSOR_POS); + let query = format!( + "select * from {}", + QueryWithCursorPosition::cursor_marker() + ); - let (position, text) = get_text_and_position(query.as_str().into()); + let (position, text) = QueryWithCursorPosition::from(query).get_text_and_position(); let tree = get_tree(text.as_str()); - let params = SanitizedCompletionParams { + let params = TreeSitterContextParams { position: (position as u32).into(), - text, - tree: std::borrow::Cow::Owned(tree), - schema: &pgt_schema_cache::SchemaCache::default(), + text: &text, + tree: &tree, }; - let ctx = CompletionContext::new(¶ms); + let ctx = TreesitterContext::new(params); let node = ctx.node_under_cursor.as_ref().unwrap(); match node { NodeUnderCursor::TsNode(node) => { - assert_eq!( - ctx.get_ts_node_content(node), - Some(NodeText::Original("from".into())) - ); + assert_eq!(ctx.get_ts_node_content(node), Some("from".into())); } _ => unreachable!(), } @@ -1034,29 +1070,25 @@ mod tests { #[test] fn does_not_fail_with_empty_statements() { - let query = format!("{}", CURSOR_POS); + let query = format!("{}", QueryWithCursorPosition::cursor_marker()); - let (position, text) = get_text_and_position(query.as_str().into()); + let (position, text) = QueryWithCursorPosition::from(query).get_text_and_position(); let tree = get_tree(text.as_str()); - let params = SanitizedCompletionParams { + let params = TreeSitterContextParams { position: (position as u32).into(), - text, - tree: std::borrow::Cow::Owned(tree), - schema: &pgt_schema_cache::SchemaCache::default(), + text: &text, + tree: &tree, }; - let ctx = CompletionContext::new(¶ms); + let ctx = TreesitterContext::new(params); let node = ctx.node_under_cursor.as_ref().unwrap(); match node { NodeUnderCursor::TsNode(node) => { - assert_eq!( - ctx.get_ts_node_content(node), - Some(NodeText::Original("".into())) - ); + assert_eq!(ctx.get_ts_node_content(node), Some("".into())); assert_eq!(ctx.wrapping_clause_type, None); } _ => unreachable!(), @@ -1067,29 +1099,25 @@ mod tests { fn does_not_fail_on_incomplete_keywords() { // Instead of autocompleting "FROM", we'll assume that the user // is selecting a certain column name, such as `frozen_account`. - let query = format!("select * fro{}", CURSOR_POS); + let query = format!("select * fro{}", QueryWithCursorPosition::cursor_marker()); - let (position, text) = get_text_and_position(query.as_str().into()); + let (position, text) = QueryWithCursorPosition::from(query).get_text_and_position(); let tree = get_tree(text.as_str()); - let params = SanitizedCompletionParams { + let params = TreeSitterContextParams { position: (position as u32).into(), - text, - tree: std::borrow::Cow::Owned(tree), - schema: &pgt_schema_cache::SchemaCache::default(), + text: &text, + tree: &tree, }; - let ctx = CompletionContext::new(¶ms); + let ctx = TreesitterContext::new(params); let node = ctx.node_under_cursor.as_ref().unwrap(); match node { NodeUnderCursor::TsNode(node) => { - assert_eq!( - ctx.get_ts_node_content(node), - Some(NodeText::Original("fro".into())) - ); + assert_eq!(ctx.get_ts_node_content(node), Some("fro".into())); assert_eq!(ctx.wrapping_clause_type, Some(WrappingClause::Select)); } _ => unreachable!(), diff --git a/crates/pgt_completions/src/context/policy_parser.rs b/crates/pgt_treesitter/src/context/policy_parser.rs similarity index 95% rename from crates/pgt_completions/src/context/policy_parser.rs rename to crates/pgt_treesitter/src/context/policy_parser.rs index bcc60499..77664516 100644 --- a/crates/pgt_completions/src/context/policy_parser.rs +++ b/crates/pgt_treesitter/src/context/policy_parser.rs @@ -212,16 +212,17 @@ mod tests { use crate::{ context::base_parser::CompletionStatementParser, context::policy_parser::{PolicyContext, PolicyStmtKind}, - test_helper::CURSOR_POS, }; + use pgt_test_utils::QueryWithCursorPosition; + use super::PolicyParser; fn with_pos(query: String) -> (usize, String) { let mut pos: Option = None; for (p, c) in query.char_indices() { - if c == CURSOR_POS { + if c == QueryWithCursorPosition::cursor_marker() { pos = Some(p); break; } @@ -229,7 +230,9 @@ mod tests { ( pos.expect("Please add cursor position!"), - query.replace(CURSOR_POS, "REPLACED_TOKEN").to_string(), + query + .replace(QueryWithCursorPosition::cursor_marker(), "REPLACED_TOKEN") + .to_string(), ) } @@ -239,7 +242,7 @@ mod tests { r#" create policy {} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = PolicyParser::get_context(query.as_str(), pos); @@ -265,7 +268,7 @@ mod tests { r#" create policy "my cool policy" {} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = PolicyParser::get_context(query.as_str(), pos); @@ -291,7 +294,7 @@ mod tests { r#" create policy "my cool policy" on {} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = PolicyParser::get_context(query.as_str(), pos); @@ -317,7 +320,7 @@ mod tests { r#" create policy "my cool policy" on auth.{} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = PolicyParser::get_context(query.as_str(), pos); @@ -344,7 +347,7 @@ mod tests { create policy "my cool policy" on auth.users as {} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = PolicyParser::get_context(query.as_str(), pos); @@ -372,7 +375,7 @@ mod tests { as permissive {} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = PolicyParser::get_context(query.as_str(), pos); @@ -400,7 +403,7 @@ mod tests { as permissive to {} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = PolicyParser::get_context(query.as_str(), pos); @@ -432,7 +435,7 @@ mod tests { to all using (true); "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = PolicyParser::get_context(query.as_str(), pos); @@ -464,7 +467,7 @@ mod tests { to all using (true); "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = PolicyParser::get_context(query.as_str(), pos); @@ -493,7 +496,7 @@ mod tests { r#" drop policy {} on auth.users; "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = PolicyParser::get_context(query.as_str(), pos); @@ -520,7 +523,7 @@ mod tests { r#" drop policy "{}" on auth.users; "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = PolicyParser::get_context(query.as_str(), pos); @@ -549,7 +552,7 @@ mod tests { r#" drop policy "{} on auth.users; "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = PolicyParser::get_context(query.as_str(), pos); @@ -567,7 +570,7 @@ mod tests { to all using (id = {}) "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = PolicyParser::get_context(query.as_str(), pos); @@ -598,7 +601,7 @@ mod tests { to all using ({} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = PolicyParser::get_context(query.as_str(), pos); @@ -629,7 +632,7 @@ mod tests { to all with check ({} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = PolicyParser::get_context(query.as_str(), pos); diff --git a/crates/pgt_completions/src/context/revoke_parser.rs b/crates/pgt_treesitter/src/context/revoke_parser.rs similarity index 94% rename from crates/pgt_completions/src/context/revoke_parser.rs rename to crates/pgt_treesitter/src/context/revoke_parser.rs index e0c43934..4f5b09ec 100644 --- a/crates/pgt_completions/src/context/revoke_parser.rs +++ b/crates/pgt_treesitter/src/context/revoke_parser.rs @@ -180,14 +180,15 @@ mod tests { use crate::{ context::base_parser::CompletionStatementParser, context::revoke_parser::{RevokeContext, RevokeParser}, - test_helper::CURSOR_POS, }; + use pgt_test_utils::QueryWithCursorPosition; + fn with_pos(query: String) -> (usize, String) { let mut pos: Option = None; for (p, c) in query.char_indices() { - if c == CURSOR_POS { + if c == QueryWithCursorPosition::cursor_marker() { pos = Some(p); break; } @@ -195,7 +196,9 @@ mod tests { ( pos.expect("Please add cursor position!"), - query.replace(CURSOR_POS, "REPLACED_TOKEN").to_string(), + query + .replace(QueryWithCursorPosition::cursor_marker(), "REPLACED_TOKEN") + .to_string(), ) } @@ -205,7 +208,7 @@ mod tests { r#" revoke {} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = RevokeParser::get_context(query.as_str(), pos); @@ -228,7 +231,7 @@ mod tests { r#" revoke select on {} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = RevokeParser::get_context(query.as_str(), pos); @@ -251,7 +254,7 @@ mod tests { r#" revoke select on public.{} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = RevokeParser::get_context(query.as_str(), pos); @@ -274,7 +277,7 @@ mod tests { r#" revoke select on public.users from {} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = RevokeParser::get_context(query.as_str(), pos); @@ -297,7 +300,7 @@ mod tests { r#" revoke select on public.users from alice, {} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = RevokeParser::get_context(query.as_str(), pos); @@ -320,7 +323,7 @@ mod tests { r#" revoke select on "MySchema"."MyTable" from {} "#, - CURSOR_POS + QueryWithCursorPosition::cursor_marker() )); let context = RevokeParser::get_context(query.as_str(), pos); diff --git a/crates/pgt_treesitter/src/lib.rs b/crates/pgt_treesitter/src/lib.rs new file mode 100644 index 00000000..6b19db53 --- /dev/null +++ b/crates/pgt_treesitter/src/lib.rs @@ -0,0 +1,5 @@ +pub mod context; +pub mod queries; + +pub use context::*; +pub use queries::*; diff --git a/crates/pgt_treesitter_queries/src/queries/insert_columns.rs b/crates/pgt_treesitter/src/queries/insert_columns.rs similarity index 97% rename from crates/pgt_treesitter_queries/src/queries/insert_columns.rs rename to crates/pgt_treesitter/src/queries/insert_columns.rs index 3e88d998..94d67b69 100644 --- a/crates/pgt_treesitter_queries/src/queries/insert_columns.rs +++ b/crates/pgt_treesitter/src/queries/insert_columns.rs @@ -1,6 +1,6 @@ use std::sync::LazyLock; -use crate::{Query, QueryResult}; +use crate::queries::{Query, QueryResult}; use super::QueryTryFrom; @@ -51,7 +51,7 @@ impl<'a> QueryTryFrom<'a> for InsertColumnMatch<'a> { } impl<'a> Query<'a> for InsertColumnMatch<'a> { - fn execute(root_node: tree_sitter::Node<'a>, stmt: &'a str) -> Vec> { + fn execute(root_node: tree_sitter::Node<'a>, stmt: &'a str) -> Vec> { let mut cursor = tree_sitter::QueryCursor::new(); let matches = cursor.matches(&TS_QUERY, root_node, stmt.as_bytes()); @@ -73,7 +73,7 @@ impl<'a> Query<'a> for InsertColumnMatch<'a> { #[cfg(test)] mod tests { use super::InsertColumnMatch; - use crate::TreeSitterQueriesExecutor; + use crate::queries::TreeSitterQueriesExecutor; #[test] fn finds_all_insert_columns() { diff --git a/crates/pgt_treesitter_queries/src/lib.rs b/crates/pgt_treesitter/src/queries/mod.rs similarity index 72% rename from crates/pgt_treesitter_queries/src/lib.rs rename to crates/pgt_treesitter/src/queries/mod.rs index 4bf71e74..1d24f07a 100644 --- a/crates/pgt_treesitter_queries/src/lib.rs +++ b/crates/pgt_treesitter/src/queries/mod.rs @@ -1,8 +1,91 @@ -pub mod queries; +mod insert_columns; +mod parameters; +mod relations; +mod select_columns; +mod table_aliases; +mod where_columns; use std::slice::Iter; -use queries::{Query, QueryResult}; +pub use insert_columns::*; +pub use parameters::*; +pub use relations::*; +pub use select_columns::*; +pub use table_aliases::*; +pub use where_columns::*; + +#[derive(Debug)] +pub enum QueryResult<'a> { + Relation(RelationMatch<'a>), + Parameter(ParameterMatch<'a>), + TableAliases(TableAliasMatch<'a>), + SelectClauseColumns(SelectColumnMatch<'a>), + InsertClauseColumns(InsertColumnMatch<'a>), + WhereClauseColumns(WhereColumnMatch<'a>), +} + +impl QueryResult<'_> { + pub fn within_range(&self, range: &tree_sitter::Range) -> bool { + match self { + QueryResult::Relation(rm) => { + let start = match rm.schema { + Some(s) => s.start_position(), + None => rm.table.start_position(), + }; + + let end = rm.table.end_position(); + + start >= range.start_point && end <= range.end_point + } + Self::Parameter(pm) => { + let node_range = pm.node.range(); + + node_range.start_point >= range.start_point + && node_range.end_point <= range.end_point + } + QueryResult::TableAliases(m) => { + let start = m.table.start_position(); + let end = m.alias.end_position(); + start >= range.start_point && end <= range.end_point + } + Self::SelectClauseColumns(cm) => { + let start = match cm.alias { + Some(n) => n.start_position(), + None => cm.column.start_position(), + }; + + let end = cm.column.end_position(); + + start >= range.start_point && end <= range.end_point + } + Self::WhereClauseColumns(cm) => { + let start = match cm.alias { + Some(n) => n.start_position(), + None => cm.column.start_position(), + }; + + let end = cm.column.end_position(); + + start >= range.start_point && end <= range.end_point + } + Self::InsertClauseColumns(cm) => { + let start = cm.column.start_position(); + let end = cm.column.end_position(); + start >= range.start_point && end <= range.end_point + } + } + } +} + +// This trait enforces that for any `Self` that implements `Query`, +// its &Self must implement TryFrom<&QueryResult> +pub(crate) trait QueryTryFrom<'a>: Sized { + type Ref: for<'any> TryFrom<&'a QueryResult<'a>, Error = String>; +} + +pub(crate) trait Query<'a>: QueryTryFrom<'a> { + fn execute(root_node: tree_sitter::Node<'a>, stmt: &'a str) -> Vec>; +} pub struct TreeSitterQueriesExecutor<'a> { root_node: tree_sitter::Node<'a>, @@ -68,9 +151,8 @@ impl<'a> Iterator for QueryResultIter<'a> { #[cfg(test)] mod tests { - use crate::{ - TreeSitterQueriesExecutor, - queries::{ParameterMatch, RelationMatch, TableAliasMatch}, + use crate::queries::{ + ParameterMatch, RelationMatch, TableAliasMatch, TreeSitterQueriesExecutor, }; #[test] diff --git a/crates/pgt_treesitter_queries/src/queries/parameters.rs b/crates/pgt_treesitter/src/queries/parameters.rs similarity index 96% rename from crates/pgt_treesitter_queries/src/queries/parameters.rs rename to crates/pgt_treesitter/src/queries/parameters.rs index 85ea9ad2..0b7f2e3d 100644 --- a/crates/pgt_treesitter_queries/src/queries/parameters.rs +++ b/crates/pgt_treesitter/src/queries/parameters.rs @@ -1,6 +1,6 @@ use std::sync::LazyLock; -use crate::{Query, QueryResult}; +use crate::queries::{Query, QueryResult}; use super::QueryTryFrom; @@ -59,7 +59,7 @@ impl<'a> QueryTryFrom<'a> for ParameterMatch<'a> { } impl<'a> Query<'a> for ParameterMatch<'a> { - fn execute(root_node: tree_sitter::Node<'a>, stmt: &'a str) -> Vec> { + fn execute(root_node: tree_sitter::Node<'a>, stmt: &'a str) -> Vec> { let mut cursor = tree_sitter::QueryCursor::new(); let matches = cursor.matches(&TS_QUERY, root_node, stmt.as_bytes()); diff --git a/crates/pgt_treesitter_queries/src/queries/relations.rs b/crates/pgt_treesitter/src/queries/relations.rs similarity index 98% rename from crates/pgt_treesitter_queries/src/queries/relations.rs rename to crates/pgt_treesitter/src/queries/relations.rs index 2d7e4431..cb6a6bea 100644 --- a/crates/pgt_treesitter_queries/src/queries/relations.rs +++ b/crates/pgt_treesitter/src/queries/relations.rs @@ -1,6 +1,6 @@ use std::sync::LazyLock; -use crate::{Query, QueryResult}; +use crate::queries::{Query, QueryResult}; use super::QueryTryFrom; @@ -79,7 +79,7 @@ impl<'a> QueryTryFrom<'a> for RelationMatch<'a> { } impl<'a> Query<'a> for RelationMatch<'a> { - fn execute(root_node: tree_sitter::Node<'a>, stmt: &'a str) -> Vec> { + fn execute(root_node: tree_sitter::Node<'a>, stmt: &'a str) -> Vec> { let mut cursor = tree_sitter::QueryCursor::new(); let matches = cursor.matches(&TS_QUERY, root_node, stmt.as_bytes()); @@ -112,8 +112,9 @@ impl<'a> Query<'a> for RelationMatch<'a> { #[cfg(test)] mod tests { + use crate::queries::TreeSitterQueriesExecutor; + use super::RelationMatch; - use crate::TreeSitterQueriesExecutor; #[test] fn finds_table_without_schema() { diff --git a/crates/pgt_treesitter_queries/src/queries/select_columns.rs b/crates/pgt_treesitter/src/queries/select_columns.rs similarity index 97% rename from crates/pgt_treesitter_queries/src/queries/select_columns.rs rename to crates/pgt_treesitter/src/queries/select_columns.rs index 00b6977d..f232abc3 100644 --- a/crates/pgt_treesitter_queries/src/queries/select_columns.rs +++ b/crates/pgt_treesitter/src/queries/select_columns.rs @@ -1,6 +1,6 @@ use std::sync::LazyLock; -use crate::{Query, QueryResult}; +use crate::queries::{Query, QueryResult}; use super::QueryTryFrom; @@ -63,7 +63,7 @@ impl<'a> QueryTryFrom<'a> for SelectColumnMatch<'a> { } impl<'a> Query<'a> for SelectColumnMatch<'a> { - fn execute(root_node: tree_sitter::Node<'a>, stmt: &'a str) -> Vec> { + fn execute(root_node: tree_sitter::Node<'a>, stmt: &'a str) -> Vec> { let mut cursor = tree_sitter::QueryCursor::new(); let matches = cursor.matches(&TS_QUERY, root_node, stmt.as_bytes()); @@ -96,7 +96,7 @@ impl<'a> Query<'a> for SelectColumnMatch<'a> { #[cfg(test)] mod tests { - use crate::TreeSitterQueriesExecutor; + use crate::queries::TreeSitterQueriesExecutor; use super::SelectColumnMatch; diff --git a/crates/pgt_treesitter_queries/src/queries/table_aliases.rs b/crates/pgt_treesitter/src/queries/table_aliases.rs similarity index 97% rename from crates/pgt_treesitter_queries/src/queries/table_aliases.rs rename to crates/pgt_treesitter/src/queries/table_aliases.rs index 4297a218..70d4d52e 100644 --- a/crates/pgt_treesitter_queries/src/queries/table_aliases.rs +++ b/crates/pgt_treesitter/src/queries/table_aliases.rs @@ -1,6 +1,6 @@ use std::sync::LazyLock; -use crate::{Query, QueryResult}; +use crate::queries::{Query, QueryResult}; use super::QueryTryFrom; @@ -69,7 +69,7 @@ impl<'a> QueryTryFrom<'a> for TableAliasMatch<'a> { } impl<'a> Query<'a> for TableAliasMatch<'a> { - fn execute(root_node: tree_sitter::Node<'a>, stmt: &'a str) -> Vec> { + fn execute(root_node: tree_sitter::Node<'a>, stmt: &'a str) -> Vec> { let mut cursor = tree_sitter::QueryCursor::new(); let matches = cursor.matches(&TS_QUERY, root_node, stmt.as_bytes()); diff --git a/crates/pgt_treesitter_queries/src/queries/where_columns.rs b/crates/pgt_treesitter/src/queries/where_columns.rs similarity index 97% rename from crates/pgt_treesitter_queries/src/queries/where_columns.rs rename to crates/pgt_treesitter/src/queries/where_columns.rs index 8e19590d..b683300b 100644 --- a/crates/pgt_treesitter_queries/src/queries/where_columns.rs +++ b/crates/pgt_treesitter/src/queries/where_columns.rs @@ -1,6 +1,6 @@ use std::sync::LazyLock; -use crate::{Query, QueryResult}; +use crate::queries::{Query, QueryResult}; use super::QueryTryFrom; @@ -64,7 +64,7 @@ impl<'a> QueryTryFrom<'a> for WhereColumnMatch<'a> { } impl<'a> Query<'a> for WhereColumnMatch<'a> { - fn execute(root_node: tree_sitter::Node<'a>, stmt: &'a str) -> Vec> { + fn execute(root_node: tree_sitter::Node<'a>, stmt: &'a str) -> Vec> { let mut cursor = tree_sitter::QueryCursor::new(); let matches = cursor.matches(&TS_QUERY, root_node, stmt.as_bytes()); diff --git a/crates/pgt_treesitter_queries/src/queries/mod.rs b/crates/pgt_treesitter_queries/src/queries/mod.rs deleted file mode 100644 index b9f39aed..00000000 --- a/crates/pgt_treesitter_queries/src/queries/mod.rs +++ /dev/null @@ -1,86 +0,0 @@ -mod insert_columns; -mod parameters; -mod relations; -mod select_columns; -mod table_aliases; -mod where_columns; - -pub use insert_columns::*; -pub use parameters::*; -pub use relations::*; -pub use select_columns::*; -pub use table_aliases::*; -pub use where_columns::*; - -#[derive(Debug)] -pub enum QueryResult<'a> { - Relation(RelationMatch<'a>), - Parameter(ParameterMatch<'a>), - TableAliases(TableAliasMatch<'a>), - SelectClauseColumns(SelectColumnMatch<'a>), - InsertClauseColumns(InsertColumnMatch<'a>), - WhereClauseColumns(WhereColumnMatch<'a>), -} - -impl QueryResult<'_> { - pub fn within_range(&self, range: &tree_sitter::Range) -> bool { - match self { - QueryResult::Relation(rm) => { - let start = match rm.schema { - Some(s) => s.start_position(), - None => rm.table.start_position(), - }; - - let end = rm.table.end_position(); - - start >= range.start_point && end <= range.end_point - } - Self::Parameter(pm) => { - let node_range = pm.node.range(); - - node_range.start_point >= range.start_point - && node_range.end_point <= range.end_point - } - QueryResult::TableAliases(m) => { - let start = m.table.start_position(); - let end = m.alias.end_position(); - start >= range.start_point && end <= range.end_point - } - Self::SelectClauseColumns(cm) => { - let start = match cm.alias { - Some(n) => n.start_position(), - None => cm.column.start_position(), - }; - - let end = cm.column.end_position(); - - start >= range.start_point && end <= range.end_point - } - Self::WhereClauseColumns(cm) => { - let start = match cm.alias { - Some(n) => n.start_position(), - None => cm.column.start_position(), - }; - - let end = cm.column.end_position(); - - start >= range.start_point && end <= range.end_point - } - Self::InsertClauseColumns(cm) => { - let start = cm.column.start_position(); - let end = cm.column.end_position(); - start >= range.start_point && end <= range.end_point - } - } - } -} - -// This trait enforces that for any `Self` that implements `Query`, -// its &Self must implement TryFrom<&QueryResult> -pub(crate) trait QueryTryFrom<'a>: Sized { - type Ref: for<'any> TryFrom<&'a QueryResult<'a>, Error = String>; -} - -pub(crate) trait Query<'a>: QueryTryFrom<'a> { - fn execute(root_node: tree_sitter::Node<'a>, stmt: &'a str) -> Vec>; -} diff --git a/crates/pgt_typecheck/Cargo.toml b/crates/pgt_typecheck/Cargo.toml index caacc6d1..175ecd59 100644 --- a/crates/pgt_typecheck/Cargo.toml +++ b/crates/pgt_typecheck/Cargo.toml @@ -12,16 +12,16 @@ version = "0.0.0" [dependencies] -pgt_console.workspace = true -pgt_diagnostics.workspace = true -pgt_query_ext.workspace = true -pgt_schema_cache.workspace = true -pgt_text_size.workspace = true -pgt_treesitter_queries.workspace = true -sqlx.workspace = true -tokio.workspace = true -tree-sitter.workspace = true -tree_sitter_sql.workspace = true +pgt_console.workspace = true +pgt_diagnostics.workspace = true +pgt_query_ext.workspace = true +pgt_schema_cache.workspace = true +pgt_text_size.workspace = true +pgt_treesitter.workspace = true +sqlx.workspace = true +tokio.workspace = true +tree-sitter.workspace = true +tree_sitter_sql.workspace = true [dev-dependencies] insta.workspace = true diff --git a/crates/pgt_typecheck/src/typed_identifier.rs b/crates/pgt_typecheck/src/typed_identifier.rs index 710b2fe9..1ee4095d 100644 --- a/crates/pgt_typecheck/src/typed_identifier.rs +++ b/crates/pgt_typecheck/src/typed_identifier.rs @@ -1,5 +1,5 @@ use pgt_schema_cache::PostgresType; -use pgt_treesitter_queries::{TreeSitterQueriesExecutor, queries::ParameterMatch}; +use pgt_treesitter::queries::{ParameterMatch, TreeSitterQueriesExecutor}; /// A typed identifier is a parameter that has a type associated with it. /// It is used to replace parameters within the SQL string. diff --git a/crates/pgt_workspace/src/features/completions.rs b/crates/pgt_workspace/src/features/completions.rs index c6f05c6e..a41dd06e 100644 --- a/crates/pgt_workspace/src/features/completions.rs +++ b/crates/pgt_workspace/src/features/completions.rs @@ -82,17 +82,17 @@ mod tests { use super::get_statement_for_completions; - static CURSOR_POSITION: &str = "€"; + use pgt_test_utils::QueryWithCursorPosition; fn get_doc_and_pos(sql: &str) -> (Document, TextSize) { let pos = sql - .find(CURSOR_POSITION) + .find(QueryWithCursorPosition::cursor_marker()) .expect("Please add cursor position to test sql"); let pos: u32 = pos.try_into().unwrap(); ( - Document::new(sql.replace(CURSOR_POSITION, ""), 5), + Document::new(sql.replace(QueryWithCursorPosition::cursor_marker(), ""), 5), TextSize::new(pos), ) } @@ -107,7 +107,7 @@ mod tests { select 1; "#, - CURSOR_POSITION + QueryWithCursorPosition::cursor_marker() ); let (doc, position) = get_doc_and_pos(sql.as_str()); @@ -120,7 +120,7 @@ mod tests { #[test] fn does_not_break_when_no_statements_exist() { - let sql = CURSOR_POSITION.to_string(); + let sql = QueryWithCursorPosition::cursor_marker().to_string(); let (doc, position) = get_doc_and_pos(sql.as_str()); @@ -129,7 +129,10 @@ mod tests { #[test] fn does_not_return_overlapping_statements_if_too_close() { - let sql = format!("select * from {}select 1;", CURSOR_POSITION); + let sql = format!( + "select * from {}select 1;", + QueryWithCursorPosition::cursor_marker() + ); let (doc, position) = get_doc_and_pos(sql.as_str()); @@ -141,7 +144,10 @@ mod tests { #[test] fn is_fine_with_spaces() { - let sql = format!("select * from {} ;", CURSOR_POSITION); + let sql = format!( + "select * from {} ;", + QueryWithCursorPosition::cursor_marker() + ); let (doc, position) = get_doc_and_pos(sql.as_str()); @@ -153,7 +159,7 @@ mod tests { #[test] fn considers_offset() { - let sql = format!("select * from {}", CURSOR_POSITION); + let sql = format!("select * from {}", QueryWithCursorPosition::cursor_marker()); let (doc, position) = get_doc_and_pos(sql.as_str()); @@ -174,7 +180,7 @@ mod tests { select {} from cool; $$; "#, - CURSOR_POSITION + QueryWithCursorPosition::cursor_marker() ); let sql = sql.trim(); @@ -189,7 +195,10 @@ mod tests { #[test] fn does_not_consider_too_far_offset() { - let sql = format!("select * from {}", CURSOR_POSITION); + let sql = format!( + "select * from {}", + QueryWithCursorPosition::cursor_marker() + ); let (doc, position) = get_doc_and_pos(sql.as_str()); @@ -198,7 +207,10 @@ mod tests { #[test] fn does_not_consider_offset_if_statement_terminated_by_semi() { - let sql = format!("select * from users;{}", CURSOR_POSITION); + let sql = format!( + "select * from users;{}", + QueryWithCursorPosition::cursor_marker() + ); let (doc, position) = get_doc_and_pos(sql.as_str()); From e745328cbca0022fdaa25cda4ed8f66472b1f168 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Steinr=C3=B6tter?= Date: Tue, 22 Jul 2025 19:03:46 +0200 Subject: [PATCH 19/29] chore: add custom libpg_query binding (#465) brings home the custom binding that was experimented in pg_parse. no functional changes, just different imports and a bit of cleanup. also removes the libpg_query submodule - we are now cloning the repo "live" in the build script. closes #453 --- .gitmodules | 4 - Cargo.lock | 277 +- Cargo.toml | 5 +- crates/pgt_analyse/Cargo.toml | 2 +- .../pgt_analyse/src/analysed_file_context.rs | 2 +- crates/pgt_analyse/src/context.rs | 6 +- crates/pgt_analyse/src/registry.rs | 2 +- crates/pgt_analyser/Cargo.toml | 2 +- crates/pgt_analyser/src/lib.rs | 9 +- .../src/lint/safety/adding_required_field.rs | 7 +- .../src/lint/safety/ban_drop_column.rs | 6 +- .../src/lint/safety/ban_drop_database.rs | 2 +- .../src/lint/safety/ban_drop_not_null.rs | 6 +- .../src/lint/safety/ban_drop_table.rs | 4 +- .../src/lint/safety/ban_truncate_cascade.rs | 4 +- crates/pgt_analyser/tests/rules_tests.rs | 4 +- crates/pgt_query/Cargo.toml | 36 + crates/pgt_query/build.rs | 260 + crates/pgt_query/examples/api_example.rs | 42 + crates/pgt_query/src/deparse.rs | 93 + crates/pgt_query/src/error.rs | 23 + crates/pgt_query/src/fingerprint.rs | 359 + crates/pgt_query/src/iter_mut.rs | 1 + crates/pgt_query/src/iter_ref.rs | 1 + crates/pgt_query/src/lib.rs | 91 + crates/pgt_query/src/node_enum.rs | 33 + crates/pgt_query/src/node_mut.rs | 26 + crates/pgt_query/src/node_ref.rs | 26 + crates/pgt_query/src/node_structs.rs | 16 + crates/pgt_query/src/normalize.rs | 136 + crates/pgt_query/src/parse.rs | 149 + crates/pgt_query/src/plpgsql.rs | 38 + crates/pgt_query/src/protobuf.rs | 8846 +++++++++++++++++ crates/pgt_query/src/scan.rs | 33 + crates/pgt_query/src/split.rs | 86 + crates/pgt_query_ext/Cargo.toml | 4 +- crates/pgt_query_ext/src/diagnostics.rs | 4 +- crates/pgt_query_ext/src/lib.rs | 61 - .../Cargo.toml | 16 +- crates/pgt_query_macros/build.rs | 59 + .../pgt_query_macros/postgres/17-6.1.0.proto | 4110 ++++++++ crates/pgt_query_macros/src/iter_mut.rs | 142 + crates/pgt_query_macros/src/iter_ref.rs | 105 + crates/pgt_query_macros/src/lib.rs | 106 + crates/pgt_query_macros/src/node_enum.rs | 44 + crates/pgt_query_macros/src/node_mut.rs | 50 + crates/pgt_query_macros/src/node_ref.rs | 46 + crates/pgt_query_macros/src/node_structs.rs | 30 + crates/pgt_query_macros/src/proto_analyser.rs | 252 + crates/pgt_query_proto_parser/src/lib.rs | 9 - .../pgt_query_proto_parser/src/proto_file.rs | 60 - .../src/proto_parser.rs | 179 - crates/pgt_statement_splitter/Cargo.toml | 2 +- crates/pgt_type_resolver/Cargo.toml | 2 +- crates/pgt_type_resolver/src/functions.rs | 4 +- crates/pgt_type_resolver/src/types.rs | 14 +- crates/pgt_type_resolver/src/util.rs | 4 +- crates/pgt_typecheck/Cargo.toml | 2 +- crates/pgt_typecheck/src/lib.rs | 12 +- crates/pgt_typecheck/tests/diagnostics.rs | 5 +- crates/pgt_workspace/Cargo.toml | 1 + .../src/workspace/server/document.rs | 9 +- .../src/workspace/server/function_utils.rs | 14 +- .../src/workspace/server/pg_query.rs | 18 +- .../src/workspace/server/sql_function.rs | 14 +- docs/codegen/Cargo.toml | 1 + docs/codegen/src/rules_docs.rs | 36 +- libpg_query | 1 - xtask/rules_check/Cargo.toml | 1 + xtask/rules_check/src/lib.rs | 36 +- 70 files changed, 15556 insertions(+), 534 deletions(-) create mode 100644 crates/pgt_query/Cargo.toml create mode 100644 crates/pgt_query/build.rs create mode 100644 crates/pgt_query/examples/api_example.rs create mode 100644 crates/pgt_query/src/deparse.rs create mode 100644 crates/pgt_query/src/error.rs create mode 100644 crates/pgt_query/src/fingerprint.rs create mode 100644 crates/pgt_query/src/iter_mut.rs create mode 100644 crates/pgt_query/src/iter_ref.rs create mode 100644 crates/pgt_query/src/lib.rs create mode 100644 crates/pgt_query/src/node_enum.rs create mode 100644 crates/pgt_query/src/node_mut.rs create mode 100644 crates/pgt_query/src/node_ref.rs create mode 100644 crates/pgt_query/src/node_structs.rs create mode 100644 crates/pgt_query/src/normalize.rs create mode 100644 crates/pgt_query/src/parse.rs create mode 100644 crates/pgt_query/src/plpgsql.rs create mode 100644 crates/pgt_query/src/protobuf.rs create mode 100644 crates/pgt_query/src/scan.rs create mode 100644 crates/pgt_query/src/split.rs rename crates/{pgt_query_proto_parser => pgt_query_macros}/Cargo.toml (51%) create mode 100644 crates/pgt_query_macros/build.rs create mode 100644 crates/pgt_query_macros/postgres/17-6.1.0.proto create mode 100644 crates/pgt_query_macros/src/iter_mut.rs create mode 100644 crates/pgt_query_macros/src/iter_ref.rs create mode 100644 crates/pgt_query_macros/src/lib.rs create mode 100644 crates/pgt_query_macros/src/node_enum.rs create mode 100644 crates/pgt_query_macros/src/node_mut.rs create mode 100644 crates/pgt_query_macros/src/node_ref.rs create mode 100644 crates/pgt_query_macros/src/node_structs.rs create mode 100644 crates/pgt_query_macros/src/proto_analyser.rs delete mode 100644 crates/pgt_query_proto_parser/src/lib.rs delete mode 100644 crates/pgt_query_proto_parser/src/proto_file.rs delete mode 100644 crates/pgt_query_proto_parser/src/proto_parser.rs delete mode 160000 libpg_query diff --git a/.gitmodules b/.gitmodules index 4b56d748..9b62ce88 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,7 +1,3 @@ -[submodule "libpg_query"] - path = libpg_query - url = https://github.com/pganalyze/libpg_query.git - branch = 17-latest [submodule "crates/tree_sitter_sql/tree-sitter-sql"] path = lib/tree_sitter_sql/tree-sitter-sql url = https://github.com/DerekStride/tree-sitter-sql diff --git a/Cargo.lock b/Cargo.lock index 1bf796b7..d76baca3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,7 +24,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" dependencies = [ "cfg-if", - "getrandom", + "getrandom 0.2.15", "once_cell", "version_check", "zerocopy", @@ -106,6 +106,18 @@ version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1fd03a028ef38ba2276dce7e33fcd6369c158a1bca17946c4b1b701891c1ff7" +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + +[[package]] +name = "arrayvec" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" + [[package]] name = "assert_cmd" version = "2.0.16" @@ -333,6 +345,12 @@ dependencies = [ "windows-targets 0.52.6", ] +[[package]] +name = "base64" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" + [[package]] name = "base64" version = "0.22.1" @@ -353,25 +371,22 @@ checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1" [[package]] name = "bindgen" -version = "0.66.1" +version = "0.72.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b84e06fc203107bfbad243f4aba2af864eb7db3b1cf46ea0a023b0b433d2a7" +checksum = "4f72209734318d0b619a5e0f5129918b848c416e122a3c4ce054e03cb87b726f" dependencies = [ "bitflags 2.6.0", "cexpr", "clang-sys", - "lazy_static", - "lazycell", + "itertools 0.10.5", "log", - "peeking_take_while", "prettyplease", "proc-macro2", "quote", "regex", - "rustc-hash 1.1.0", + "rustc-hash 2.1.0", "shlex", "syn 2.0.90", - "which", ] [[package]] @@ -683,6 +698,17 @@ dependencies = [ "serde", ] +[[package]] +name = "blake2b_simd" +version = "0.5.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afa748e348ad3be8263be728124b24a24f268266f6f5d58af9d75f6a40b5c587" +dependencies = [ + "arrayref", + "arrayvec", + "constant_time_eq", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -884,6 +910,15 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +[[package]] +name = "clippy" +version = "0.0.302" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d911ee15579a3f50880d8c1d59ef6e79f9533127a3bd342462f5d584f5e8c294" +dependencies = [ + "term", +] + [[package]] name = "colorchoice" version = "1.0.3" @@ -917,6 +952,12 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + [[package]] name = "convert_case" version = "0.6.0" @@ -1204,6 +1245,17 @@ dependencies = [ "dirs-sys", ] +[[package]] +name = "dirs" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fd78930633bd1c6e35c4b42b1df7b0cbc6bc191146e512bb3bedf243fcc3901" +dependencies = [ + "libc", + "redox_users 0.3.5", + "winapi", +] + [[package]] name = "dirs-sys" version = "0.4.1" @@ -1212,7 +1264,7 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.4.6", "windows-sys 0.48.0", ] @@ -1247,6 +1299,7 @@ dependencies = [ "pgt_console", "pgt_diagnostics", "pgt_flags", + "pgt_query", "pgt_query_ext", "pgt_statement_splitter", "pgt_workspace", @@ -1287,6 +1340,12 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d6ef0072f8a535281e4876be788938b528e9a1d43900b82c2569af7da799125" +[[package]] +name = "easy-parallel" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2afbb9b0aef60e4f0d2b18129b6c0dff035a6f7dbbd17c2f38c1432102ee223c" + [[package]] name = "either" version = "1.13.0" @@ -1641,6 +1700,17 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "getrandom" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.9.0+wasi-snapshot-preview1", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -1649,7 +1719,7 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" dependencies = [ "cfg-if", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", ] [[package]] @@ -2128,12 +2198,6 @@ dependencies = [ "spin", ] -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - [[package]] name = "libc" version = "0.2.168" @@ -2373,7 +2437,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" dependencies = [ "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", "windows-sys 0.52.0", ] @@ -2605,7 +2669,7 @@ checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.5.7", "smallvec", "windows-targets 0.52.6", ] @@ -2634,12 +2698,6 @@ dependencies = [ "once_cell", ] -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -2665,24 +2723,6 @@ dependencies = [ "indexmap 2.7.0", ] -[[package]] -name = "pg_query" -version = "6.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f71c7c56dfe299ec6f98aa210aa23458be3b0610c485be60a5873c2f3627c40e" -dependencies = [ - "bindgen", - "cc", - "fs_extra", - "glob", - "itertools 0.10.5", - "prost", - "prost-build", - "serde", - "serde_json", - "thiserror 1.0.69", -] - [[package]] name = "pgt_analyse" version = "0.0.0" @@ -2692,7 +2732,7 @@ dependencies = [ "enumflags2", "pgt_console", "pgt_diagnostics", - "pgt_query_ext", + "pgt_query", "pgt_schema_cache", "pgt_text_size", "rustc-hash 2.1.0", @@ -2708,7 +2748,7 @@ dependencies = [ "pgt_analyse", "pgt_console", "pgt_diagnostics", - "pgt_query_ext", + "pgt_query", "pgt_schema_cache", "pgt_test_macros", "pgt_text_size", @@ -2936,23 +2976,42 @@ dependencies = [ "quote", ] +[[package]] +name = "pgt_query" +version = "0.0.0" +dependencies = [ + "bindgen", + "cc", + "clippy", + "easy-parallel", + "fs_extra", + "glob", + "pgt_query_macros", + "prost", + "prost-build", + "thiserror 1.0.69", + "which", +] + [[package]] name = "pgt_query_ext" version = "0.0.0" dependencies = [ - "petgraph", - "pg_query", "pgt_diagnostics", + "pgt_query", "pgt_text_size", ] [[package]] -name = "pgt_query_proto_parser" +name = "pgt_query_macros" version = "0.0.0" dependencies = [ "convert_case", - "protobuf", - "protobuf-parse", + "proc-macro2", + "prost-reflect", + "protox", + "quote", + "ureq", ] [[package]] @@ -2980,7 +3039,7 @@ dependencies = [ "ntest", "pgt_diagnostics", "pgt_lexer", - "pgt_query_ext", + "pgt_query", "pgt_text_size", "regex", ] @@ -3062,7 +3121,7 @@ dependencies = [ name = "pgt_type_resolver" version = "0.0.0" dependencies = [ - "pgt_query_ext", + "pgt_query", "pgt_schema_cache", ] @@ -3073,7 +3132,7 @@ dependencies = [ "insta", "pgt_console", "pgt_diagnostics", - "pgt_query_ext", + "pgt_query", "pgt_schema_cache", "pgt_test_utils", "pgt_text_size", @@ -3104,6 +3163,7 @@ dependencies = [ "pgt_diagnostics", "pgt_fs", "pgt_lexer", + "pgt_query", "pgt_query_ext", "pgt_schema_cache", "pgt_statement_splitter", @@ -3425,42 +3485,6 @@ dependencies = [ "prost", ] -[[package]] -name = "protobuf" -version = "3.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3a7c64d9bf75b1b8d981124c14c179074e8caa7dfe7b6a12e6222ddcd0c8f72" -dependencies = [ - "once_cell", - "protobuf-support", - "thiserror 1.0.69", -] - -[[package]] -name = "protobuf-parse" -version = "3.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322330e133eab455718444b4e033ebfac7c6528972c784fcde28d2cc783c6257" -dependencies = [ - "anyhow", - "indexmap 2.7.0", - "log", - "protobuf", - "protobuf-support", - "tempfile", - "thiserror 1.0.69", - "which", -] - -[[package]] -name = "protobuf-support" -version = "3.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b088fd20b938a875ea00843b6faf48579462630015c3788d397ad6a786663252" -dependencies = [ - "thiserror 1.0.69", -] - [[package]] name = "protox" version = "0.8.0" @@ -3567,7 +3591,7 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] @@ -3590,6 +3614,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + [[package]] name = "redox_syscall" version = "0.5.7" @@ -3599,13 +3629,24 @@ dependencies = [ "bitflags 2.6.0", ] +[[package]] +name = "redox_users" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de0737333e7a9502c789a36d7c7fa6092a49895d4faa31ca5df163857ded2e9d" +dependencies = [ + "getrandom 0.1.16", + "redox_syscall 0.1.57", + "rust-argon2", +] + [[package]] name = "redox_users" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom", + "getrandom 0.2.15", "libredox", "thiserror 1.0.69", ] @@ -3662,7 +3703,7 @@ checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.15", "libc", "untrusted", "windows-sys 0.52.0", @@ -3697,12 +3738,25 @@ dependencies = [ "pgt_analyser", "pgt_console", "pgt_diagnostics", + "pgt_query", "pgt_query_ext", "pgt_statement_splitter", "pgt_workspace", "pulldown-cmark", ] +[[package]] +name = "rust-argon2" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b18820d944b33caa75a71378964ac46f58517c92b6ae5f762636247c09e78fb" +dependencies = [ + "base64 0.13.1", + "blake2b_simd", + "constant_time_eq", + "crossbeam-utils", +] + [[package]] name = "rustc-demangle" version = "0.1.24" @@ -4197,7 +4251,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a" dependencies = [ "atoi", - "base64", + "base64 0.22.1", "bitflags 2.6.0", "byteorder", "bytes", @@ -4239,7 +4293,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8" dependencies = [ "atoi", - "base64", + "base64 0.22.1", "bitflags 2.6.0", "byteorder", "crc", @@ -4415,12 +4469,23 @@ checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" dependencies = [ "cfg-if", "fastrand 2.3.0", - "getrandom", + "getrandom 0.2.15", "once_cell", "rustix 0.38.42", "windows-sys 0.59.0", ] +[[package]] +name = "term" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" +dependencies = [ + "byteorder", + "dirs", + "winapi", +] + [[package]] name = "termcolor" version = "1.4.1" @@ -4991,7 +5056,7 @@ version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d" dependencies = [ - "base64", + "base64 0.22.1", "flate2", "log", "once_cell", @@ -5037,7 +5102,7 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" dependencies = [ - "getrandom", + "getrandom 0.2.15", ] [[package]] @@ -5109,6 +5174,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -5218,14 +5289,14 @@ dependencies = [ [[package]] name = "which" -version = "4.4.2" +version = "6.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +checksum = "b4ee928febd44d98f2f459a4a79bd4d928591333a494a10a868418ac1b39cf1f" dependencies = [ "either", "home", - "once_cell", "rustix 0.38.42", + "winsafe", ] [[package]] @@ -5234,7 +5305,7 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "372d5b87f58ec45c384ba03563b03544dc5fadc3983e434b286913f5b4a9bb6d" dependencies = [ - "redox_syscall", + "redox_syscall 0.5.7", "wasite", ] @@ -5426,6 +5497,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + [[package]] name = "write-json" version = "0.1.4" diff --git a/Cargo.toml b/Cargo.toml index 23e21889..e243ab3e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -44,12 +44,14 @@ smallvec = { version = "1.13.2", features = ["union", "const_new strum = { version = "0.27.1", features = ["derive"] } # this will use tokio if available, otherwise async-std convert_case = "0.6.0" +prost = "0.13.5" prost-reflect = "0.15.3" protox = "0.8.0" sqlx = { version = "0.8.2", features = ["runtime-tokio", "runtime-async-std", "postgres", "json"] } syn = { version = "1.0.109", features = ["full"] } termcolor = "1.4.1" test-log = "0.2.17" +thiserror = "1.0.31" tokio = { version = "1.40.0", features = ["full"] } tracing = { version = "0.1.40", default-features = false, features = ["std"] } tracing-bunyan-formatter = { version = "0.3.10 " } @@ -74,8 +76,9 @@ pgt_lexer = { path = "./crates/pgt_lexer", version = "0.0.0" } pgt_lexer_codegen = { path = "./crates/pgt_lexer_codegen", version = "0.0.0" } pgt_lsp = { path = "./crates/pgt_lsp", version = "0.0.0" } pgt_markup = { path = "./crates/pgt_markup", version = "0.0.0" } +pgt_query = { path = "./crates/pgt_query", version = "0.0.0" } pgt_query_ext = { path = "./crates/pgt_query_ext", version = "0.0.0" } -pgt_query_proto_parser = { path = "./crates/pgt_query_proto_parser", version = "0.0.0" } +pgt_query_macros = { path = "./crates/pgt_query_macros", version = "0.0.0" } pgt_schema_cache = { path = "./crates/pgt_schema_cache", version = "0.0.0" } pgt_statement_splitter = { path = "./crates/pgt_statement_splitter", version = "0.0.0" } pgt_suppressions = { path = "./crates/pgt_suppressions", version = "0.0.0" } diff --git a/crates/pgt_analyse/Cargo.toml b/crates/pgt_analyse/Cargo.toml index 4d30784c..3da60034 100644 --- a/crates/pgt_analyse/Cargo.toml +++ b/crates/pgt_analyse/Cargo.toml @@ -15,7 +15,7 @@ version = "0.0.0" [dependencies] pgt_console.workspace = true pgt_diagnostics.workspace = true -pgt_query_ext.workspace = true +pgt_query.workspace = true pgt_schema_cache.workspace = true rustc-hash = { workspace = true } diff --git a/crates/pgt_analyse/src/analysed_file_context.rs b/crates/pgt_analyse/src/analysed_file_context.rs index cba53eeb..82dc4071 100644 --- a/crates/pgt_analyse/src/analysed_file_context.rs +++ b/crates/pgt_analyse/src/analysed_file_context.rs @@ -3,5 +3,5 @@ pub struct AnalysedFileContext {} impl AnalysedFileContext { #[allow(unused)] - pub fn update_from(&mut self, stmt_root: &pgt_query_ext::NodeEnum) {} + pub fn update_from(&mut self, stmt_root: &pgt_query::NodeEnum) {} } diff --git a/crates/pgt_analyse/src/context.rs b/crates/pgt_analyse/src/context.rs index 7447c0bb..ddd5d28d 100644 --- a/crates/pgt_analyse/src/context.rs +++ b/crates/pgt_analyse/src/context.rs @@ -7,7 +7,7 @@ use crate::{ }; pub struct RuleContext<'a, R: Rule> { - stmt: &'a pgt_query_ext::NodeEnum, + stmt: &'a pgt_query::NodeEnum, options: &'a R::Options, schema_cache: Option<&'a SchemaCache>, file_context: &'a AnalysedFileContext, @@ -19,7 +19,7 @@ where { #[allow(clippy::too_many_arguments)] pub fn new( - stmt: &'a pgt_query_ext::NodeEnum, + stmt: &'a pgt_query::NodeEnum, options: &'a R::Options, schema_cache: Option<&'a SchemaCache>, file_context: &'a AnalysedFileContext, @@ -43,7 +43,7 @@ where } /// Returns the AST root - pub fn stmt(&self) -> &pgt_query_ext::NodeEnum { + pub fn stmt(&self) -> &pgt_query::NodeEnum { self.stmt } diff --git a/crates/pgt_analyse/src/registry.rs b/crates/pgt_analyse/src/registry.rs index d43d7711..45d2c202 100644 --- a/crates/pgt_analyse/src/registry.rs +++ b/crates/pgt_analyse/src/registry.rs @@ -157,7 +157,7 @@ impl RuleRegistry { } pub struct RegistryRuleParams<'a> { - pub root: &'a pgt_query_ext::NodeEnum, + pub root: &'a pgt_query::NodeEnum, pub options: &'a AnalyserOptions, pub analysed_file_context: &'a AnalysedFileContext, pub schema_cache: Option<&'a pgt_schema_cache::SchemaCache>, diff --git a/crates/pgt_analyser/Cargo.toml b/crates/pgt_analyser/Cargo.toml index 5f65b978..0cf7a334 100644 --- a/crates/pgt_analyser/Cargo.toml +++ b/crates/pgt_analyser/Cargo.toml @@ -15,7 +15,7 @@ version = "0.0.0" pgt_analyse = { workspace = true } pgt_console = { workspace = true } pgt_diagnostics = { workspace = true } -pgt_query_ext = { workspace = true } +pgt_query = { workspace = true } pgt_schema_cache = { workspace = true } pgt_text_size = { workspace = true } serde = { workspace = true } diff --git a/crates/pgt_analyser/src/lib.rs b/crates/pgt_analyser/src/lib.rs index f96b6f6d..ccdc0420 100644 --- a/crates/pgt_analyser/src/lib.rs +++ b/crates/pgt_analyser/src/lib.rs @@ -32,7 +32,7 @@ pub struct Analyser<'a> { #[derive(Debug)] pub struct AnalysableStatement { - pub root: pgt_query_ext::NodeEnum, + pub root: pgt_query::NodeEnum, pub range: pgt_text_size::TextRange, } @@ -123,7 +123,7 @@ mod tests { ..Default::default() }; - let ast = pgt_query_ext::parse(SQL).expect("failed to parse SQL"); + let ast = pgt_query::parse(SQL).expect("failed to parse SQL"); let range = TextRange::new(0.into(), u32::try_from(SQL.len()).unwrap().into()); let options = AnalyserOptions::default(); @@ -134,7 +134,10 @@ mod tests { }); let results = analyser.run(crate::AnalyserParams { - stmts: vec![AnalysableStatement { root: ast, range }], + stmts: vec![AnalysableStatement { + root: ast.into_root().unwrap(), + range, + }], schema_cache: None, }); diff --git a/crates/pgt_analyser/src/lint/safety/adding_required_field.rs b/crates/pgt_analyser/src/lint/safety/adding_required_field.rs index 06901952..d853d30a 100644 --- a/crates/pgt_analyser/src/lint/safety/adding_required_field.rs +++ b/crates/pgt_analyser/src/lint/safety/adding_required_field.rs @@ -30,7 +30,7 @@ impl Rule for AddingRequiredField { fn run(ctx: &RuleContext) -> Vec { let mut diagnostics = vec![]; - if let pgt_query_ext::NodeEnum::AlterTableStmt(stmt) = ctx.stmt() { + if let pgt_query::NodeEnum::AlterTableStmt(stmt) = ctx.stmt() { // We are currently lacking a way to check if a `AtAddColumn` subtype sets a // not null constraint – so we'll need to check the plain SQL. let plain_sql = ctx.stmt().to_ref().deparse().unwrap().to_ascii_lowercase(); @@ -41,9 +41,8 @@ impl Rule for AddingRequiredField { } for cmd in &stmt.cmds { - if let Some(pgt_query_ext::NodeEnum::AlterTableCmd(alter_table_cmd)) = &cmd.node { - if alter_table_cmd.subtype() - == pgt_query_ext::protobuf::AlterTableType::AtAddColumn + if let Some(pgt_query::NodeEnum::AlterTableCmd(alter_table_cmd)) = &cmd.node { + if alter_table_cmd.subtype() == pgt_query::protobuf::AlterTableType::AtAddColumn { diagnostics.push( RuleDiagnostic::new( diff --git a/crates/pgt_analyser/src/lint/safety/ban_drop_column.rs b/crates/pgt_analyser/src/lint/safety/ban_drop_column.rs index 165d4230..d73b39d2 100644 --- a/crates/pgt_analyser/src/lint/safety/ban_drop_column.rs +++ b/crates/pgt_analyser/src/lint/safety/ban_drop_column.rs @@ -32,10 +32,10 @@ impl Rule for BanDropColumn { fn run(ctx: &RuleContext) -> Vec { let mut diagnostics = Vec::new(); - if let pgt_query_ext::NodeEnum::AlterTableStmt(stmt) = &ctx.stmt() { + if let pgt_query::NodeEnum::AlterTableStmt(stmt) = &ctx.stmt() { for cmd in &stmt.cmds { - if let Some(pgt_query_ext::NodeEnum::AlterTableCmd(cmd)) = &cmd.node { - if cmd.subtype() == pgt_query_ext::protobuf::AlterTableType::AtDropColumn { + if let Some(pgt_query::NodeEnum::AlterTableCmd(cmd)) = &cmd.node { + if cmd.subtype() == pgt_query::protobuf::AlterTableType::AtDropColumn { diagnostics.push(RuleDiagnostic::new( rule_category!(), None, diff --git a/crates/pgt_analyser/src/lint/safety/ban_drop_database.rs b/crates/pgt_analyser/src/lint/safety/ban_drop_database.rs index 11d07da9..3011cf88 100644 --- a/crates/pgt_analyser/src/lint/safety/ban_drop_database.rs +++ b/crates/pgt_analyser/src/lint/safety/ban_drop_database.rs @@ -21,7 +21,7 @@ impl Rule for BanDropDatabase { fn run(ctx: &RuleContext) -> Vec { let mut diagnostics = vec![]; - if let pgt_query_ext::NodeEnum::DropdbStmt(_) = &ctx.stmt() { + if let pgt_query::NodeEnum::DropdbStmt(_) = &ctx.stmt() { diagnostics.push( RuleDiagnostic::new( rule_category!(), diff --git a/crates/pgt_analyser/src/lint/safety/ban_drop_not_null.rs b/crates/pgt_analyser/src/lint/safety/ban_drop_not_null.rs index fa4c9011..c1e69461 100644 --- a/crates/pgt_analyser/src/lint/safety/ban_drop_not_null.rs +++ b/crates/pgt_analyser/src/lint/safety/ban_drop_not_null.rs @@ -32,10 +32,10 @@ impl Rule for BanDropNotNull { fn run(ctx: &RuleContext) -> Vec { let mut diagnostics = Vec::new(); - if let pgt_query_ext::NodeEnum::AlterTableStmt(stmt) = &ctx.stmt() { + if let pgt_query::NodeEnum::AlterTableStmt(stmt) = &ctx.stmt() { for cmd in &stmt.cmds { - if let Some(pgt_query_ext::NodeEnum::AlterTableCmd(cmd)) = &cmd.node { - if cmd.subtype() == pgt_query_ext::protobuf::AlterTableType::AtDropNotNull { + if let Some(pgt_query::NodeEnum::AlterTableCmd(cmd)) = &cmd.node { + if cmd.subtype() == pgt_query::protobuf::AlterTableType::AtDropNotNull { diagnostics.push(RuleDiagnostic::new( rule_category!(), None, diff --git a/crates/pgt_analyser/src/lint/safety/ban_drop_table.rs b/crates/pgt_analyser/src/lint/safety/ban_drop_table.rs index 90c08514..bcf78453 100644 --- a/crates/pgt_analyser/src/lint/safety/ban_drop_table.rs +++ b/crates/pgt_analyser/src/lint/safety/ban_drop_table.rs @@ -31,8 +31,8 @@ impl Rule for BanDropTable { fn run(ctx: &RuleContext) -> Vec { let mut diagnostics = vec![]; - if let pgt_query_ext::NodeEnum::DropStmt(stmt) = &ctx.stmt() { - if stmt.remove_type() == pgt_query_ext::protobuf::ObjectType::ObjectTable { + if let pgt_query::NodeEnum::DropStmt(stmt) = &ctx.stmt() { + if stmt.remove_type() == pgt_query::protobuf::ObjectType::ObjectTable { diagnostics.push( RuleDiagnostic::new( rule_category!(), diff --git a/crates/pgt_analyser/src/lint/safety/ban_truncate_cascade.rs b/crates/pgt_analyser/src/lint/safety/ban_truncate_cascade.rs index cef5cd47..1bc42d49 100644 --- a/crates/pgt_analyser/src/lint/safety/ban_truncate_cascade.rs +++ b/crates/pgt_analyser/src/lint/safety/ban_truncate_cascade.rs @@ -1,7 +1,7 @@ use pgt_analyse::{Rule, RuleDiagnostic, RuleSource, context::RuleContext, declare_lint_rule}; use pgt_console::markup; use pgt_diagnostics::Severity; -use pgt_query_ext::protobuf::DropBehavior; +use pgt_query::protobuf::DropBehavior; declare_lint_rule! { /// Using `TRUNCATE`'s `CASCADE` option will truncate any tables that are also foreign-keyed to the specified tables. @@ -34,7 +34,7 @@ impl Rule for BanTruncateCascade { fn run(ctx: &RuleContext) -> Vec { let mut diagnostics = Vec::new(); - if let pgt_query_ext::NodeEnum::TruncateStmt(stmt) = &ctx.stmt() { + if let pgt_query::NodeEnum::TruncateStmt(stmt) = &ctx.stmt() { if stmt.behavior() == DropBehavior::DropCascade { diagnostics.push(RuleDiagnostic::new( rule_category!(), diff --git a/crates/pgt_analyser/tests/rules_tests.rs b/crates/pgt_analyser/tests/rules_tests.rs index 0a6b47ec..d8e5b0ef 100644 --- a/crates/pgt_analyser/tests/rules_tests.rs +++ b/crates/pgt_analyser/tests/rules_tests.rs @@ -25,7 +25,7 @@ fn rule_test(full_path: &'static str, _: &str, _: &str) { let query = read_to_string(full_path).unwrap_or_else(|_| panic!("Failed to read file: {} ", full_path)); - let ast = pgt_query_ext::parse(&query).expect("failed to parse SQL"); + let ast = pgt_query::parse(&query).expect("failed to parse SQL"); let options = AnalyserOptions::default(); let analyser = Analyser::new(AnalyserConfig { options: &options, @@ -33,7 +33,7 @@ fn rule_test(full_path: &'static str, _: &str, _: &str) { }); let stmt = AnalysableStatement { - root: ast, + root: ast.into_root().expect("Failed to convert AST to root node"), range: pgt_text_size::TextRange::new(0.into(), u32::try_from(query.len()).unwrap().into()), }; diff --git a/crates/pgt_query/Cargo.toml b/crates/pgt_query/Cargo.toml new file mode 100644 index 00000000..881b1b80 --- /dev/null +++ b/crates/pgt_query/Cargo.toml @@ -0,0 +1,36 @@ +[package] +authors.workspace = true +categories.workspace = true +description = "" +edition.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +name = "pgt_query" +repository.workspace = true +version = "0.0.0" + +[dependencies] +prost = { workspace = true } +thiserror = { workspace = true } + +pgt_query_macros = { workspace = true } + + +[features] +default = ["postgres-17"] +postgres-15 = [] +postgres-16 = [] +postgres-17 = [] + +[build-dependencies] +bindgen = "0.72.0" +cc = "1.0.83" +clippy = { version = "0.0.302", optional = true } +fs_extra = "1.2.0" +glob = "0.3.1" +prost-build = "0.13.5" +which = "6.0.0" + +[dev-dependencies] +easy-parallel = "3.2.0" diff --git a/crates/pgt_query/build.rs b/crates/pgt_query/build.rs new file mode 100644 index 00000000..292b3af2 --- /dev/null +++ b/crates/pgt_query/build.rs @@ -0,0 +1,260 @@ +#![cfg_attr(feature = "clippy", feature(plugin))] +#![cfg_attr(feature = "clippy", plugin(clippy))] + +use fs_extra::dir::CopyOptions; +use glob::glob; +use std::env; +use std::path::PathBuf; +use std::process::Command; + +static LIBRARY_NAME: &str = "pg_query"; +static LIBPG_QUERY_REPO: &str = "https://github.com/pganalyze/libpg_query.git"; +fn get_libpg_query_tag() -> &'static str { + #[cfg(feature = "postgres-15")] + return "15-5.3.0"; + #[cfg(feature = "postgres-16")] + return "16-6.1.0"; + #[cfg(feature = "postgres-17")] + return "17-6.1.0"; +} + +fn main() -> Result<(), Box> { + let libpg_query_tag = get_libpg_query_tag(); + let out_dir = PathBuf::from(env::var("OUT_DIR")?); + let vendor_dir = out_dir.join("vendor"); + let libpg_query_dir = vendor_dir.join("libpg_query").join(libpg_query_tag); + let stamp_file = libpg_query_dir.join(".stamp"); + + let src_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")?).join("src"); + let target = env::var("TARGET").unwrap(); + let is_emscripten = target.contains("emscripten"); + + // Configure cargo through stdout + println!("cargo:rustc-link-search=native={}", out_dir.display()); + println!("cargo:rustc-link-lib=static={LIBRARY_NAME}"); + + // Clone libpg_query if not already present + if !stamp_file.exists() { + println!("cargo:warning=Cloning libpg_query {}", libpg_query_tag); + + // Create vendor directory + std::fs::create_dir_all(&vendor_dir)?; + + // Clone the repository with partial clone for faster download + let status = Command::new("git") + .args([ + "clone", + "--filter=blob:none", + "--depth", + "1", + "--branch", + libpg_query_tag, + LIBPG_QUERY_REPO, + libpg_query_dir.to_str().unwrap(), + ]) + .status()?; + + if !status.success() { + return Err("Failed to clone libpg_query".into()); + } + + // Create stamp file + std::fs::File::create(&stamp_file)?; + } + + // Tell cargo to rerun if the stamp file is deleted + println!("cargo:rerun-if-changed={}", stamp_file.display()); + + // Copy necessary files to OUT_DIR for compilation + let out_header_path = out_dir.join(LIBRARY_NAME).with_extension("h"); + let out_protobuf_path = out_dir.join("protobuf"); + + let source_paths = vec![ + libpg_query_dir.join(LIBRARY_NAME).with_extension("h"), + libpg_query_dir.join("Makefile"), + libpg_query_dir.join("src"), + libpg_query_dir.join("protobuf"), + libpg_query_dir.join("vendor"), + ]; + + let copy_options = CopyOptions { + overwrite: true, + ..CopyOptions::default() + }; + + fs_extra::copy_items(&source_paths, &out_dir, ©_options)?; + + // Compile the C library. + let mut build = cc::Build::new(); + + // Configure for Emscripten if needed + if is_emscripten { + // Use emcc as the compiler instead of gcc/clang + build.compiler("emcc"); + // Use emar as the archiver instead of ar + build.archiver("emar"); + // Note: We don't add WASM-specific flags here as this creates a static library + // The final linking flags should be added when building the final WASM module + } + + build + .files( + glob(out_dir.join("src/*.c").to_str().unwrap()) + .unwrap() + .map(|p| p.unwrap()), + ) + .files( + glob(out_dir.join("src/postgres/*.c").to_str().unwrap()) + .unwrap() + .map(|p| p.unwrap()), + ) + .file(out_dir.join("vendor/protobuf-c/protobuf-c.c")) + .file(out_dir.join("vendor/xxhash/xxhash.c")) + .file(out_dir.join("protobuf/pg_query.pb-c.c")) + .include(out_dir.join(".")) + .include(out_dir.join("./vendor")) + .include(out_dir.join("./src/postgres/include")) + .include(out_dir.join("./src/include")) + .warnings(false); // Avoid unnecessary warnings, as they are already considered as part of libpg_query development + if env::var("PROFILE").unwrap() == "debug" || env::var("DEBUG").unwrap() == "1" { + build.define("USE_ASSERT_CHECKING", None); + } + if target.contains("windows") && !is_emscripten { + build.include(out_dir.join("./src/postgres/include/port/win32")); + if target.contains("msvc") { + build.include(out_dir.join("./src/postgres/include/port/win32_msvc")); + } + } + build.compile(LIBRARY_NAME); + + // Generate bindings for Rust + let mut bindgen_builder = bindgen::Builder::default() + .header(out_header_path.to_str().ok_or("Invalid header path")?) + // Allowlist only the functions we need + .allowlist_function("pg_query_parse_protobuf") + .allowlist_function("pg_query_scan") + .allowlist_function("pg_query_deparse_protobuf") + .allowlist_function("pg_query_normalize") + .allowlist_function("pg_query_fingerprint") + .allowlist_function("pg_query_split_with_parser") + .allowlist_function("pg_query_split_with_scanner") + .allowlist_function("pg_query_parse_plpgsql") + .allowlist_function("pg_query_free_protobuf_parse_result") + .allowlist_function("pg_query_free_scan_result") + .allowlist_function("pg_query_free_deparse_result") + .allowlist_function("pg_query_free_normalize_result") + .allowlist_function("pg_query_free_fingerprint_result") + .allowlist_function("pg_query_free_split_result") + .allowlist_function("pg_query_free_plpgsql_parse_result") + // Allowlist the types used by these functions + .allowlist_type("PgQueryProtobufParseResult") + .allowlist_type("PgQueryScanResult") + .allowlist_type("PgQueryError") + .allowlist_type("PgQueryProtobuf") + .allowlist_type("PgQueryDeparseResult") + .allowlist_type("PgQueryNormalizeResult") + .allowlist_type("PgQueryFingerprintResult") + .allowlist_type("PgQuerySplitResult") + .allowlist_type("PgQuerySplitStmt") + // Also generate bindings for size_t since it's used in PgQueryProtobuf + .allowlist_type("size_t") + .allowlist_var("PG_VERSION_NUM"); + + // Configure bindgen for Emscripten target + if is_emscripten { + // Tell bindgen to generate bindings for the wasm32 target + bindgen_builder = bindgen_builder.clang_arg("--target=wasm32-unknown-emscripten"); + + // Add emscripten sysroot includes + // First try to use EMSDK environment variable (set in CI and when sourcing emsdk_env.sh) + if let Ok(emsdk) = env::var("EMSDK") { + bindgen_builder = bindgen_builder.clang_arg(format!( + "-I{}/upstream/emscripten/cache/sysroot/include", + emsdk + )); + } else { + // Fallback to the default path if EMSDK is not set + bindgen_builder = + bindgen_builder.clang_arg("-I/emsdk/upstream/emscripten/cache/sysroot/include"); + } + + // Ensure we have the basic C standard library headers + bindgen_builder = bindgen_builder.clang_arg("-D__EMSCRIPTEN__"); + + // Use environment variable if set (from our justfile) + if let Ok(extra_args) = env::var("BINDGEN_EXTRA_CLANG_ARGS") { + for arg in extra_args.split_whitespace() { + bindgen_builder = bindgen_builder.clang_arg(arg); + } + } + } + + let bindings = bindgen_builder + .generate() + .map_err(|_| "Unable to generate bindings")?; + + let bindings_path = out_dir.join("bindings.rs"); + bindings.write_to_file(&bindings_path)?; + + // For WASM/emscripten builds, manually add the function declarations + // since bindgen sometimes misses them due to preprocessor conditions + if is_emscripten { + let mut bindings_content = std::fs::read_to_string(&bindings_path)?; + + // Check if we need to add the extern "C" block + if !bindings_content.contains("extern \"C\"") { + bindings_content.push_str("\nextern \"C\" {\n"); + bindings_content.push_str(" pub fn pg_query_scan(input: *const ::std::os::raw::c_char) -> PgQueryScanResult;\n"); + bindings_content.push_str(" pub fn pg_query_parse_protobuf(input: *const ::std::os::raw::c_char) -> PgQueryProtobufParseResult;\n"); + bindings_content.push_str(" pub fn pg_query_parse_plpgsql(input: *const ::std::os::raw::c_char) -> PgQueryPlpgsqlParseResult;\n"); + bindings_content.push_str(" pub fn pg_query_deparse_protobuf(protobuf: PgQueryProtobuf) -> PgQueryDeparseResult;\n"); + bindings_content.push_str(" pub fn pg_query_normalize(input: *const ::std::os::raw::c_char) -> PgQueryNormalizeResult;\n"); + bindings_content.push_str(" pub fn pg_query_fingerprint(input: *const ::std::os::raw::c_char) -> PgQueryFingerprintResult;\n"); + bindings_content.push_str(" pub fn pg_query_split_with_parser(input: *const ::std::os::raw::c_char) -> PgQuerySplitResult;\n"); + bindings_content.push_str(" pub fn pg_query_split_with_scanner(input: *const ::std::os::raw::c_char) -> PgQuerySplitResult;\n"); + bindings_content + .push_str(" pub fn pg_query_free_scan_result(result: PgQueryScanResult);\n"); + bindings_content.push_str(" pub fn pg_query_free_protobuf_parse_result(result: PgQueryProtobufParseResult);\n"); + bindings_content.push_str(" pub fn pg_query_free_plpgsql_parse_result(result: PgQueryPlpgsqlParseResult);\n"); + bindings_content.push_str( + " pub fn pg_query_free_deparse_result(result: PgQueryDeparseResult);\n", + ); + bindings_content.push_str( + " pub fn pg_query_free_normalize_result(result: PgQueryNormalizeResult);\n", + ); + bindings_content.push_str( + " pub fn pg_query_free_fingerprint_result(result: PgQueryFingerprintResult);\n", + ); + bindings_content + .push_str(" pub fn pg_query_free_split_result(result: PgQuerySplitResult);\n"); + bindings_content.push_str("}\n"); + + std::fs::write(&bindings_path, bindings_content)?; + } + } + + let protoc_exists = Command::new("protoc").arg("--version").status().is_ok(); + if protoc_exists { + println!("generating protobuf bindings"); + // HACK: Set OUT_DIR to src/ so that the generated protobuf file is copied to src/protobuf.rs + unsafe { + env::set_var("OUT_DIR", &src_dir); + } + + prost_build::compile_protos( + &[&out_protobuf_path.join(LIBRARY_NAME).with_extension("proto")], + &[&out_protobuf_path], + )?; + + std::fs::rename(src_dir.join("pg_query.rs"), src_dir.join("protobuf.rs"))?; + + // Reset OUT_DIR to the original value + unsafe { + env::set_var("OUT_DIR", &out_dir); + } + } else { + println!("skipping protobuf generation"); + } + + Ok(()) +} diff --git a/crates/pgt_query/examples/api_example.rs b/crates/pgt_query/examples/api_example.rs new file mode 100644 index 00000000..d71b1c0f --- /dev/null +++ b/crates/pgt_query/examples/api_example.rs @@ -0,0 +1,42 @@ +use pgt_query::{NodeRef, parse}; + +fn main() { + let mut result = parse("SELECT * FROM users WHERE id IN (SELECT id FROM admins)").unwrap(); + + // Immutable access + { + let stmts = result.stmts(); + let stmt = stmts.first().unwrap(); + + // nodes() returns a Vec + let all_nodes = stmt.nodes(); + println!("Total nodes in AST: {}", all_nodes.len()); + + // Can still iterate with iter() + let select_count = stmt + .iter() + .filter(|n| matches!(n, NodeRef::SelectStmt(_))) + .count(); + println!("Number of SELECT statements: {}", select_count); + } + + // Mutable access - no cloning needed! + { + let mut stmts = result.stmts_mut(); + if let Some(stmt) = stmts.first_mut() { + // Now we can iterate mutably without cloning + for mut_node in stmt.iter_mut() { + // Modify nodes here if needed + if let pgt_query::NodeMut::SelectStmt(_select) = mut_node { + println!("Found a SELECT statement to modify"); + // You can modify _select here + } + } + } + } + + // Alternative: using root_mut() for single statement queries + if let Some(root) = result.root_mut() { + println!("Root node type: {:?}", std::mem::discriminant(root)); + } +} diff --git a/crates/pgt_query/src/deparse.rs b/crates/pgt_query/src/deparse.rs new file mode 100644 index 00000000..91f3d450 --- /dev/null +++ b/crates/pgt_query/src/deparse.rs @@ -0,0 +1,93 @@ +use std::ffi::CStr; +use std::os::raw::c_char; + +use crate::bindings::*; +use crate::error::*; +use crate::protobuf; + +use prost::Message; + +/// Converts a parsed tree back into a string. +/// +/// # Example +/// +/// ```rust +/// use pgt_query::{parse, NodeEnum, NodeRef}; +/// +/// let result = parse("INSERT INTO other (name) SELECT name FROM contacts"); +/// let result = result.unwrap(); +/// let stmts = result.stmts(); +/// let insert = stmts.first().unwrap(); +/// assert!(matches!(insert, NodeEnum::InsertStmt(_))); +/// let select = insert.iter().find(|n| matches!(n, NodeRef::SelectStmt(_))).unwrap(); +/// +/// // The entire parse result can be deparsed: +/// assert_eq!(result.deparse().unwrap(), "INSERT INTO other (name) SELECT name FROM contacts"); +/// // Or an individual node can be deparsed: +/// assert_eq!(insert.deparse().unwrap(), "INSERT INTO other (name) SELECT name FROM contacts"); +/// assert_eq!(select.deparse().unwrap(), "SELECT name FROM contacts"); +/// ``` +/// +/// Note that this function will panic if called on a node not defined in `deparseStmt` +pub fn deparse(protobuf: &protobuf::ParseResult) -> Result { + let buffer = protobuf.encode_to_vec(); + let len = buffer.len(); + let data = buffer.as_ptr() as *const c_char as *mut c_char; + let protobuf = PgQueryProtobuf { data, len }; + let result = unsafe { pg_query_deparse_protobuf(protobuf) }; + + let deparse_result = if !result.error.is_null() { + let message = unsafe { CStr::from_ptr((*result.error).message) } + .to_string_lossy() + .to_string(); + Err(Error::Parse(message)) + } else { + let query = unsafe { CStr::from_ptr(result.query) } + .to_string_lossy() + .to_string(); + Ok(query) + }; + + unsafe { pg_query_free_deparse_result(result) }; + deparse_result +} + +#[cfg(test)] +mod tests { + use crate::parse; + + fn assert_deparse(input: &str, output: &str) { + let result = parse(input).unwrap(); + assert_eq!(result.deparse().unwrap(), output); + } + + #[test] + fn it_deparses_select() { + let query = "SELECT a AS b FROM x WHERE y = 5 AND z = y"; + assert_deparse(query, query); + } + + #[test] + fn it_deparses_select_with_empty_target_list() { + let query = "SELECT FROM x WHERE y = 5 AND z = y"; + assert_deparse(query, query); + } + + #[test] + fn it_deparses_select_with_schema() { + let query = "SELECT a AS b FROM public.x WHERE y = 5 AND z = y"; + assert_deparse(query, query); + } + + #[test] + fn it_deparses_select_with_distinct() { + let query = "SELECT DISTINCT a, b, * FROM c WHERE d = e"; + assert_deparse(query, query); + } + + #[test] + fn it_deparses_select_with_distinct_on() { + let query = "SELECT DISTINCT ON (a) a, b FROM c"; + assert_deparse(query, query); + } +} diff --git a/crates/pgt_query/src/error.rs b/crates/pgt_query/src/error.rs new file mode 100644 index 00000000..50845b44 --- /dev/null +++ b/crates/pgt_query/src/error.rs @@ -0,0 +1,23 @@ +use thiserror::Error; + +/// Error structure representing the basic error scenarios for `pg_query`. +#[derive(Debug, Error, Eq, PartialEq)] +pub enum Error { + #[error("Invalid statement format: {0}")] + Conversion(#[from] std::ffi::NulError), + #[error("Error decoding result: {0}")] + Decode(#[from] prost::DecodeError), + #[error("Invalid statement: {0}")] + Parse(String), + #[error("Error parsing JSON: {0}")] + InvalidJson(String), + #[error("Invalid pointer")] + InvalidPointer, + #[error("Error scanning: {0}")] + Scan(String), + #[error("Error splitting: {0}")] + Split(String), +} + +/// Convenient Result alias for returning `pg_query::Error`. +pub type Result = core::result::Result; diff --git a/crates/pgt_query/src/fingerprint.rs b/crates/pgt_query/src/fingerprint.rs new file mode 100644 index 00000000..127b6ca6 --- /dev/null +++ b/crates/pgt_query/src/fingerprint.rs @@ -0,0 +1,359 @@ +use std::ffi::{CStr, CString}; + +use crate::bindings::*; +use crate::error::*; + +/// Represents the resulting fingerprint containing both the raw integer form as well as the +/// corresponding 16 character hex value. +pub struct Fingerprint { + pub value: u64, + pub hex: String, +} + +/// Fingerprints the given SQL statement. Useful for comparing parse trees across different implementations +/// of `libpg_query`. +/// +/// # Example +/// +/// ```rust +/// let result = pgt_query::fingerprint("SELECT * FROM contacts WHERE name='Paul'"); +/// assert!(result.is_ok()); +/// let result = result.unwrap(); +/// assert_eq!(result.hex, "0e2581a461ece536"); +/// ``` +pub fn fingerprint(statement: &str) -> Result { + let input = CString::new(statement)?; + let result = unsafe { pg_query_fingerprint(input.as_ptr()) }; + let fingerprint = if !result.error.is_null() { + let message = unsafe { CStr::from_ptr((*result.error).message) } + .to_string_lossy() + .to_string(); + Err(Error::Parse(message)) + } else { + let hex = unsafe { CStr::from_ptr(result.fingerprint_str) }; + Ok(Fingerprint { + value: result.fingerprint, + hex: hex.to_string_lossy().to_string(), + }) + }; + unsafe { pg_query_free_fingerprint_result(result) }; + fingerprint +} + +#[cfg(test)] +mod tests { + use crate::{Error, fingerprint}; + + #[test] + fn it_can_fingerprint_a_simple_statement() { + let result = + fingerprint("SELECT * FROM contacts.person WHERE id IN (1, 2, 3, 4);").unwrap(); + assert_eq!(result.hex, "643d2a3c294ab8a7"); + } + + #[test] + fn it_will_error_on_invalid_input() { + let error = fingerprint("CREATE RANDOM ix_test ON contacts.person;") + .err() + .unwrap(); + assert_eq!( + error, + Error::Parse("syntax error at or near \"RANDOM\"".into()) + ); + } + + #[test] + fn it_works_for_multi_statement_queries() { + let q1 = "SET x=$1; SELECT A"; + let q2 = "SET x=$1; SELECT a"; + assert_eq!(fingerprint(q1).unwrap().hex, fingerprint(q2).unwrap().hex); + + let q1 = "SET x=$1; SELECT A"; + let q2 = "SELECT a"; + assert_ne!(fingerprint(q1).unwrap().hex, fingerprint(q2).unwrap().hex); + } + + #[test] + fn it_ignores_aliases() { + let q1 = "SELECT a AS b"; + let q2 = "SELECT a AS c"; + assert_eq!(fingerprint(q1).unwrap().hex, fingerprint(q2).unwrap().hex); + + let q1 = "SELECT a"; + let q2 = "SELECT a AS c"; + assert_eq!(fingerprint(q1).unwrap().hex, fingerprint(q2).unwrap().hex); + + let q1 = "SELECT * FROM a AS b"; + let q2 = "SELECT * FROM a AS c"; + assert_eq!(fingerprint(q1).unwrap().hex, fingerprint(q2).unwrap().hex); + + let q1 = "SELECT * FROM a"; + let q2 = "SELECT * FROM a AS c"; + assert_eq!(fingerprint(q1).unwrap().hex, fingerprint(q2).unwrap().hex); + + let q1 = "SELECT * FROM (SELECT * FROM x AS y) AS a"; + let q2 = "SELECT * FROM (SELECT * FROM x AS z) AS b"; + assert_eq!(fingerprint(q1).unwrap().hex, fingerprint(q2).unwrap().hex); + + let q1 = "SELECT a AS b UNION SELECT x AS y"; + let q2 = "SELECT a AS c UNION SELECT x AS z"; + assert_eq!(fingerprint(q1).unwrap().hex, fingerprint(q2).unwrap().hex); + } + + #[test] + fn it_ignores_param_references() { + let q1 = "SELECT $1"; + let q2 = "SELECT $2"; + assert_eq!(fingerprint(q1).unwrap().hex, fingerprint(q2).unwrap().hex); + } + + #[test] + fn it_ignores_select_target_list_ordering() { + let q1 = "SELECT a, b FROM x"; + let q2 = "SELECT b, a FROM x"; + assert_eq!(fingerprint(q1).unwrap().hex, fingerprint(q2).unwrap().hex); + let q1 = "SELECT $1, b FROM x"; + let q2 = "SELECT b, $1 FROM x"; + assert_eq!(fingerprint(q1).unwrap().hex, fingerprint(q2).unwrap().hex); + let q1 = "SELECT $1, $2, b FROM x"; + let q2 = "SELECT $1, b, $2 FROM x"; + assert_eq!(fingerprint(q1).unwrap().hex, fingerprint(q2).unwrap().hex); + + // Testing uniqueness + let q1 = "SELECT a, c FROM x"; + let q2 = "SELECT b, a FROM x"; + assert_ne!(fingerprint(q1).unwrap().hex, fingerprint(q2).unwrap().hex); + let q1 = "SELECT b FROM x"; + let q2 = "SELECT b, a FROM x"; + assert_ne!(fingerprint(q1).unwrap().hex, fingerprint(q2).unwrap().hex); + } + + #[test] + fn it_ignores_insert_col_ordering() { + let q1 = "INSERT INTO test (a, b) VALUES ($1, $2)"; + let q2 = "INSERT INTO test (b, a) VALUES ($1, $2)"; + assert_eq!(fingerprint(q1).unwrap().hex, fingerprint(q2).unwrap().hex); + + // Testing uniqueness + let q1 = "INSERT INTO test (a, c) VALUES ($1, $2)"; + let q2 = "INSERT INTO test (b, a) VALUES ($1, $2)"; + assert_ne!(fingerprint(q1).unwrap().hex, fingerprint(q2).unwrap().hex); + let q1 = "INSERT INTO test (b) VALUES ($1, $2)"; + let q2 = "INSERT INTO test (b, a) VALUES ($1, $2)"; + assert_ne!(fingerprint(q1).unwrap().hex, fingerprint(q2).unwrap().hex); + } + + #[test] + fn it_ignores_in_list_size() { + let q1 = "SELECT * FROM x WHERE y IN ($1, $2, $3)"; + let q2 = "SELECT * FROM x WHERE y IN ($1)"; + assert_eq!(fingerprint(q1).unwrap().hex, fingerprint(q2).unwrap().hex); + + let q1 = "SELECT * FROM x WHERE y IN ( $1::uuid, $2::uuid, $3::uuid )"; + let q2 = "SELECT * FROM x WHERE y IN ( $1::uuid )"; + assert_eq!(fingerprint(q1).unwrap().hex, fingerprint(q2).unwrap().hex); + } + + #[test] + fn it_works() { + let result = fingerprint("SELECT 1").unwrap(); + assert_eq!(result.hex, "50fde20626009aba"); + + let result = fingerprint("SELECT 2").unwrap(); + assert_eq!(result.hex, "50fde20626009aba"); + + let result = fingerprint("SELECT $1").unwrap(); + assert_eq!(result.hex, "50fde20626009aba"); + + let result = fingerprint("SELECT 1; SELECT a FROM b").unwrap(); + assert_eq!(result.hex, "3efa3b10d558d06d"); + + let result = fingerprint("SELECT COUNT(DISTINCT id), * FROM targets WHERE something IS NOT NULL AND elsewhere::interval < now()").unwrap(); + assert_eq!(result.hex, "26b6553101185d22"); + + let result = fingerprint("INSERT INTO test (a, b) VALUES ($1, $2)").unwrap(); + assert_eq!(result.hex, "51e63b8083b48bdd"); + + let result = fingerprint("INSERT INTO test (b, a) VALUES ($1, $2)").unwrap(); + assert_eq!(result.hex, "51e63b8083b48bdd"); + + let result = fingerprint( + "INSERT INTO test (a, b) VALUES (ARRAY[$1, $2, $3, $4], $5::timestamptz), (ARRAY[$6, $7, $8, $9], $10::timestamptz), ($11, $12::timestamptz)", + ) + .unwrap(); + assert_eq!(result.hex, "4dfdd5260cac5acf"); + + let result = fingerprint("SELECT b AS x, a AS y FROM z").unwrap(); + assert_eq!(result.hex, "1a8bf5d7614de3a5"); + + let result = fingerprint("SELECT * FROM x WHERE y = $1").unwrap(); + assert_eq!(result.hex, "4ff39426bd074231"); + + let result = fingerprint("SELECT * FROM x WHERE y = ANY ($1)").unwrap(); + assert_eq!(result.hex, "4ff39426bd074231"); + + let result = fingerprint("SELECT * FROM x WHERE y IN ($1)").unwrap(); + assert_eq!(result.hex, "4ff39426bd074231"); + + let result = fingerprint("SELECT * FROM x WHERE y IN ($1, $2, $3)").unwrap(); + assert_eq!(result.hex, "4ff39426bd074231"); + + let result = fingerprint("SELECT * FROM x WHERE y IN ( $1::uuid )").unwrap(); + assert_eq!(result.hex, "4ff39426bd074231"); + + let result = + fingerprint("SELECT * FROM x WHERE y IN ( $1::uuid, $2::uuid, $3::uuid )").unwrap(); + assert_eq!(result.hex, "4ff39426bd074231"); + + let result = fingerprint("PREPARE a123 AS SELECT a").unwrap(); + assert_eq!(result.hex, "9b5e6ead8be993e8"); + + let result = fingerprint("EXECUTE a123").unwrap(); + assert_eq!(result.hex, "44ef1d2beabd53e8"); + + let result = fingerprint("DEALLOCATE a123").unwrap(); + assert_eq!(result.hex, "d8a65a814fbc5f95"); + + let result = fingerprint("DEALLOCATE ALL").unwrap(); + assert_eq!(result.hex, "2debfb8745df64a7"); + + let result = fingerprint("EXPLAIN ANALYZE SELECT a").unwrap(); + assert_eq!(result.hex, "82845c1b5c6102e5"); + + let result = + fingerprint("WITH a AS (SELECT * FROM x WHERE x.y = $1 AND x.z = 1) SELECT * FROM a") + .unwrap(); + assert_eq!(result.hex, "6831e38bbb3dd18c"); + + let result = + fingerprint("CREATE TABLE types (a float(2), b float(49), c NUMERIC(2, 3), d character(4), e char(5), f varchar(6), g character varying(7))") + .unwrap(); + assert_eq!(result.hex, "008d6ba4aa0f4c6e"); + + let result = + fingerprint("CREATE VIEW view_a (a, b) AS WITH RECURSIVE view_a (a, b) AS (SELECT * FROM a(1)) SELECT \"a\", \"b\" FROM \"view_a\"").unwrap(); + assert_eq!(result.hex, "c6ef6b9f498feda4"); + + let result = fingerprint("VACUUM FULL my_table").unwrap(); + assert_eq!(result.hex, "fdf2f4127644f4d8"); + + let result = fingerprint("SELECT * FROM x AS a, y AS b").unwrap(); + assert_eq!(result.hex, "4e9acae841dae228"); + + let result = fingerprint("SELECT * FROM y AS a, x AS b").unwrap(); + assert_eq!(result.hex, "4e9acae841dae228"); + + let result = fingerprint("SELECT x AS a, y AS b FROM x").unwrap(); + assert_eq!(result.hex, "65dff5f5e9a643ad"); + + let result = fingerprint("SELECT y AS a, x AS b FROM x").unwrap(); + assert_eq!(result.hex, "65dff5f5e9a643ad"); + + let result = fingerprint("SELECT x, y FROM z").unwrap(); + assert_eq!(result.hex, "330267237da5535f"); + + let result = fingerprint("SELECT y, x FROM z").unwrap(); + assert_eq!(result.hex, "330267237da5535f"); + + let result = fingerprint("INSERT INTO films (code, title, did) VALUES ('UA502', 'Bananas', 105), ('T_601', 'Yojimbo', DEFAULT)").unwrap(); + assert_eq!(result.hex, "459fdc70778b841e"); + + let result = + fingerprint("INSERT INTO films (code, title, did) VALUES ($1, $2, $3)").unwrap(); + assert_eq!(result.hex, "459fdc70778b841e"); + + let result = fingerprint("SELECT * FROM a").unwrap(); + assert_eq!(result.hex, "fcf44da7b597ef43"); + + let result = fingerprint("SELECT * FROM a AS b").unwrap(); + assert_eq!(result.hex, "fcf44da7b597ef43"); + + let result = + fingerprint("UPDATE users SET one_thing = $1, second_thing = $2 WHERE users.id = $1") + .unwrap(); + assert_eq!(result.hex, "a0ea386c1cfd1e69"); + + let result = + fingerprint("UPDATE users SET something_else = $1 WHERE users.id = $1").unwrap(); + assert_eq!(result.hex, "3172bc3e0d631d55"); + + let result = fingerprint("UPDATE users SET something_else = (SELECT a FROM x WHERE uid = users.id LIMIT 1) WHERE users.id = $1").unwrap(); + assert_eq!(result.hex, "f1127a8b91fbecbf"); + + let result = fingerprint("SAVEPOINT some_id").unwrap(); + assert_eq!(result.hex, "8ebd566ea1bf947b"); + + let result = fingerprint("RELEASE some_id").unwrap(); + assert_eq!(result.hex, "60d618658252d2af"); + + let result = fingerprint("PREPARE TRANSACTION 'some_id'").unwrap(); + assert_eq!(result.hex, "d993959a33d627d4"); + + let result = fingerprint("START TRANSACTION READ WRITE").unwrap(); + assert_eq!(result.hex, "4ca25828c835d55a"); + + let result = + fingerprint("DECLARE cursor_123 CURSOR FOR SELECT * FROM test WHERE id = 123").unwrap(); + assert_eq!(result.hex, "d2bec62d2a7ec7cb"); + + let result = fingerprint("FETCH 1000 FROM cursor_123").unwrap(); + assert_eq!(result.hex, "37f4d2f6a957ae48"); + + let result = fingerprint("CLOSE cursor_123").unwrap(); + assert_eq!(result.hex, "2c7963684fc2bad9"); + + let result = fingerprint("-- nothing").unwrap(); + assert_eq!(result.hex, "d8d13f8b2da6c9ad"); + + let result = fingerprint("CREATE FOREIGN TABLE ft1 () SERVER no_server").unwrap(); + assert_eq!(result.hex, "74481c4af7c76be1"); + + let result = fingerprint("UPDATE x SET a = 1, b = 2, c = 3").unwrap(); + assert_eq!(result.hex, "fd5c248c0e642ce4"); + + let result = fingerprint("UPDATE x SET z = now()").unwrap(); + assert_eq!(result.hex, "a222eaabaa1e7cb1"); + + let result = fingerprint( + "CREATE TEMPORARY TABLE my_temp_table (test_id integer NOT NULL) ON COMMIT DROP", + ) + .unwrap(); + assert_eq!(result.hex, "1407ed5c5bb00967"); + + let result = fingerprint("CREATE TEMPORARY TABLE my_temp_table AS SELECT 1").unwrap(); + assert_eq!(result.hex, "695ebe73a3abc45c"); + + let result = fingerprint("SELECT INTERVAL (0) $2").unwrap(); + assert_eq!(result.hex, "50fde20626009aba"); + + let result = fingerprint("SELECT INTERVAL (2) $2").unwrap(); + assert_eq!(result.hex, "50fde20626009aba"); + + let result = fingerprint("SELECT * FROM t WHERE t.a IN (1, 2) AND t.b = 3").unwrap(); + assert_eq!(result.hex, "346aea01be9173b6"); + + let result = fingerprint("SELECT * FROM t WHERE t.b = 3 AND t.a IN (1, 2)").unwrap(); + assert_eq!(result.hex, "346aea01be9173b6"); + + let result = fingerprint("SELECT * FROM t WHERE a && '[1,2]'").unwrap(); + assert_eq!(result.hex, "673f199f13dfe665"); + + let result = fingerprint("SELECT * FROM t WHERE a && '[1,2]'::int4range").unwrap(); + assert_eq!(result.hex, "673f199f13dfe665"); + + let result = fingerprint("SELECT * FROM t_20210301_x").unwrap(); + assert_eq!(result.hex, "6f8169980cd70a25"); + + let result = fingerprint("SELECT * FROM t_20210302_x").unwrap(); + assert_eq!(result.hex, "6f8169980cd70a25"); + + let result = fingerprint("SELECT * FROM t_20210302_y").unwrap(); + assert_eq!(result.hex, "d357dac4a24fcf1b"); + + let result = fingerprint("SELECT * FROM t_1").unwrap(); + assert_eq!(result.hex, "018bd9230646143e"); + + let result = fingerprint("SELECT * FROM t_2").unwrap(); + assert_eq!(result.hex, "3f1444da570c1a66"); + } +} diff --git a/crates/pgt_query/src/iter_mut.rs b/crates/pgt_query/src/iter_mut.rs new file mode 100644 index 00000000..fe5e8806 --- /dev/null +++ b/crates/pgt_query/src/iter_mut.rs @@ -0,0 +1 @@ +pgt_query_macros::iter_mut_codegen!(); diff --git a/crates/pgt_query/src/iter_ref.rs b/crates/pgt_query/src/iter_ref.rs new file mode 100644 index 00000000..6ac4f220 --- /dev/null +++ b/crates/pgt_query/src/iter_ref.rs @@ -0,0 +1 @@ +pgt_query_macros::iter_ref_codegen!(); diff --git a/crates/pgt_query/src/lib.rs b/crates/pgt_query/src/lib.rs new file mode 100644 index 00000000..e8981719 --- /dev/null +++ b/crates/pgt_query/src/lib.rs @@ -0,0 +1,91 @@ +mod deparse; +mod error; +mod fingerprint; +mod iter_mut; +mod iter_ref; +mod node_enum; +mod node_mut; +mod node_ref; +mod node_structs; +mod normalize; +mod parse; +mod plpgsql; +mod scan; +mod split; + +pub use deparse::*; +pub use error::*; +pub use fingerprint::*; +pub use iter_mut::*; +pub use iter_ref::*; +pub use node_enum::*; +pub use node_mut::*; +pub use node_ref::*; +pub use normalize::*; +pub use parse::*; +pub use plpgsql::*; +pub use scan::*; +pub use split::*; + +pub use protobuf::Node; + +// Include the generated bindings with 2024 edition compatibility +#[allow(non_upper_case_globals)] +#[allow(non_camel_case_types)] +#[allow(non_snake_case)] +#[allow(dead_code)] +#[allow(improper_ctypes)] +#[allow(unsafe_op_in_unsafe_fn)] +mod bindings { + include!(concat!(env!("OUT_DIR"), "/bindings.rs")); +} + +// Include the generated protobuf code +#[allow(clippy::all)] +pub mod protobuf { + include!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/protobuf.rs")); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn it_does_not_error_when_run_in_parallel() { + use easy_parallel::Parallel; + + let mut queries = vec![]; + for _ in 0..100 { + queries.push( + r#" + SELECT * FROM "t0" + JOIN "t1" ON (1) JOIN "t2" ON (1) JOIN "t3" ON (1) JOIN "t4" ON (1) JOIN "t5" ON (1) + JOIN "t6" ON (1) JOIN "t7" ON (1) JOIN "t8" ON (1) JOIN "t9" ON (1) JOIN "t10" ON (1) + JOIN "t11" ON (1) JOIN "t12" ON (1) JOIN "t13" ON (1) JOIN "t14" ON (1) JOIN "t15" ON (1) + JOIN "t16" ON (1) JOIN "t17" ON (1) JOIN "t18" ON (1) JOIN "t19" ON (1) JOIN "t20" ON (1) + JOIN "t21" ON (1) JOIN "t22" ON (1) JOIN "t23" ON (1) JOIN "t24" ON (1) JOIN "t25" ON (1) + JOIN "t26" ON (1) JOIN "t27" ON (1) JOIN "t28" ON (1) JOIN "t29" ON (1) + "#, + ); + queries.push( + " + SELECT memory_total_bytes, memory_free_bytes, memory_pagecache_bytes, memory_buffers_bytes, memory_applications_bytes, + (memory_swap_total_bytes - memory_swap_free_bytes) AS swap, date_part($0, s.collected_at) AS collected_at + FROM snapshots s JOIN system_snapshots ON (snapshot_id = s.id) + WHERE s.database_id = $0 AND s.collected_at BETWEEN $0 AND $0 + ORDER BY collected_at + ", + ); + } + + Parallel::new() + .each(queries, |query| { + for _ in 0..100 { + let _result = parse(query).unwrap(); + fingerprint(query).unwrap(); + normalize(query).unwrap(); + } + }) + .run(); + } +} diff --git a/crates/pgt_query/src/node_enum.rs b/crates/pgt_query/src/node_enum.rs new file mode 100644 index 00000000..5d5b6bf7 --- /dev/null +++ b/crates/pgt_query/src/node_enum.rs @@ -0,0 +1,33 @@ +use crate::*; + +use protobuf::Node; +pub use protobuf::node::Node as NodeEnum; + +pgt_query_macros::node_enum_codegen!(); + +impl NodeEnum { + pub fn deparse(&self) -> Result { + crate::deparse(&protobuf::ParseResult { + version: crate::bindings::PG_VERSION_NUM as i32, + stmts: vec![protobuf::RawStmt { + stmt: Some(Box::new(Node { + node: Some(self.clone()), + })), + stmt_location: 0, + stmt_len: 0, + }], + }) + } + + pub fn nodes(&self) -> Vec> { + self.iter().collect() + } + + pub fn iter(&self) -> NodeRefIterator<'_> { + NodeRefIterator::new(self.to_ref()) + } + + pub fn iter_mut(&mut self) -> NodeMutIterator { + NodeMutIterator::new(self.to_mut()) + } +} diff --git a/crates/pgt_query/src/node_mut.rs b/crates/pgt_query/src/node_mut.rs new file mode 100644 index 00000000..f2da254b --- /dev/null +++ b/crates/pgt_query/src/node_mut.rs @@ -0,0 +1,26 @@ +use protobuf::Node; + +pgt_query_macros::node_mut_codegen!(); + +impl NodeMut { + pub fn deparse(&self) -> Result { + crate::deparse(&protobuf::ParseResult { + version: crate::bindings::PG_VERSION_NUM as i32, + stmts: vec![protobuf::RawStmt { + stmt: Some(Box::new(Node { + node: Some(self.to_enum()?), + })), + stmt_location: 0, + stmt_len: 0, + }], + }) + } + + pub fn nodes_mut(&self) -> Vec { + self.iter_mut().collect() + } + + pub fn iter_mut(&self) -> NodeMutIterator { + NodeMutIterator::new(*self) + } +} diff --git a/crates/pgt_query/src/node_ref.rs b/crates/pgt_query/src/node_ref.rs new file mode 100644 index 00000000..603913cb --- /dev/null +++ b/crates/pgt_query/src/node_ref.rs @@ -0,0 +1,26 @@ +use protobuf::Node; + +pgt_query_macros::node_ref_codegen!(); + +impl<'a> NodeRef<'a> { + pub fn deparse(&self) -> Result { + crate::deparse(&protobuf::ParseResult { + version: crate::bindings::PG_VERSION_NUM as i32, + stmts: vec![protobuf::RawStmt { + stmt: Some(Box::new(Node { + node: Some(self.to_enum()), + })), + stmt_location: 0, + stmt_len: 0, + }], + }) + } + + pub fn nodes(&self) -> Vec> { + self.iter().collect() + } + + pub fn iter(&self) -> NodeRefIterator<'a> { + NodeRefIterator::new(*self) + } +} diff --git a/crates/pgt_query/src/node_structs.rs b/crates/pgt_query/src/node_structs.rs new file mode 100644 index 00000000..8b81c98e --- /dev/null +++ b/crates/pgt_query/src/node_structs.rs @@ -0,0 +1,16 @@ +use protobuf::Node; + +pgt_query_macros::node_structs_codegen!(); + +impl Node { + pub fn deparse(&self) -> Result { + crate::deparse(&protobuf::ParseResult { + version: crate::bindings::PG_VERSION_NUM as i32, + stmts: vec![protobuf::RawStmt { + stmt: Some(Box::new(self.clone())), + stmt_location: 0, + stmt_len: 0, + }], + }) + } +} diff --git a/crates/pgt_query/src/normalize.rs b/crates/pgt_query/src/normalize.rs new file mode 100644 index 00000000..71ff683c --- /dev/null +++ b/crates/pgt_query/src/normalize.rs @@ -0,0 +1,136 @@ +use std::ffi::{CStr, CString}; + +use crate::bindings::*; +use crate::error::*; + +/// Normalizes the given SQL statement, returning a parametized version. +/// +/// # Example +/// +/// ```rust +/// let result = pgt_query::normalize("SELECT * FROM contacts WHERE name='Paul'"); +/// assert!(result.is_ok()); +/// let result = result.unwrap(); +/// assert_eq!(result, "SELECT * FROM contacts WHERE name=$1"); +/// ``` +pub fn normalize(statement: &str) -> Result { + let input = CString::new(statement).unwrap(); + let result = unsafe { pg_query_normalize(input.as_ptr()) }; + let normalized_query = if !result.error.is_null() { + let message = unsafe { CStr::from_ptr((*result.error).message) } + .to_string_lossy() + .to_string(); + Err(Error::Parse(message)) + } else { + let n = unsafe { CStr::from_ptr(result.normalized_query) }; + Ok(n.to_string_lossy().to_string()) + }; + unsafe { pg_query_free_normalize_result(result) }; + normalized_query +} + +#[cfg(test)] +mod tests { + use crate::{Error, normalize}; + + #[test] + fn it_normalizes_simple_query() { + let result = normalize("SELECT 1").unwrap(); + assert_eq!(result, "SELECT $1"); + } + + #[test] + fn it_normalizes_in() { + let result = + normalize("SELECT 1 FROM x WHERE y = 12561 AND z = '124' AND b IN (1, 2, 3)").unwrap(); + assert_eq!( + result, + "SELECT $1 FROM x WHERE y = $2 AND z = $3 AND b IN ($4, $5, $6)" + ); + } + + #[test] + fn it_errors_on_invalid_input() { + let error = normalize("CREATE RANDOM ix_test ON contacts.person;") + .err() + .unwrap(); + assert_eq!( + error, + Error::Parse("syntax error at or near \"RANDOM\"".into()) + ); + } + + #[test] + fn it_normalizes_subselects() { + let result = + normalize("SELECT 1 FROM x WHERE y = (SELECT 123 FROM a WHERE z = 'bla')").unwrap(); + assert_eq!( + result, + "SELECT $1 FROM x WHERE y = (SELECT $2 FROM a WHERE z = $3)" + ); + } + + #[test] + fn it_normalizes_any() { + let result = normalize("SELECT * FROM x WHERE y = ANY(array[1, 2])").unwrap(); + assert_eq!(result, "SELECT * FROM x WHERE y = ANY(array[$1, $2])"); + + let result = normalize("SELECT * FROM x WHERE y = ANY(SELECT 1)").unwrap(); + assert_eq!(result, "SELECT * FROM x WHERE y = ANY(SELECT $1)"); + } + + #[test] + fn it_normalizes_complicated_strings() { + let result = normalize("SELECT U&'d\\0061t\\+000061' FROM x").unwrap(); + assert_eq!(result, "SELECT $1 FROM x"); + + let result = normalize("SELECT u&'d\\0061t\\+000061' FROM x").unwrap(); + assert_eq!(result, "SELECT $1 FROM x"); + + let result = normalize("SELECT * FROM x WHERE z NOT LIKE E'abc'AND TRUE").unwrap(); + assert_eq!(result, "SELECT * FROM x WHERE z NOT LIKE $1AND $2"); + + let result = normalize("SELECT U&'d\\0061t\\+000061'-- comment\nFROM x").unwrap(); + assert_eq!(result, "SELECT $1-- comment\nFROM x"); + } + + #[test] + fn it_normalizes_copy() { + let result = normalize("COPY (SELECT * FROM t WHERE id IN ('1', '2')) TO STDOUT").unwrap(); + assert_eq!( + result, + "COPY (SELECT * FROM t WHERE id IN ($1, $2)) TO STDOUT" + ); + } + + #[test] + fn it_normalizes_set() { + let result = normalize("SET test=123").unwrap(); + assert_eq!(result, "SET test=$1"); + + let result = normalize("SET CLIENT_ENCODING = UTF8").unwrap(); + assert_eq!(result, "SET CLIENT_ENCODING = $1"); + } + + #[test] + fn it_does_not_error_on_deallocate() { + let result = normalize("DEALLOCATE bla; SELECT 1").unwrap(); + assert_eq!(result, "DEALLOCATE bla; SELECT $1"); + } + + #[test] + fn it_normalizes_explain() { + let result = normalize("EXPLAIN SELECT x FROM y WHERE z = 1").unwrap(); + assert_eq!(result, "EXPLAIN SELECT x FROM y WHERE z = $1"); + } + + #[test] + fn it_normalizes_declare_curson() { + let result = + normalize("DECLARE cursor_b CURSOR FOR SELECT * FROM databases WHERE id = 23").unwrap(); + assert_eq!( + result, + "DECLARE cursor_b CURSOR FOR SELECT * FROM databases WHERE id = $1" + ); + } +} diff --git a/crates/pgt_query/src/parse.rs b/crates/pgt_query/src/parse.rs new file mode 100644 index 00000000..5853dfbc --- /dev/null +++ b/crates/pgt_query/src/parse.rs @@ -0,0 +1,149 @@ +use std::ffi::{CStr, CString}; + +use crate::NodeEnum; +use crate::bindings::*; +use crate::error::*; +use crate::protobuf; + +use prost::Message; + +/// Parses the given SQL statement into the given abstract syntax tree. +/// +/// # Example +/// +/// ```rust +/// use pgt_query::parse; +/// +/// let result = parse("SELECT * FROM contacts"); +/// assert!(result.is_ok()); +/// let result = result.unwrap(); +/// assert_eq!(result.protobuf.stmts.len(), 1); +/// ``` +pub fn parse(statement: &str) -> Result { + let input = CString::new(statement)?; + let result = unsafe { pg_query_parse_protobuf(input.as_ptr()) }; + let parse_result = if !result.error.is_null() { + let message = unsafe { CStr::from_ptr((*result.error).message) } + .to_string_lossy() + .to_string(); + Err(Error::Parse(message)) + } else { + let data = unsafe { + std::slice::from_raw_parts( + result.parse_tree.data as *const u8, + result.parse_tree.len as usize, + ) + }; + let stderr = unsafe { CStr::from_ptr(result.stderr_buffer) } + .to_string_lossy() + .to_string(); + protobuf::ParseResult::decode(data) + .map_err(Error::Decode) + .map(|result| ParseResult::new(result, stderr)) + }; + unsafe { pg_query_free_protobuf_parse_result(result) }; + parse_result +} + +/// The result of parsing a SQL query +#[derive(Debug)] +pub struct ParseResult { + /// The parsed protobuf result + pub protobuf: protobuf::ParseResult, + /// Warnings captured during parsing + pub warnings: Vec, +} + +impl ParseResult { + /// Create a new ParseResult + pub fn new(protobuf: protobuf::ParseResult, stderr: String) -> Self { + let warnings = stderr + .lines() + .filter_map(|l| { + if l.starts_with("WARNING") { + Some(l.trim().into()) + } else { + None + } + }) + .collect(); + + Self { protobuf, warnings } + } + + pub fn deparse(&self) -> Result { + crate::deparse(&self.protobuf) + } + + pub fn stmts(&self) -> Vec<&NodeEnum> { + self.protobuf + .stmts + .iter() + .filter_map(|s| s.stmt.as_ref().and_then(|s| s.node.as_ref())) + .collect() + } + + pub fn stmts_mut(&mut self) -> Vec<&mut NodeEnum> { + self.protobuf + .stmts + .iter_mut() + .filter_map(|s| s.stmt.as_mut().and_then(|s| s.node.as_mut())) + .collect() + } + + /// Returns a reference to the root node of the parse tree. + /// + /// Returns None if there is not exactly one statement in the parse result. + pub fn root(&self) -> Option<&NodeEnum> { + if self.protobuf.stmts.len() != 1 { + return None; + } + + // Get the first (and only) statement + let raw_stmt = &self.protobuf.stmts[0]; + + // Navigate: RawStmt -> Node -> NodeEnum + raw_stmt.stmt.as_ref().and_then(|stmt| stmt.node.as_ref()) + } + + /// Consumes the ParseResult and returns the root node of the parse tree. + /// + /// Returns None if there is not exactly one statement in the parse result. + /// This method avoids cloning by taking ownership of the ParseResult. + pub fn into_root(self) -> Option { + if self.protobuf.stmts.len() != 1 { + return None; + } + + // Extract the first (and only) statement by taking ownership + let raw_stmt = self.protobuf.stmts.into_iter().next()?; + + // Navigate: RawStmt -> Node -> NodeEnum + raw_stmt.stmt.and_then(|stmt| stmt.node) + } + + /// Returns a mutable reference to the root node of the parse tree. + /// + /// Returns None if there is not exactly one statement in the parse result. + pub fn root_mut(&mut self) -> Option<&mut NodeEnum> { + if self.protobuf.stmts.len() != 1 { + return None; + } + + // Get the first (and only) statement + let raw_stmt = &mut self.protobuf.stmts[0]; + + // Navigate: RawStmt -> Node -> NodeEnum + raw_stmt.stmt.as_mut().and_then(|stmt| stmt.node.as_mut()) + } +} + +#[cfg(test)] +mod tests { + use crate::parse; + + #[test] + fn it_parses_parameter_queries() { + assert!(parse("select $0 + $1 + $2 + $3 + $4 + $5").is_ok()); + } +} diff --git a/crates/pgt_query/src/plpgsql.rs b/crates/pgt_query/src/plpgsql.rs new file mode 100644 index 00000000..fbaa9694 --- /dev/null +++ b/crates/pgt_query/src/plpgsql.rs @@ -0,0 +1,38 @@ +use std::ffi::{CStr, CString}; + +use crate::bindings::*; +use crate::error::*; + +/// An experimental API which parses a PLPGSQL function. This currently drops the returned +/// structure and returns only a Result<()>. +/// +/// # Example +/// +/// ```rust +/// let result = pgt_query::parse_plpgsql(" +/// CREATE OR REPLACE FUNCTION cs_fmt_browser_version(v_name varchar, v_version varchar) +/// RETURNS varchar AS $$ +/// BEGIN +/// IF v_version IS NULL THEN +/// RETURN v_name; +/// END IF; +/// RETURN v_name || '/' || v_version; +/// END; +/// $$ LANGUAGE plpgsql; +/// "); +/// assert!(result.is_ok()); +/// ``` +pub fn parse_plpgsql(stmt: &str) -> Result<()> { + let input = CString::new(stmt)?; + let result = unsafe { pg_query_parse_plpgsql(input.as_ptr()) }; + let structure = if !result.error.is_null() { + let message = unsafe { CStr::from_ptr((*result.error).message) } + .to_string_lossy() + .to_string(); + Err(Error::Parse(message)) + } else { + Ok(()) + }; + unsafe { pg_query_free_plpgsql_parse_result(result) }; + structure +} diff --git a/crates/pgt_query/src/protobuf.rs b/crates/pgt_query/src/protobuf.rs new file mode 100644 index 00000000..c47bfe52 --- /dev/null +++ b/crates/pgt_query/src/protobuf.rs @@ -0,0 +1,8846 @@ +// This file is @generated by prost-build. +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ParseResult { + #[prost(int32, tag = "1")] + pub version: i32, + #[prost(message, repeated, tag = "2")] + pub stmts: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ScanResult { + #[prost(int32, tag = "1")] + pub version: i32, + #[prost(message, repeated, tag = "2")] + pub tokens: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Node { + #[prost( + oneof = "node::Node", + tags = "1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268" + )] + pub node: ::core::option::Option, +} +/// Nested message and enum types in `Node`. +pub mod node { + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Node { + #[prost(message, tag = "1")] + Alias(super::Alias), + #[prost(message, tag = "2")] + RangeVar(super::RangeVar), + #[prost(message, tag = "3")] + TableFunc(::prost::alloc::boxed::Box), + #[prost(message, tag = "4")] + IntoClause(::prost::alloc::boxed::Box), + #[prost(message, tag = "5")] + Var(::prost::alloc::boxed::Box), + #[prost(message, tag = "6")] + Param(::prost::alloc::boxed::Box), + #[prost(message, tag = "7")] + Aggref(::prost::alloc::boxed::Box), + #[prost(message, tag = "8")] + GroupingFunc(::prost::alloc::boxed::Box), + #[prost(message, tag = "9")] + WindowFunc(::prost::alloc::boxed::Box), + #[prost(message, tag = "10")] + WindowFuncRunCondition( + ::prost::alloc::boxed::Box, + ), + #[prost(message, tag = "11")] + MergeSupportFunc(::prost::alloc::boxed::Box), + #[prost(message, tag = "12")] + SubscriptingRef(::prost::alloc::boxed::Box), + #[prost(message, tag = "13")] + FuncExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "14")] + NamedArgExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "15")] + OpExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "16")] + DistinctExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "17")] + NullIfExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "18")] + ScalarArrayOpExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "19")] + BoolExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "20")] + SubLink(::prost::alloc::boxed::Box), + #[prost(message, tag = "21")] + SubPlan(::prost::alloc::boxed::Box), + #[prost(message, tag = "22")] + AlternativeSubPlan(::prost::alloc::boxed::Box), + #[prost(message, tag = "23")] + FieldSelect(::prost::alloc::boxed::Box), + #[prost(message, tag = "24")] + FieldStore(::prost::alloc::boxed::Box), + #[prost(message, tag = "25")] + RelabelType(::prost::alloc::boxed::Box), + #[prost(message, tag = "26")] + CoerceViaIo(::prost::alloc::boxed::Box), + #[prost(message, tag = "27")] + ArrayCoerceExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "28")] + ConvertRowtypeExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "29")] + CollateExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "30")] + CaseExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "31")] + CaseWhen(::prost::alloc::boxed::Box), + #[prost(message, tag = "32")] + CaseTestExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "33")] + ArrayExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "34")] + RowExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "35")] + RowCompareExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "36")] + CoalesceExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "37")] + MinMaxExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "38")] + SqlvalueFunction(::prost::alloc::boxed::Box), + #[prost(message, tag = "39")] + XmlExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "40")] + JsonFormat(super::JsonFormat), + #[prost(message, tag = "41")] + JsonReturning(super::JsonReturning), + #[prost(message, tag = "42")] + JsonValueExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "43")] + JsonConstructorExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "44")] + JsonIsPredicate(::prost::alloc::boxed::Box), + #[prost(message, tag = "45")] + JsonBehavior(::prost::alloc::boxed::Box), + #[prost(message, tag = "46")] + JsonExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "47")] + JsonTablePath(super::JsonTablePath), + #[prost(message, tag = "48")] + JsonTablePathScan(::prost::alloc::boxed::Box), + #[prost(message, tag = "49")] + JsonTableSiblingJoin(::prost::alloc::boxed::Box), + #[prost(message, tag = "50")] + NullTest(::prost::alloc::boxed::Box), + #[prost(message, tag = "51")] + BooleanTest(::prost::alloc::boxed::Box), + #[prost(message, tag = "52")] + MergeAction(::prost::alloc::boxed::Box), + #[prost(message, tag = "53")] + CoerceToDomain(::prost::alloc::boxed::Box), + #[prost(message, tag = "54")] + CoerceToDomainValue(::prost::alloc::boxed::Box), + #[prost(message, tag = "55")] + SetToDefault(::prost::alloc::boxed::Box), + #[prost(message, tag = "56")] + CurrentOfExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "57")] + NextValueExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "58")] + InferenceElem(::prost::alloc::boxed::Box), + #[prost(message, tag = "59")] + TargetEntry(::prost::alloc::boxed::Box), + #[prost(message, tag = "60")] + RangeTblRef(super::RangeTblRef), + #[prost(message, tag = "61")] + JoinExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "62")] + FromExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "63")] + OnConflictExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "64")] + Query(::prost::alloc::boxed::Box), + #[prost(message, tag = "65")] + TypeName(super::TypeName), + #[prost(message, tag = "66")] + ColumnRef(super::ColumnRef), + #[prost(message, tag = "67")] + ParamRef(super::ParamRef), + #[prost(message, tag = "68")] + AExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "69")] + TypeCast(::prost::alloc::boxed::Box), + #[prost(message, tag = "70")] + CollateClause(::prost::alloc::boxed::Box), + #[prost(message, tag = "71")] + RoleSpec(super::RoleSpec), + #[prost(message, tag = "72")] + FuncCall(::prost::alloc::boxed::Box), + #[prost(message, tag = "73")] + AStar(super::AStar), + #[prost(message, tag = "74")] + AIndices(::prost::alloc::boxed::Box), + #[prost(message, tag = "75")] + AIndirection(::prost::alloc::boxed::Box), + #[prost(message, tag = "76")] + AArrayExpr(super::AArrayExpr), + #[prost(message, tag = "77")] + ResTarget(::prost::alloc::boxed::Box), + #[prost(message, tag = "78")] + MultiAssignRef(::prost::alloc::boxed::Box), + #[prost(message, tag = "79")] + SortBy(::prost::alloc::boxed::Box), + #[prost(message, tag = "80")] + WindowDef(::prost::alloc::boxed::Box), + #[prost(message, tag = "81")] + RangeSubselect(::prost::alloc::boxed::Box), + #[prost(message, tag = "82")] + RangeFunction(super::RangeFunction), + #[prost(message, tag = "83")] + RangeTableFunc(::prost::alloc::boxed::Box), + #[prost(message, tag = "84")] + RangeTableFuncCol(::prost::alloc::boxed::Box), + #[prost(message, tag = "85")] + RangeTableSample(::prost::alloc::boxed::Box), + #[prost(message, tag = "86")] + ColumnDef(::prost::alloc::boxed::Box), + #[prost(message, tag = "87")] + TableLikeClause(super::TableLikeClause), + #[prost(message, tag = "88")] + IndexElem(::prost::alloc::boxed::Box), + #[prost(message, tag = "89")] + DefElem(::prost::alloc::boxed::Box), + #[prost(message, tag = "90")] + LockingClause(super::LockingClause), + #[prost(message, tag = "91")] + XmlSerialize(::prost::alloc::boxed::Box), + #[prost(message, tag = "92")] + PartitionElem(::prost::alloc::boxed::Box), + #[prost(message, tag = "93")] + PartitionSpec(super::PartitionSpec), + #[prost(message, tag = "94")] + PartitionBoundSpec(super::PartitionBoundSpec), + #[prost(message, tag = "95")] + PartitionRangeDatum(::prost::alloc::boxed::Box), + #[prost(message, tag = "96")] + SinglePartitionSpec(super::SinglePartitionSpec), + #[prost(message, tag = "97")] + PartitionCmd(super::PartitionCmd), + #[prost(message, tag = "98")] + RangeTblEntry(::prost::alloc::boxed::Box), + #[prost(message, tag = "99")] + RtepermissionInfo(super::RtePermissionInfo), + #[prost(message, tag = "100")] + RangeTblFunction(::prost::alloc::boxed::Box), + #[prost(message, tag = "101")] + TableSampleClause(::prost::alloc::boxed::Box), + #[prost(message, tag = "102")] + WithCheckOption(::prost::alloc::boxed::Box), + #[prost(message, tag = "103")] + SortGroupClause(super::SortGroupClause), + #[prost(message, tag = "104")] + GroupingSet(super::GroupingSet), + #[prost(message, tag = "105")] + WindowClause(::prost::alloc::boxed::Box), + #[prost(message, tag = "106")] + RowMarkClause(super::RowMarkClause), + #[prost(message, tag = "107")] + WithClause(super::WithClause), + #[prost(message, tag = "108")] + InferClause(::prost::alloc::boxed::Box), + #[prost(message, tag = "109")] + OnConflictClause(::prost::alloc::boxed::Box), + #[prost(message, tag = "110")] + CtesearchClause(super::CteSearchClause), + #[prost(message, tag = "111")] + CtecycleClause(::prost::alloc::boxed::Box), + #[prost(message, tag = "112")] + CommonTableExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "113")] + MergeWhenClause(::prost::alloc::boxed::Box), + #[prost(message, tag = "114")] + TriggerTransition(super::TriggerTransition), + #[prost(message, tag = "115")] + JsonOutput(super::JsonOutput), + #[prost(message, tag = "116")] + JsonArgument(::prost::alloc::boxed::Box), + #[prost(message, tag = "117")] + JsonFuncExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "118")] + JsonTablePathSpec(::prost::alloc::boxed::Box), + #[prost(message, tag = "119")] + JsonTable(::prost::alloc::boxed::Box), + #[prost(message, tag = "120")] + JsonTableColumn(::prost::alloc::boxed::Box), + #[prost(message, tag = "121")] + JsonKeyValue(::prost::alloc::boxed::Box), + #[prost(message, tag = "122")] + JsonParseExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "123")] + JsonScalarExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "124")] + JsonSerializeExpr(::prost::alloc::boxed::Box), + #[prost(message, tag = "125")] + JsonObjectConstructor(super::JsonObjectConstructor), + #[prost(message, tag = "126")] + JsonArrayConstructor(super::JsonArrayConstructor), + #[prost(message, tag = "127")] + JsonArrayQueryConstructor( + ::prost::alloc::boxed::Box, + ), + #[prost(message, tag = "128")] + JsonAggConstructor(::prost::alloc::boxed::Box), + #[prost(message, tag = "129")] + JsonObjectAgg(::prost::alloc::boxed::Box), + #[prost(message, tag = "130")] + JsonArrayAgg(::prost::alloc::boxed::Box), + #[prost(message, tag = "131")] + RawStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "132")] + InsertStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "133")] + DeleteStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "134")] + UpdateStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "135")] + MergeStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "136")] + SelectStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "137")] + SetOperationStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "138")] + ReturnStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "139")] + PlassignStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "140")] + CreateSchemaStmt(super::CreateSchemaStmt), + #[prost(message, tag = "141")] + AlterTableStmt(super::AlterTableStmt), + #[prost(message, tag = "142")] + ReplicaIdentityStmt(super::ReplicaIdentityStmt), + #[prost(message, tag = "143")] + AlterTableCmd(::prost::alloc::boxed::Box), + #[prost(message, tag = "144")] + AlterCollationStmt(super::AlterCollationStmt), + #[prost(message, tag = "145")] + AlterDomainStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "146")] + GrantStmt(super::GrantStmt), + #[prost(message, tag = "147")] + ObjectWithArgs(super::ObjectWithArgs), + #[prost(message, tag = "148")] + AccessPriv(super::AccessPriv), + #[prost(message, tag = "149")] + GrantRoleStmt(super::GrantRoleStmt), + #[prost(message, tag = "150")] + AlterDefaultPrivilegesStmt(super::AlterDefaultPrivilegesStmt), + #[prost(message, tag = "151")] + CopyStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "152")] + VariableSetStmt(super::VariableSetStmt), + #[prost(message, tag = "153")] + VariableShowStmt(super::VariableShowStmt), + #[prost(message, tag = "154")] + CreateStmt(super::CreateStmt), + #[prost(message, tag = "155")] + Constraint(::prost::alloc::boxed::Box), + #[prost(message, tag = "156")] + CreateTableSpaceStmt(super::CreateTableSpaceStmt), + #[prost(message, tag = "157")] + DropTableSpaceStmt(super::DropTableSpaceStmt), + #[prost(message, tag = "158")] + AlterTableSpaceOptionsStmt(super::AlterTableSpaceOptionsStmt), + #[prost(message, tag = "159")] + AlterTableMoveAllStmt(super::AlterTableMoveAllStmt), + #[prost(message, tag = "160")] + CreateExtensionStmt(super::CreateExtensionStmt), + #[prost(message, tag = "161")] + AlterExtensionStmt(super::AlterExtensionStmt), + #[prost(message, tag = "162")] + AlterExtensionContentsStmt( + ::prost::alloc::boxed::Box, + ), + #[prost(message, tag = "163")] + CreateFdwStmt(super::CreateFdwStmt), + #[prost(message, tag = "164")] + AlterFdwStmt(super::AlterFdwStmt), + #[prost(message, tag = "165")] + CreateForeignServerStmt(super::CreateForeignServerStmt), + #[prost(message, tag = "166")] + AlterForeignServerStmt(super::AlterForeignServerStmt), + #[prost(message, tag = "167")] + CreateForeignTableStmt(super::CreateForeignTableStmt), + #[prost(message, tag = "168")] + CreateUserMappingStmt(super::CreateUserMappingStmt), + #[prost(message, tag = "169")] + AlterUserMappingStmt(super::AlterUserMappingStmt), + #[prost(message, tag = "170")] + DropUserMappingStmt(super::DropUserMappingStmt), + #[prost(message, tag = "171")] + ImportForeignSchemaStmt(super::ImportForeignSchemaStmt), + #[prost(message, tag = "172")] + CreatePolicyStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "173")] + AlterPolicyStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "174")] + CreateAmStmt(super::CreateAmStmt), + #[prost(message, tag = "175")] + CreateTrigStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "176")] + CreateEventTrigStmt(super::CreateEventTrigStmt), + #[prost(message, tag = "177")] + AlterEventTrigStmt(super::AlterEventTrigStmt), + #[prost(message, tag = "178")] + CreatePlangStmt(super::CreatePLangStmt), + #[prost(message, tag = "179")] + CreateRoleStmt(super::CreateRoleStmt), + #[prost(message, tag = "180")] + AlterRoleStmt(super::AlterRoleStmt), + #[prost(message, tag = "181")] + AlterRoleSetStmt(super::AlterRoleSetStmt), + #[prost(message, tag = "182")] + DropRoleStmt(super::DropRoleStmt), + #[prost(message, tag = "183")] + CreateSeqStmt(super::CreateSeqStmt), + #[prost(message, tag = "184")] + AlterSeqStmt(super::AlterSeqStmt), + #[prost(message, tag = "185")] + DefineStmt(super::DefineStmt), + #[prost(message, tag = "186")] + CreateDomainStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "187")] + CreateOpClassStmt(super::CreateOpClassStmt), + #[prost(message, tag = "188")] + CreateOpClassItem(super::CreateOpClassItem), + #[prost(message, tag = "189")] + CreateOpFamilyStmt(super::CreateOpFamilyStmt), + #[prost(message, tag = "190")] + AlterOpFamilyStmt(super::AlterOpFamilyStmt), + #[prost(message, tag = "191")] + DropStmt(super::DropStmt), + #[prost(message, tag = "192")] + TruncateStmt(super::TruncateStmt), + #[prost(message, tag = "193")] + CommentStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "194")] + SecLabelStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "195")] + DeclareCursorStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "196")] + ClosePortalStmt(super::ClosePortalStmt), + #[prost(message, tag = "197")] + FetchStmt(super::FetchStmt), + #[prost(message, tag = "198")] + IndexStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "199")] + CreateStatsStmt(super::CreateStatsStmt), + #[prost(message, tag = "200")] + StatsElem(::prost::alloc::boxed::Box), + #[prost(message, tag = "201")] + AlterStatsStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "202")] + CreateFunctionStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "203")] + FunctionParameter(::prost::alloc::boxed::Box), + #[prost(message, tag = "204")] + AlterFunctionStmt(super::AlterFunctionStmt), + #[prost(message, tag = "205")] + DoStmt(super::DoStmt), + #[prost(message, tag = "206")] + InlineCodeBlock(super::InlineCodeBlock), + #[prost(message, tag = "207")] + CallStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "208")] + CallContext(super::CallContext), + #[prost(message, tag = "209")] + RenameStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "210")] + AlterObjectDependsStmt( + ::prost::alloc::boxed::Box, + ), + #[prost(message, tag = "211")] + AlterObjectSchemaStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "212")] + AlterOwnerStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "213")] + AlterOperatorStmt(super::AlterOperatorStmt), + #[prost(message, tag = "214")] + AlterTypeStmt(super::AlterTypeStmt), + #[prost(message, tag = "215")] + RuleStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "216")] + NotifyStmt(super::NotifyStmt), + #[prost(message, tag = "217")] + ListenStmt(super::ListenStmt), + #[prost(message, tag = "218")] + UnlistenStmt(super::UnlistenStmt), + #[prost(message, tag = "219")] + TransactionStmt(super::TransactionStmt), + #[prost(message, tag = "220")] + CompositeTypeStmt(super::CompositeTypeStmt), + #[prost(message, tag = "221")] + CreateEnumStmt(super::CreateEnumStmt), + #[prost(message, tag = "222")] + CreateRangeStmt(super::CreateRangeStmt), + #[prost(message, tag = "223")] + AlterEnumStmt(super::AlterEnumStmt), + #[prost(message, tag = "224")] + ViewStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "225")] + LoadStmt(super::LoadStmt), + #[prost(message, tag = "226")] + CreatedbStmt(super::CreatedbStmt), + #[prost(message, tag = "227")] + AlterDatabaseStmt(super::AlterDatabaseStmt), + #[prost(message, tag = "228")] + AlterDatabaseRefreshCollStmt(super::AlterDatabaseRefreshCollStmt), + #[prost(message, tag = "229")] + AlterDatabaseSetStmt(super::AlterDatabaseSetStmt), + #[prost(message, tag = "230")] + DropdbStmt(super::DropdbStmt), + #[prost(message, tag = "231")] + AlterSystemStmt(super::AlterSystemStmt), + #[prost(message, tag = "232")] + ClusterStmt(super::ClusterStmt), + #[prost(message, tag = "233")] + VacuumStmt(super::VacuumStmt), + #[prost(message, tag = "234")] + VacuumRelation(super::VacuumRelation), + #[prost(message, tag = "235")] + ExplainStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "236")] + CreateTableAsStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "237")] + RefreshMatViewStmt(super::RefreshMatViewStmt), + #[prost(message, tag = "238")] + CheckPointStmt(super::CheckPointStmt), + #[prost(message, tag = "239")] + DiscardStmt(super::DiscardStmt), + #[prost(message, tag = "240")] + LockStmt(super::LockStmt), + #[prost(message, tag = "241")] + ConstraintsSetStmt(super::ConstraintsSetStmt), + #[prost(message, tag = "242")] + ReindexStmt(super::ReindexStmt), + #[prost(message, tag = "243")] + CreateConversionStmt(super::CreateConversionStmt), + #[prost(message, tag = "244")] + CreateCastStmt(super::CreateCastStmt), + #[prost(message, tag = "245")] + CreateTransformStmt(super::CreateTransformStmt), + #[prost(message, tag = "246")] + PrepareStmt(::prost::alloc::boxed::Box), + #[prost(message, tag = "247")] + ExecuteStmt(super::ExecuteStmt), + #[prost(message, tag = "248")] + DeallocateStmt(super::DeallocateStmt), + #[prost(message, tag = "249")] + DropOwnedStmt(super::DropOwnedStmt), + #[prost(message, tag = "250")] + ReassignOwnedStmt(super::ReassignOwnedStmt), + #[prost(message, tag = "251")] + AlterTsdictionaryStmt(super::AlterTsDictionaryStmt), + #[prost(message, tag = "252")] + AlterTsconfigurationStmt(super::AlterTsConfigurationStmt), + #[prost(message, tag = "253")] + PublicationTable(::prost::alloc::boxed::Box), + #[prost(message, tag = "254")] + PublicationObjSpec(::prost::alloc::boxed::Box), + #[prost(message, tag = "255")] + CreatePublicationStmt(super::CreatePublicationStmt), + #[prost(message, tag = "256")] + AlterPublicationStmt(super::AlterPublicationStmt), + #[prost(message, tag = "257")] + CreateSubscriptionStmt(super::CreateSubscriptionStmt), + #[prost(message, tag = "258")] + AlterSubscriptionStmt(super::AlterSubscriptionStmt), + #[prost(message, tag = "259")] + DropSubscriptionStmt(super::DropSubscriptionStmt), + #[prost(message, tag = "260")] + Integer(super::Integer), + #[prost(message, tag = "261")] + Float(super::Float), + #[prost(message, tag = "262")] + Boolean(super::Boolean), + #[prost(message, tag = "263")] + String(super::String), + #[prost(message, tag = "264")] + BitString(super::BitString), + #[prost(message, tag = "265")] + List(super::List), + #[prost(message, tag = "266")] + IntList(super::IntList), + #[prost(message, tag = "267")] + OidList(super::OidList), + #[prost(message, tag = "268")] + AConst(super::AConst), + } +} +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct Integer { + /// machine integer + #[prost(int32, tag = "1")] + pub ival: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Float { + /// string + #[prost(string, tag = "1")] + pub fval: ::prost::alloc::string::String, +} +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct Boolean { + #[prost(bool, tag = "1")] + pub boolval: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct String { + /// string + #[prost(string, tag = "1")] + pub sval: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BitString { + /// string + #[prost(string, tag = "1")] + pub bsval: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct List { + #[prost(message, repeated, tag = "1")] + pub items: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct OidList { + #[prost(message, repeated, tag = "1")] + pub items: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct IntList { + #[prost(message, repeated, tag = "1")] + pub items: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AConst { + #[prost(bool, tag = "10")] + pub isnull: bool, + #[prost(int32, tag = "11")] + pub location: i32, + #[prost(oneof = "a_const::Val", tags = "1, 2, 3, 4, 5")] + pub val: ::core::option::Option, +} +/// Nested message and enum types in `A_Const`. +pub mod a_const { + #[derive(Clone, PartialEq, ::prost::Oneof)] + pub enum Val { + #[prost(message, tag = "1")] + Ival(super::Integer), + #[prost(message, tag = "2")] + Fval(super::Float), + #[prost(message, tag = "3")] + Boolval(super::Boolean), + #[prost(message, tag = "4")] + Sval(super::String), + #[prost(message, tag = "5")] + Bsval(super::BitString), + } +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Alias { + #[prost(string, tag = "1")] + pub aliasname: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub colnames: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RangeVar { + #[prost(string, tag = "1")] + pub catalogname: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub schemaname: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub relname: ::prost::alloc::string::String, + #[prost(bool, tag = "4")] + pub inh: bool, + #[prost(string, tag = "5")] + pub relpersistence: ::prost::alloc::string::String, + #[prost(message, optional, tag = "6")] + pub alias: ::core::option::Option, + #[prost(int32, tag = "7")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TableFunc { + #[prost(enumeration = "TableFuncType", tag = "1")] + pub functype: i32, + #[prost(message, repeated, tag = "2")] + pub ns_uris: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub ns_names: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "4")] + pub docexpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "5")] + pub rowexpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "6")] + pub colnames: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "7")] + pub coltypes: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "8")] + pub coltypmods: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "9")] + pub colcollations: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "10")] + pub colexprs: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "11")] + pub coldefexprs: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "12")] + pub colvalexprs: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "13")] + pub passingvalexprs: ::prost::alloc::vec::Vec, + #[prost(uint64, repeated, tag = "14")] + pub notnulls: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "15")] + pub plan: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "16")] + pub ordinalitycol: i32, + #[prost(int32, tag = "17")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct IntoClause { + #[prost(message, optional, tag = "1")] + pub rel: ::core::option::Option, + #[prost(message, repeated, tag = "2")] + pub col_names: ::prost::alloc::vec::Vec, + #[prost(string, tag = "3")] + pub access_method: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "4")] + pub options: ::prost::alloc::vec::Vec, + #[prost(enumeration = "OnCommitAction", tag = "5")] + pub on_commit: i32, + #[prost(string, tag = "6")] + pub table_space_name: ::prost::alloc::string::String, + #[prost(message, optional, boxed, tag = "7")] + pub view_query: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(bool, tag = "8")] + pub skip_data: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Var { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "2")] + pub varno: i32, + #[prost(int32, tag = "3")] + pub varattno: i32, + #[prost(uint32, tag = "4")] + pub vartype: u32, + #[prost(int32, tag = "5")] + pub vartypmod: i32, + #[prost(uint32, tag = "6")] + pub varcollid: u32, + #[prost(uint64, repeated, tag = "7")] + pub varnullingrels: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "8")] + pub varlevelsup: u32, + #[prost(int32, tag = "9")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Param { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(enumeration = "ParamKind", tag = "2")] + pub paramkind: i32, + #[prost(int32, tag = "3")] + pub paramid: i32, + #[prost(uint32, tag = "4")] + pub paramtype: u32, + #[prost(int32, tag = "5")] + pub paramtypmod: i32, + #[prost(uint32, tag = "6")] + pub paramcollid: u32, + #[prost(int32, tag = "7")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Aggref { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "2")] + pub aggfnoid: u32, + #[prost(uint32, tag = "3")] + pub aggtype: u32, + #[prost(uint32, tag = "4")] + pub aggcollid: u32, + #[prost(uint32, tag = "5")] + pub inputcollid: u32, + #[prost(message, repeated, tag = "6")] + pub aggargtypes: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "7")] + pub aggdirectargs: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "8")] + pub args: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "9")] + pub aggorder: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "10")] + pub aggdistinct: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "11")] + pub aggfilter: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(bool, tag = "12")] + pub aggstar: bool, + #[prost(bool, tag = "13")] + pub aggvariadic: bool, + #[prost(string, tag = "14")] + pub aggkind: ::prost::alloc::string::String, + #[prost(uint32, tag = "15")] + pub agglevelsup: u32, + #[prost(enumeration = "AggSplit", tag = "16")] + pub aggsplit: i32, + #[prost(int32, tag = "17")] + pub aggno: i32, + #[prost(int32, tag = "18")] + pub aggtransno: i32, + #[prost(int32, tag = "19")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GroupingFunc { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "2")] + pub args: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub refs: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "4")] + pub agglevelsup: u32, + #[prost(int32, tag = "5")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct WindowFunc { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "2")] + pub winfnoid: u32, + #[prost(uint32, tag = "3")] + pub wintype: u32, + #[prost(uint32, tag = "4")] + pub wincollid: u32, + #[prost(uint32, tag = "5")] + pub inputcollid: u32, + #[prost(message, repeated, tag = "6")] + pub args: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "7")] + pub aggfilter: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "8")] + pub run_condition: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "9")] + pub winref: u32, + #[prost(bool, tag = "10")] + pub winstar: bool, + #[prost(bool, tag = "11")] + pub winagg: bool, + #[prost(int32, tag = "12")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct WindowFuncRunCondition { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "2")] + pub opno: u32, + #[prost(uint32, tag = "3")] + pub inputcollid: u32, + #[prost(bool, tag = "4")] + pub wfunc_left: bool, + #[prost(message, optional, boxed, tag = "5")] + pub arg: ::core::option::Option<::prost::alloc::boxed::Box>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MergeSupportFunc { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "2")] + pub msftype: u32, + #[prost(uint32, tag = "3")] + pub msfcollid: u32, + #[prost(int32, tag = "4")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SubscriptingRef { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "2")] + pub refcontainertype: u32, + #[prost(uint32, tag = "3")] + pub refelemtype: u32, + #[prost(uint32, tag = "4")] + pub refrestype: u32, + #[prost(int32, tag = "5")] + pub reftypmod: i32, + #[prost(uint32, tag = "6")] + pub refcollid: u32, + #[prost(message, repeated, tag = "7")] + pub refupperindexpr: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "8")] + pub reflowerindexpr: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "9")] + pub refexpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "10")] + pub refassgnexpr: ::core::option::Option<::prost::alloc::boxed::Box>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FuncExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "2")] + pub funcid: u32, + #[prost(uint32, tag = "3")] + pub funcresulttype: u32, + #[prost(bool, tag = "4")] + pub funcretset: bool, + #[prost(bool, tag = "5")] + pub funcvariadic: bool, + #[prost(enumeration = "CoercionForm", tag = "6")] + pub funcformat: i32, + #[prost(uint32, tag = "7")] + pub funccollid: u32, + #[prost(uint32, tag = "8")] + pub inputcollid: u32, + #[prost(message, repeated, tag = "9")] + pub args: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "10")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NamedArgExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "2")] + pub arg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(string, tag = "3")] + pub name: ::prost::alloc::string::String, + #[prost(int32, tag = "4")] + pub argnumber: i32, + #[prost(int32, tag = "5")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct OpExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "2")] + pub opno: u32, + #[prost(uint32, tag = "3")] + pub opresulttype: u32, + #[prost(bool, tag = "4")] + pub opretset: bool, + #[prost(uint32, tag = "5")] + pub opcollid: u32, + #[prost(uint32, tag = "6")] + pub inputcollid: u32, + #[prost(message, repeated, tag = "7")] + pub args: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "8")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DistinctExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "2")] + pub opno: u32, + #[prost(uint32, tag = "3")] + pub opresulttype: u32, + #[prost(bool, tag = "4")] + pub opretset: bool, + #[prost(uint32, tag = "5")] + pub opcollid: u32, + #[prost(uint32, tag = "6")] + pub inputcollid: u32, + #[prost(message, repeated, tag = "7")] + pub args: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "8")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NullIfExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "2")] + pub opno: u32, + #[prost(uint32, tag = "3")] + pub opresulttype: u32, + #[prost(bool, tag = "4")] + pub opretset: bool, + #[prost(uint32, tag = "5")] + pub opcollid: u32, + #[prost(uint32, tag = "6")] + pub inputcollid: u32, + #[prost(message, repeated, tag = "7")] + pub args: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "8")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ScalarArrayOpExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "2")] + pub opno: u32, + #[prost(bool, tag = "3")] + pub use_or: bool, + #[prost(uint32, tag = "4")] + pub inputcollid: u32, + #[prost(message, repeated, tag = "5")] + pub args: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "6")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BoolExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(enumeration = "BoolExprType", tag = "2")] + pub boolop: i32, + #[prost(message, repeated, tag = "3")] + pub args: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "4")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SubLink { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(enumeration = "SubLinkType", tag = "2")] + pub sub_link_type: i32, + #[prost(int32, tag = "3")] + pub sub_link_id: i32, + #[prost(message, optional, boxed, tag = "4")] + pub testexpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "5")] + pub oper_name: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "6")] + pub subselect: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "7")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SubPlan { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(enumeration = "SubLinkType", tag = "2")] + pub sub_link_type: i32, + #[prost(message, optional, boxed, tag = "3")] + pub testexpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "4")] + pub param_ids: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "5")] + pub plan_id: i32, + #[prost(string, tag = "6")] + pub plan_name: ::prost::alloc::string::String, + #[prost(uint32, tag = "7")] + pub first_col_type: u32, + #[prost(int32, tag = "8")] + pub first_col_typmod: i32, + #[prost(uint32, tag = "9")] + pub first_col_collation: u32, + #[prost(bool, tag = "10")] + pub use_hash_table: bool, + #[prost(bool, tag = "11")] + pub unknown_eq_false: bool, + #[prost(bool, tag = "12")] + pub parallel_safe: bool, + #[prost(message, repeated, tag = "13")] + pub set_param: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "14")] + pub par_param: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "15")] + pub args: ::prost::alloc::vec::Vec, + #[prost(double, tag = "16")] + pub startup_cost: f64, + #[prost(double, tag = "17")] + pub per_call_cost: f64, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlternativeSubPlan { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "2")] + pub subplans: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FieldSelect { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "2")] + pub arg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "3")] + pub fieldnum: i32, + #[prost(uint32, tag = "4")] + pub resulttype: u32, + #[prost(int32, tag = "5")] + pub resulttypmod: i32, + #[prost(uint32, tag = "6")] + pub resultcollid: u32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FieldStore { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "2")] + pub arg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "3")] + pub newvals: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub fieldnums: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "5")] + pub resulttype: u32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RelabelType { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "2")] + pub arg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "3")] + pub resulttype: u32, + #[prost(int32, tag = "4")] + pub resulttypmod: i32, + #[prost(uint32, tag = "5")] + pub resultcollid: u32, + #[prost(enumeration = "CoercionForm", tag = "6")] + pub relabelformat: i32, + #[prost(int32, tag = "7")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CoerceViaIo { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "2")] + pub arg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "3")] + pub resulttype: u32, + #[prost(uint32, tag = "4")] + pub resultcollid: u32, + #[prost(enumeration = "CoercionForm", tag = "5")] + pub coerceformat: i32, + #[prost(int32, tag = "6")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ArrayCoerceExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "2")] + pub arg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "3")] + pub elemexpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "4")] + pub resulttype: u32, + #[prost(int32, tag = "5")] + pub resulttypmod: i32, + #[prost(uint32, tag = "6")] + pub resultcollid: u32, + #[prost(enumeration = "CoercionForm", tag = "7")] + pub coerceformat: i32, + #[prost(int32, tag = "8")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ConvertRowtypeExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "2")] + pub arg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "3")] + pub resulttype: u32, + #[prost(enumeration = "CoercionForm", tag = "4")] + pub convertformat: i32, + #[prost(int32, tag = "5")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CollateExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "2")] + pub arg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "3")] + pub coll_oid: u32, + #[prost(int32, tag = "4")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CaseExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "2")] + pub casetype: u32, + #[prost(uint32, tag = "3")] + pub casecollid: u32, + #[prost(message, optional, boxed, tag = "4")] + pub arg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "5")] + pub args: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "6")] + pub defresult: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "7")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CaseWhen { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "2")] + pub expr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "3")] + pub result: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "4")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CaseTestExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "2")] + pub type_id: u32, + #[prost(int32, tag = "3")] + pub type_mod: i32, + #[prost(uint32, tag = "4")] + pub collation: u32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ArrayExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "2")] + pub array_typeid: u32, + #[prost(uint32, tag = "3")] + pub array_collid: u32, + #[prost(uint32, tag = "4")] + pub element_typeid: u32, + #[prost(message, repeated, tag = "5")] + pub elements: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "6")] + pub multidims: bool, + #[prost(int32, tag = "7")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RowExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "2")] + pub args: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "3")] + pub row_typeid: u32, + #[prost(enumeration = "CoercionForm", tag = "4")] + pub row_format: i32, + #[prost(message, repeated, tag = "5")] + pub colnames: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "6")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RowCompareExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(enumeration = "RowCompareType", tag = "2")] + pub rctype: i32, + #[prost(message, repeated, tag = "3")] + pub opnos: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub opfamilies: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "5")] + pub inputcollids: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "6")] + pub largs: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "7")] + pub rargs: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CoalesceExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "2")] + pub coalescetype: u32, + #[prost(uint32, tag = "3")] + pub coalescecollid: u32, + #[prost(message, repeated, tag = "4")] + pub args: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "5")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MinMaxExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "2")] + pub minmaxtype: u32, + #[prost(uint32, tag = "3")] + pub minmaxcollid: u32, + #[prost(uint32, tag = "4")] + pub inputcollid: u32, + #[prost(enumeration = "MinMaxOp", tag = "5")] + pub op: i32, + #[prost(message, repeated, tag = "6")] + pub args: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "7")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SqlValueFunction { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(enumeration = "SqlValueFunctionOp", tag = "2")] + pub op: i32, + #[prost(uint32, tag = "3")] + pub r#type: u32, + #[prost(int32, tag = "4")] + pub typmod: i32, + #[prost(int32, tag = "5")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct XmlExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(enumeration = "XmlExprOp", tag = "2")] + pub op: i32, + #[prost(string, tag = "3")] + pub name: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "4")] + pub named_args: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "5")] + pub arg_names: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "6")] + pub args: ::prost::alloc::vec::Vec, + #[prost(enumeration = "XmlOptionType", tag = "7")] + pub xmloption: i32, + #[prost(bool, tag = "8")] + pub indent: bool, + #[prost(uint32, tag = "9")] + pub r#type: u32, + #[prost(int32, tag = "10")] + pub typmod: i32, + #[prost(int32, tag = "11")] + pub location: i32, +} +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct JsonFormat { + #[prost(enumeration = "JsonFormatType", tag = "1")] + pub format_type: i32, + #[prost(enumeration = "JsonEncoding", tag = "2")] + pub encoding: i32, + #[prost(int32, tag = "3")] + pub location: i32, +} +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct JsonReturning { + #[prost(message, optional, tag = "1")] + pub format: ::core::option::Option, + #[prost(uint32, tag = "2")] + pub typid: u32, + #[prost(int32, tag = "3")] + pub typmod: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonValueExpr { + #[prost(message, optional, boxed, tag = "1")] + pub raw_expr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "2")] + pub formatted_expr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, tag = "3")] + pub format: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonConstructorExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(enumeration = "JsonConstructorType", tag = "2")] + pub r#type: i32, + #[prost(message, repeated, tag = "3")] + pub args: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "4")] + pub func: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "5")] + pub coercion: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, tag = "6")] + pub returning: ::core::option::Option, + #[prost(bool, tag = "7")] + pub absent_on_null: bool, + #[prost(bool, tag = "8")] + pub unique: bool, + #[prost(int32, tag = "9")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonIsPredicate { + #[prost(message, optional, boxed, tag = "1")] + pub expr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, tag = "2")] + pub format: ::core::option::Option, + #[prost(enumeration = "JsonValueType", tag = "3")] + pub item_type: i32, + #[prost(bool, tag = "4")] + pub unique_keys: bool, + #[prost(int32, tag = "5")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonBehavior { + #[prost(enumeration = "JsonBehaviorType", tag = "1")] + pub btype: i32, + #[prost(message, optional, boxed, tag = "2")] + pub expr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(bool, tag = "3")] + pub coerce: bool, + #[prost(int32, tag = "4")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(enumeration = "JsonExprOp", tag = "2")] + pub op: i32, + #[prost(string, tag = "3")] + pub column_name: ::prost::alloc::string::String, + #[prost(message, optional, boxed, tag = "4")] + pub formatted_expr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, tag = "5")] + pub format: ::core::option::Option, + #[prost(message, optional, boxed, tag = "6")] + pub path_spec: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, tag = "7")] + pub returning: ::core::option::Option, + #[prost(message, repeated, tag = "8")] + pub passing_names: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "9")] + pub passing_values: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "10")] + pub on_empty: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "11")] + pub on_error: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(bool, tag = "12")] + pub use_io_coercion: bool, + #[prost(bool, tag = "13")] + pub use_json_coercion: bool, + #[prost(enumeration = "JsonWrapper", tag = "14")] + pub wrapper: i32, + #[prost(bool, tag = "15")] + pub omit_quotes: bool, + #[prost(uint32, tag = "16")] + pub collation: u32, + #[prost(int32, tag = "17")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonTablePath { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonTablePathScan { + #[prost(message, optional, boxed, tag = "1")] + pub plan: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, tag = "2")] + pub path: ::core::option::Option, + #[prost(bool, tag = "3")] + pub error_on_error: bool, + #[prost(message, optional, boxed, tag = "4")] + pub child: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "5")] + pub col_min: i32, + #[prost(int32, tag = "6")] + pub col_max: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonTableSiblingJoin { + #[prost(message, optional, boxed, tag = "1")] + pub plan: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "2")] + pub lplan: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "3")] + pub rplan: ::core::option::Option<::prost::alloc::boxed::Box>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NullTest { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "2")] + pub arg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(enumeration = "NullTestType", tag = "3")] + pub nulltesttype: i32, + #[prost(bool, tag = "4")] + pub argisrow: bool, + #[prost(int32, tag = "5")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct BooleanTest { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "2")] + pub arg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(enumeration = "BoolTestType", tag = "3")] + pub booltesttype: i32, + #[prost(int32, tag = "4")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MergeAction { + #[prost(enumeration = "MergeMatchKind", tag = "1")] + pub match_kind: i32, + #[prost(enumeration = "CmdType", tag = "2")] + pub command_type: i32, + #[prost(enumeration = "OverridingKind", tag = "3")] + pub r#override: i32, + #[prost(message, optional, boxed, tag = "4")] + pub qual: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "5")] + pub target_list: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "6")] + pub update_colnos: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CoerceToDomain { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "2")] + pub arg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "3")] + pub resulttype: u32, + #[prost(int32, tag = "4")] + pub resulttypmod: i32, + #[prost(uint32, tag = "5")] + pub resultcollid: u32, + #[prost(enumeration = "CoercionForm", tag = "6")] + pub coercionformat: i32, + #[prost(int32, tag = "7")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CoerceToDomainValue { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "2")] + pub type_id: u32, + #[prost(int32, tag = "3")] + pub type_mod: i32, + #[prost(uint32, tag = "4")] + pub collation: u32, + #[prost(int32, tag = "5")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SetToDefault { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "2")] + pub type_id: u32, + #[prost(int32, tag = "3")] + pub type_mod: i32, + #[prost(uint32, tag = "4")] + pub collation: u32, + #[prost(int32, tag = "5")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CurrentOfExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "2")] + pub cvarno: u32, + #[prost(string, tag = "3")] + pub cursor_name: ::prost::alloc::string::String, + #[prost(int32, tag = "4")] + pub cursor_param: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NextValueExpr { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "2")] + pub seqid: u32, + #[prost(uint32, tag = "3")] + pub type_id: u32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct InferenceElem { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "2")] + pub expr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "3")] + pub infercollid: u32, + #[prost(uint32, tag = "4")] + pub inferopclass: u32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TargetEntry { + #[prost(message, optional, boxed, tag = "1")] + pub xpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "2")] + pub expr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "3")] + pub resno: i32, + #[prost(string, tag = "4")] + pub resname: ::prost::alloc::string::String, + #[prost(uint32, tag = "5")] + pub ressortgroupref: u32, + #[prost(uint32, tag = "6")] + pub resorigtbl: u32, + #[prost(int32, tag = "7")] + pub resorigcol: i32, + #[prost(bool, tag = "8")] + pub resjunk: bool, +} +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct RangeTblRef { + #[prost(int32, tag = "1")] + pub rtindex: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JoinExpr { + #[prost(enumeration = "JoinType", tag = "1")] + pub jointype: i32, + #[prost(bool, tag = "2")] + pub is_natural: bool, + #[prost(message, optional, boxed, tag = "3")] + pub larg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "4")] + pub rarg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "5")] + pub using_clause: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "6")] + pub join_using_alias: ::core::option::Option, + #[prost(message, optional, boxed, tag = "7")] + pub quals: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, tag = "8")] + pub alias: ::core::option::Option, + #[prost(int32, tag = "9")] + pub rtindex: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FromExpr { + #[prost(message, repeated, tag = "1")] + pub fromlist: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "2")] + pub quals: ::core::option::Option<::prost::alloc::boxed::Box>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct OnConflictExpr { + #[prost(enumeration = "OnConflictAction", tag = "1")] + pub action: i32, + #[prost(message, repeated, tag = "2")] + pub arbiter_elems: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "3")] + pub arbiter_where: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "4")] + pub constraint: u32, + #[prost(message, repeated, tag = "5")] + pub on_conflict_set: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "6")] + pub on_conflict_where: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "7")] + pub excl_rel_index: i32, + #[prost(message, repeated, tag = "8")] + pub excl_rel_tlist: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Query { + #[prost(enumeration = "CmdType", tag = "1")] + pub command_type: i32, + #[prost(enumeration = "QuerySource", tag = "2")] + pub query_source: i32, + #[prost(bool, tag = "3")] + pub can_set_tag: bool, + #[prost(message, optional, boxed, tag = "4")] + pub utility_stmt: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "5")] + pub result_relation: i32, + #[prost(bool, tag = "6")] + pub has_aggs: bool, + #[prost(bool, tag = "7")] + pub has_window_funcs: bool, + #[prost(bool, tag = "8")] + pub has_target_srfs: bool, + #[prost(bool, tag = "9")] + pub has_sub_links: bool, + #[prost(bool, tag = "10")] + pub has_distinct_on: bool, + #[prost(bool, tag = "11")] + pub has_recursive: bool, + #[prost(bool, tag = "12")] + pub has_modifying_cte: bool, + #[prost(bool, tag = "13")] + pub has_for_update: bool, + #[prost(bool, tag = "14")] + pub has_row_security: bool, + #[prost(bool, tag = "15")] + pub is_return: bool, + #[prost(message, repeated, tag = "16")] + pub cte_list: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "17")] + pub rtable: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "18")] + pub rteperminfos: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "19")] + pub jointree: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "20")] + pub merge_action_list: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "21")] + pub merge_target_relation: i32, + #[prost(message, optional, boxed, tag = "22")] + pub merge_join_condition: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "23")] + pub target_list: ::prost::alloc::vec::Vec, + #[prost(enumeration = "OverridingKind", tag = "24")] + pub r#override: i32, + #[prost(message, optional, boxed, tag = "25")] + pub on_conflict: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "26")] + pub returning_list: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "27")] + pub group_clause: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "28")] + pub group_distinct: bool, + #[prost(message, repeated, tag = "29")] + pub grouping_sets: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "30")] + pub having_qual: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "31")] + pub window_clause: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "32")] + pub distinct_clause: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "33")] + pub sort_clause: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "34")] + pub limit_offset: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "35")] + pub limit_count: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(enumeration = "LimitOption", tag = "36")] + pub limit_option: i32, + #[prost(message, repeated, tag = "37")] + pub row_marks: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "38")] + pub set_operations: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "39")] + pub constraint_deps: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "40")] + pub with_check_options: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "41")] + pub stmt_location: i32, + #[prost(int32, tag = "42")] + pub stmt_len: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TypeName { + #[prost(message, repeated, tag = "1")] + pub names: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "2")] + pub type_oid: u32, + #[prost(bool, tag = "3")] + pub setof: bool, + #[prost(bool, tag = "4")] + pub pct_type: bool, + #[prost(message, repeated, tag = "5")] + pub typmods: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "6")] + pub typemod: i32, + #[prost(message, repeated, tag = "7")] + pub array_bounds: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "8")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ColumnRef { + #[prost(message, repeated, tag = "1")] + pub fields: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "2")] + pub location: i32, +} +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct ParamRef { + #[prost(int32, tag = "1")] + pub number: i32, + #[prost(int32, tag = "2")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AExpr { + #[prost(enumeration = "AExprKind", tag = "1")] + pub kind: i32, + #[prost(message, repeated, tag = "2")] + pub name: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "3")] + pub lexpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "4")] + pub rexpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "5")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TypeCast { + #[prost(message, optional, boxed, tag = "1")] + pub arg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, tag = "2")] + pub type_name: ::core::option::Option, + #[prost(int32, tag = "3")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CollateClause { + #[prost(message, optional, boxed, tag = "1")] + pub arg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "2")] + pub collname: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "3")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RoleSpec { + #[prost(enumeration = "RoleSpecType", tag = "1")] + pub roletype: i32, + #[prost(string, tag = "2")] + pub rolename: ::prost::alloc::string::String, + #[prost(int32, tag = "3")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FuncCall { + #[prost(message, repeated, tag = "1")] + pub funcname: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "2")] + pub args: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub agg_order: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "4")] + pub agg_filter: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "5")] + pub over: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(bool, tag = "6")] + pub agg_within_group: bool, + #[prost(bool, tag = "7")] + pub agg_star: bool, + #[prost(bool, tag = "8")] + pub agg_distinct: bool, + #[prost(bool, tag = "9")] + pub func_variadic: bool, + #[prost(enumeration = "CoercionForm", tag = "10")] + pub funcformat: i32, + #[prost(int32, tag = "11")] + pub location: i32, +} +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct AStar {} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AIndices { + #[prost(bool, tag = "1")] + pub is_slice: bool, + #[prost(message, optional, boxed, tag = "2")] + pub lidx: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "3")] + pub uidx: ::core::option::Option<::prost::alloc::boxed::Box>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AIndirection { + #[prost(message, optional, boxed, tag = "1")] + pub arg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "2")] + pub indirection: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AArrayExpr { + #[prost(message, repeated, tag = "1")] + pub elements: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "2")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ResTarget { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub indirection: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "3")] + pub val: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "4")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MultiAssignRef { + #[prost(message, optional, boxed, tag = "1")] + pub source: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "2")] + pub colno: i32, + #[prost(int32, tag = "3")] + pub ncolumns: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SortBy { + #[prost(message, optional, boxed, tag = "1")] + pub node: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(enumeration = "SortByDir", tag = "2")] + pub sortby_dir: i32, + #[prost(enumeration = "SortByNulls", tag = "3")] + pub sortby_nulls: i32, + #[prost(message, repeated, tag = "4")] + pub use_op: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "5")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct WindowDef { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub refname: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + pub partition_clause: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub order_clause: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "5")] + pub frame_options: i32, + #[prost(message, optional, boxed, tag = "6")] + pub start_offset: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "7")] + pub end_offset: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "8")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RangeSubselect { + #[prost(bool, tag = "1")] + pub lateral: bool, + #[prost(message, optional, boxed, tag = "2")] + pub subquery: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, tag = "3")] + pub alias: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RangeFunction { + #[prost(bool, tag = "1")] + pub lateral: bool, + #[prost(bool, tag = "2")] + pub ordinality: bool, + #[prost(bool, tag = "3")] + pub is_rowsfrom: bool, + #[prost(message, repeated, tag = "4")] + pub functions: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "5")] + pub alias: ::core::option::Option, + #[prost(message, repeated, tag = "6")] + pub coldeflist: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RangeTableFunc { + #[prost(bool, tag = "1")] + pub lateral: bool, + #[prost(message, optional, boxed, tag = "2")] + pub docexpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "3")] + pub rowexpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "4")] + pub namespaces: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "5")] + pub columns: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "6")] + pub alias: ::core::option::Option, + #[prost(int32, tag = "7")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RangeTableFuncCol { + #[prost(string, tag = "1")] + pub colname: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub type_name: ::core::option::Option, + #[prost(bool, tag = "3")] + pub for_ordinality: bool, + #[prost(bool, tag = "4")] + pub is_not_null: bool, + #[prost(message, optional, boxed, tag = "5")] + pub colexpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "6")] + pub coldefexpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "7")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RangeTableSample { + #[prost(message, optional, boxed, tag = "1")] + pub relation: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "2")] + pub method: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub args: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "4")] + pub repeatable: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "5")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ColumnDef { + #[prost(string, tag = "1")] + pub colname: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub type_name: ::core::option::Option, + #[prost(string, tag = "3")] + pub compression: ::prost::alloc::string::String, + #[prost(int32, tag = "4")] + pub inhcount: i32, + #[prost(bool, tag = "5")] + pub is_local: bool, + #[prost(bool, tag = "6")] + pub is_not_null: bool, + #[prost(bool, tag = "7")] + pub is_from_type: bool, + #[prost(string, tag = "8")] + pub storage: ::prost::alloc::string::String, + #[prost(string, tag = "9")] + pub storage_name: ::prost::alloc::string::String, + #[prost(message, optional, boxed, tag = "10")] + pub raw_default: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "11")] + pub cooked_default: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(string, tag = "12")] + pub identity: ::prost::alloc::string::String, + #[prost(message, optional, tag = "13")] + pub identity_sequence: ::core::option::Option, + #[prost(string, tag = "14")] + pub generated: ::prost::alloc::string::String, + #[prost(message, optional, boxed, tag = "15")] + pub coll_clause: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "16")] + pub coll_oid: u32, + #[prost(message, repeated, tag = "17")] + pub constraints: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "18")] + pub fdwoptions: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "19")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TableLikeClause { + #[prost(message, optional, tag = "1")] + pub relation: ::core::option::Option, + #[prost(uint32, tag = "2")] + pub options: u32, + #[prost(uint32, tag = "3")] + pub relation_oid: u32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct IndexElem { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(message, optional, boxed, tag = "2")] + pub expr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(string, tag = "3")] + pub indexcolname: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "4")] + pub collation: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "5")] + pub opclass: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "6")] + pub opclassopts: ::prost::alloc::vec::Vec, + #[prost(enumeration = "SortByDir", tag = "7")] + pub ordering: i32, + #[prost(enumeration = "SortByNulls", tag = "8")] + pub nulls_ordering: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DefElem { + #[prost(string, tag = "1")] + pub defnamespace: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub defname: ::prost::alloc::string::String, + #[prost(message, optional, boxed, tag = "3")] + pub arg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(enumeration = "DefElemAction", tag = "4")] + pub defaction: i32, + #[prost(int32, tag = "5")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct LockingClause { + #[prost(message, repeated, tag = "1")] + pub locked_rels: ::prost::alloc::vec::Vec, + #[prost(enumeration = "LockClauseStrength", tag = "2")] + pub strength: i32, + #[prost(enumeration = "LockWaitPolicy", tag = "3")] + pub wait_policy: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct XmlSerialize { + #[prost(enumeration = "XmlOptionType", tag = "1")] + pub xmloption: i32, + #[prost(message, optional, boxed, tag = "2")] + pub expr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, tag = "3")] + pub type_name: ::core::option::Option, + #[prost(bool, tag = "4")] + pub indent: bool, + #[prost(int32, tag = "5")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PartitionElem { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(message, optional, boxed, tag = "2")] + pub expr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "3")] + pub collation: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub opclass: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "5")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PartitionSpec { + #[prost(enumeration = "PartitionStrategy", tag = "1")] + pub strategy: i32, + #[prost(message, repeated, tag = "2")] + pub part_params: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "3")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PartitionBoundSpec { + #[prost(string, tag = "1")] + pub strategy: ::prost::alloc::string::String, + #[prost(bool, tag = "2")] + pub is_default: bool, + #[prost(int32, tag = "3")] + pub modulus: i32, + #[prost(int32, tag = "4")] + pub remainder: i32, + #[prost(message, repeated, tag = "5")] + pub listdatums: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "6")] + pub lowerdatums: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "7")] + pub upperdatums: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "8")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PartitionRangeDatum { + #[prost(enumeration = "PartitionRangeDatumKind", tag = "1")] + pub kind: i32, + #[prost(message, optional, boxed, tag = "2")] + pub value: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "3")] + pub location: i32, +} +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct SinglePartitionSpec {} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PartitionCmd { + #[prost(message, optional, tag = "1")] + pub name: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub bound: ::core::option::Option, + #[prost(bool, tag = "3")] + pub concurrent: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RangeTblEntry { + #[prost(message, optional, tag = "1")] + pub alias: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub eref: ::core::option::Option, + #[prost(enumeration = "RteKind", tag = "3")] + pub rtekind: i32, + #[prost(uint32, tag = "4")] + pub relid: u32, + #[prost(bool, tag = "5")] + pub inh: bool, + #[prost(string, tag = "6")] + pub relkind: ::prost::alloc::string::String, + #[prost(int32, tag = "7")] + pub rellockmode: i32, + #[prost(uint32, tag = "8")] + pub perminfoindex: u32, + #[prost(message, optional, boxed, tag = "9")] + pub tablesample: ::core::option::Option< + ::prost::alloc::boxed::Box, + >, + #[prost(message, optional, boxed, tag = "10")] + pub subquery: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(bool, tag = "11")] + pub security_barrier: bool, + #[prost(enumeration = "JoinType", tag = "12")] + pub jointype: i32, + #[prost(int32, tag = "13")] + pub joinmergedcols: i32, + #[prost(message, repeated, tag = "14")] + pub joinaliasvars: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "15")] + pub joinleftcols: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "16")] + pub joinrightcols: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "17")] + pub join_using_alias: ::core::option::Option, + #[prost(message, repeated, tag = "18")] + pub functions: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "19")] + pub funcordinality: bool, + #[prost(message, optional, boxed, tag = "20")] + pub tablefunc: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "21")] + pub values_lists: ::prost::alloc::vec::Vec, + #[prost(string, tag = "22")] + pub ctename: ::prost::alloc::string::String, + #[prost(uint32, tag = "23")] + pub ctelevelsup: u32, + #[prost(bool, tag = "24")] + pub self_reference: bool, + #[prost(message, repeated, tag = "25")] + pub coltypes: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "26")] + pub coltypmods: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "27")] + pub colcollations: ::prost::alloc::vec::Vec, + #[prost(string, tag = "28")] + pub enrname: ::prost::alloc::string::String, + #[prost(double, tag = "29")] + pub enrtuples: f64, + #[prost(bool, tag = "30")] + pub lateral: bool, + #[prost(bool, tag = "31")] + pub in_from_cl: bool, + #[prost(message, repeated, tag = "32")] + pub security_quals: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RtePermissionInfo { + #[prost(uint32, tag = "1")] + pub relid: u32, + #[prost(bool, tag = "2")] + pub inh: bool, + #[prost(uint64, tag = "3")] + pub required_perms: u64, + #[prost(uint32, tag = "4")] + pub check_as_user: u32, + #[prost(uint64, repeated, tag = "5")] + pub selected_cols: ::prost::alloc::vec::Vec, + #[prost(uint64, repeated, tag = "6")] + pub inserted_cols: ::prost::alloc::vec::Vec, + #[prost(uint64, repeated, tag = "7")] + pub updated_cols: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RangeTblFunction { + #[prost(message, optional, boxed, tag = "1")] + pub funcexpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "2")] + pub funccolcount: i32, + #[prost(message, repeated, tag = "3")] + pub funccolnames: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub funccoltypes: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "5")] + pub funccoltypmods: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "6")] + pub funccolcollations: ::prost::alloc::vec::Vec, + #[prost(uint64, repeated, tag = "7")] + pub funcparams: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TableSampleClause { + #[prost(uint32, tag = "1")] + pub tsmhandler: u32, + #[prost(message, repeated, tag = "2")] + pub args: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "3")] + pub repeatable: ::core::option::Option<::prost::alloc::boxed::Box>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct WithCheckOption { + #[prost(enumeration = "WcoKind", tag = "1")] + pub kind: i32, + #[prost(string, tag = "2")] + pub relname: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub polname: ::prost::alloc::string::String, + #[prost(message, optional, boxed, tag = "4")] + pub qual: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(bool, tag = "5")] + pub cascaded: bool, +} +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct SortGroupClause { + #[prost(uint32, tag = "1")] + pub tle_sort_group_ref: u32, + #[prost(uint32, tag = "2")] + pub eqop: u32, + #[prost(uint32, tag = "3")] + pub sortop: u32, + #[prost(bool, tag = "4")] + pub nulls_first: bool, + #[prost(bool, tag = "5")] + pub hashable: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GroupingSet { + #[prost(enumeration = "GroupingSetKind", tag = "1")] + pub kind: i32, + #[prost(message, repeated, tag = "2")] + pub content: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "3")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct WindowClause { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub refname: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + pub partition_clause: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub order_clause: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "5")] + pub frame_options: i32, + #[prost(message, optional, boxed, tag = "6")] + pub start_offset: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "7")] + pub end_offset: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(uint32, tag = "8")] + pub start_in_range_func: u32, + #[prost(uint32, tag = "9")] + pub end_in_range_func: u32, + #[prost(uint32, tag = "10")] + pub in_range_coll: u32, + #[prost(bool, tag = "11")] + pub in_range_asc: bool, + #[prost(bool, tag = "12")] + pub in_range_nulls_first: bool, + #[prost(uint32, tag = "13")] + pub winref: u32, + #[prost(bool, tag = "14")] + pub copied_order: bool, +} +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct RowMarkClause { + #[prost(uint32, tag = "1")] + pub rti: u32, + #[prost(enumeration = "LockClauseStrength", tag = "2")] + pub strength: i32, + #[prost(enumeration = "LockWaitPolicy", tag = "3")] + pub wait_policy: i32, + #[prost(bool, tag = "4")] + pub pushed_down: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct WithClause { + #[prost(message, repeated, tag = "1")] + pub ctes: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "2")] + pub recursive: bool, + #[prost(int32, tag = "3")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct InferClause { + #[prost(message, repeated, tag = "1")] + pub index_elems: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "2")] + pub where_clause: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(string, tag = "3")] + pub conname: ::prost::alloc::string::String, + #[prost(int32, tag = "4")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct OnConflictClause { + #[prost(enumeration = "OnConflictAction", tag = "1")] + pub action: i32, + #[prost(message, optional, boxed, tag = "2")] + pub infer: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "3")] + pub target_list: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "4")] + pub where_clause: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "5")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CteSearchClause { + #[prost(message, repeated, tag = "1")] + pub search_col_list: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "2")] + pub search_breadth_first: bool, + #[prost(string, tag = "3")] + pub search_seq_column: ::prost::alloc::string::String, + #[prost(int32, tag = "4")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CteCycleClause { + #[prost(message, repeated, tag = "1")] + pub cycle_col_list: ::prost::alloc::vec::Vec, + #[prost(string, tag = "2")] + pub cycle_mark_column: ::prost::alloc::string::String, + #[prost(message, optional, boxed, tag = "3")] + pub cycle_mark_value: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "4")] + pub cycle_mark_default: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(string, tag = "5")] + pub cycle_path_column: ::prost::alloc::string::String, + #[prost(int32, tag = "6")] + pub location: i32, + #[prost(uint32, tag = "7")] + pub cycle_mark_type: u32, + #[prost(int32, tag = "8")] + pub cycle_mark_typmod: i32, + #[prost(uint32, tag = "9")] + pub cycle_mark_collation: u32, + #[prost(uint32, tag = "10")] + pub cycle_mark_neop: u32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CommonTableExpr { + #[prost(string, tag = "1")] + pub ctename: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub aliascolnames: ::prost::alloc::vec::Vec, + #[prost(enumeration = "CteMaterialize", tag = "3")] + pub ctematerialized: i32, + #[prost(message, optional, boxed, tag = "4")] + pub ctequery: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, tag = "5")] + pub search_clause: ::core::option::Option, + #[prost(message, optional, boxed, tag = "6")] + pub cycle_clause: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "7")] + pub location: i32, + #[prost(bool, tag = "8")] + pub cterecursive: bool, + #[prost(int32, tag = "9")] + pub cterefcount: i32, + #[prost(message, repeated, tag = "10")] + pub ctecolnames: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "11")] + pub ctecoltypes: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "12")] + pub ctecoltypmods: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "13")] + pub ctecolcollations: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MergeWhenClause { + #[prost(enumeration = "MergeMatchKind", tag = "1")] + pub match_kind: i32, + #[prost(enumeration = "CmdType", tag = "2")] + pub command_type: i32, + #[prost(enumeration = "OverridingKind", tag = "3")] + pub r#override: i32, + #[prost(message, optional, boxed, tag = "4")] + pub condition: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "5")] + pub target_list: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "6")] + pub values: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TriggerTransition { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(bool, tag = "2")] + pub is_new: bool, + #[prost(bool, tag = "3")] + pub is_table: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonOutput { + #[prost(message, optional, tag = "1")] + pub type_name: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub returning: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonArgument { + #[prost(message, optional, boxed, tag = "1")] + pub val: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(string, tag = "2")] + pub name: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonFuncExpr { + #[prost(enumeration = "JsonExprOp", tag = "1")] + pub op: i32, + #[prost(string, tag = "2")] + pub column_name: ::prost::alloc::string::String, + #[prost(message, optional, boxed, tag = "3")] + pub context_item: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "4")] + pub pathspec: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "5")] + pub passing: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "6")] + pub output: ::core::option::Option, + #[prost(message, optional, boxed, tag = "7")] + pub on_empty: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "8")] + pub on_error: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(enumeration = "JsonWrapper", tag = "9")] + pub wrapper: i32, + #[prost(enumeration = "JsonQuotes", tag = "10")] + pub quotes: i32, + #[prost(int32, tag = "11")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonTablePathSpec { + #[prost(message, optional, boxed, tag = "1")] + pub string: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(string, tag = "2")] + pub name: ::prost::alloc::string::String, + #[prost(int32, tag = "3")] + pub name_location: i32, + #[prost(int32, tag = "4")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonTable { + #[prost(message, optional, boxed, tag = "1")] + pub context_item: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "2")] + pub pathspec: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "3")] + pub passing: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub columns: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "5")] + pub on_error: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, tag = "6")] + pub alias: ::core::option::Option, + #[prost(bool, tag = "7")] + pub lateral: bool, + #[prost(int32, tag = "8")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonTableColumn { + #[prost(enumeration = "JsonTableColumnType", tag = "1")] + pub coltype: i32, + #[prost(string, tag = "2")] + pub name: ::prost::alloc::string::String, + #[prost(message, optional, tag = "3")] + pub type_name: ::core::option::Option, + #[prost(message, optional, boxed, tag = "4")] + pub pathspec: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, tag = "5")] + pub format: ::core::option::Option, + #[prost(enumeration = "JsonWrapper", tag = "6")] + pub wrapper: i32, + #[prost(enumeration = "JsonQuotes", tag = "7")] + pub quotes: i32, + #[prost(message, repeated, tag = "8")] + pub columns: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "9")] + pub on_empty: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "10")] + pub on_error: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "11")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonKeyValue { + #[prost(message, optional, boxed, tag = "1")] + pub key: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "2")] + pub value: ::core::option::Option<::prost::alloc::boxed::Box>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonParseExpr { + #[prost(message, optional, boxed, tag = "1")] + pub expr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, tag = "2")] + pub output: ::core::option::Option, + #[prost(bool, tag = "3")] + pub unique_keys: bool, + #[prost(int32, tag = "4")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonScalarExpr { + #[prost(message, optional, boxed, tag = "1")] + pub expr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, tag = "2")] + pub output: ::core::option::Option, + #[prost(int32, tag = "3")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonSerializeExpr { + #[prost(message, optional, boxed, tag = "1")] + pub expr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, tag = "2")] + pub output: ::core::option::Option, + #[prost(int32, tag = "3")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonObjectConstructor { + #[prost(message, repeated, tag = "1")] + pub exprs: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "2")] + pub output: ::core::option::Option, + #[prost(bool, tag = "3")] + pub absent_on_null: bool, + #[prost(bool, tag = "4")] + pub unique: bool, + #[prost(int32, tag = "5")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonArrayConstructor { + #[prost(message, repeated, tag = "1")] + pub exprs: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "2")] + pub output: ::core::option::Option, + #[prost(bool, tag = "3")] + pub absent_on_null: bool, + #[prost(int32, tag = "4")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonArrayQueryConstructor { + #[prost(message, optional, boxed, tag = "1")] + pub query: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, tag = "2")] + pub output: ::core::option::Option, + #[prost(message, optional, tag = "3")] + pub format: ::core::option::Option, + #[prost(bool, tag = "4")] + pub absent_on_null: bool, + #[prost(int32, tag = "5")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonAggConstructor { + #[prost(message, optional, tag = "1")] + pub output: ::core::option::Option, + #[prost(message, optional, boxed, tag = "2")] + pub agg_filter: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "3")] + pub agg_order: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "4")] + pub over: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "5")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonObjectAgg { + #[prost(message, optional, boxed, tag = "1")] + pub constructor: ::core::option::Option< + ::prost::alloc::boxed::Box, + >, + #[prost(message, optional, boxed, tag = "2")] + pub arg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(bool, tag = "3")] + pub absent_on_null: bool, + #[prost(bool, tag = "4")] + pub unique: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct JsonArrayAgg { + #[prost(message, optional, boxed, tag = "1")] + pub constructor: ::core::option::Option< + ::prost::alloc::boxed::Box, + >, + #[prost(message, optional, boxed, tag = "2")] + pub arg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(bool, tag = "3")] + pub absent_on_null: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RawStmt { + #[prost(message, optional, boxed, tag = "1")] + pub stmt: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "2")] + pub stmt_location: i32, + #[prost(int32, tag = "3")] + pub stmt_len: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct InsertStmt { + #[prost(message, optional, tag = "1")] + pub relation: ::core::option::Option, + #[prost(message, repeated, tag = "2")] + pub cols: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "3")] + pub select_stmt: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "4")] + pub on_conflict_clause: ::core::option::Option< + ::prost::alloc::boxed::Box, + >, + #[prost(message, repeated, tag = "5")] + pub returning_list: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "6")] + pub with_clause: ::core::option::Option, + #[prost(enumeration = "OverridingKind", tag = "7")] + pub r#override: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DeleteStmt { + #[prost(message, optional, tag = "1")] + pub relation: ::core::option::Option, + #[prost(message, repeated, tag = "2")] + pub using_clause: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "3")] + pub where_clause: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "4")] + pub returning_list: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "5")] + pub with_clause: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UpdateStmt { + #[prost(message, optional, tag = "1")] + pub relation: ::core::option::Option, + #[prost(message, repeated, tag = "2")] + pub target_list: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "3")] + pub where_clause: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "4")] + pub from_clause: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "5")] + pub returning_list: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "6")] + pub with_clause: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct MergeStmt { + #[prost(message, optional, tag = "1")] + pub relation: ::core::option::Option, + #[prost(message, optional, boxed, tag = "2")] + pub source_relation: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "3")] + pub join_condition: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "4")] + pub merge_when_clauses: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "5")] + pub returning_list: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "6")] + pub with_clause: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SelectStmt { + #[prost(message, repeated, tag = "1")] + pub distinct_clause: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "2")] + pub into_clause: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "3")] + pub target_list: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub from_clause: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "5")] + pub where_clause: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "6")] + pub group_clause: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "7")] + pub group_distinct: bool, + #[prost(message, optional, boxed, tag = "8")] + pub having_clause: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "9")] + pub window_clause: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "10")] + pub values_lists: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "11")] + pub sort_clause: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "12")] + pub limit_offset: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "13")] + pub limit_count: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(enumeration = "LimitOption", tag = "14")] + pub limit_option: i32, + #[prost(message, repeated, tag = "15")] + pub locking_clause: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "16")] + pub with_clause: ::core::option::Option, + #[prost(enumeration = "SetOperation", tag = "17")] + pub op: i32, + #[prost(bool, tag = "18")] + pub all: bool, + #[prost(message, optional, boxed, tag = "19")] + pub larg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "20")] + pub rarg: ::core::option::Option<::prost::alloc::boxed::Box>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SetOperationStmt { + #[prost(enumeration = "SetOperation", tag = "1")] + pub op: i32, + #[prost(bool, tag = "2")] + pub all: bool, + #[prost(message, optional, boxed, tag = "3")] + pub larg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "4")] + pub rarg: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "5")] + pub col_types: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "6")] + pub col_typmods: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "7")] + pub col_collations: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "8")] + pub group_clauses: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ReturnStmt { + #[prost(message, optional, boxed, tag = "1")] + pub returnval: ::core::option::Option<::prost::alloc::boxed::Box>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PlAssignStmt { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub indirection: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "3")] + pub nnames: i32, + #[prost(message, optional, boxed, tag = "4")] + pub val: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "5")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateSchemaStmt { + #[prost(string, tag = "1")] + pub schemaname: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub authrole: ::core::option::Option, + #[prost(message, repeated, tag = "3")] + pub schema_elts: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "4")] + pub if_not_exists: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterTableStmt { + #[prost(message, optional, tag = "1")] + pub relation: ::core::option::Option, + #[prost(message, repeated, tag = "2")] + pub cmds: ::prost::alloc::vec::Vec, + #[prost(enumeration = "ObjectType", tag = "3")] + pub objtype: i32, + #[prost(bool, tag = "4")] + pub missing_ok: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ReplicaIdentityStmt { + #[prost(string, tag = "1")] + pub identity_type: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub name: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterTableCmd { + #[prost(enumeration = "AlterTableType", tag = "1")] + pub subtype: i32, + #[prost(string, tag = "2")] + pub name: ::prost::alloc::string::String, + #[prost(int32, tag = "3")] + pub num: i32, + #[prost(message, optional, tag = "4")] + pub newowner: ::core::option::Option, + #[prost(message, optional, boxed, tag = "5")] + pub def: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(enumeration = "DropBehavior", tag = "6")] + pub behavior: i32, + #[prost(bool, tag = "7")] + pub missing_ok: bool, + #[prost(bool, tag = "8")] + pub recurse: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterCollationStmt { + #[prost(message, repeated, tag = "1")] + pub collname: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterDomainStmt { + #[prost(string, tag = "1")] + pub subtype: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub type_name: ::prost::alloc::vec::Vec, + #[prost(string, tag = "3")] + pub name: ::prost::alloc::string::String, + #[prost(message, optional, boxed, tag = "4")] + pub def: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(enumeration = "DropBehavior", tag = "5")] + pub behavior: i32, + #[prost(bool, tag = "6")] + pub missing_ok: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GrantStmt { + #[prost(bool, tag = "1")] + pub is_grant: bool, + #[prost(enumeration = "GrantTargetType", tag = "2")] + pub targtype: i32, + #[prost(enumeration = "ObjectType", tag = "3")] + pub objtype: i32, + #[prost(message, repeated, tag = "4")] + pub objects: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "5")] + pub privileges: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "6")] + pub grantees: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "7")] + pub grant_option: bool, + #[prost(message, optional, tag = "8")] + pub grantor: ::core::option::Option, + #[prost(enumeration = "DropBehavior", tag = "9")] + pub behavior: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ObjectWithArgs { + #[prost(message, repeated, tag = "1")] + pub objname: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "2")] + pub objargs: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub objfuncargs: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "4")] + pub args_unspecified: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AccessPriv { + #[prost(string, tag = "1")] + pub priv_name: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub cols: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct GrantRoleStmt { + #[prost(message, repeated, tag = "1")] + pub granted_roles: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "2")] + pub grantee_roles: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "3")] + pub is_grant: bool, + #[prost(message, repeated, tag = "4")] + pub opt: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "5")] + pub grantor: ::core::option::Option, + #[prost(enumeration = "DropBehavior", tag = "6")] + pub behavior: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterDefaultPrivilegesStmt { + #[prost(message, repeated, tag = "1")] + pub options: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "2")] + pub action: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CopyStmt { + #[prost(message, optional, tag = "1")] + pub relation: ::core::option::Option, + #[prost(message, optional, boxed, tag = "2")] + pub query: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "3")] + pub attlist: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "4")] + pub is_from: bool, + #[prost(bool, tag = "5")] + pub is_program: bool, + #[prost(string, tag = "6")] + pub filename: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "7")] + pub options: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "8")] + pub where_clause: ::core::option::Option<::prost::alloc::boxed::Box>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct VariableSetStmt { + #[prost(enumeration = "VariableSetKind", tag = "1")] + pub kind: i32, + #[prost(string, tag = "2")] + pub name: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + pub args: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "4")] + pub is_local: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct VariableShowStmt { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateStmt { + #[prost(message, optional, tag = "1")] + pub relation: ::core::option::Option, + #[prost(message, repeated, tag = "2")] + pub table_elts: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub inh_relations: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "4")] + pub partbound: ::core::option::Option, + #[prost(message, optional, tag = "5")] + pub partspec: ::core::option::Option, + #[prost(message, optional, tag = "6")] + pub of_typename: ::core::option::Option, + #[prost(message, repeated, tag = "7")] + pub constraints: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "8")] + pub options: ::prost::alloc::vec::Vec, + #[prost(enumeration = "OnCommitAction", tag = "9")] + pub oncommit: i32, + #[prost(string, tag = "10")] + pub tablespacename: ::prost::alloc::string::String, + #[prost(string, tag = "11")] + pub access_method: ::prost::alloc::string::String, + #[prost(bool, tag = "12")] + pub if_not_exists: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct Constraint { + #[prost(enumeration = "ConstrType", tag = "1")] + pub contype: i32, + #[prost(string, tag = "2")] + pub conname: ::prost::alloc::string::String, + #[prost(bool, tag = "3")] + pub deferrable: bool, + #[prost(bool, tag = "4")] + pub initdeferred: bool, + #[prost(bool, tag = "5")] + pub skip_validation: bool, + #[prost(bool, tag = "6")] + pub initially_valid: bool, + #[prost(bool, tag = "7")] + pub is_no_inherit: bool, + #[prost(message, optional, boxed, tag = "8")] + pub raw_expr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(string, tag = "9")] + pub cooked_expr: ::prost::alloc::string::String, + #[prost(string, tag = "10")] + pub generated_when: ::prost::alloc::string::String, + #[prost(int32, tag = "11")] + pub inhcount: i32, + #[prost(bool, tag = "12")] + pub nulls_not_distinct: bool, + #[prost(message, repeated, tag = "13")] + pub keys: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "14")] + pub including: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "15")] + pub exclusions: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "16")] + pub options: ::prost::alloc::vec::Vec, + #[prost(string, tag = "17")] + pub indexname: ::prost::alloc::string::String, + #[prost(string, tag = "18")] + pub indexspace: ::prost::alloc::string::String, + #[prost(bool, tag = "19")] + pub reset_default_tblspc: bool, + #[prost(string, tag = "20")] + pub access_method: ::prost::alloc::string::String, + #[prost(message, optional, boxed, tag = "21")] + pub where_clause: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, tag = "22")] + pub pktable: ::core::option::Option, + #[prost(message, repeated, tag = "23")] + pub fk_attrs: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "24")] + pub pk_attrs: ::prost::alloc::vec::Vec, + #[prost(string, tag = "25")] + pub fk_matchtype: ::prost::alloc::string::String, + #[prost(string, tag = "26")] + pub fk_upd_action: ::prost::alloc::string::String, + #[prost(string, tag = "27")] + pub fk_del_action: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "28")] + pub fk_del_set_cols: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "29")] + pub old_conpfeqop: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "30")] + pub old_pktable_oid: u32, + #[prost(int32, tag = "31")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateTableSpaceStmt { + #[prost(string, tag = "1")] + pub tablespacename: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub owner: ::core::option::Option, + #[prost(string, tag = "3")] + pub location: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "4")] + pub options: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DropTableSpaceStmt { + #[prost(string, tag = "1")] + pub tablespacename: ::prost::alloc::string::String, + #[prost(bool, tag = "2")] + pub missing_ok: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterTableSpaceOptionsStmt { + #[prost(string, tag = "1")] + pub tablespacename: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub options: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "3")] + pub is_reset: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterTableMoveAllStmt { + #[prost(string, tag = "1")] + pub orig_tablespacename: ::prost::alloc::string::String, + #[prost(enumeration = "ObjectType", tag = "2")] + pub objtype: i32, + #[prost(message, repeated, tag = "3")] + pub roles: ::prost::alloc::vec::Vec, + #[prost(string, tag = "4")] + pub new_tablespacename: ::prost::alloc::string::String, + #[prost(bool, tag = "5")] + pub nowait: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateExtensionStmt { + #[prost(string, tag = "1")] + pub extname: ::prost::alloc::string::String, + #[prost(bool, tag = "2")] + pub if_not_exists: bool, + #[prost(message, repeated, tag = "3")] + pub options: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterExtensionStmt { + #[prost(string, tag = "1")] + pub extname: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub options: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterExtensionContentsStmt { + #[prost(string, tag = "1")] + pub extname: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub action: i32, + #[prost(enumeration = "ObjectType", tag = "3")] + pub objtype: i32, + #[prost(message, optional, boxed, tag = "4")] + pub object: ::core::option::Option<::prost::alloc::boxed::Box>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateFdwStmt { + #[prost(string, tag = "1")] + pub fdwname: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub func_options: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub options: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterFdwStmt { + #[prost(string, tag = "1")] + pub fdwname: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub func_options: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub options: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateForeignServerStmt { + #[prost(string, tag = "1")] + pub servername: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub servertype: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub version: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub fdwname: ::prost::alloc::string::String, + #[prost(bool, tag = "5")] + pub if_not_exists: bool, + #[prost(message, repeated, tag = "6")] + pub options: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterForeignServerStmt { + #[prost(string, tag = "1")] + pub servername: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub version: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + pub options: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "4")] + pub has_version: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateForeignTableStmt { + #[prost(message, optional, tag = "1")] + pub base_stmt: ::core::option::Option, + #[prost(string, tag = "2")] + pub servername: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + pub options: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateUserMappingStmt { + #[prost(message, optional, tag = "1")] + pub user: ::core::option::Option, + #[prost(string, tag = "2")] + pub servername: ::prost::alloc::string::String, + #[prost(bool, tag = "3")] + pub if_not_exists: bool, + #[prost(message, repeated, tag = "4")] + pub options: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterUserMappingStmt { + #[prost(message, optional, tag = "1")] + pub user: ::core::option::Option, + #[prost(string, tag = "2")] + pub servername: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + pub options: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DropUserMappingStmt { + #[prost(message, optional, tag = "1")] + pub user: ::core::option::Option, + #[prost(string, tag = "2")] + pub servername: ::prost::alloc::string::String, + #[prost(bool, tag = "3")] + pub missing_ok: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ImportForeignSchemaStmt { + #[prost(string, tag = "1")] + pub server_name: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub remote_schema: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub local_schema: ::prost::alloc::string::String, + #[prost(enumeration = "ImportForeignSchemaType", tag = "4")] + pub list_type: i32, + #[prost(message, repeated, tag = "5")] + pub table_list: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "6")] + pub options: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreatePolicyStmt { + #[prost(string, tag = "1")] + pub policy_name: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub table: ::core::option::Option, + #[prost(string, tag = "3")] + pub cmd_name: ::prost::alloc::string::String, + #[prost(bool, tag = "4")] + pub permissive: bool, + #[prost(message, repeated, tag = "5")] + pub roles: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "6")] + pub qual: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "7")] + pub with_check: ::core::option::Option<::prost::alloc::boxed::Box>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterPolicyStmt { + #[prost(string, tag = "1")] + pub policy_name: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub table: ::core::option::Option, + #[prost(message, repeated, tag = "3")] + pub roles: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "4")] + pub qual: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "5")] + pub with_check: ::core::option::Option<::prost::alloc::boxed::Box>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateAmStmt { + #[prost(string, tag = "1")] + pub amname: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub handler_name: ::prost::alloc::vec::Vec, + #[prost(string, tag = "3")] + pub amtype: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateTrigStmt { + #[prost(bool, tag = "1")] + pub replace: bool, + #[prost(bool, tag = "2")] + pub isconstraint: bool, + #[prost(string, tag = "3")] + pub trigname: ::prost::alloc::string::String, + #[prost(message, optional, tag = "4")] + pub relation: ::core::option::Option, + #[prost(message, repeated, tag = "5")] + pub funcname: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "6")] + pub args: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "7")] + pub row: bool, + #[prost(int32, tag = "8")] + pub timing: i32, + #[prost(int32, tag = "9")] + pub events: i32, + #[prost(message, repeated, tag = "10")] + pub columns: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "11")] + pub when_clause: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "12")] + pub transition_rels: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "13")] + pub deferrable: bool, + #[prost(bool, tag = "14")] + pub initdeferred: bool, + #[prost(message, optional, tag = "15")] + pub constrrel: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateEventTrigStmt { + #[prost(string, tag = "1")] + pub trigname: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub eventname: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + pub whenclause: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub funcname: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterEventTrigStmt { + #[prost(string, tag = "1")] + pub trigname: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub tgenabled: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreatePLangStmt { + #[prost(bool, tag = "1")] + pub replace: bool, + #[prost(string, tag = "2")] + pub plname: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + pub plhandler: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub plinline: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "5")] + pub plvalidator: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "6")] + pub pltrusted: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateRoleStmt { + #[prost(enumeration = "RoleStmtType", tag = "1")] + pub stmt_type: i32, + #[prost(string, tag = "2")] + pub role: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + pub options: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterRoleStmt { + #[prost(message, optional, tag = "1")] + pub role: ::core::option::Option, + #[prost(message, repeated, tag = "2")] + pub options: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "3")] + pub action: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterRoleSetStmt { + #[prost(message, optional, tag = "1")] + pub role: ::core::option::Option, + #[prost(string, tag = "2")] + pub database: ::prost::alloc::string::String, + #[prost(message, optional, tag = "3")] + pub setstmt: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DropRoleStmt { + #[prost(message, repeated, tag = "1")] + pub roles: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "2")] + pub missing_ok: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateSeqStmt { + #[prost(message, optional, tag = "1")] + pub sequence: ::core::option::Option, + #[prost(message, repeated, tag = "2")] + pub options: ::prost::alloc::vec::Vec, + #[prost(uint32, tag = "3")] + pub owner_id: u32, + #[prost(bool, tag = "4")] + pub for_identity: bool, + #[prost(bool, tag = "5")] + pub if_not_exists: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterSeqStmt { + #[prost(message, optional, tag = "1")] + pub sequence: ::core::option::Option, + #[prost(message, repeated, tag = "2")] + pub options: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "3")] + pub for_identity: bool, + #[prost(bool, tag = "4")] + pub missing_ok: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DefineStmt { + #[prost(enumeration = "ObjectType", tag = "1")] + pub kind: i32, + #[prost(bool, tag = "2")] + pub oldstyle: bool, + #[prost(message, repeated, tag = "3")] + pub defnames: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub args: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "5")] + pub definition: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "6")] + pub if_not_exists: bool, + #[prost(bool, tag = "7")] + pub replace: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateDomainStmt { + #[prost(message, repeated, tag = "1")] + pub domainname: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "2")] + pub type_name: ::core::option::Option, + #[prost(message, optional, boxed, tag = "3")] + pub coll_clause: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "4")] + pub constraints: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateOpClassStmt { + #[prost(message, repeated, tag = "1")] + pub opclassname: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "2")] + pub opfamilyname: ::prost::alloc::vec::Vec, + #[prost(string, tag = "3")] + pub amname: ::prost::alloc::string::String, + #[prost(message, optional, tag = "4")] + pub datatype: ::core::option::Option, + #[prost(message, repeated, tag = "5")] + pub items: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "6")] + pub is_default: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateOpClassItem { + #[prost(int32, tag = "1")] + pub itemtype: i32, + #[prost(message, optional, tag = "2")] + pub name: ::core::option::Option, + #[prost(int32, tag = "3")] + pub number: i32, + #[prost(message, repeated, tag = "4")] + pub order_family: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "5")] + pub class_args: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "6")] + pub storedtype: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateOpFamilyStmt { + #[prost(message, repeated, tag = "1")] + pub opfamilyname: ::prost::alloc::vec::Vec, + #[prost(string, tag = "2")] + pub amname: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterOpFamilyStmt { + #[prost(message, repeated, tag = "1")] + pub opfamilyname: ::prost::alloc::vec::Vec, + #[prost(string, tag = "2")] + pub amname: ::prost::alloc::string::String, + #[prost(bool, tag = "3")] + pub is_drop: bool, + #[prost(message, repeated, tag = "4")] + pub items: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DropStmt { + #[prost(message, repeated, tag = "1")] + pub objects: ::prost::alloc::vec::Vec, + #[prost(enumeration = "ObjectType", tag = "2")] + pub remove_type: i32, + #[prost(enumeration = "DropBehavior", tag = "3")] + pub behavior: i32, + #[prost(bool, tag = "4")] + pub missing_ok: bool, + #[prost(bool, tag = "5")] + pub concurrent: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TruncateStmt { + #[prost(message, repeated, tag = "1")] + pub relations: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "2")] + pub restart_seqs: bool, + #[prost(enumeration = "DropBehavior", tag = "3")] + pub behavior: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CommentStmt { + #[prost(enumeration = "ObjectType", tag = "1")] + pub objtype: i32, + #[prost(message, optional, boxed, tag = "2")] + pub object: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(string, tag = "3")] + pub comment: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct SecLabelStmt { + #[prost(enumeration = "ObjectType", tag = "1")] + pub objtype: i32, + #[prost(message, optional, boxed, tag = "2")] + pub object: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(string, tag = "3")] + pub provider: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub label: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DeclareCursorStmt { + #[prost(string, tag = "1")] + pub portalname: ::prost::alloc::string::String, + #[prost(int32, tag = "2")] + pub options: i32, + #[prost(message, optional, boxed, tag = "3")] + pub query: ::core::option::Option<::prost::alloc::boxed::Box>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ClosePortalStmt { + #[prost(string, tag = "1")] + pub portalname: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FetchStmt { + #[prost(enumeration = "FetchDirection", tag = "1")] + pub direction: i32, + #[prost(int64, tag = "2")] + pub how_many: i64, + #[prost(string, tag = "3")] + pub portalname: ::prost::alloc::string::String, + #[prost(bool, tag = "4")] + pub ismove: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct IndexStmt { + #[prost(string, tag = "1")] + pub idxname: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub relation: ::core::option::Option, + #[prost(string, tag = "3")] + pub access_method: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub table_space: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "5")] + pub index_params: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "6")] + pub index_including_params: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "7")] + pub options: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "8")] + pub where_clause: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "9")] + pub exclude_op_names: ::prost::alloc::vec::Vec, + #[prost(string, tag = "10")] + pub idxcomment: ::prost::alloc::string::String, + #[prost(uint32, tag = "11")] + pub index_oid: u32, + #[prost(uint32, tag = "12")] + pub old_number: u32, + #[prost(uint32, tag = "13")] + pub old_create_subid: u32, + #[prost(uint32, tag = "14")] + pub old_first_relfilelocator_subid: u32, + #[prost(bool, tag = "15")] + pub unique: bool, + #[prost(bool, tag = "16")] + pub nulls_not_distinct: bool, + #[prost(bool, tag = "17")] + pub primary: bool, + #[prost(bool, tag = "18")] + pub isconstraint: bool, + #[prost(bool, tag = "19")] + pub deferrable: bool, + #[prost(bool, tag = "20")] + pub initdeferred: bool, + #[prost(bool, tag = "21")] + pub transformed: bool, + #[prost(bool, tag = "22")] + pub concurrent: bool, + #[prost(bool, tag = "23")] + pub if_not_exists: bool, + #[prost(bool, tag = "24")] + pub reset_default_tblspc: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateStatsStmt { + #[prost(message, repeated, tag = "1")] + pub defnames: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "2")] + pub stat_types: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub exprs: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub relations: ::prost::alloc::vec::Vec, + #[prost(string, tag = "5")] + pub stxcomment: ::prost::alloc::string::String, + #[prost(bool, tag = "6")] + pub transformed: bool, + #[prost(bool, tag = "7")] + pub if_not_exists: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct StatsElem { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(message, optional, boxed, tag = "2")] + pub expr: ::core::option::Option<::prost::alloc::boxed::Box>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterStatsStmt { + #[prost(message, repeated, tag = "1")] + pub defnames: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "2")] + pub stxstattarget: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(bool, tag = "3")] + pub missing_ok: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateFunctionStmt { + #[prost(bool, tag = "1")] + pub is_procedure: bool, + #[prost(bool, tag = "2")] + pub replace: bool, + #[prost(message, repeated, tag = "3")] + pub funcname: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub parameters: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "5")] + pub return_type: ::core::option::Option, + #[prost(message, repeated, tag = "6")] + pub options: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "7")] + pub sql_body: ::core::option::Option<::prost::alloc::boxed::Box>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct FunctionParameter { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub arg_type: ::core::option::Option, + #[prost(enumeration = "FunctionParameterMode", tag = "3")] + pub mode: i32, + #[prost(message, optional, boxed, tag = "4")] + pub defexpr: ::core::option::Option<::prost::alloc::boxed::Box>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterFunctionStmt { + #[prost(enumeration = "ObjectType", tag = "1")] + pub objtype: i32, + #[prost(message, optional, tag = "2")] + pub func: ::core::option::Option, + #[prost(message, repeated, tag = "3")] + pub actions: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DoStmt { + #[prost(message, repeated, tag = "1")] + pub args: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct InlineCodeBlock { + #[prost(string, tag = "1")] + pub source_text: ::prost::alloc::string::String, + #[prost(uint32, tag = "2")] + pub lang_oid: u32, + #[prost(bool, tag = "3")] + pub lang_is_trusted: bool, + #[prost(bool, tag = "4")] + pub atomic: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CallStmt { + #[prost(message, optional, boxed, tag = "1")] + pub funccall: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "2")] + pub funcexpr: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "3")] + pub outargs: ::prost::alloc::vec::Vec, +} +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct CallContext { + #[prost(bool, tag = "1")] + pub atomic: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RenameStmt { + #[prost(enumeration = "ObjectType", tag = "1")] + pub rename_type: i32, + #[prost(enumeration = "ObjectType", tag = "2")] + pub relation_type: i32, + #[prost(message, optional, tag = "3")] + pub relation: ::core::option::Option, + #[prost(message, optional, boxed, tag = "4")] + pub object: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(string, tag = "5")] + pub subname: ::prost::alloc::string::String, + #[prost(string, tag = "6")] + pub newname: ::prost::alloc::string::String, + #[prost(enumeration = "DropBehavior", tag = "7")] + pub behavior: i32, + #[prost(bool, tag = "8")] + pub missing_ok: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterObjectDependsStmt { + #[prost(enumeration = "ObjectType", tag = "1")] + pub object_type: i32, + #[prost(message, optional, tag = "2")] + pub relation: ::core::option::Option, + #[prost(message, optional, boxed, tag = "3")] + pub object: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, tag = "4")] + pub extname: ::core::option::Option, + #[prost(bool, tag = "5")] + pub remove: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterObjectSchemaStmt { + #[prost(enumeration = "ObjectType", tag = "1")] + pub object_type: i32, + #[prost(message, optional, tag = "2")] + pub relation: ::core::option::Option, + #[prost(message, optional, boxed, tag = "3")] + pub object: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(string, tag = "4")] + pub newschema: ::prost::alloc::string::String, + #[prost(bool, tag = "5")] + pub missing_ok: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterOwnerStmt { + #[prost(enumeration = "ObjectType", tag = "1")] + pub object_type: i32, + #[prost(message, optional, tag = "2")] + pub relation: ::core::option::Option, + #[prost(message, optional, boxed, tag = "3")] + pub object: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, tag = "4")] + pub newowner: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterOperatorStmt { + #[prost(message, optional, tag = "1")] + pub opername: ::core::option::Option, + #[prost(message, repeated, tag = "2")] + pub options: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterTypeStmt { + #[prost(message, repeated, tag = "1")] + pub type_name: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "2")] + pub options: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RuleStmt { + #[prost(message, optional, tag = "1")] + pub relation: ::core::option::Option, + #[prost(string, tag = "2")] + pub rulename: ::prost::alloc::string::String, + #[prost(message, optional, boxed, tag = "3")] + pub where_clause: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(enumeration = "CmdType", tag = "4")] + pub event: i32, + #[prost(bool, tag = "5")] + pub instead: bool, + #[prost(message, repeated, tag = "6")] + pub actions: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "7")] + pub replace: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct NotifyStmt { + #[prost(string, tag = "1")] + pub conditionname: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub payload: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ListenStmt { + #[prost(string, tag = "1")] + pub conditionname: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct UnlistenStmt { + #[prost(string, tag = "1")] + pub conditionname: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TransactionStmt { + #[prost(enumeration = "TransactionStmtKind", tag = "1")] + pub kind: i32, + #[prost(message, repeated, tag = "2")] + pub options: ::prost::alloc::vec::Vec, + #[prost(string, tag = "3")] + pub savepoint_name: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub gid: ::prost::alloc::string::String, + #[prost(bool, tag = "5")] + pub chain: bool, + #[prost(int32, tag = "6")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CompositeTypeStmt { + #[prost(message, optional, tag = "1")] + pub typevar: ::core::option::Option, + #[prost(message, repeated, tag = "2")] + pub coldeflist: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateEnumStmt { + #[prost(message, repeated, tag = "1")] + pub type_name: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "2")] + pub vals: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateRangeStmt { + #[prost(message, repeated, tag = "1")] + pub type_name: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "2")] + pub params: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterEnumStmt { + #[prost(message, repeated, tag = "1")] + pub type_name: ::prost::alloc::vec::Vec, + #[prost(string, tag = "2")] + pub old_val: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub new_val: ::prost::alloc::string::String, + #[prost(string, tag = "4")] + pub new_val_neighbor: ::prost::alloc::string::String, + #[prost(bool, tag = "5")] + pub new_val_is_after: bool, + #[prost(bool, tag = "6")] + pub skip_if_new_val_exists: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ViewStmt { + #[prost(message, optional, tag = "1")] + pub view: ::core::option::Option, + #[prost(message, repeated, tag = "2")] + pub aliases: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "3")] + pub query: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(bool, tag = "4")] + pub replace: bool, + #[prost(message, repeated, tag = "5")] + pub options: ::prost::alloc::vec::Vec, + #[prost(enumeration = "ViewCheckOption", tag = "6")] + pub with_check_option: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct LoadStmt { + #[prost(string, tag = "1")] + pub filename: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreatedbStmt { + #[prost(string, tag = "1")] + pub dbname: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub options: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterDatabaseStmt { + #[prost(string, tag = "1")] + pub dbname: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub options: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterDatabaseRefreshCollStmt { + #[prost(string, tag = "1")] + pub dbname: ::prost::alloc::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterDatabaseSetStmt { + #[prost(string, tag = "1")] + pub dbname: ::prost::alloc::string::String, + #[prost(message, optional, tag = "2")] + pub setstmt: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DropdbStmt { + #[prost(string, tag = "1")] + pub dbname: ::prost::alloc::string::String, + #[prost(bool, tag = "2")] + pub missing_ok: bool, + #[prost(message, repeated, tag = "3")] + pub options: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterSystemStmt { + #[prost(message, optional, tag = "1")] + pub setstmt: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ClusterStmt { + #[prost(message, optional, tag = "1")] + pub relation: ::core::option::Option, + #[prost(string, tag = "2")] + pub indexname: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + pub params: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct VacuumStmt { + #[prost(message, repeated, tag = "1")] + pub options: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "2")] + pub rels: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "3")] + pub is_vacuumcmd: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct VacuumRelation { + #[prost(message, optional, tag = "1")] + pub relation: ::core::option::Option, + #[prost(uint32, tag = "2")] + pub oid: u32, + #[prost(message, repeated, tag = "3")] + pub va_cols: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ExplainStmt { + #[prost(message, optional, boxed, tag = "1")] + pub query: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "2")] + pub options: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateTableAsStmt { + #[prost(message, optional, boxed, tag = "1")] + pub query: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, optional, boxed, tag = "2")] + pub into: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(enumeration = "ObjectType", tag = "3")] + pub objtype: i32, + #[prost(bool, tag = "4")] + pub is_select_into: bool, + #[prost(bool, tag = "5")] + pub if_not_exists: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct RefreshMatViewStmt { + #[prost(bool, tag = "1")] + pub concurrent: bool, + #[prost(bool, tag = "2")] + pub skip_data: bool, + #[prost(message, optional, tag = "3")] + pub relation: ::core::option::Option, +} +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct CheckPointStmt {} +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct DiscardStmt { + #[prost(enumeration = "DiscardMode", tag = "1")] + pub target: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct LockStmt { + #[prost(message, repeated, tag = "1")] + pub relations: ::prost::alloc::vec::Vec, + #[prost(int32, tag = "2")] + pub mode: i32, + #[prost(bool, tag = "3")] + pub nowait: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ConstraintsSetStmt { + #[prost(message, repeated, tag = "1")] + pub constraints: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "2")] + pub deferred: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ReindexStmt { + #[prost(enumeration = "ReindexObjectType", tag = "1")] + pub kind: i32, + #[prost(message, optional, tag = "2")] + pub relation: ::core::option::Option, + #[prost(string, tag = "3")] + pub name: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "4")] + pub params: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateConversionStmt { + #[prost(message, repeated, tag = "1")] + pub conversion_name: ::prost::alloc::vec::Vec, + #[prost(string, tag = "2")] + pub for_encoding_name: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub to_encoding_name: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "4")] + pub func_name: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "5")] + pub def: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateCastStmt { + #[prost(message, optional, tag = "1")] + pub sourcetype: ::core::option::Option, + #[prost(message, optional, tag = "2")] + pub targettype: ::core::option::Option, + #[prost(message, optional, tag = "3")] + pub func: ::core::option::Option, + #[prost(enumeration = "CoercionContext", tag = "4")] + pub context: i32, + #[prost(bool, tag = "5")] + pub inout: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateTransformStmt { + #[prost(bool, tag = "1")] + pub replace: bool, + #[prost(message, optional, tag = "2")] + pub type_name: ::core::option::Option, + #[prost(string, tag = "3")] + pub lang: ::prost::alloc::string::String, + #[prost(message, optional, tag = "4")] + pub fromsql: ::core::option::Option, + #[prost(message, optional, tag = "5")] + pub tosql: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PrepareStmt { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub argtypes: ::prost::alloc::vec::Vec, + #[prost(message, optional, boxed, tag = "3")] + pub query: ::core::option::Option<::prost::alloc::boxed::Box>, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ExecuteStmt { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub params: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DeallocateStmt { + #[prost(string, tag = "1")] + pub name: ::prost::alloc::string::String, + #[prost(bool, tag = "2")] + pub isall: bool, + #[prost(int32, tag = "3")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DropOwnedStmt { + #[prost(message, repeated, tag = "1")] + pub roles: ::prost::alloc::vec::Vec, + #[prost(enumeration = "DropBehavior", tag = "2")] + pub behavior: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ReassignOwnedStmt { + #[prost(message, repeated, tag = "1")] + pub roles: ::prost::alloc::vec::Vec, + #[prost(message, optional, tag = "2")] + pub newrole: ::core::option::Option, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterTsDictionaryStmt { + #[prost(message, repeated, tag = "1")] + pub dictname: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "2")] + pub options: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterTsConfigurationStmt { + #[prost(enumeration = "AlterTsConfigType", tag = "1")] + pub kind: i32, + #[prost(message, repeated, tag = "2")] + pub cfgname: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub tokentype: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub dicts: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "5")] + pub r#override: bool, + #[prost(bool, tag = "6")] + pub replace: bool, + #[prost(bool, tag = "7")] + pub missing_ok: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PublicationTable { + #[prost(message, optional, tag = "1")] + pub relation: ::core::option::Option, + #[prost(message, optional, boxed, tag = "2")] + pub where_clause: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(message, repeated, tag = "3")] + pub columns: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct PublicationObjSpec { + #[prost(enumeration = "PublicationObjSpecType", tag = "1")] + pub pubobjtype: i32, + #[prost(string, tag = "2")] + pub name: ::prost::alloc::string::String, + #[prost(message, optional, boxed, tag = "3")] + pub pubtable: ::core::option::Option<::prost::alloc::boxed::Box>, + #[prost(int32, tag = "4")] + pub location: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreatePublicationStmt { + #[prost(string, tag = "1")] + pub pubname: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub options: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub pubobjects: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "4")] + pub for_all_tables: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterPublicationStmt { + #[prost(string, tag = "1")] + pub pubname: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "2")] + pub options: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "3")] + pub pubobjects: ::prost::alloc::vec::Vec, + #[prost(bool, tag = "4")] + pub for_all_tables: bool, + #[prost(enumeration = "AlterPublicationAction", tag = "5")] + pub action: i32, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct CreateSubscriptionStmt { + #[prost(string, tag = "1")] + pub subname: ::prost::alloc::string::String, + #[prost(string, tag = "2")] + pub conninfo: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "3")] + pub publication: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "4")] + pub options: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct AlterSubscriptionStmt { + #[prost(enumeration = "AlterSubscriptionType", tag = "1")] + pub kind: i32, + #[prost(string, tag = "2")] + pub subname: ::prost::alloc::string::String, + #[prost(string, tag = "3")] + pub conninfo: ::prost::alloc::string::String, + #[prost(message, repeated, tag = "4")] + pub publication: ::prost::alloc::vec::Vec, + #[prost(message, repeated, tag = "5")] + pub options: ::prost::alloc::vec::Vec, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct DropSubscriptionStmt { + #[prost(string, tag = "1")] + pub subname: ::prost::alloc::string::String, + #[prost(bool, tag = "2")] + pub missing_ok: bool, + #[prost(enumeration = "DropBehavior", tag = "3")] + pub behavior: i32, +} +#[derive(Clone, Copy, PartialEq, ::prost::Message)] +pub struct ScanToken { + #[prost(int32, tag = "1")] + pub start: i32, + #[prost(int32, tag = "2")] + pub end: i32, + #[prost(enumeration = "Token", tag = "4")] + pub token: i32, + #[prost(enumeration = "KeywordKind", tag = "5")] + pub keyword_kind: i32, +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum QuerySource { + Undefined = 0, + QsrcOriginal = 1, + QsrcParser = 2, + QsrcInsteadRule = 3, + QsrcQualInsteadRule = 4, + QsrcNonInsteadRule = 5, +} +impl QuerySource { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "QUERY_SOURCE_UNDEFINED", + Self::QsrcOriginal => "QSRC_ORIGINAL", + Self::QsrcParser => "QSRC_PARSER", + Self::QsrcInsteadRule => "QSRC_INSTEAD_RULE", + Self::QsrcQualInsteadRule => "QSRC_QUAL_INSTEAD_RULE", + Self::QsrcNonInsteadRule => "QSRC_NON_INSTEAD_RULE", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "QUERY_SOURCE_UNDEFINED" => Some(Self::Undefined), + "QSRC_ORIGINAL" => Some(Self::QsrcOriginal), + "QSRC_PARSER" => Some(Self::QsrcParser), + "QSRC_INSTEAD_RULE" => Some(Self::QsrcInsteadRule), + "QSRC_QUAL_INSTEAD_RULE" => Some(Self::QsrcQualInsteadRule), + "QSRC_NON_INSTEAD_RULE" => Some(Self::QsrcNonInsteadRule), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum SortByDir { + Undefined = 0, + SortbyDefault = 1, + SortbyAsc = 2, + SortbyDesc = 3, + SortbyUsing = 4, +} +impl SortByDir { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "SORT_BY_DIR_UNDEFINED", + Self::SortbyDefault => "SORTBY_DEFAULT", + Self::SortbyAsc => "SORTBY_ASC", + Self::SortbyDesc => "SORTBY_DESC", + Self::SortbyUsing => "SORTBY_USING", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SORT_BY_DIR_UNDEFINED" => Some(Self::Undefined), + "SORTBY_DEFAULT" => Some(Self::SortbyDefault), + "SORTBY_ASC" => Some(Self::SortbyAsc), + "SORTBY_DESC" => Some(Self::SortbyDesc), + "SORTBY_USING" => Some(Self::SortbyUsing), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum SortByNulls { + Undefined = 0, + SortbyNullsDefault = 1, + SortbyNullsFirst = 2, + SortbyNullsLast = 3, +} +impl SortByNulls { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "SORT_BY_NULLS_UNDEFINED", + Self::SortbyNullsDefault => "SORTBY_NULLS_DEFAULT", + Self::SortbyNullsFirst => "SORTBY_NULLS_FIRST", + Self::SortbyNullsLast => "SORTBY_NULLS_LAST", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SORT_BY_NULLS_UNDEFINED" => Some(Self::Undefined), + "SORTBY_NULLS_DEFAULT" => Some(Self::SortbyNullsDefault), + "SORTBY_NULLS_FIRST" => Some(Self::SortbyNullsFirst), + "SORTBY_NULLS_LAST" => Some(Self::SortbyNullsLast), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum SetQuantifier { + Undefined = 0, + Default = 1, + All = 2, + Distinct = 3, +} +impl SetQuantifier { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "SET_QUANTIFIER_UNDEFINED", + Self::Default => "SET_QUANTIFIER_DEFAULT", + Self::All => "SET_QUANTIFIER_ALL", + Self::Distinct => "SET_QUANTIFIER_DISTINCT", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SET_QUANTIFIER_UNDEFINED" => Some(Self::Undefined), + "SET_QUANTIFIER_DEFAULT" => Some(Self::Default), + "SET_QUANTIFIER_ALL" => Some(Self::All), + "SET_QUANTIFIER_DISTINCT" => Some(Self::Distinct), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum AExprKind { + Undefined = 0, + AexprOp = 1, + AexprOpAny = 2, + AexprOpAll = 3, + AexprDistinct = 4, + AexprNotDistinct = 5, + AexprNullif = 6, + AexprIn = 7, + AexprLike = 8, + AexprIlike = 9, + AexprSimilar = 10, + AexprBetween = 11, + AexprNotBetween = 12, + AexprBetweenSym = 13, + AexprNotBetweenSym = 14, +} +impl AExprKind { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "A_EXPR_KIND_UNDEFINED", + Self::AexprOp => "AEXPR_OP", + Self::AexprOpAny => "AEXPR_OP_ANY", + Self::AexprOpAll => "AEXPR_OP_ALL", + Self::AexprDistinct => "AEXPR_DISTINCT", + Self::AexprNotDistinct => "AEXPR_NOT_DISTINCT", + Self::AexprNullif => "AEXPR_NULLIF", + Self::AexprIn => "AEXPR_IN", + Self::AexprLike => "AEXPR_LIKE", + Self::AexprIlike => "AEXPR_ILIKE", + Self::AexprSimilar => "AEXPR_SIMILAR", + Self::AexprBetween => "AEXPR_BETWEEN", + Self::AexprNotBetween => "AEXPR_NOT_BETWEEN", + Self::AexprBetweenSym => "AEXPR_BETWEEN_SYM", + Self::AexprNotBetweenSym => "AEXPR_NOT_BETWEEN_SYM", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "A_EXPR_KIND_UNDEFINED" => Some(Self::Undefined), + "AEXPR_OP" => Some(Self::AexprOp), + "AEXPR_OP_ANY" => Some(Self::AexprOpAny), + "AEXPR_OP_ALL" => Some(Self::AexprOpAll), + "AEXPR_DISTINCT" => Some(Self::AexprDistinct), + "AEXPR_NOT_DISTINCT" => Some(Self::AexprNotDistinct), + "AEXPR_NULLIF" => Some(Self::AexprNullif), + "AEXPR_IN" => Some(Self::AexprIn), + "AEXPR_LIKE" => Some(Self::AexprLike), + "AEXPR_ILIKE" => Some(Self::AexprIlike), + "AEXPR_SIMILAR" => Some(Self::AexprSimilar), + "AEXPR_BETWEEN" => Some(Self::AexprBetween), + "AEXPR_NOT_BETWEEN" => Some(Self::AexprNotBetween), + "AEXPR_BETWEEN_SYM" => Some(Self::AexprBetweenSym), + "AEXPR_NOT_BETWEEN_SYM" => Some(Self::AexprNotBetweenSym), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum RoleSpecType { + Undefined = 0, + RolespecCstring = 1, + RolespecCurrentRole = 2, + RolespecCurrentUser = 3, + RolespecSessionUser = 4, + RolespecPublic = 5, +} +impl RoleSpecType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "ROLE_SPEC_TYPE_UNDEFINED", + Self::RolespecCstring => "ROLESPEC_CSTRING", + Self::RolespecCurrentRole => "ROLESPEC_CURRENT_ROLE", + Self::RolespecCurrentUser => "ROLESPEC_CURRENT_USER", + Self::RolespecSessionUser => "ROLESPEC_SESSION_USER", + Self::RolespecPublic => "ROLESPEC_PUBLIC", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "ROLE_SPEC_TYPE_UNDEFINED" => Some(Self::Undefined), + "ROLESPEC_CSTRING" => Some(Self::RolespecCstring), + "ROLESPEC_CURRENT_ROLE" => Some(Self::RolespecCurrentRole), + "ROLESPEC_CURRENT_USER" => Some(Self::RolespecCurrentUser), + "ROLESPEC_SESSION_USER" => Some(Self::RolespecSessionUser), + "ROLESPEC_PUBLIC" => Some(Self::RolespecPublic), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum TableLikeOption { + Undefined = 0, + CreateTableLikeComments = 1, + CreateTableLikeCompression = 2, + CreateTableLikeConstraints = 3, + CreateTableLikeDefaults = 4, + CreateTableLikeGenerated = 5, + CreateTableLikeIdentity = 6, + CreateTableLikeIndexes = 7, + CreateTableLikeStatistics = 8, + CreateTableLikeStorage = 9, + CreateTableLikeAll = 10, +} +impl TableLikeOption { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "TABLE_LIKE_OPTION_UNDEFINED", + Self::CreateTableLikeComments => "CREATE_TABLE_LIKE_COMMENTS", + Self::CreateTableLikeCompression => "CREATE_TABLE_LIKE_COMPRESSION", + Self::CreateTableLikeConstraints => "CREATE_TABLE_LIKE_CONSTRAINTS", + Self::CreateTableLikeDefaults => "CREATE_TABLE_LIKE_DEFAULTS", + Self::CreateTableLikeGenerated => "CREATE_TABLE_LIKE_GENERATED", + Self::CreateTableLikeIdentity => "CREATE_TABLE_LIKE_IDENTITY", + Self::CreateTableLikeIndexes => "CREATE_TABLE_LIKE_INDEXES", + Self::CreateTableLikeStatistics => "CREATE_TABLE_LIKE_STATISTICS", + Self::CreateTableLikeStorage => "CREATE_TABLE_LIKE_STORAGE", + Self::CreateTableLikeAll => "CREATE_TABLE_LIKE_ALL", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "TABLE_LIKE_OPTION_UNDEFINED" => Some(Self::Undefined), + "CREATE_TABLE_LIKE_COMMENTS" => Some(Self::CreateTableLikeComments), + "CREATE_TABLE_LIKE_COMPRESSION" => Some(Self::CreateTableLikeCompression), + "CREATE_TABLE_LIKE_CONSTRAINTS" => Some(Self::CreateTableLikeConstraints), + "CREATE_TABLE_LIKE_DEFAULTS" => Some(Self::CreateTableLikeDefaults), + "CREATE_TABLE_LIKE_GENERATED" => Some(Self::CreateTableLikeGenerated), + "CREATE_TABLE_LIKE_IDENTITY" => Some(Self::CreateTableLikeIdentity), + "CREATE_TABLE_LIKE_INDEXES" => Some(Self::CreateTableLikeIndexes), + "CREATE_TABLE_LIKE_STATISTICS" => Some(Self::CreateTableLikeStatistics), + "CREATE_TABLE_LIKE_STORAGE" => Some(Self::CreateTableLikeStorage), + "CREATE_TABLE_LIKE_ALL" => Some(Self::CreateTableLikeAll), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum DefElemAction { + Undefined = 0, + DefelemUnspec = 1, + DefelemSet = 2, + DefelemAdd = 3, + DefelemDrop = 4, +} +impl DefElemAction { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "DEF_ELEM_ACTION_UNDEFINED", + Self::DefelemUnspec => "DEFELEM_UNSPEC", + Self::DefelemSet => "DEFELEM_SET", + Self::DefelemAdd => "DEFELEM_ADD", + Self::DefelemDrop => "DEFELEM_DROP", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "DEF_ELEM_ACTION_UNDEFINED" => Some(Self::Undefined), + "DEFELEM_UNSPEC" => Some(Self::DefelemUnspec), + "DEFELEM_SET" => Some(Self::DefelemSet), + "DEFELEM_ADD" => Some(Self::DefelemAdd), + "DEFELEM_DROP" => Some(Self::DefelemDrop), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum PartitionStrategy { + Undefined = 0, + List = 1, + Range = 2, + Hash = 3, +} +impl PartitionStrategy { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "PARTITION_STRATEGY_UNDEFINED", + Self::List => "PARTITION_STRATEGY_LIST", + Self::Range => "PARTITION_STRATEGY_RANGE", + Self::Hash => "PARTITION_STRATEGY_HASH", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "PARTITION_STRATEGY_UNDEFINED" => Some(Self::Undefined), + "PARTITION_STRATEGY_LIST" => Some(Self::List), + "PARTITION_STRATEGY_RANGE" => Some(Self::Range), + "PARTITION_STRATEGY_HASH" => Some(Self::Hash), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum PartitionRangeDatumKind { + Undefined = 0, + PartitionRangeDatumMinvalue = 1, + PartitionRangeDatumValue = 2, + PartitionRangeDatumMaxvalue = 3, +} +impl PartitionRangeDatumKind { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "PARTITION_RANGE_DATUM_KIND_UNDEFINED", + Self::PartitionRangeDatumMinvalue => "PARTITION_RANGE_DATUM_MINVALUE", + Self::PartitionRangeDatumValue => "PARTITION_RANGE_DATUM_VALUE", + Self::PartitionRangeDatumMaxvalue => "PARTITION_RANGE_DATUM_MAXVALUE", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "PARTITION_RANGE_DATUM_KIND_UNDEFINED" => Some(Self::Undefined), + "PARTITION_RANGE_DATUM_MINVALUE" => Some(Self::PartitionRangeDatumMinvalue), + "PARTITION_RANGE_DATUM_VALUE" => Some(Self::PartitionRangeDatumValue), + "PARTITION_RANGE_DATUM_MAXVALUE" => Some(Self::PartitionRangeDatumMaxvalue), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum RteKind { + RtekindUndefined = 0, + RteRelation = 1, + RteSubquery = 2, + RteJoin = 3, + RteFunction = 4, + RteTablefunc = 5, + RteValues = 6, + RteCte = 7, + RteNamedtuplestore = 8, + RteResult = 9, +} +impl RteKind { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::RtekindUndefined => "RTEKIND_UNDEFINED", + Self::RteRelation => "RTE_RELATION", + Self::RteSubquery => "RTE_SUBQUERY", + Self::RteJoin => "RTE_JOIN", + Self::RteFunction => "RTE_FUNCTION", + Self::RteTablefunc => "RTE_TABLEFUNC", + Self::RteValues => "RTE_VALUES", + Self::RteCte => "RTE_CTE", + Self::RteNamedtuplestore => "RTE_NAMEDTUPLESTORE", + Self::RteResult => "RTE_RESULT", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "RTEKIND_UNDEFINED" => Some(Self::RtekindUndefined), + "RTE_RELATION" => Some(Self::RteRelation), + "RTE_SUBQUERY" => Some(Self::RteSubquery), + "RTE_JOIN" => Some(Self::RteJoin), + "RTE_FUNCTION" => Some(Self::RteFunction), + "RTE_TABLEFUNC" => Some(Self::RteTablefunc), + "RTE_VALUES" => Some(Self::RteValues), + "RTE_CTE" => Some(Self::RteCte), + "RTE_NAMEDTUPLESTORE" => Some(Self::RteNamedtuplestore), + "RTE_RESULT" => Some(Self::RteResult), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum WcoKind { + WcokindUndefined = 0, + WcoViewCheck = 1, + WcoRlsInsertCheck = 2, + WcoRlsUpdateCheck = 3, + WcoRlsConflictCheck = 4, + WcoRlsMergeUpdateCheck = 5, + WcoRlsMergeDeleteCheck = 6, +} +impl WcoKind { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::WcokindUndefined => "WCOKIND_UNDEFINED", + Self::WcoViewCheck => "WCO_VIEW_CHECK", + Self::WcoRlsInsertCheck => "WCO_RLS_INSERT_CHECK", + Self::WcoRlsUpdateCheck => "WCO_RLS_UPDATE_CHECK", + Self::WcoRlsConflictCheck => "WCO_RLS_CONFLICT_CHECK", + Self::WcoRlsMergeUpdateCheck => "WCO_RLS_MERGE_UPDATE_CHECK", + Self::WcoRlsMergeDeleteCheck => "WCO_RLS_MERGE_DELETE_CHECK", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "WCOKIND_UNDEFINED" => Some(Self::WcokindUndefined), + "WCO_VIEW_CHECK" => Some(Self::WcoViewCheck), + "WCO_RLS_INSERT_CHECK" => Some(Self::WcoRlsInsertCheck), + "WCO_RLS_UPDATE_CHECK" => Some(Self::WcoRlsUpdateCheck), + "WCO_RLS_CONFLICT_CHECK" => Some(Self::WcoRlsConflictCheck), + "WCO_RLS_MERGE_UPDATE_CHECK" => Some(Self::WcoRlsMergeUpdateCheck), + "WCO_RLS_MERGE_DELETE_CHECK" => Some(Self::WcoRlsMergeDeleteCheck), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum GroupingSetKind { + Undefined = 0, + GroupingSetEmpty = 1, + GroupingSetSimple = 2, + GroupingSetRollup = 3, + GroupingSetCube = 4, + GroupingSetSets = 5, +} +impl GroupingSetKind { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "GROUPING_SET_KIND_UNDEFINED", + Self::GroupingSetEmpty => "GROUPING_SET_EMPTY", + Self::GroupingSetSimple => "GROUPING_SET_SIMPLE", + Self::GroupingSetRollup => "GROUPING_SET_ROLLUP", + Self::GroupingSetCube => "GROUPING_SET_CUBE", + Self::GroupingSetSets => "GROUPING_SET_SETS", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "GROUPING_SET_KIND_UNDEFINED" => Some(Self::Undefined), + "GROUPING_SET_EMPTY" => Some(Self::GroupingSetEmpty), + "GROUPING_SET_SIMPLE" => Some(Self::GroupingSetSimple), + "GROUPING_SET_ROLLUP" => Some(Self::GroupingSetRollup), + "GROUPING_SET_CUBE" => Some(Self::GroupingSetCube), + "GROUPING_SET_SETS" => Some(Self::GroupingSetSets), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum CteMaterialize { + CtematerializeUndefined = 0, + Default = 1, + Always = 2, + Never = 3, +} +impl CteMaterialize { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::CtematerializeUndefined => "CTEMATERIALIZE_UNDEFINED", + Self::Default => "CTEMaterializeDefault", + Self::Always => "CTEMaterializeAlways", + Self::Never => "CTEMaterializeNever", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "CTEMATERIALIZE_UNDEFINED" => Some(Self::CtematerializeUndefined), + "CTEMaterializeDefault" => Some(Self::Default), + "CTEMaterializeAlways" => Some(Self::Always), + "CTEMaterializeNever" => Some(Self::Never), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum JsonQuotes { + Undefined = 0, + JsQuotesUnspec = 1, + JsQuotesKeep = 2, + JsQuotesOmit = 3, +} +impl JsonQuotes { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "JSON_QUOTES_UNDEFINED", + Self::JsQuotesUnspec => "JS_QUOTES_UNSPEC", + Self::JsQuotesKeep => "JS_QUOTES_KEEP", + Self::JsQuotesOmit => "JS_QUOTES_OMIT", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "JSON_QUOTES_UNDEFINED" => Some(Self::Undefined), + "JS_QUOTES_UNSPEC" => Some(Self::JsQuotesUnspec), + "JS_QUOTES_KEEP" => Some(Self::JsQuotesKeep), + "JS_QUOTES_OMIT" => Some(Self::JsQuotesOmit), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum JsonTableColumnType { + Undefined = 0, + JtcForOrdinality = 1, + JtcRegular = 2, + JtcExists = 3, + JtcFormatted = 4, + JtcNested = 5, +} +impl JsonTableColumnType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "JSON_TABLE_COLUMN_TYPE_UNDEFINED", + Self::JtcForOrdinality => "JTC_FOR_ORDINALITY", + Self::JtcRegular => "JTC_REGULAR", + Self::JtcExists => "JTC_EXISTS", + Self::JtcFormatted => "JTC_FORMATTED", + Self::JtcNested => "JTC_NESTED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "JSON_TABLE_COLUMN_TYPE_UNDEFINED" => Some(Self::Undefined), + "JTC_FOR_ORDINALITY" => Some(Self::JtcForOrdinality), + "JTC_REGULAR" => Some(Self::JtcRegular), + "JTC_EXISTS" => Some(Self::JtcExists), + "JTC_FORMATTED" => Some(Self::JtcFormatted), + "JTC_NESTED" => Some(Self::JtcNested), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum SetOperation { + Undefined = 0, + SetopNone = 1, + SetopUnion = 2, + SetopIntersect = 3, + SetopExcept = 4, +} +impl SetOperation { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "SET_OPERATION_UNDEFINED", + Self::SetopNone => "SETOP_NONE", + Self::SetopUnion => "SETOP_UNION", + Self::SetopIntersect => "SETOP_INTERSECT", + Self::SetopExcept => "SETOP_EXCEPT", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SET_OPERATION_UNDEFINED" => Some(Self::Undefined), + "SETOP_NONE" => Some(Self::SetopNone), + "SETOP_UNION" => Some(Self::SetopUnion), + "SETOP_INTERSECT" => Some(Self::SetopIntersect), + "SETOP_EXCEPT" => Some(Self::SetopExcept), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ObjectType { + Undefined = 0, + ObjectAccessMethod = 1, + ObjectAggregate = 2, + ObjectAmop = 3, + ObjectAmproc = 4, + ObjectAttribute = 5, + ObjectCast = 6, + ObjectColumn = 7, + ObjectCollation = 8, + ObjectConversion = 9, + ObjectDatabase = 10, + ObjectDefault = 11, + ObjectDefacl = 12, + ObjectDomain = 13, + ObjectDomconstraint = 14, + ObjectEventTrigger = 15, + ObjectExtension = 16, + ObjectFdw = 17, + ObjectForeignServer = 18, + ObjectForeignTable = 19, + ObjectFunction = 20, + ObjectIndex = 21, + ObjectLanguage = 22, + ObjectLargeobject = 23, + ObjectMatview = 24, + ObjectOpclass = 25, + ObjectOperator = 26, + ObjectOpfamily = 27, + ObjectParameterAcl = 28, + ObjectPolicy = 29, + ObjectProcedure = 30, + ObjectPublication = 31, + ObjectPublicationNamespace = 32, + ObjectPublicationRel = 33, + ObjectRole = 34, + ObjectRoutine = 35, + ObjectRule = 36, + ObjectSchema = 37, + ObjectSequence = 38, + ObjectSubscription = 39, + ObjectStatisticExt = 40, + ObjectTabconstraint = 41, + ObjectTable = 42, + ObjectTablespace = 43, + ObjectTransform = 44, + ObjectTrigger = 45, + ObjectTsconfiguration = 46, + ObjectTsdictionary = 47, + ObjectTsparser = 48, + ObjectTstemplate = 49, + ObjectType = 50, + ObjectUserMapping = 51, + ObjectView = 52, +} +impl ObjectType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "OBJECT_TYPE_UNDEFINED", + Self::ObjectAccessMethod => "OBJECT_ACCESS_METHOD", + Self::ObjectAggregate => "OBJECT_AGGREGATE", + Self::ObjectAmop => "OBJECT_AMOP", + Self::ObjectAmproc => "OBJECT_AMPROC", + Self::ObjectAttribute => "OBJECT_ATTRIBUTE", + Self::ObjectCast => "OBJECT_CAST", + Self::ObjectColumn => "OBJECT_COLUMN", + Self::ObjectCollation => "OBJECT_COLLATION", + Self::ObjectConversion => "OBJECT_CONVERSION", + Self::ObjectDatabase => "OBJECT_DATABASE", + Self::ObjectDefault => "OBJECT_DEFAULT", + Self::ObjectDefacl => "OBJECT_DEFACL", + Self::ObjectDomain => "OBJECT_DOMAIN", + Self::ObjectDomconstraint => "OBJECT_DOMCONSTRAINT", + Self::ObjectEventTrigger => "OBJECT_EVENT_TRIGGER", + Self::ObjectExtension => "OBJECT_EXTENSION", + Self::ObjectFdw => "OBJECT_FDW", + Self::ObjectForeignServer => "OBJECT_FOREIGN_SERVER", + Self::ObjectForeignTable => "OBJECT_FOREIGN_TABLE", + Self::ObjectFunction => "OBJECT_FUNCTION", + Self::ObjectIndex => "OBJECT_INDEX", + Self::ObjectLanguage => "OBJECT_LANGUAGE", + Self::ObjectLargeobject => "OBJECT_LARGEOBJECT", + Self::ObjectMatview => "OBJECT_MATVIEW", + Self::ObjectOpclass => "OBJECT_OPCLASS", + Self::ObjectOperator => "OBJECT_OPERATOR", + Self::ObjectOpfamily => "OBJECT_OPFAMILY", + Self::ObjectParameterAcl => "OBJECT_PARAMETER_ACL", + Self::ObjectPolicy => "OBJECT_POLICY", + Self::ObjectProcedure => "OBJECT_PROCEDURE", + Self::ObjectPublication => "OBJECT_PUBLICATION", + Self::ObjectPublicationNamespace => "OBJECT_PUBLICATION_NAMESPACE", + Self::ObjectPublicationRel => "OBJECT_PUBLICATION_REL", + Self::ObjectRole => "OBJECT_ROLE", + Self::ObjectRoutine => "OBJECT_ROUTINE", + Self::ObjectRule => "OBJECT_RULE", + Self::ObjectSchema => "OBJECT_SCHEMA", + Self::ObjectSequence => "OBJECT_SEQUENCE", + Self::ObjectSubscription => "OBJECT_SUBSCRIPTION", + Self::ObjectStatisticExt => "OBJECT_STATISTIC_EXT", + Self::ObjectTabconstraint => "OBJECT_TABCONSTRAINT", + Self::ObjectTable => "OBJECT_TABLE", + Self::ObjectTablespace => "OBJECT_TABLESPACE", + Self::ObjectTransform => "OBJECT_TRANSFORM", + Self::ObjectTrigger => "OBJECT_TRIGGER", + Self::ObjectTsconfiguration => "OBJECT_TSCONFIGURATION", + Self::ObjectTsdictionary => "OBJECT_TSDICTIONARY", + Self::ObjectTsparser => "OBJECT_TSPARSER", + Self::ObjectTstemplate => "OBJECT_TSTEMPLATE", + Self::ObjectType => "OBJECT_TYPE", + Self::ObjectUserMapping => "OBJECT_USER_MAPPING", + Self::ObjectView => "OBJECT_VIEW", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "OBJECT_TYPE_UNDEFINED" => Some(Self::Undefined), + "OBJECT_ACCESS_METHOD" => Some(Self::ObjectAccessMethod), + "OBJECT_AGGREGATE" => Some(Self::ObjectAggregate), + "OBJECT_AMOP" => Some(Self::ObjectAmop), + "OBJECT_AMPROC" => Some(Self::ObjectAmproc), + "OBJECT_ATTRIBUTE" => Some(Self::ObjectAttribute), + "OBJECT_CAST" => Some(Self::ObjectCast), + "OBJECT_COLUMN" => Some(Self::ObjectColumn), + "OBJECT_COLLATION" => Some(Self::ObjectCollation), + "OBJECT_CONVERSION" => Some(Self::ObjectConversion), + "OBJECT_DATABASE" => Some(Self::ObjectDatabase), + "OBJECT_DEFAULT" => Some(Self::ObjectDefault), + "OBJECT_DEFACL" => Some(Self::ObjectDefacl), + "OBJECT_DOMAIN" => Some(Self::ObjectDomain), + "OBJECT_DOMCONSTRAINT" => Some(Self::ObjectDomconstraint), + "OBJECT_EVENT_TRIGGER" => Some(Self::ObjectEventTrigger), + "OBJECT_EXTENSION" => Some(Self::ObjectExtension), + "OBJECT_FDW" => Some(Self::ObjectFdw), + "OBJECT_FOREIGN_SERVER" => Some(Self::ObjectForeignServer), + "OBJECT_FOREIGN_TABLE" => Some(Self::ObjectForeignTable), + "OBJECT_FUNCTION" => Some(Self::ObjectFunction), + "OBJECT_INDEX" => Some(Self::ObjectIndex), + "OBJECT_LANGUAGE" => Some(Self::ObjectLanguage), + "OBJECT_LARGEOBJECT" => Some(Self::ObjectLargeobject), + "OBJECT_MATVIEW" => Some(Self::ObjectMatview), + "OBJECT_OPCLASS" => Some(Self::ObjectOpclass), + "OBJECT_OPERATOR" => Some(Self::ObjectOperator), + "OBJECT_OPFAMILY" => Some(Self::ObjectOpfamily), + "OBJECT_PARAMETER_ACL" => Some(Self::ObjectParameterAcl), + "OBJECT_POLICY" => Some(Self::ObjectPolicy), + "OBJECT_PROCEDURE" => Some(Self::ObjectProcedure), + "OBJECT_PUBLICATION" => Some(Self::ObjectPublication), + "OBJECT_PUBLICATION_NAMESPACE" => Some(Self::ObjectPublicationNamespace), + "OBJECT_PUBLICATION_REL" => Some(Self::ObjectPublicationRel), + "OBJECT_ROLE" => Some(Self::ObjectRole), + "OBJECT_ROUTINE" => Some(Self::ObjectRoutine), + "OBJECT_RULE" => Some(Self::ObjectRule), + "OBJECT_SCHEMA" => Some(Self::ObjectSchema), + "OBJECT_SEQUENCE" => Some(Self::ObjectSequence), + "OBJECT_SUBSCRIPTION" => Some(Self::ObjectSubscription), + "OBJECT_STATISTIC_EXT" => Some(Self::ObjectStatisticExt), + "OBJECT_TABCONSTRAINT" => Some(Self::ObjectTabconstraint), + "OBJECT_TABLE" => Some(Self::ObjectTable), + "OBJECT_TABLESPACE" => Some(Self::ObjectTablespace), + "OBJECT_TRANSFORM" => Some(Self::ObjectTransform), + "OBJECT_TRIGGER" => Some(Self::ObjectTrigger), + "OBJECT_TSCONFIGURATION" => Some(Self::ObjectTsconfiguration), + "OBJECT_TSDICTIONARY" => Some(Self::ObjectTsdictionary), + "OBJECT_TSPARSER" => Some(Self::ObjectTsparser), + "OBJECT_TSTEMPLATE" => Some(Self::ObjectTstemplate), + "OBJECT_TYPE" => Some(Self::ObjectType), + "OBJECT_USER_MAPPING" => Some(Self::ObjectUserMapping), + "OBJECT_VIEW" => Some(Self::ObjectView), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum DropBehavior { + Undefined = 0, + DropRestrict = 1, + DropCascade = 2, +} +impl DropBehavior { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "DROP_BEHAVIOR_UNDEFINED", + Self::DropRestrict => "DROP_RESTRICT", + Self::DropCascade => "DROP_CASCADE", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "DROP_BEHAVIOR_UNDEFINED" => Some(Self::Undefined), + "DROP_RESTRICT" => Some(Self::DropRestrict), + "DROP_CASCADE" => Some(Self::DropCascade), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum AlterTableType { + Undefined = 0, + AtAddColumn = 1, + AtAddColumnToView = 2, + AtColumnDefault = 3, + AtCookedColumnDefault = 4, + AtDropNotNull = 5, + AtSetNotNull = 6, + AtSetExpression = 7, + AtDropExpression = 8, + AtCheckNotNull = 9, + AtSetStatistics = 10, + AtSetOptions = 11, + AtResetOptions = 12, + AtSetStorage = 13, + AtSetCompression = 14, + AtDropColumn = 15, + AtAddIndex = 16, + AtReAddIndex = 17, + AtAddConstraint = 18, + AtReAddConstraint = 19, + AtReAddDomainConstraint = 20, + AtAlterConstraint = 21, + AtValidateConstraint = 22, + AtAddIndexConstraint = 23, + AtDropConstraint = 24, + AtReAddComment = 25, + AtAlterColumnType = 26, + AtAlterColumnGenericOptions = 27, + AtChangeOwner = 28, + AtClusterOn = 29, + AtDropCluster = 30, + AtSetLogged = 31, + AtSetUnLogged = 32, + AtDropOids = 33, + AtSetAccessMethod = 34, + AtSetTableSpace = 35, + AtSetRelOptions = 36, + AtResetRelOptions = 37, + AtReplaceRelOptions = 38, + AtEnableTrig = 39, + AtEnableAlwaysTrig = 40, + AtEnableReplicaTrig = 41, + AtDisableTrig = 42, + AtEnableTrigAll = 43, + AtDisableTrigAll = 44, + AtEnableTrigUser = 45, + AtDisableTrigUser = 46, + AtEnableRule = 47, + AtEnableAlwaysRule = 48, + AtEnableReplicaRule = 49, + AtDisableRule = 50, + AtAddInherit = 51, + AtDropInherit = 52, + AtAddOf = 53, + AtDropOf = 54, + AtReplicaIdentity = 55, + AtEnableRowSecurity = 56, + AtDisableRowSecurity = 57, + AtForceRowSecurity = 58, + AtNoForceRowSecurity = 59, + AtGenericOptions = 60, + AtAttachPartition = 61, + AtDetachPartition = 62, + AtDetachPartitionFinalize = 63, + AtAddIdentity = 64, + AtSetIdentity = 65, + AtDropIdentity = 66, + AtReAddStatistics = 67, +} +impl AlterTableType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "ALTER_TABLE_TYPE_UNDEFINED", + Self::AtAddColumn => "AT_AddColumn", + Self::AtAddColumnToView => "AT_AddColumnToView", + Self::AtColumnDefault => "AT_ColumnDefault", + Self::AtCookedColumnDefault => "AT_CookedColumnDefault", + Self::AtDropNotNull => "AT_DropNotNull", + Self::AtSetNotNull => "AT_SetNotNull", + Self::AtSetExpression => "AT_SetExpression", + Self::AtDropExpression => "AT_DropExpression", + Self::AtCheckNotNull => "AT_CheckNotNull", + Self::AtSetStatistics => "AT_SetStatistics", + Self::AtSetOptions => "AT_SetOptions", + Self::AtResetOptions => "AT_ResetOptions", + Self::AtSetStorage => "AT_SetStorage", + Self::AtSetCompression => "AT_SetCompression", + Self::AtDropColumn => "AT_DropColumn", + Self::AtAddIndex => "AT_AddIndex", + Self::AtReAddIndex => "AT_ReAddIndex", + Self::AtAddConstraint => "AT_AddConstraint", + Self::AtReAddConstraint => "AT_ReAddConstraint", + Self::AtReAddDomainConstraint => "AT_ReAddDomainConstraint", + Self::AtAlterConstraint => "AT_AlterConstraint", + Self::AtValidateConstraint => "AT_ValidateConstraint", + Self::AtAddIndexConstraint => "AT_AddIndexConstraint", + Self::AtDropConstraint => "AT_DropConstraint", + Self::AtReAddComment => "AT_ReAddComment", + Self::AtAlterColumnType => "AT_AlterColumnType", + Self::AtAlterColumnGenericOptions => "AT_AlterColumnGenericOptions", + Self::AtChangeOwner => "AT_ChangeOwner", + Self::AtClusterOn => "AT_ClusterOn", + Self::AtDropCluster => "AT_DropCluster", + Self::AtSetLogged => "AT_SetLogged", + Self::AtSetUnLogged => "AT_SetUnLogged", + Self::AtDropOids => "AT_DropOids", + Self::AtSetAccessMethod => "AT_SetAccessMethod", + Self::AtSetTableSpace => "AT_SetTableSpace", + Self::AtSetRelOptions => "AT_SetRelOptions", + Self::AtResetRelOptions => "AT_ResetRelOptions", + Self::AtReplaceRelOptions => "AT_ReplaceRelOptions", + Self::AtEnableTrig => "AT_EnableTrig", + Self::AtEnableAlwaysTrig => "AT_EnableAlwaysTrig", + Self::AtEnableReplicaTrig => "AT_EnableReplicaTrig", + Self::AtDisableTrig => "AT_DisableTrig", + Self::AtEnableTrigAll => "AT_EnableTrigAll", + Self::AtDisableTrigAll => "AT_DisableTrigAll", + Self::AtEnableTrigUser => "AT_EnableTrigUser", + Self::AtDisableTrigUser => "AT_DisableTrigUser", + Self::AtEnableRule => "AT_EnableRule", + Self::AtEnableAlwaysRule => "AT_EnableAlwaysRule", + Self::AtEnableReplicaRule => "AT_EnableReplicaRule", + Self::AtDisableRule => "AT_DisableRule", + Self::AtAddInherit => "AT_AddInherit", + Self::AtDropInherit => "AT_DropInherit", + Self::AtAddOf => "AT_AddOf", + Self::AtDropOf => "AT_DropOf", + Self::AtReplicaIdentity => "AT_ReplicaIdentity", + Self::AtEnableRowSecurity => "AT_EnableRowSecurity", + Self::AtDisableRowSecurity => "AT_DisableRowSecurity", + Self::AtForceRowSecurity => "AT_ForceRowSecurity", + Self::AtNoForceRowSecurity => "AT_NoForceRowSecurity", + Self::AtGenericOptions => "AT_GenericOptions", + Self::AtAttachPartition => "AT_AttachPartition", + Self::AtDetachPartition => "AT_DetachPartition", + Self::AtDetachPartitionFinalize => "AT_DetachPartitionFinalize", + Self::AtAddIdentity => "AT_AddIdentity", + Self::AtSetIdentity => "AT_SetIdentity", + Self::AtDropIdentity => "AT_DropIdentity", + Self::AtReAddStatistics => "AT_ReAddStatistics", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "ALTER_TABLE_TYPE_UNDEFINED" => Some(Self::Undefined), + "AT_AddColumn" => Some(Self::AtAddColumn), + "AT_AddColumnToView" => Some(Self::AtAddColumnToView), + "AT_ColumnDefault" => Some(Self::AtColumnDefault), + "AT_CookedColumnDefault" => Some(Self::AtCookedColumnDefault), + "AT_DropNotNull" => Some(Self::AtDropNotNull), + "AT_SetNotNull" => Some(Self::AtSetNotNull), + "AT_SetExpression" => Some(Self::AtSetExpression), + "AT_DropExpression" => Some(Self::AtDropExpression), + "AT_CheckNotNull" => Some(Self::AtCheckNotNull), + "AT_SetStatistics" => Some(Self::AtSetStatistics), + "AT_SetOptions" => Some(Self::AtSetOptions), + "AT_ResetOptions" => Some(Self::AtResetOptions), + "AT_SetStorage" => Some(Self::AtSetStorage), + "AT_SetCompression" => Some(Self::AtSetCompression), + "AT_DropColumn" => Some(Self::AtDropColumn), + "AT_AddIndex" => Some(Self::AtAddIndex), + "AT_ReAddIndex" => Some(Self::AtReAddIndex), + "AT_AddConstraint" => Some(Self::AtAddConstraint), + "AT_ReAddConstraint" => Some(Self::AtReAddConstraint), + "AT_ReAddDomainConstraint" => Some(Self::AtReAddDomainConstraint), + "AT_AlterConstraint" => Some(Self::AtAlterConstraint), + "AT_ValidateConstraint" => Some(Self::AtValidateConstraint), + "AT_AddIndexConstraint" => Some(Self::AtAddIndexConstraint), + "AT_DropConstraint" => Some(Self::AtDropConstraint), + "AT_ReAddComment" => Some(Self::AtReAddComment), + "AT_AlterColumnType" => Some(Self::AtAlterColumnType), + "AT_AlterColumnGenericOptions" => Some(Self::AtAlterColumnGenericOptions), + "AT_ChangeOwner" => Some(Self::AtChangeOwner), + "AT_ClusterOn" => Some(Self::AtClusterOn), + "AT_DropCluster" => Some(Self::AtDropCluster), + "AT_SetLogged" => Some(Self::AtSetLogged), + "AT_SetUnLogged" => Some(Self::AtSetUnLogged), + "AT_DropOids" => Some(Self::AtDropOids), + "AT_SetAccessMethod" => Some(Self::AtSetAccessMethod), + "AT_SetTableSpace" => Some(Self::AtSetTableSpace), + "AT_SetRelOptions" => Some(Self::AtSetRelOptions), + "AT_ResetRelOptions" => Some(Self::AtResetRelOptions), + "AT_ReplaceRelOptions" => Some(Self::AtReplaceRelOptions), + "AT_EnableTrig" => Some(Self::AtEnableTrig), + "AT_EnableAlwaysTrig" => Some(Self::AtEnableAlwaysTrig), + "AT_EnableReplicaTrig" => Some(Self::AtEnableReplicaTrig), + "AT_DisableTrig" => Some(Self::AtDisableTrig), + "AT_EnableTrigAll" => Some(Self::AtEnableTrigAll), + "AT_DisableTrigAll" => Some(Self::AtDisableTrigAll), + "AT_EnableTrigUser" => Some(Self::AtEnableTrigUser), + "AT_DisableTrigUser" => Some(Self::AtDisableTrigUser), + "AT_EnableRule" => Some(Self::AtEnableRule), + "AT_EnableAlwaysRule" => Some(Self::AtEnableAlwaysRule), + "AT_EnableReplicaRule" => Some(Self::AtEnableReplicaRule), + "AT_DisableRule" => Some(Self::AtDisableRule), + "AT_AddInherit" => Some(Self::AtAddInherit), + "AT_DropInherit" => Some(Self::AtDropInherit), + "AT_AddOf" => Some(Self::AtAddOf), + "AT_DropOf" => Some(Self::AtDropOf), + "AT_ReplicaIdentity" => Some(Self::AtReplicaIdentity), + "AT_EnableRowSecurity" => Some(Self::AtEnableRowSecurity), + "AT_DisableRowSecurity" => Some(Self::AtDisableRowSecurity), + "AT_ForceRowSecurity" => Some(Self::AtForceRowSecurity), + "AT_NoForceRowSecurity" => Some(Self::AtNoForceRowSecurity), + "AT_GenericOptions" => Some(Self::AtGenericOptions), + "AT_AttachPartition" => Some(Self::AtAttachPartition), + "AT_DetachPartition" => Some(Self::AtDetachPartition), + "AT_DetachPartitionFinalize" => Some(Self::AtDetachPartitionFinalize), + "AT_AddIdentity" => Some(Self::AtAddIdentity), + "AT_SetIdentity" => Some(Self::AtSetIdentity), + "AT_DropIdentity" => Some(Self::AtDropIdentity), + "AT_ReAddStatistics" => Some(Self::AtReAddStatistics), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum GrantTargetType { + Undefined = 0, + AclTargetObject = 1, + AclTargetAllInSchema = 2, + AclTargetDefaults = 3, +} +impl GrantTargetType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "GRANT_TARGET_TYPE_UNDEFINED", + Self::AclTargetObject => "ACL_TARGET_OBJECT", + Self::AclTargetAllInSchema => "ACL_TARGET_ALL_IN_SCHEMA", + Self::AclTargetDefaults => "ACL_TARGET_DEFAULTS", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "GRANT_TARGET_TYPE_UNDEFINED" => Some(Self::Undefined), + "ACL_TARGET_OBJECT" => Some(Self::AclTargetObject), + "ACL_TARGET_ALL_IN_SCHEMA" => Some(Self::AclTargetAllInSchema), + "ACL_TARGET_DEFAULTS" => Some(Self::AclTargetDefaults), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum VariableSetKind { + Undefined = 0, + VarSetValue = 1, + VarSetDefault = 2, + VarSetCurrent = 3, + VarSetMulti = 4, + VarReset = 5, + VarResetAll = 6, +} +impl VariableSetKind { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "VARIABLE_SET_KIND_UNDEFINED", + Self::VarSetValue => "VAR_SET_VALUE", + Self::VarSetDefault => "VAR_SET_DEFAULT", + Self::VarSetCurrent => "VAR_SET_CURRENT", + Self::VarSetMulti => "VAR_SET_MULTI", + Self::VarReset => "VAR_RESET", + Self::VarResetAll => "VAR_RESET_ALL", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "VARIABLE_SET_KIND_UNDEFINED" => Some(Self::Undefined), + "VAR_SET_VALUE" => Some(Self::VarSetValue), + "VAR_SET_DEFAULT" => Some(Self::VarSetDefault), + "VAR_SET_CURRENT" => Some(Self::VarSetCurrent), + "VAR_SET_MULTI" => Some(Self::VarSetMulti), + "VAR_RESET" => Some(Self::VarReset), + "VAR_RESET_ALL" => Some(Self::VarResetAll), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ConstrType { + Undefined = 0, + ConstrNull = 1, + ConstrNotnull = 2, + ConstrDefault = 3, + ConstrIdentity = 4, + ConstrGenerated = 5, + ConstrCheck = 6, + ConstrPrimary = 7, + ConstrUnique = 8, + ConstrExclusion = 9, + ConstrForeign = 10, + ConstrAttrDeferrable = 11, + ConstrAttrNotDeferrable = 12, + ConstrAttrDeferred = 13, + ConstrAttrImmediate = 14, +} +impl ConstrType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "CONSTR_TYPE_UNDEFINED", + Self::ConstrNull => "CONSTR_NULL", + Self::ConstrNotnull => "CONSTR_NOTNULL", + Self::ConstrDefault => "CONSTR_DEFAULT", + Self::ConstrIdentity => "CONSTR_IDENTITY", + Self::ConstrGenerated => "CONSTR_GENERATED", + Self::ConstrCheck => "CONSTR_CHECK", + Self::ConstrPrimary => "CONSTR_PRIMARY", + Self::ConstrUnique => "CONSTR_UNIQUE", + Self::ConstrExclusion => "CONSTR_EXCLUSION", + Self::ConstrForeign => "CONSTR_FOREIGN", + Self::ConstrAttrDeferrable => "CONSTR_ATTR_DEFERRABLE", + Self::ConstrAttrNotDeferrable => "CONSTR_ATTR_NOT_DEFERRABLE", + Self::ConstrAttrDeferred => "CONSTR_ATTR_DEFERRED", + Self::ConstrAttrImmediate => "CONSTR_ATTR_IMMEDIATE", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "CONSTR_TYPE_UNDEFINED" => Some(Self::Undefined), + "CONSTR_NULL" => Some(Self::ConstrNull), + "CONSTR_NOTNULL" => Some(Self::ConstrNotnull), + "CONSTR_DEFAULT" => Some(Self::ConstrDefault), + "CONSTR_IDENTITY" => Some(Self::ConstrIdentity), + "CONSTR_GENERATED" => Some(Self::ConstrGenerated), + "CONSTR_CHECK" => Some(Self::ConstrCheck), + "CONSTR_PRIMARY" => Some(Self::ConstrPrimary), + "CONSTR_UNIQUE" => Some(Self::ConstrUnique), + "CONSTR_EXCLUSION" => Some(Self::ConstrExclusion), + "CONSTR_FOREIGN" => Some(Self::ConstrForeign), + "CONSTR_ATTR_DEFERRABLE" => Some(Self::ConstrAttrDeferrable), + "CONSTR_ATTR_NOT_DEFERRABLE" => Some(Self::ConstrAttrNotDeferrable), + "CONSTR_ATTR_DEFERRED" => Some(Self::ConstrAttrDeferred), + "CONSTR_ATTR_IMMEDIATE" => Some(Self::ConstrAttrImmediate), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ImportForeignSchemaType { + Undefined = 0, + FdwImportSchemaAll = 1, + FdwImportSchemaLimitTo = 2, + FdwImportSchemaExcept = 3, +} +impl ImportForeignSchemaType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "IMPORT_FOREIGN_SCHEMA_TYPE_UNDEFINED", + Self::FdwImportSchemaAll => "FDW_IMPORT_SCHEMA_ALL", + Self::FdwImportSchemaLimitTo => "FDW_IMPORT_SCHEMA_LIMIT_TO", + Self::FdwImportSchemaExcept => "FDW_IMPORT_SCHEMA_EXCEPT", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "IMPORT_FOREIGN_SCHEMA_TYPE_UNDEFINED" => Some(Self::Undefined), + "FDW_IMPORT_SCHEMA_ALL" => Some(Self::FdwImportSchemaAll), + "FDW_IMPORT_SCHEMA_LIMIT_TO" => Some(Self::FdwImportSchemaLimitTo), + "FDW_IMPORT_SCHEMA_EXCEPT" => Some(Self::FdwImportSchemaExcept), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum RoleStmtType { + Undefined = 0, + RolestmtRole = 1, + RolestmtUser = 2, + RolestmtGroup = 3, +} +impl RoleStmtType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "ROLE_STMT_TYPE_UNDEFINED", + Self::RolestmtRole => "ROLESTMT_ROLE", + Self::RolestmtUser => "ROLESTMT_USER", + Self::RolestmtGroup => "ROLESTMT_GROUP", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "ROLE_STMT_TYPE_UNDEFINED" => Some(Self::Undefined), + "ROLESTMT_ROLE" => Some(Self::RolestmtRole), + "ROLESTMT_USER" => Some(Self::RolestmtUser), + "ROLESTMT_GROUP" => Some(Self::RolestmtGroup), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum FetchDirection { + Undefined = 0, + FetchForward = 1, + FetchBackward = 2, + FetchAbsolute = 3, + FetchRelative = 4, +} +impl FetchDirection { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "FETCH_DIRECTION_UNDEFINED", + Self::FetchForward => "FETCH_FORWARD", + Self::FetchBackward => "FETCH_BACKWARD", + Self::FetchAbsolute => "FETCH_ABSOLUTE", + Self::FetchRelative => "FETCH_RELATIVE", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "FETCH_DIRECTION_UNDEFINED" => Some(Self::Undefined), + "FETCH_FORWARD" => Some(Self::FetchForward), + "FETCH_BACKWARD" => Some(Self::FetchBackward), + "FETCH_ABSOLUTE" => Some(Self::FetchAbsolute), + "FETCH_RELATIVE" => Some(Self::FetchRelative), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum FunctionParameterMode { + Undefined = 0, + FuncParamIn = 1, + FuncParamOut = 2, + FuncParamInout = 3, + FuncParamVariadic = 4, + FuncParamTable = 5, + FuncParamDefault = 6, +} +impl FunctionParameterMode { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "FUNCTION_PARAMETER_MODE_UNDEFINED", + Self::FuncParamIn => "FUNC_PARAM_IN", + Self::FuncParamOut => "FUNC_PARAM_OUT", + Self::FuncParamInout => "FUNC_PARAM_INOUT", + Self::FuncParamVariadic => "FUNC_PARAM_VARIADIC", + Self::FuncParamTable => "FUNC_PARAM_TABLE", + Self::FuncParamDefault => "FUNC_PARAM_DEFAULT", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "FUNCTION_PARAMETER_MODE_UNDEFINED" => Some(Self::Undefined), + "FUNC_PARAM_IN" => Some(Self::FuncParamIn), + "FUNC_PARAM_OUT" => Some(Self::FuncParamOut), + "FUNC_PARAM_INOUT" => Some(Self::FuncParamInout), + "FUNC_PARAM_VARIADIC" => Some(Self::FuncParamVariadic), + "FUNC_PARAM_TABLE" => Some(Self::FuncParamTable), + "FUNC_PARAM_DEFAULT" => Some(Self::FuncParamDefault), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum TransactionStmtKind { + Undefined = 0, + TransStmtBegin = 1, + TransStmtStart = 2, + TransStmtCommit = 3, + TransStmtRollback = 4, + TransStmtSavepoint = 5, + TransStmtRelease = 6, + TransStmtRollbackTo = 7, + TransStmtPrepare = 8, + TransStmtCommitPrepared = 9, + TransStmtRollbackPrepared = 10, +} +impl TransactionStmtKind { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "TRANSACTION_STMT_KIND_UNDEFINED", + Self::TransStmtBegin => "TRANS_STMT_BEGIN", + Self::TransStmtStart => "TRANS_STMT_START", + Self::TransStmtCommit => "TRANS_STMT_COMMIT", + Self::TransStmtRollback => "TRANS_STMT_ROLLBACK", + Self::TransStmtSavepoint => "TRANS_STMT_SAVEPOINT", + Self::TransStmtRelease => "TRANS_STMT_RELEASE", + Self::TransStmtRollbackTo => "TRANS_STMT_ROLLBACK_TO", + Self::TransStmtPrepare => "TRANS_STMT_PREPARE", + Self::TransStmtCommitPrepared => "TRANS_STMT_COMMIT_PREPARED", + Self::TransStmtRollbackPrepared => "TRANS_STMT_ROLLBACK_PREPARED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "TRANSACTION_STMT_KIND_UNDEFINED" => Some(Self::Undefined), + "TRANS_STMT_BEGIN" => Some(Self::TransStmtBegin), + "TRANS_STMT_START" => Some(Self::TransStmtStart), + "TRANS_STMT_COMMIT" => Some(Self::TransStmtCommit), + "TRANS_STMT_ROLLBACK" => Some(Self::TransStmtRollback), + "TRANS_STMT_SAVEPOINT" => Some(Self::TransStmtSavepoint), + "TRANS_STMT_RELEASE" => Some(Self::TransStmtRelease), + "TRANS_STMT_ROLLBACK_TO" => Some(Self::TransStmtRollbackTo), + "TRANS_STMT_PREPARE" => Some(Self::TransStmtPrepare), + "TRANS_STMT_COMMIT_PREPARED" => Some(Self::TransStmtCommitPrepared), + "TRANS_STMT_ROLLBACK_PREPARED" => Some(Self::TransStmtRollbackPrepared), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ViewCheckOption { + Undefined = 0, + NoCheckOption = 1, + LocalCheckOption = 2, + CascadedCheckOption = 3, +} +impl ViewCheckOption { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "VIEW_CHECK_OPTION_UNDEFINED", + Self::NoCheckOption => "NO_CHECK_OPTION", + Self::LocalCheckOption => "LOCAL_CHECK_OPTION", + Self::CascadedCheckOption => "CASCADED_CHECK_OPTION", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "VIEW_CHECK_OPTION_UNDEFINED" => Some(Self::Undefined), + "NO_CHECK_OPTION" => Some(Self::NoCheckOption), + "LOCAL_CHECK_OPTION" => Some(Self::LocalCheckOption), + "CASCADED_CHECK_OPTION" => Some(Self::CascadedCheckOption), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum DiscardMode { + Undefined = 0, + DiscardAll = 1, + DiscardPlans = 2, + DiscardSequences = 3, + DiscardTemp = 4, +} +impl DiscardMode { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "DISCARD_MODE_UNDEFINED", + Self::DiscardAll => "DISCARD_ALL", + Self::DiscardPlans => "DISCARD_PLANS", + Self::DiscardSequences => "DISCARD_SEQUENCES", + Self::DiscardTemp => "DISCARD_TEMP", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "DISCARD_MODE_UNDEFINED" => Some(Self::Undefined), + "DISCARD_ALL" => Some(Self::DiscardAll), + "DISCARD_PLANS" => Some(Self::DiscardPlans), + "DISCARD_SEQUENCES" => Some(Self::DiscardSequences), + "DISCARD_TEMP" => Some(Self::DiscardTemp), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ReindexObjectType { + Undefined = 0, + ReindexObjectIndex = 1, + ReindexObjectTable = 2, + ReindexObjectSchema = 3, + ReindexObjectSystem = 4, + ReindexObjectDatabase = 5, +} +impl ReindexObjectType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "REINDEX_OBJECT_TYPE_UNDEFINED", + Self::ReindexObjectIndex => "REINDEX_OBJECT_INDEX", + Self::ReindexObjectTable => "REINDEX_OBJECT_TABLE", + Self::ReindexObjectSchema => "REINDEX_OBJECT_SCHEMA", + Self::ReindexObjectSystem => "REINDEX_OBJECT_SYSTEM", + Self::ReindexObjectDatabase => "REINDEX_OBJECT_DATABASE", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "REINDEX_OBJECT_TYPE_UNDEFINED" => Some(Self::Undefined), + "REINDEX_OBJECT_INDEX" => Some(Self::ReindexObjectIndex), + "REINDEX_OBJECT_TABLE" => Some(Self::ReindexObjectTable), + "REINDEX_OBJECT_SCHEMA" => Some(Self::ReindexObjectSchema), + "REINDEX_OBJECT_SYSTEM" => Some(Self::ReindexObjectSystem), + "REINDEX_OBJECT_DATABASE" => Some(Self::ReindexObjectDatabase), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum AlterTsConfigType { + AlterTsconfigTypeUndefined = 0, + AlterTsconfigAddMapping = 1, + AlterTsconfigAlterMappingForToken = 2, + AlterTsconfigReplaceDict = 3, + AlterTsconfigReplaceDictForToken = 4, + AlterTsconfigDropMapping = 5, +} +impl AlterTsConfigType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::AlterTsconfigTypeUndefined => "ALTER_TSCONFIG_TYPE_UNDEFINED", + Self::AlterTsconfigAddMapping => "ALTER_TSCONFIG_ADD_MAPPING", + Self::AlterTsconfigAlterMappingForToken => { + "ALTER_TSCONFIG_ALTER_MAPPING_FOR_TOKEN" + } + Self::AlterTsconfigReplaceDict => "ALTER_TSCONFIG_REPLACE_DICT", + Self::AlterTsconfigReplaceDictForToken => { + "ALTER_TSCONFIG_REPLACE_DICT_FOR_TOKEN" + } + Self::AlterTsconfigDropMapping => "ALTER_TSCONFIG_DROP_MAPPING", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "ALTER_TSCONFIG_TYPE_UNDEFINED" => Some(Self::AlterTsconfigTypeUndefined), + "ALTER_TSCONFIG_ADD_MAPPING" => Some(Self::AlterTsconfigAddMapping), + "ALTER_TSCONFIG_ALTER_MAPPING_FOR_TOKEN" => { + Some(Self::AlterTsconfigAlterMappingForToken) + } + "ALTER_TSCONFIG_REPLACE_DICT" => Some(Self::AlterTsconfigReplaceDict), + "ALTER_TSCONFIG_REPLACE_DICT_FOR_TOKEN" => { + Some(Self::AlterTsconfigReplaceDictForToken) + } + "ALTER_TSCONFIG_DROP_MAPPING" => Some(Self::AlterTsconfigDropMapping), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum PublicationObjSpecType { + Undefined = 0, + PublicationobjTable = 1, + PublicationobjTablesInSchema = 2, + PublicationobjTablesInCurSchema = 3, + PublicationobjContinuation = 4, +} +impl PublicationObjSpecType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "PUBLICATION_OBJ_SPEC_TYPE_UNDEFINED", + Self::PublicationobjTable => "PUBLICATIONOBJ_TABLE", + Self::PublicationobjTablesInSchema => "PUBLICATIONOBJ_TABLES_IN_SCHEMA", + Self::PublicationobjTablesInCurSchema => { + "PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA" + } + Self::PublicationobjContinuation => "PUBLICATIONOBJ_CONTINUATION", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "PUBLICATION_OBJ_SPEC_TYPE_UNDEFINED" => Some(Self::Undefined), + "PUBLICATIONOBJ_TABLE" => Some(Self::PublicationobjTable), + "PUBLICATIONOBJ_TABLES_IN_SCHEMA" => Some(Self::PublicationobjTablesInSchema), + "PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA" => { + Some(Self::PublicationobjTablesInCurSchema) + } + "PUBLICATIONOBJ_CONTINUATION" => Some(Self::PublicationobjContinuation), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum AlterPublicationAction { + Undefined = 0, + ApAddObjects = 1, + ApDropObjects = 2, + ApSetObjects = 3, +} +impl AlterPublicationAction { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "ALTER_PUBLICATION_ACTION_UNDEFINED", + Self::ApAddObjects => "AP_AddObjects", + Self::ApDropObjects => "AP_DropObjects", + Self::ApSetObjects => "AP_SetObjects", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "ALTER_PUBLICATION_ACTION_UNDEFINED" => Some(Self::Undefined), + "AP_AddObjects" => Some(Self::ApAddObjects), + "AP_DropObjects" => Some(Self::ApDropObjects), + "AP_SetObjects" => Some(Self::ApSetObjects), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum AlterSubscriptionType { + Undefined = 0, + AlterSubscriptionOptions = 1, + AlterSubscriptionConnection = 2, + AlterSubscriptionSetPublication = 3, + AlterSubscriptionAddPublication = 4, + AlterSubscriptionDropPublication = 5, + AlterSubscriptionRefresh = 6, + AlterSubscriptionEnabled = 7, + AlterSubscriptionSkip = 8, +} +impl AlterSubscriptionType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "ALTER_SUBSCRIPTION_TYPE_UNDEFINED", + Self::AlterSubscriptionOptions => "ALTER_SUBSCRIPTION_OPTIONS", + Self::AlterSubscriptionConnection => "ALTER_SUBSCRIPTION_CONNECTION", + Self::AlterSubscriptionSetPublication => "ALTER_SUBSCRIPTION_SET_PUBLICATION", + Self::AlterSubscriptionAddPublication => "ALTER_SUBSCRIPTION_ADD_PUBLICATION", + Self::AlterSubscriptionDropPublication => { + "ALTER_SUBSCRIPTION_DROP_PUBLICATION" + } + Self::AlterSubscriptionRefresh => "ALTER_SUBSCRIPTION_REFRESH", + Self::AlterSubscriptionEnabled => "ALTER_SUBSCRIPTION_ENABLED", + Self::AlterSubscriptionSkip => "ALTER_SUBSCRIPTION_SKIP", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "ALTER_SUBSCRIPTION_TYPE_UNDEFINED" => Some(Self::Undefined), + "ALTER_SUBSCRIPTION_OPTIONS" => Some(Self::AlterSubscriptionOptions), + "ALTER_SUBSCRIPTION_CONNECTION" => Some(Self::AlterSubscriptionConnection), + "ALTER_SUBSCRIPTION_SET_PUBLICATION" => { + Some(Self::AlterSubscriptionSetPublication) + } + "ALTER_SUBSCRIPTION_ADD_PUBLICATION" => { + Some(Self::AlterSubscriptionAddPublication) + } + "ALTER_SUBSCRIPTION_DROP_PUBLICATION" => { + Some(Self::AlterSubscriptionDropPublication) + } + "ALTER_SUBSCRIPTION_REFRESH" => Some(Self::AlterSubscriptionRefresh), + "ALTER_SUBSCRIPTION_ENABLED" => Some(Self::AlterSubscriptionEnabled), + "ALTER_SUBSCRIPTION_SKIP" => Some(Self::AlterSubscriptionSkip), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum OverridingKind { + Undefined = 0, + OverridingNotSet = 1, + OverridingUserValue = 2, + OverridingSystemValue = 3, +} +impl OverridingKind { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "OVERRIDING_KIND_UNDEFINED", + Self::OverridingNotSet => "OVERRIDING_NOT_SET", + Self::OverridingUserValue => "OVERRIDING_USER_VALUE", + Self::OverridingSystemValue => "OVERRIDING_SYSTEM_VALUE", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "OVERRIDING_KIND_UNDEFINED" => Some(Self::Undefined), + "OVERRIDING_NOT_SET" => Some(Self::OverridingNotSet), + "OVERRIDING_USER_VALUE" => Some(Self::OverridingUserValue), + "OVERRIDING_SYSTEM_VALUE" => Some(Self::OverridingSystemValue), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum OnCommitAction { + Undefined = 0, + OncommitNoop = 1, + OncommitPreserveRows = 2, + OncommitDeleteRows = 3, + OncommitDrop = 4, +} +impl OnCommitAction { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "ON_COMMIT_ACTION_UNDEFINED", + Self::OncommitNoop => "ONCOMMIT_NOOP", + Self::OncommitPreserveRows => "ONCOMMIT_PRESERVE_ROWS", + Self::OncommitDeleteRows => "ONCOMMIT_DELETE_ROWS", + Self::OncommitDrop => "ONCOMMIT_DROP", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "ON_COMMIT_ACTION_UNDEFINED" => Some(Self::Undefined), + "ONCOMMIT_NOOP" => Some(Self::OncommitNoop), + "ONCOMMIT_PRESERVE_ROWS" => Some(Self::OncommitPreserveRows), + "ONCOMMIT_DELETE_ROWS" => Some(Self::OncommitDeleteRows), + "ONCOMMIT_DROP" => Some(Self::OncommitDrop), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum TableFuncType { + Undefined = 0, + TftXmltable = 1, + TftJsonTable = 2, +} +impl TableFuncType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "TABLE_FUNC_TYPE_UNDEFINED", + Self::TftXmltable => "TFT_XMLTABLE", + Self::TftJsonTable => "TFT_JSON_TABLE", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "TABLE_FUNC_TYPE_UNDEFINED" => Some(Self::Undefined), + "TFT_XMLTABLE" => Some(Self::TftXmltable), + "TFT_JSON_TABLE" => Some(Self::TftJsonTable), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum ParamKind { + Undefined = 0, + ParamExtern = 1, + ParamExec = 2, + ParamSublink = 3, + ParamMultiexpr = 4, +} +impl ParamKind { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "PARAM_KIND_UNDEFINED", + Self::ParamExtern => "PARAM_EXTERN", + Self::ParamExec => "PARAM_EXEC", + Self::ParamSublink => "PARAM_SUBLINK", + Self::ParamMultiexpr => "PARAM_MULTIEXPR", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "PARAM_KIND_UNDEFINED" => Some(Self::Undefined), + "PARAM_EXTERN" => Some(Self::ParamExtern), + "PARAM_EXEC" => Some(Self::ParamExec), + "PARAM_SUBLINK" => Some(Self::ParamSublink), + "PARAM_MULTIEXPR" => Some(Self::ParamMultiexpr), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum CoercionContext { + Undefined = 0, + CoercionImplicit = 1, + CoercionAssignment = 2, + CoercionPlpgsql = 3, + CoercionExplicit = 4, +} +impl CoercionContext { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "COERCION_CONTEXT_UNDEFINED", + Self::CoercionImplicit => "COERCION_IMPLICIT", + Self::CoercionAssignment => "COERCION_ASSIGNMENT", + Self::CoercionPlpgsql => "COERCION_PLPGSQL", + Self::CoercionExplicit => "COERCION_EXPLICIT", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "COERCION_CONTEXT_UNDEFINED" => Some(Self::Undefined), + "COERCION_IMPLICIT" => Some(Self::CoercionImplicit), + "COERCION_ASSIGNMENT" => Some(Self::CoercionAssignment), + "COERCION_PLPGSQL" => Some(Self::CoercionPlpgsql), + "COERCION_EXPLICIT" => Some(Self::CoercionExplicit), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum CoercionForm { + Undefined = 0, + CoerceExplicitCall = 1, + CoerceExplicitCast = 2, + CoerceImplicitCast = 3, + CoerceSqlSyntax = 4, +} +impl CoercionForm { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "COERCION_FORM_UNDEFINED", + Self::CoerceExplicitCall => "COERCE_EXPLICIT_CALL", + Self::CoerceExplicitCast => "COERCE_EXPLICIT_CAST", + Self::CoerceImplicitCast => "COERCE_IMPLICIT_CAST", + Self::CoerceSqlSyntax => "COERCE_SQL_SYNTAX", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "COERCION_FORM_UNDEFINED" => Some(Self::Undefined), + "COERCE_EXPLICIT_CALL" => Some(Self::CoerceExplicitCall), + "COERCE_EXPLICIT_CAST" => Some(Self::CoerceExplicitCast), + "COERCE_IMPLICIT_CAST" => Some(Self::CoerceImplicitCast), + "COERCE_SQL_SYNTAX" => Some(Self::CoerceSqlSyntax), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum BoolExprType { + Undefined = 0, + AndExpr = 1, + OrExpr = 2, + NotExpr = 3, +} +impl BoolExprType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "BOOL_EXPR_TYPE_UNDEFINED", + Self::AndExpr => "AND_EXPR", + Self::OrExpr => "OR_EXPR", + Self::NotExpr => "NOT_EXPR", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "BOOL_EXPR_TYPE_UNDEFINED" => Some(Self::Undefined), + "AND_EXPR" => Some(Self::AndExpr), + "OR_EXPR" => Some(Self::OrExpr), + "NOT_EXPR" => Some(Self::NotExpr), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum SubLinkType { + Undefined = 0, + ExistsSublink = 1, + AllSublink = 2, + AnySublink = 3, + RowcompareSublink = 4, + ExprSublink = 5, + MultiexprSublink = 6, + ArraySublink = 7, + CteSublink = 8, +} +impl SubLinkType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "SUB_LINK_TYPE_UNDEFINED", + Self::ExistsSublink => "EXISTS_SUBLINK", + Self::AllSublink => "ALL_SUBLINK", + Self::AnySublink => "ANY_SUBLINK", + Self::RowcompareSublink => "ROWCOMPARE_SUBLINK", + Self::ExprSublink => "EXPR_SUBLINK", + Self::MultiexprSublink => "MULTIEXPR_SUBLINK", + Self::ArraySublink => "ARRAY_SUBLINK", + Self::CteSublink => "CTE_SUBLINK", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SUB_LINK_TYPE_UNDEFINED" => Some(Self::Undefined), + "EXISTS_SUBLINK" => Some(Self::ExistsSublink), + "ALL_SUBLINK" => Some(Self::AllSublink), + "ANY_SUBLINK" => Some(Self::AnySublink), + "ROWCOMPARE_SUBLINK" => Some(Self::RowcompareSublink), + "EXPR_SUBLINK" => Some(Self::ExprSublink), + "MULTIEXPR_SUBLINK" => Some(Self::MultiexprSublink), + "ARRAY_SUBLINK" => Some(Self::ArraySublink), + "CTE_SUBLINK" => Some(Self::CteSublink), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum RowCompareType { + Undefined = 0, + RowcompareLt = 1, + RowcompareLe = 2, + RowcompareEq = 3, + RowcompareGe = 4, + RowcompareGt = 5, + RowcompareNe = 6, +} +impl RowCompareType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "ROW_COMPARE_TYPE_UNDEFINED", + Self::RowcompareLt => "ROWCOMPARE_LT", + Self::RowcompareLe => "ROWCOMPARE_LE", + Self::RowcompareEq => "ROWCOMPARE_EQ", + Self::RowcompareGe => "ROWCOMPARE_GE", + Self::RowcompareGt => "ROWCOMPARE_GT", + Self::RowcompareNe => "ROWCOMPARE_NE", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "ROW_COMPARE_TYPE_UNDEFINED" => Some(Self::Undefined), + "ROWCOMPARE_LT" => Some(Self::RowcompareLt), + "ROWCOMPARE_LE" => Some(Self::RowcompareLe), + "ROWCOMPARE_EQ" => Some(Self::RowcompareEq), + "ROWCOMPARE_GE" => Some(Self::RowcompareGe), + "ROWCOMPARE_GT" => Some(Self::RowcompareGt), + "ROWCOMPARE_NE" => Some(Self::RowcompareNe), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum MinMaxOp { + Undefined = 0, + IsGreatest = 1, + IsLeast = 2, +} +impl MinMaxOp { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "MIN_MAX_OP_UNDEFINED", + Self::IsGreatest => "IS_GREATEST", + Self::IsLeast => "IS_LEAST", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "MIN_MAX_OP_UNDEFINED" => Some(Self::Undefined), + "IS_GREATEST" => Some(Self::IsGreatest), + "IS_LEAST" => Some(Self::IsLeast), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum SqlValueFunctionOp { + SqlvalueFunctionOpUndefined = 0, + SvfopCurrentDate = 1, + SvfopCurrentTime = 2, + SvfopCurrentTimeN = 3, + SvfopCurrentTimestamp = 4, + SvfopCurrentTimestampN = 5, + SvfopLocaltime = 6, + SvfopLocaltimeN = 7, + SvfopLocaltimestamp = 8, + SvfopLocaltimestampN = 9, + SvfopCurrentRole = 10, + SvfopCurrentUser = 11, + SvfopUser = 12, + SvfopSessionUser = 13, + SvfopCurrentCatalog = 14, + SvfopCurrentSchema = 15, +} +impl SqlValueFunctionOp { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::SqlvalueFunctionOpUndefined => "SQLVALUE_FUNCTION_OP_UNDEFINED", + Self::SvfopCurrentDate => "SVFOP_CURRENT_DATE", + Self::SvfopCurrentTime => "SVFOP_CURRENT_TIME", + Self::SvfopCurrentTimeN => "SVFOP_CURRENT_TIME_N", + Self::SvfopCurrentTimestamp => "SVFOP_CURRENT_TIMESTAMP", + Self::SvfopCurrentTimestampN => "SVFOP_CURRENT_TIMESTAMP_N", + Self::SvfopLocaltime => "SVFOP_LOCALTIME", + Self::SvfopLocaltimeN => "SVFOP_LOCALTIME_N", + Self::SvfopLocaltimestamp => "SVFOP_LOCALTIMESTAMP", + Self::SvfopLocaltimestampN => "SVFOP_LOCALTIMESTAMP_N", + Self::SvfopCurrentRole => "SVFOP_CURRENT_ROLE", + Self::SvfopCurrentUser => "SVFOP_CURRENT_USER", + Self::SvfopUser => "SVFOP_USER", + Self::SvfopSessionUser => "SVFOP_SESSION_USER", + Self::SvfopCurrentCatalog => "SVFOP_CURRENT_CATALOG", + Self::SvfopCurrentSchema => "SVFOP_CURRENT_SCHEMA", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SQLVALUE_FUNCTION_OP_UNDEFINED" => Some(Self::SqlvalueFunctionOpUndefined), + "SVFOP_CURRENT_DATE" => Some(Self::SvfopCurrentDate), + "SVFOP_CURRENT_TIME" => Some(Self::SvfopCurrentTime), + "SVFOP_CURRENT_TIME_N" => Some(Self::SvfopCurrentTimeN), + "SVFOP_CURRENT_TIMESTAMP" => Some(Self::SvfopCurrentTimestamp), + "SVFOP_CURRENT_TIMESTAMP_N" => Some(Self::SvfopCurrentTimestampN), + "SVFOP_LOCALTIME" => Some(Self::SvfopLocaltime), + "SVFOP_LOCALTIME_N" => Some(Self::SvfopLocaltimeN), + "SVFOP_LOCALTIMESTAMP" => Some(Self::SvfopLocaltimestamp), + "SVFOP_LOCALTIMESTAMP_N" => Some(Self::SvfopLocaltimestampN), + "SVFOP_CURRENT_ROLE" => Some(Self::SvfopCurrentRole), + "SVFOP_CURRENT_USER" => Some(Self::SvfopCurrentUser), + "SVFOP_USER" => Some(Self::SvfopUser), + "SVFOP_SESSION_USER" => Some(Self::SvfopSessionUser), + "SVFOP_CURRENT_CATALOG" => Some(Self::SvfopCurrentCatalog), + "SVFOP_CURRENT_SCHEMA" => Some(Self::SvfopCurrentSchema), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum XmlExprOp { + Undefined = 0, + IsXmlconcat = 1, + IsXmlelement = 2, + IsXmlforest = 3, + IsXmlparse = 4, + IsXmlpi = 5, + IsXmlroot = 6, + IsXmlserialize = 7, + IsDocument = 8, +} +impl XmlExprOp { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "XML_EXPR_OP_UNDEFINED", + Self::IsXmlconcat => "IS_XMLCONCAT", + Self::IsXmlelement => "IS_XMLELEMENT", + Self::IsXmlforest => "IS_XMLFOREST", + Self::IsXmlparse => "IS_XMLPARSE", + Self::IsXmlpi => "IS_XMLPI", + Self::IsXmlroot => "IS_XMLROOT", + Self::IsXmlserialize => "IS_XMLSERIALIZE", + Self::IsDocument => "IS_DOCUMENT", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "XML_EXPR_OP_UNDEFINED" => Some(Self::Undefined), + "IS_XMLCONCAT" => Some(Self::IsXmlconcat), + "IS_XMLELEMENT" => Some(Self::IsXmlelement), + "IS_XMLFOREST" => Some(Self::IsXmlforest), + "IS_XMLPARSE" => Some(Self::IsXmlparse), + "IS_XMLPI" => Some(Self::IsXmlpi), + "IS_XMLROOT" => Some(Self::IsXmlroot), + "IS_XMLSERIALIZE" => Some(Self::IsXmlserialize), + "IS_DOCUMENT" => Some(Self::IsDocument), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum XmlOptionType { + Undefined = 0, + XmloptionDocument = 1, + XmloptionContent = 2, +} +impl XmlOptionType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "XML_OPTION_TYPE_UNDEFINED", + Self::XmloptionDocument => "XMLOPTION_DOCUMENT", + Self::XmloptionContent => "XMLOPTION_CONTENT", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "XML_OPTION_TYPE_UNDEFINED" => Some(Self::Undefined), + "XMLOPTION_DOCUMENT" => Some(Self::XmloptionDocument), + "XMLOPTION_CONTENT" => Some(Self::XmloptionContent), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum JsonEncoding { + Undefined = 0, + JsEncDefault = 1, + JsEncUtf8 = 2, + JsEncUtf16 = 3, + JsEncUtf32 = 4, +} +impl JsonEncoding { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "JSON_ENCODING_UNDEFINED", + Self::JsEncDefault => "JS_ENC_DEFAULT", + Self::JsEncUtf8 => "JS_ENC_UTF8", + Self::JsEncUtf16 => "JS_ENC_UTF16", + Self::JsEncUtf32 => "JS_ENC_UTF32", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "JSON_ENCODING_UNDEFINED" => Some(Self::Undefined), + "JS_ENC_DEFAULT" => Some(Self::JsEncDefault), + "JS_ENC_UTF8" => Some(Self::JsEncUtf8), + "JS_ENC_UTF16" => Some(Self::JsEncUtf16), + "JS_ENC_UTF32" => Some(Self::JsEncUtf32), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum JsonFormatType { + Undefined = 0, + JsFormatDefault = 1, + JsFormatJson = 2, + JsFormatJsonb = 3, +} +impl JsonFormatType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "JSON_FORMAT_TYPE_UNDEFINED", + Self::JsFormatDefault => "JS_FORMAT_DEFAULT", + Self::JsFormatJson => "JS_FORMAT_JSON", + Self::JsFormatJsonb => "JS_FORMAT_JSONB", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "JSON_FORMAT_TYPE_UNDEFINED" => Some(Self::Undefined), + "JS_FORMAT_DEFAULT" => Some(Self::JsFormatDefault), + "JS_FORMAT_JSON" => Some(Self::JsFormatJson), + "JS_FORMAT_JSONB" => Some(Self::JsFormatJsonb), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum JsonConstructorType { + Undefined = 0, + JsctorJsonObject = 1, + JsctorJsonArray = 2, + JsctorJsonObjectagg = 3, + JsctorJsonArrayagg = 4, + JsctorJsonParse = 5, + JsctorJsonScalar = 6, + JsctorJsonSerialize = 7, +} +impl JsonConstructorType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "JSON_CONSTRUCTOR_TYPE_UNDEFINED", + Self::JsctorJsonObject => "JSCTOR_JSON_OBJECT", + Self::JsctorJsonArray => "JSCTOR_JSON_ARRAY", + Self::JsctorJsonObjectagg => "JSCTOR_JSON_OBJECTAGG", + Self::JsctorJsonArrayagg => "JSCTOR_JSON_ARRAYAGG", + Self::JsctorJsonParse => "JSCTOR_JSON_PARSE", + Self::JsctorJsonScalar => "JSCTOR_JSON_SCALAR", + Self::JsctorJsonSerialize => "JSCTOR_JSON_SERIALIZE", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "JSON_CONSTRUCTOR_TYPE_UNDEFINED" => Some(Self::Undefined), + "JSCTOR_JSON_OBJECT" => Some(Self::JsctorJsonObject), + "JSCTOR_JSON_ARRAY" => Some(Self::JsctorJsonArray), + "JSCTOR_JSON_OBJECTAGG" => Some(Self::JsctorJsonObjectagg), + "JSCTOR_JSON_ARRAYAGG" => Some(Self::JsctorJsonArrayagg), + "JSCTOR_JSON_PARSE" => Some(Self::JsctorJsonParse), + "JSCTOR_JSON_SCALAR" => Some(Self::JsctorJsonScalar), + "JSCTOR_JSON_SERIALIZE" => Some(Self::JsctorJsonSerialize), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum JsonValueType { + Undefined = 0, + JsTypeAny = 1, + JsTypeObject = 2, + JsTypeArray = 3, + JsTypeScalar = 4, +} +impl JsonValueType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "JSON_VALUE_TYPE_UNDEFINED", + Self::JsTypeAny => "JS_TYPE_ANY", + Self::JsTypeObject => "JS_TYPE_OBJECT", + Self::JsTypeArray => "JS_TYPE_ARRAY", + Self::JsTypeScalar => "JS_TYPE_SCALAR", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "JSON_VALUE_TYPE_UNDEFINED" => Some(Self::Undefined), + "JS_TYPE_ANY" => Some(Self::JsTypeAny), + "JS_TYPE_OBJECT" => Some(Self::JsTypeObject), + "JS_TYPE_ARRAY" => Some(Self::JsTypeArray), + "JS_TYPE_SCALAR" => Some(Self::JsTypeScalar), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum JsonWrapper { + Undefined = 0, + JswUnspec = 1, + JswNone = 2, + JswConditional = 3, + JswUnconditional = 4, +} +impl JsonWrapper { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "JSON_WRAPPER_UNDEFINED", + Self::JswUnspec => "JSW_UNSPEC", + Self::JswNone => "JSW_NONE", + Self::JswConditional => "JSW_CONDITIONAL", + Self::JswUnconditional => "JSW_UNCONDITIONAL", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "JSON_WRAPPER_UNDEFINED" => Some(Self::Undefined), + "JSW_UNSPEC" => Some(Self::JswUnspec), + "JSW_NONE" => Some(Self::JswNone), + "JSW_CONDITIONAL" => Some(Self::JswConditional), + "JSW_UNCONDITIONAL" => Some(Self::JswUnconditional), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum JsonBehaviorType { + Undefined = 0, + JsonBehaviorNull = 1, + JsonBehaviorError = 2, + JsonBehaviorEmpty = 3, + JsonBehaviorTrue = 4, + JsonBehaviorFalse = 5, + JsonBehaviorUnknown = 6, + JsonBehaviorEmptyArray = 7, + JsonBehaviorEmptyObject = 8, + JsonBehaviorDefault = 9, +} +impl JsonBehaviorType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "JSON_BEHAVIOR_TYPE_UNDEFINED", + Self::JsonBehaviorNull => "JSON_BEHAVIOR_NULL", + Self::JsonBehaviorError => "JSON_BEHAVIOR_ERROR", + Self::JsonBehaviorEmpty => "JSON_BEHAVIOR_EMPTY", + Self::JsonBehaviorTrue => "JSON_BEHAVIOR_TRUE", + Self::JsonBehaviorFalse => "JSON_BEHAVIOR_FALSE", + Self::JsonBehaviorUnknown => "JSON_BEHAVIOR_UNKNOWN", + Self::JsonBehaviorEmptyArray => "JSON_BEHAVIOR_EMPTY_ARRAY", + Self::JsonBehaviorEmptyObject => "JSON_BEHAVIOR_EMPTY_OBJECT", + Self::JsonBehaviorDefault => "JSON_BEHAVIOR_DEFAULT", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "JSON_BEHAVIOR_TYPE_UNDEFINED" => Some(Self::Undefined), + "JSON_BEHAVIOR_NULL" => Some(Self::JsonBehaviorNull), + "JSON_BEHAVIOR_ERROR" => Some(Self::JsonBehaviorError), + "JSON_BEHAVIOR_EMPTY" => Some(Self::JsonBehaviorEmpty), + "JSON_BEHAVIOR_TRUE" => Some(Self::JsonBehaviorTrue), + "JSON_BEHAVIOR_FALSE" => Some(Self::JsonBehaviorFalse), + "JSON_BEHAVIOR_UNKNOWN" => Some(Self::JsonBehaviorUnknown), + "JSON_BEHAVIOR_EMPTY_ARRAY" => Some(Self::JsonBehaviorEmptyArray), + "JSON_BEHAVIOR_EMPTY_OBJECT" => Some(Self::JsonBehaviorEmptyObject), + "JSON_BEHAVIOR_DEFAULT" => Some(Self::JsonBehaviorDefault), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum JsonExprOp { + Undefined = 0, + JsonExistsOp = 1, + JsonQueryOp = 2, + JsonValueOp = 3, + JsonTableOp = 4, +} +impl JsonExprOp { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "JSON_EXPR_OP_UNDEFINED", + Self::JsonExistsOp => "JSON_EXISTS_OP", + Self::JsonQueryOp => "JSON_QUERY_OP", + Self::JsonValueOp => "JSON_VALUE_OP", + Self::JsonTableOp => "JSON_TABLE_OP", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "JSON_EXPR_OP_UNDEFINED" => Some(Self::Undefined), + "JSON_EXISTS_OP" => Some(Self::JsonExistsOp), + "JSON_QUERY_OP" => Some(Self::JsonQueryOp), + "JSON_VALUE_OP" => Some(Self::JsonValueOp), + "JSON_TABLE_OP" => Some(Self::JsonTableOp), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum NullTestType { + Undefined = 0, + IsNull = 1, + IsNotNull = 2, +} +impl NullTestType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "NULL_TEST_TYPE_UNDEFINED", + Self::IsNull => "IS_NULL", + Self::IsNotNull => "IS_NOT_NULL", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "NULL_TEST_TYPE_UNDEFINED" => Some(Self::Undefined), + "IS_NULL" => Some(Self::IsNull), + "IS_NOT_NULL" => Some(Self::IsNotNull), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum BoolTestType { + Undefined = 0, + IsTrue = 1, + IsNotTrue = 2, + IsFalse = 3, + IsNotFalse = 4, + IsUnknown = 5, + IsNotUnknown = 6, +} +impl BoolTestType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "BOOL_TEST_TYPE_UNDEFINED", + Self::IsTrue => "IS_TRUE", + Self::IsNotTrue => "IS_NOT_TRUE", + Self::IsFalse => "IS_FALSE", + Self::IsNotFalse => "IS_NOT_FALSE", + Self::IsUnknown => "IS_UNKNOWN", + Self::IsNotUnknown => "IS_NOT_UNKNOWN", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "BOOL_TEST_TYPE_UNDEFINED" => Some(Self::Undefined), + "IS_TRUE" => Some(Self::IsTrue), + "IS_NOT_TRUE" => Some(Self::IsNotTrue), + "IS_FALSE" => Some(Self::IsFalse), + "IS_NOT_FALSE" => Some(Self::IsNotFalse), + "IS_UNKNOWN" => Some(Self::IsUnknown), + "IS_NOT_UNKNOWN" => Some(Self::IsNotUnknown), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum MergeMatchKind { + Undefined = 0, + MergeWhenMatched = 1, + MergeWhenNotMatchedBySource = 2, + MergeWhenNotMatchedByTarget = 3, +} +impl MergeMatchKind { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "MERGE_MATCH_KIND_UNDEFINED", + Self::MergeWhenMatched => "MERGE_WHEN_MATCHED", + Self::MergeWhenNotMatchedBySource => "MERGE_WHEN_NOT_MATCHED_BY_SOURCE", + Self::MergeWhenNotMatchedByTarget => "MERGE_WHEN_NOT_MATCHED_BY_TARGET", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "MERGE_MATCH_KIND_UNDEFINED" => Some(Self::Undefined), + "MERGE_WHEN_MATCHED" => Some(Self::MergeWhenMatched), + "MERGE_WHEN_NOT_MATCHED_BY_SOURCE" => Some(Self::MergeWhenNotMatchedBySource), + "MERGE_WHEN_NOT_MATCHED_BY_TARGET" => Some(Self::MergeWhenNotMatchedByTarget), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum CmdType { + Undefined = 0, + CmdUnknown = 1, + CmdSelect = 2, + CmdUpdate = 3, + CmdInsert = 4, + CmdDelete = 5, + CmdMerge = 6, + CmdUtility = 7, + CmdNothing = 8, +} +impl CmdType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "CMD_TYPE_UNDEFINED", + Self::CmdUnknown => "CMD_UNKNOWN", + Self::CmdSelect => "CMD_SELECT", + Self::CmdUpdate => "CMD_UPDATE", + Self::CmdInsert => "CMD_INSERT", + Self::CmdDelete => "CMD_DELETE", + Self::CmdMerge => "CMD_MERGE", + Self::CmdUtility => "CMD_UTILITY", + Self::CmdNothing => "CMD_NOTHING", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "CMD_TYPE_UNDEFINED" => Some(Self::Undefined), + "CMD_UNKNOWN" => Some(Self::CmdUnknown), + "CMD_SELECT" => Some(Self::CmdSelect), + "CMD_UPDATE" => Some(Self::CmdUpdate), + "CMD_INSERT" => Some(Self::CmdInsert), + "CMD_DELETE" => Some(Self::CmdDelete), + "CMD_MERGE" => Some(Self::CmdMerge), + "CMD_UTILITY" => Some(Self::CmdUtility), + "CMD_NOTHING" => Some(Self::CmdNothing), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum JoinType { + Undefined = 0, + JoinInner = 1, + JoinLeft = 2, + JoinFull = 3, + JoinRight = 4, + JoinSemi = 5, + JoinAnti = 6, + JoinRightAnti = 7, + JoinUniqueOuter = 8, + JoinUniqueInner = 9, +} +impl JoinType { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "JOIN_TYPE_UNDEFINED", + Self::JoinInner => "JOIN_INNER", + Self::JoinLeft => "JOIN_LEFT", + Self::JoinFull => "JOIN_FULL", + Self::JoinRight => "JOIN_RIGHT", + Self::JoinSemi => "JOIN_SEMI", + Self::JoinAnti => "JOIN_ANTI", + Self::JoinRightAnti => "JOIN_RIGHT_ANTI", + Self::JoinUniqueOuter => "JOIN_UNIQUE_OUTER", + Self::JoinUniqueInner => "JOIN_UNIQUE_INNER", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "JOIN_TYPE_UNDEFINED" => Some(Self::Undefined), + "JOIN_INNER" => Some(Self::JoinInner), + "JOIN_LEFT" => Some(Self::JoinLeft), + "JOIN_FULL" => Some(Self::JoinFull), + "JOIN_RIGHT" => Some(Self::JoinRight), + "JOIN_SEMI" => Some(Self::JoinSemi), + "JOIN_ANTI" => Some(Self::JoinAnti), + "JOIN_RIGHT_ANTI" => Some(Self::JoinRightAnti), + "JOIN_UNIQUE_OUTER" => Some(Self::JoinUniqueOuter), + "JOIN_UNIQUE_INNER" => Some(Self::JoinUniqueInner), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum AggStrategy { + Undefined = 0, + AggPlain = 1, + AggSorted = 2, + AggHashed = 3, + AggMixed = 4, +} +impl AggStrategy { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "AGG_STRATEGY_UNDEFINED", + Self::AggPlain => "AGG_PLAIN", + Self::AggSorted => "AGG_SORTED", + Self::AggHashed => "AGG_HASHED", + Self::AggMixed => "AGG_MIXED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "AGG_STRATEGY_UNDEFINED" => Some(Self::Undefined), + "AGG_PLAIN" => Some(Self::AggPlain), + "AGG_SORTED" => Some(Self::AggSorted), + "AGG_HASHED" => Some(Self::AggHashed), + "AGG_MIXED" => Some(Self::AggMixed), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum AggSplit { + Undefined = 0, + AggsplitSimple = 1, + AggsplitInitialSerial = 2, + AggsplitFinalDeserial = 3, +} +impl AggSplit { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "AGG_SPLIT_UNDEFINED", + Self::AggsplitSimple => "AGGSPLIT_SIMPLE", + Self::AggsplitInitialSerial => "AGGSPLIT_INITIAL_SERIAL", + Self::AggsplitFinalDeserial => "AGGSPLIT_FINAL_DESERIAL", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "AGG_SPLIT_UNDEFINED" => Some(Self::Undefined), + "AGGSPLIT_SIMPLE" => Some(Self::AggsplitSimple), + "AGGSPLIT_INITIAL_SERIAL" => Some(Self::AggsplitInitialSerial), + "AGGSPLIT_FINAL_DESERIAL" => Some(Self::AggsplitFinalDeserial), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum SetOpCmd { + Undefined = 0, + SetopcmdIntersect = 1, + SetopcmdIntersectAll = 2, + SetopcmdExcept = 3, + SetopcmdExceptAll = 4, +} +impl SetOpCmd { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "SET_OP_CMD_UNDEFINED", + Self::SetopcmdIntersect => "SETOPCMD_INTERSECT", + Self::SetopcmdIntersectAll => "SETOPCMD_INTERSECT_ALL", + Self::SetopcmdExcept => "SETOPCMD_EXCEPT", + Self::SetopcmdExceptAll => "SETOPCMD_EXCEPT_ALL", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SET_OP_CMD_UNDEFINED" => Some(Self::Undefined), + "SETOPCMD_INTERSECT" => Some(Self::SetopcmdIntersect), + "SETOPCMD_INTERSECT_ALL" => Some(Self::SetopcmdIntersectAll), + "SETOPCMD_EXCEPT" => Some(Self::SetopcmdExcept), + "SETOPCMD_EXCEPT_ALL" => Some(Self::SetopcmdExceptAll), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum SetOpStrategy { + Undefined = 0, + SetopSorted = 1, + SetopHashed = 2, +} +impl SetOpStrategy { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "SET_OP_STRATEGY_UNDEFINED", + Self::SetopSorted => "SETOP_SORTED", + Self::SetopHashed => "SETOP_HASHED", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "SET_OP_STRATEGY_UNDEFINED" => Some(Self::Undefined), + "SETOP_SORTED" => Some(Self::SetopSorted), + "SETOP_HASHED" => Some(Self::SetopHashed), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum OnConflictAction { + Undefined = 0, + OnconflictNone = 1, + OnconflictNothing = 2, + OnconflictUpdate = 3, +} +impl OnConflictAction { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "ON_CONFLICT_ACTION_UNDEFINED", + Self::OnconflictNone => "ONCONFLICT_NONE", + Self::OnconflictNothing => "ONCONFLICT_NOTHING", + Self::OnconflictUpdate => "ONCONFLICT_UPDATE", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "ON_CONFLICT_ACTION_UNDEFINED" => Some(Self::Undefined), + "ONCONFLICT_NONE" => Some(Self::OnconflictNone), + "ONCONFLICT_NOTHING" => Some(Self::OnconflictNothing), + "ONCONFLICT_UPDATE" => Some(Self::OnconflictUpdate), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum LimitOption { + Undefined = 0, + Default = 1, + Count = 2, + WithTies = 3, +} +impl LimitOption { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "LIMIT_OPTION_UNDEFINED", + Self::Default => "LIMIT_OPTION_DEFAULT", + Self::Count => "LIMIT_OPTION_COUNT", + Self::WithTies => "LIMIT_OPTION_WITH_TIES", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "LIMIT_OPTION_UNDEFINED" => Some(Self::Undefined), + "LIMIT_OPTION_DEFAULT" => Some(Self::Default), + "LIMIT_OPTION_COUNT" => Some(Self::Count), + "LIMIT_OPTION_WITH_TIES" => Some(Self::WithTies), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum LockClauseStrength { + Undefined = 0, + LcsNone = 1, + LcsForkeyshare = 2, + LcsForshare = 3, + LcsFornokeyupdate = 4, + LcsForupdate = 5, +} +impl LockClauseStrength { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "LOCK_CLAUSE_STRENGTH_UNDEFINED", + Self::LcsNone => "LCS_NONE", + Self::LcsForkeyshare => "LCS_FORKEYSHARE", + Self::LcsForshare => "LCS_FORSHARE", + Self::LcsFornokeyupdate => "LCS_FORNOKEYUPDATE", + Self::LcsForupdate => "LCS_FORUPDATE", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "LOCK_CLAUSE_STRENGTH_UNDEFINED" => Some(Self::Undefined), + "LCS_NONE" => Some(Self::LcsNone), + "LCS_FORKEYSHARE" => Some(Self::LcsForkeyshare), + "LCS_FORSHARE" => Some(Self::LcsForshare), + "LCS_FORNOKEYUPDATE" => Some(Self::LcsFornokeyupdate), + "LCS_FORUPDATE" => Some(Self::LcsForupdate), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum LockWaitPolicy { + Undefined = 0, + LockWaitBlock = 1, + LockWaitSkip = 2, + LockWaitError = 3, +} +impl LockWaitPolicy { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "LOCK_WAIT_POLICY_UNDEFINED", + Self::LockWaitBlock => "LockWaitBlock", + Self::LockWaitSkip => "LockWaitSkip", + Self::LockWaitError => "LockWaitError", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "LOCK_WAIT_POLICY_UNDEFINED" => Some(Self::Undefined), + "LockWaitBlock" => Some(Self::LockWaitBlock), + "LockWaitSkip" => Some(Self::LockWaitSkip), + "LockWaitError" => Some(Self::LockWaitError), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum LockTupleMode { + Undefined = 0, + LockTupleKeyShare = 1, + LockTupleShare = 2, + LockTupleNoKeyExclusive = 3, + LockTupleExclusive = 4, +} +impl LockTupleMode { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Undefined => "LOCK_TUPLE_MODE_UNDEFINED", + Self::LockTupleKeyShare => "LockTupleKeyShare", + Self::LockTupleShare => "LockTupleShare", + Self::LockTupleNoKeyExclusive => "LockTupleNoKeyExclusive", + Self::LockTupleExclusive => "LockTupleExclusive", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "LOCK_TUPLE_MODE_UNDEFINED" => Some(Self::Undefined), + "LockTupleKeyShare" => Some(Self::LockTupleKeyShare), + "LockTupleShare" => Some(Self::LockTupleShare), + "LockTupleNoKeyExclusive" => Some(Self::LockTupleNoKeyExclusive), + "LockTupleExclusive" => Some(Self::LockTupleExclusive), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum KeywordKind { + NoKeyword = 0, + UnreservedKeyword = 1, + ColNameKeyword = 2, + TypeFuncNameKeyword = 3, + ReservedKeyword = 4, +} +impl KeywordKind { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::NoKeyword => "NO_KEYWORD", + Self::UnreservedKeyword => "UNRESERVED_KEYWORD", + Self::ColNameKeyword => "COL_NAME_KEYWORD", + Self::TypeFuncNameKeyword => "TYPE_FUNC_NAME_KEYWORD", + Self::ReservedKeyword => "RESERVED_KEYWORD", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "NO_KEYWORD" => Some(Self::NoKeyword), + "UNRESERVED_KEYWORD" => Some(Self::UnreservedKeyword), + "COL_NAME_KEYWORD" => Some(Self::ColNameKeyword), + "TYPE_FUNC_NAME_KEYWORD" => Some(Self::TypeFuncNameKeyword), + "RESERVED_KEYWORD" => Some(Self::ReservedKeyword), + _ => None, + } + } +} +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] +#[repr(i32)] +pub enum Token { + Nul = 0, + /// Single-character tokens that are returned 1:1 (identical with "self" list in scan.l) + /// Either supporting syntax, or single-character operators (some can be both) + /// Also see + /// + /// "$" + Ascii36 = 36, + /// "%" + Ascii37 = 37, + /// "(" + Ascii40 = 40, + /// ")" + Ascii41 = 41, + /// "*" + Ascii42 = 42, + /// "+" + Ascii43 = 43, + /// "," + Ascii44 = 44, + /// "-" + Ascii45 = 45, + /// "." + Ascii46 = 46, + /// "/" + Ascii47 = 47, + /// ":" + Ascii58 = 58, + /// ";" + Ascii59 = 59, + /// "<" + Ascii60 = 60, + /// "=" + Ascii61 = 61, + /// ">" + Ascii62 = 62, + /// "?" + Ascii63 = 63, + /// "[" + Ascii91 = 91, + /// "\" + Ascii92 = 92, + /// "]" + Ascii93 = 93, + /// "^" + Ascii94 = 94, + /// Named tokens in scan.l + Ident = 258, + Uident = 259, + Fconst = 260, + Sconst = 261, + Usconst = 262, + Bconst = 263, + Xconst = 264, + Op = 265, + Iconst = 266, + Param = 267, + Typecast = 268, + DotDot = 269, + ColonEquals = 270, + EqualsGreater = 271, + LessEquals = 272, + GreaterEquals = 273, + NotEquals = 274, + SqlComment = 275, + CComment = 276, + AbortP = 277, + Absent = 278, + AbsoluteP = 279, + Access = 280, + Action = 281, + AddP = 282, + Admin = 283, + After = 284, + Aggregate = 285, + All = 286, + Also = 287, + Alter = 288, + Always = 289, + Analyse = 290, + Analyze = 291, + And = 292, + Any = 293, + Array = 294, + As = 295, + Asc = 296, + Asensitive = 297, + Assertion = 298, + Assignment = 299, + Asymmetric = 300, + Atomic = 301, + At = 302, + Attach = 303, + Attribute = 304, + Authorization = 305, + Backward = 306, + Before = 307, + BeginP = 308, + Between = 309, + Bigint = 310, + Binary = 311, + Bit = 312, + BooleanP = 313, + Both = 314, + Breadth = 315, + By = 316, + Cache = 317, + Call = 318, + Called = 319, + Cascade = 320, + Cascaded = 321, + Case = 322, + Cast = 323, + CatalogP = 324, + Chain = 325, + CharP = 326, + Character = 327, + Characteristics = 328, + Check = 329, + Checkpoint = 330, + Class = 331, + Close = 332, + Cluster = 333, + Coalesce = 334, + Collate = 335, + Collation = 336, + Column = 337, + Columns = 338, + Comment = 339, + Comments = 340, + Commit = 341, + Committed = 342, + Compression = 343, + Concurrently = 344, + Conditional = 345, + Configuration = 346, + Conflict = 347, + Connection = 348, + Constraint = 349, + Constraints = 350, + ContentP = 351, + ContinueP = 352, + ConversionP = 353, + Copy = 354, + Cost = 355, + Create = 356, + Cross = 357, + Csv = 358, + Cube = 359, + CurrentP = 360, + CurrentCatalog = 361, + CurrentDate = 362, + CurrentRole = 363, + CurrentSchema = 364, + CurrentTime = 365, + CurrentTimestamp = 366, + CurrentUser = 367, + Cursor = 368, + Cycle = 369, + DataP = 370, + Database = 371, + DayP = 372, + Deallocate = 373, + Dec = 374, + DecimalP = 375, + Declare = 376, + Default = 377, + Defaults = 378, + Deferrable = 379, + Deferred = 380, + Definer = 381, + DeleteP = 382, + Delimiter = 383, + Delimiters = 384, + Depends = 385, + Depth = 386, + Desc = 387, + Detach = 388, + Dictionary = 389, + DisableP = 390, + Discard = 391, + Distinct = 392, + Do = 393, + DocumentP = 394, + DomainP = 395, + DoubleP = 396, + Drop = 397, + Each = 398, + Else = 399, + EmptyP = 400, + EnableP = 401, + Encoding = 402, + Encrypted = 403, + EndP = 404, + EnumP = 405, + ErrorP = 406, + Escape = 407, + Event = 408, + Except = 409, + Exclude = 410, + Excluding = 411, + Exclusive = 412, + Execute = 413, + Exists = 414, + Explain = 415, + Expression = 416, + Extension = 417, + External = 418, + Extract = 419, + FalseP = 420, + Family = 421, + Fetch = 422, + Filter = 423, + Finalize = 424, + FirstP = 425, + FloatP = 426, + Following = 427, + For = 428, + Force = 429, + Foreign = 430, + Format = 431, + Forward = 432, + Freeze = 433, + From = 434, + Full = 435, + Function = 436, + Functions = 437, + Generated = 438, + Global = 439, + Grant = 440, + Granted = 441, + Greatest = 442, + GroupP = 443, + Grouping = 444, + Groups = 445, + Handler = 446, + Having = 447, + HeaderP = 448, + Hold = 449, + HourP = 450, + IdentityP = 451, + IfP = 452, + Ilike = 453, + Immediate = 454, + Immutable = 455, + ImplicitP = 456, + ImportP = 457, + InP = 458, + Include = 459, + Including = 460, + Increment = 461, + Indent = 462, + Index = 463, + Indexes = 464, + Inherit = 465, + Inherits = 466, + Initially = 467, + InlineP = 468, + InnerP = 469, + Inout = 470, + InputP = 471, + Insensitive = 472, + Insert = 473, + Instead = 474, + IntP = 475, + Integer = 476, + Intersect = 477, + Interval = 478, + Into = 479, + Invoker = 480, + Is = 481, + Isnull = 482, + Isolation = 483, + Join = 484, + Json = 485, + JsonArray = 486, + JsonArrayagg = 487, + JsonExists = 488, + JsonObject = 489, + JsonObjectagg = 490, + JsonQuery = 491, + JsonScalar = 492, + JsonSerialize = 493, + JsonTable = 494, + JsonValue = 495, + Keep = 496, + Key = 497, + Keys = 498, + Label = 499, + Language = 500, + LargeP = 501, + LastP = 502, + LateralP = 503, + Leading = 504, + Leakproof = 505, + Least = 506, + Left = 507, + Level = 508, + Like = 509, + Limit = 510, + Listen = 511, + Load = 512, + Local = 513, + Localtime = 514, + Localtimestamp = 515, + Location = 516, + LockP = 517, + Locked = 518, + Logged = 519, + Mapping = 520, + Match = 521, + Matched = 522, + Materialized = 523, + Maxvalue = 524, + Merge = 525, + MergeAction = 526, + Method = 527, + MinuteP = 528, + Minvalue = 529, + Mode = 530, + MonthP = 531, + Move = 532, + NameP = 533, + Names = 534, + National = 535, + Natural = 536, + Nchar = 537, + Nested = 538, + New = 539, + Next = 540, + Nfc = 541, + Nfd = 542, + Nfkc = 543, + Nfkd = 544, + No = 545, + None = 546, + Normalize = 547, + Normalized = 548, + Not = 549, + Nothing = 550, + Notify = 551, + Notnull = 552, + Nowait = 553, + NullP = 554, + Nullif = 555, + NullsP = 556, + Numeric = 557, + ObjectP = 558, + Of = 559, + Off = 560, + Offset = 561, + Oids = 562, + Old = 563, + Omit = 564, + On = 565, + Only = 566, + Operator = 567, + Option = 568, + Options = 569, + Or = 570, + Order = 571, + Ordinality = 572, + Others = 573, + OutP = 574, + OuterP = 575, + Over = 576, + Overlaps = 577, + Overlay = 578, + Overriding = 579, + Owned = 580, + Owner = 581, + Parallel = 582, + Parameter = 583, + Parser = 584, + Partial = 585, + Partition = 586, + Passing = 587, + Password = 588, + Path = 589, + Placing = 590, + Plan = 591, + Plans = 592, + Policy = 593, + Position = 594, + Preceding = 595, + Precision = 596, + Preserve = 597, + Prepare = 598, + Prepared = 599, + Primary = 600, + Prior = 601, + Privileges = 602, + Procedural = 603, + Procedure = 604, + Procedures = 605, + Program = 606, + Publication = 607, + Quote = 608, + Quotes = 609, + Range = 610, + Read = 611, + Real = 612, + Reassign = 613, + Recheck = 614, + Recursive = 615, + RefP = 616, + References = 617, + Referencing = 618, + Refresh = 619, + Reindex = 620, + RelativeP = 621, + Release = 622, + Rename = 623, + Repeatable = 624, + Replace = 625, + Replica = 626, + Reset = 627, + Restart = 628, + Restrict = 629, + Return = 630, + Returning = 631, + Returns = 632, + Revoke = 633, + Right = 634, + Role = 635, + Rollback = 636, + Rollup = 637, + Routine = 638, + Routines = 639, + Row = 640, + Rows = 641, + Rule = 642, + Savepoint = 643, + Scalar = 644, + Schema = 645, + Schemas = 646, + Scroll = 647, + Search = 648, + SecondP = 649, + Security = 650, + Select = 651, + Sequence = 652, + Sequences = 653, + Serializable = 654, + Server = 655, + Session = 656, + SessionUser = 657, + Set = 658, + Sets = 659, + Setof = 660, + Share = 661, + Show = 662, + Similar = 663, + Simple = 664, + Skip = 665, + Smallint = 666, + Snapshot = 667, + Some = 668, + Source = 669, + SqlP = 670, + Stable = 671, + StandaloneP = 672, + Start = 673, + Statement = 674, + Statistics = 675, + Stdin = 676, + Stdout = 677, + Storage = 678, + Stored = 679, + StrictP = 680, + StringP = 681, + StripP = 682, + Subscription = 683, + Substring = 684, + Support = 685, + Symmetric = 686, + Sysid = 687, + SystemP = 688, + SystemUser = 689, + Table = 690, + Tables = 691, + Tablesample = 692, + Tablespace = 693, + Target = 694, + Temp = 695, + Template = 696, + Temporary = 697, + TextP = 698, + Then = 699, + Ties = 700, + Time = 701, + Timestamp = 702, + To = 703, + Trailing = 704, + Transaction = 705, + Transform = 706, + Treat = 707, + Trigger = 708, + Trim = 709, + TrueP = 710, + Truncate = 711, + Trusted = 712, + TypeP = 713, + TypesP = 714, + Uescape = 715, + Unbounded = 716, + Unconditional = 717, + Uncommitted = 718, + Unencrypted = 719, + Union = 720, + Unique = 721, + Unknown = 722, + Unlisten = 723, + Unlogged = 724, + Until = 725, + Update = 726, + User = 727, + Using = 728, + Vacuum = 729, + Valid = 730, + Validate = 731, + Validator = 732, + ValueP = 733, + Values = 734, + Varchar = 735, + Variadic = 736, + Varying = 737, + Verbose = 738, + VersionP = 739, + View = 740, + Views = 741, + Volatile = 742, + When = 743, + Where = 744, + WhitespaceP = 745, + Window = 746, + With = 747, + Within = 748, + Without = 749, + Work = 750, + Wrapper = 751, + Write = 752, + XmlP = 753, + Xmlattributes = 754, + Xmlconcat = 755, + Xmlelement = 756, + Xmlexists = 757, + Xmlforest = 758, + Xmlnamespaces = 759, + Xmlparse = 760, + Xmlpi = 761, + Xmlroot = 762, + Xmlserialize = 763, + Xmltable = 764, + YearP = 765, + YesP = 766, + Zone = 767, + FormatLa = 768, + NotLa = 769, + NullsLa = 770, + WithLa = 771, + WithoutLa = 772, + ModeTypeName = 773, + ModePlpgsqlExpr = 774, + ModePlpgsqlAssign1 = 775, + ModePlpgsqlAssign2 = 776, + ModePlpgsqlAssign3 = 777, + Uminus = 778, +} +impl Token { + /// String value of the enum field names used in the ProtoBuf definition. + /// + /// The values are not transformed in any way and thus are considered stable + /// (if the ProtoBuf definition does not change) and safe for programmatic use. + pub fn as_str_name(&self) -> &'static str { + match self { + Self::Nul => "NUL", + Self::Ascii36 => "ASCII_36", + Self::Ascii37 => "ASCII_37", + Self::Ascii40 => "ASCII_40", + Self::Ascii41 => "ASCII_41", + Self::Ascii42 => "ASCII_42", + Self::Ascii43 => "ASCII_43", + Self::Ascii44 => "ASCII_44", + Self::Ascii45 => "ASCII_45", + Self::Ascii46 => "ASCII_46", + Self::Ascii47 => "ASCII_47", + Self::Ascii58 => "ASCII_58", + Self::Ascii59 => "ASCII_59", + Self::Ascii60 => "ASCII_60", + Self::Ascii61 => "ASCII_61", + Self::Ascii62 => "ASCII_62", + Self::Ascii63 => "ASCII_63", + Self::Ascii91 => "ASCII_91", + Self::Ascii92 => "ASCII_92", + Self::Ascii93 => "ASCII_93", + Self::Ascii94 => "ASCII_94", + Self::Ident => "IDENT", + Self::Uident => "UIDENT", + Self::Fconst => "FCONST", + Self::Sconst => "SCONST", + Self::Usconst => "USCONST", + Self::Bconst => "BCONST", + Self::Xconst => "XCONST", + Self::Op => "Op", + Self::Iconst => "ICONST", + Self::Param => "PARAM", + Self::Typecast => "TYPECAST", + Self::DotDot => "DOT_DOT", + Self::ColonEquals => "COLON_EQUALS", + Self::EqualsGreater => "EQUALS_GREATER", + Self::LessEquals => "LESS_EQUALS", + Self::GreaterEquals => "GREATER_EQUALS", + Self::NotEquals => "NOT_EQUALS", + Self::SqlComment => "SQL_COMMENT", + Self::CComment => "C_COMMENT", + Self::AbortP => "ABORT_P", + Self::Absent => "ABSENT", + Self::AbsoluteP => "ABSOLUTE_P", + Self::Access => "ACCESS", + Self::Action => "ACTION", + Self::AddP => "ADD_P", + Self::Admin => "ADMIN", + Self::After => "AFTER", + Self::Aggregate => "AGGREGATE", + Self::All => "ALL", + Self::Also => "ALSO", + Self::Alter => "ALTER", + Self::Always => "ALWAYS", + Self::Analyse => "ANALYSE", + Self::Analyze => "ANALYZE", + Self::And => "AND", + Self::Any => "ANY", + Self::Array => "ARRAY", + Self::As => "AS", + Self::Asc => "ASC", + Self::Asensitive => "ASENSITIVE", + Self::Assertion => "ASSERTION", + Self::Assignment => "ASSIGNMENT", + Self::Asymmetric => "ASYMMETRIC", + Self::Atomic => "ATOMIC", + Self::At => "AT", + Self::Attach => "ATTACH", + Self::Attribute => "ATTRIBUTE", + Self::Authorization => "AUTHORIZATION", + Self::Backward => "BACKWARD", + Self::Before => "BEFORE", + Self::BeginP => "BEGIN_P", + Self::Between => "BETWEEN", + Self::Bigint => "BIGINT", + Self::Binary => "BINARY", + Self::Bit => "BIT", + Self::BooleanP => "BOOLEAN_P", + Self::Both => "BOTH", + Self::Breadth => "BREADTH", + Self::By => "BY", + Self::Cache => "CACHE", + Self::Call => "CALL", + Self::Called => "CALLED", + Self::Cascade => "CASCADE", + Self::Cascaded => "CASCADED", + Self::Case => "CASE", + Self::Cast => "CAST", + Self::CatalogP => "CATALOG_P", + Self::Chain => "CHAIN", + Self::CharP => "CHAR_P", + Self::Character => "CHARACTER", + Self::Characteristics => "CHARACTERISTICS", + Self::Check => "CHECK", + Self::Checkpoint => "CHECKPOINT", + Self::Class => "CLASS", + Self::Close => "CLOSE", + Self::Cluster => "CLUSTER", + Self::Coalesce => "COALESCE", + Self::Collate => "COLLATE", + Self::Collation => "COLLATION", + Self::Column => "COLUMN", + Self::Columns => "COLUMNS", + Self::Comment => "COMMENT", + Self::Comments => "COMMENTS", + Self::Commit => "COMMIT", + Self::Committed => "COMMITTED", + Self::Compression => "COMPRESSION", + Self::Concurrently => "CONCURRENTLY", + Self::Conditional => "CONDITIONAL", + Self::Configuration => "CONFIGURATION", + Self::Conflict => "CONFLICT", + Self::Connection => "CONNECTION", + Self::Constraint => "CONSTRAINT", + Self::Constraints => "CONSTRAINTS", + Self::ContentP => "CONTENT_P", + Self::ContinueP => "CONTINUE_P", + Self::ConversionP => "CONVERSION_P", + Self::Copy => "COPY", + Self::Cost => "COST", + Self::Create => "CREATE", + Self::Cross => "CROSS", + Self::Csv => "CSV", + Self::Cube => "CUBE", + Self::CurrentP => "CURRENT_P", + Self::CurrentCatalog => "CURRENT_CATALOG", + Self::CurrentDate => "CURRENT_DATE", + Self::CurrentRole => "CURRENT_ROLE", + Self::CurrentSchema => "CURRENT_SCHEMA", + Self::CurrentTime => "CURRENT_TIME", + Self::CurrentTimestamp => "CURRENT_TIMESTAMP", + Self::CurrentUser => "CURRENT_USER", + Self::Cursor => "CURSOR", + Self::Cycle => "CYCLE", + Self::DataP => "DATA_P", + Self::Database => "DATABASE", + Self::DayP => "DAY_P", + Self::Deallocate => "DEALLOCATE", + Self::Dec => "DEC", + Self::DecimalP => "DECIMAL_P", + Self::Declare => "DECLARE", + Self::Default => "DEFAULT", + Self::Defaults => "DEFAULTS", + Self::Deferrable => "DEFERRABLE", + Self::Deferred => "DEFERRED", + Self::Definer => "DEFINER", + Self::DeleteP => "DELETE_P", + Self::Delimiter => "DELIMITER", + Self::Delimiters => "DELIMITERS", + Self::Depends => "DEPENDS", + Self::Depth => "DEPTH", + Self::Desc => "DESC", + Self::Detach => "DETACH", + Self::Dictionary => "DICTIONARY", + Self::DisableP => "DISABLE_P", + Self::Discard => "DISCARD", + Self::Distinct => "DISTINCT", + Self::Do => "DO", + Self::DocumentP => "DOCUMENT_P", + Self::DomainP => "DOMAIN_P", + Self::DoubleP => "DOUBLE_P", + Self::Drop => "DROP", + Self::Each => "EACH", + Self::Else => "ELSE", + Self::EmptyP => "EMPTY_P", + Self::EnableP => "ENABLE_P", + Self::Encoding => "ENCODING", + Self::Encrypted => "ENCRYPTED", + Self::EndP => "END_P", + Self::EnumP => "ENUM_P", + Self::ErrorP => "ERROR_P", + Self::Escape => "ESCAPE", + Self::Event => "EVENT", + Self::Except => "EXCEPT", + Self::Exclude => "EXCLUDE", + Self::Excluding => "EXCLUDING", + Self::Exclusive => "EXCLUSIVE", + Self::Execute => "EXECUTE", + Self::Exists => "EXISTS", + Self::Explain => "EXPLAIN", + Self::Expression => "EXPRESSION", + Self::Extension => "EXTENSION", + Self::External => "EXTERNAL", + Self::Extract => "EXTRACT", + Self::FalseP => "FALSE_P", + Self::Family => "FAMILY", + Self::Fetch => "FETCH", + Self::Filter => "FILTER", + Self::Finalize => "FINALIZE", + Self::FirstP => "FIRST_P", + Self::FloatP => "FLOAT_P", + Self::Following => "FOLLOWING", + Self::For => "FOR", + Self::Force => "FORCE", + Self::Foreign => "FOREIGN", + Self::Format => "FORMAT", + Self::Forward => "FORWARD", + Self::Freeze => "FREEZE", + Self::From => "FROM", + Self::Full => "FULL", + Self::Function => "FUNCTION", + Self::Functions => "FUNCTIONS", + Self::Generated => "GENERATED", + Self::Global => "GLOBAL", + Self::Grant => "GRANT", + Self::Granted => "GRANTED", + Self::Greatest => "GREATEST", + Self::GroupP => "GROUP_P", + Self::Grouping => "GROUPING", + Self::Groups => "GROUPS", + Self::Handler => "HANDLER", + Self::Having => "HAVING", + Self::HeaderP => "HEADER_P", + Self::Hold => "HOLD", + Self::HourP => "HOUR_P", + Self::IdentityP => "IDENTITY_P", + Self::IfP => "IF_P", + Self::Ilike => "ILIKE", + Self::Immediate => "IMMEDIATE", + Self::Immutable => "IMMUTABLE", + Self::ImplicitP => "IMPLICIT_P", + Self::ImportP => "IMPORT_P", + Self::InP => "IN_P", + Self::Include => "INCLUDE", + Self::Including => "INCLUDING", + Self::Increment => "INCREMENT", + Self::Indent => "INDENT", + Self::Index => "INDEX", + Self::Indexes => "INDEXES", + Self::Inherit => "INHERIT", + Self::Inherits => "INHERITS", + Self::Initially => "INITIALLY", + Self::InlineP => "INLINE_P", + Self::InnerP => "INNER_P", + Self::Inout => "INOUT", + Self::InputP => "INPUT_P", + Self::Insensitive => "INSENSITIVE", + Self::Insert => "INSERT", + Self::Instead => "INSTEAD", + Self::IntP => "INT_P", + Self::Integer => "INTEGER", + Self::Intersect => "INTERSECT", + Self::Interval => "INTERVAL", + Self::Into => "INTO", + Self::Invoker => "INVOKER", + Self::Is => "IS", + Self::Isnull => "ISNULL", + Self::Isolation => "ISOLATION", + Self::Join => "JOIN", + Self::Json => "JSON", + Self::JsonArray => "JSON_ARRAY", + Self::JsonArrayagg => "JSON_ARRAYAGG", + Self::JsonExists => "JSON_EXISTS", + Self::JsonObject => "JSON_OBJECT", + Self::JsonObjectagg => "JSON_OBJECTAGG", + Self::JsonQuery => "JSON_QUERY", + Self::JsonScalar => "JSON_SCALAR", + Self::JsonSerialize => "JSON_SERIALIZE", + Self::JsonTable => "JSON_TABLE", + Self::JsonValue => "JSON_VALUE", + Self::Keep => "KEEP", + Self::Key => "KEY", + Self::Keys => "KEYS", + Self::Label => "LABEL", + Self::Language => "LANGUAGE", + Self::LargeP => "LARGE_P", + Self::LastP => "LAST_P", + Self::LateralP => "LATERAL_P", + Self::Leading => "LEADING", + Self::Leakproof => "LEAKPROOF", + Self::Least => "LEAST", + Self::Left => "LEFT", + Self::Level => "LEVEL", + Self::Like => "LIKE", + Self::Limit => "LIMIT", + Self::Listen => "LISTEN", + Self::Load => "LOAD", + Self::Local => "LOCAL", + Self::Localtime => "LOCALTIME", + Self::Localtimestamp => "LOCALTIMESTAMP", + Self::Location => "LOCATION", + Self::LockP => "LOCK_P", + Self::Locked => "LOCKED", + Self::Logged => "LOGGED", + Self::Mapping => "MAPPING", + Self::Match => "MATCH", + Self::Matched => "MATCHED", + Self::Materialized => "MATERIALIZED", + Self::Maxvalue => "MAXVALUE", + Self::Merge => "MERGE", + Self::MergeAction => "MERGE_ACTION", + Self::Method => "METHOD", + Self::MinuteP => "MINUTE_P", + Self::Minvalue => "MINVALUE", + Self::Mode => "MODE", + Self::MonthP => "MONTH_P", + Self::Move => "MOVE", + Self::NameP => "NAME_P", + Self::Names => "NAMES", + Self::National => "NATIONAL", + Self::Natural => "NATURAL", + Self::Nchar => "NCHAR", + Self::Nested => "NESTED", + Self::New => "NEW", + Self::Next => "NEXT", + Self::Nfc => "NFC", + Self::Nfd => "NFD", + Self::Nfkc => "NFKC", + Self::Nfkd => "NFKD", + Self::No => "NO", + Self::None => "NONE", + Self::Normalize => "NORMALIZE", + Self::Normalized => "NORMALIZED", + Self::Not => "NOT", + Self::Nothing => "NOTHING", + Self::Notify => "NOTIFY", + Self::Notnull => "NOTNULL", + Self::Nowait => "NOWAIT", + Self::NullP => "NULL_P", + Self::Nullif => "NULLIF", + Self::NullsP => "NULLS_P", + Self::Numeric => "NUMERIC", + Self::ObjectP => "OBJECT_P", + Self::Of => "OF", + Self::Off => "OFF", + Self::Offset => "OFFSET", + Self::Oids => "OIDS", + Self::Old => "OLD", + Self::Omit => "OMIT", + Self::On => "ON", + Self::Only => "ONLY", + Self::Operator => "OPERATOR", + Self::Option => "OPTION", + Self::Options => "OPTIONS", + Self::Or => "OR", + Self::Order => "ORDER", + Self::Ordinality => "ORDINALITY", + Self::Others => "OTHERS", + Self::OutP => "OUT_P", + Self::OuterP => "OUTER_P", + Self::Over => "OVER", + Self::Overlaps => "OVERLAPS", + Self::Overlay => "OVERLAY", + Self::Overriding => "OVERRIDING", + Self::Owned => "OWNED", + Self::Owner => "OWNER", + Self::Parallel => "PARALLEL", + Self::Parameter => "PARAMETER", + Self::Parser => "PARSER", + Self::Partial => "PARTIAL", + Self::Partition => "PARTITION", + Self::Passing => "PASSING", + Self::Password => "PASSWORD", + Self::Path => "PATH", + Self::Placing => "PLACING", + Self::Plan => "PLAN", + Self::Plans => "PLANS", + Self::Policy => "POLICY", + Self::Position => "POSITION", + Self::Preceding => "PRECEDING", + Self::Precision => "PRECISION", + Self::Preserve => "PRESERVE", + Self::Prepare => "PREPARE", + Self::Prepared => "PREPARED", + Self::Primary => "PRIMARY", + Self::Prior => "PRIOR", + Self::Privileges => "PRIVILEGES", + Self::Procedural => "PROCEDURAL", + Self::Procedure => "PROCEDURE", + Self::Procedures => "PROCEDURES", + Self::Program => "PROGRAM", + Self::Publication => "PUBLICATION", + Self::Quote => "QUOTE", + Self::Quotes => "QUOTES", + Self::Range => "RANGE", + Self::Read => "READ", + Self::Real => "REAL", + Self::Reassign => "REASSIGN", + Self::Recheck => "RECHECK", + Self::Recursive => "RECURSIVE", + Self::RefP => "REF_P", + Self::References => "REFERENCES", + Self::Referencing => "REFERENCING", + Self::Refresh => "REFRESH", + Self::Reindex => "REINDEX", + Self::RelativeP => "RELATIVE_P", + Self::Release => "RELEASE", + Self::Rename => "RENAME", + Self::Repeatable => "REPEATABLE", + Self::Replace => "REPLACE", + Self::Replica => "REPLICA", + Self::Reset => "RESET", + Self::Restart => "RESTART", + Self::Restrict => "RESTRICT", + Self::Return => "RETURN", + Self::Returning => "RETURNING", + Self::Returns => "RETURNS", + Self::Revoke => "REVOKE", + Self::Right => "RIGHT", + Self::Role => "ROLE", + Self::Rollback => "ROLLBACK", + Self::Rollup => "ROLLUP", + Self::Routine => "ROUTINE", + Self::Routines => "ROUTINES", + Self::Row => "ROW", + Self::Rows => "ROWS", + Self::Rule => "RULE", + Self::Savepoint => "SAVEPOINT", + Self::Scalar => "SCALAR", + Self::Schema => "SCHEMA", + Self::Schemas => "SCHEMAS", + Self::Scroll => "SCROLL", + Self::Search => "SEARCH", + Self::SecondP => "SECOND_P", + Self::Security => "SECURITY", + Self::Select => "SELECT", + Self::Sequence => "SEQUENCE", + Self::Sequences => "SEQUENCES", + Self::Serializable => "SERIALIZABLE", + Self::Server => "SERVER", + Self::Session => "SESSION", + Self::SessionUser => "SESSION_USER", + Self::Set => "SET", + Self::Sets => "SETS", + Self::Setof => "SETOF", + Self::Share => "SHARE", + Self::Show => "SHOW", + Self::Similar => "SIMILAR", + Self::Simple => "SIMPLE", + Self::Skip => "SKIP", + Self::Smallint => "SMALLINT", + Self::Snapshot => "SNAPSHOT", + Self::Some => "SOME", + Self::Source => "SOURCE", + Self::SqlP => "SQL_P", + Self::Stable => "STABLE", + Self::StandaloneP => "STANDALONE_P", + Self::Start => "START", + Self::Statement => "STATEMENT", + Self::Statistics => "STATISTICS", + Self::Stdin => "STDIN", + Self::Stdout => "STDOUT", + Self::Storage => "STORAGE", + Self::Stored => "STORED", + Self::StrictP => "STRICT_P", + Self::StringP => "STRING_P", + Self::StripP => "STRIP_P", + Self::Subscription => "SUBSCRIPTION", + Self::Substring => "SUBSTRING", + Self::Support => "SUPPORT", + Self::Symmetric => "SYMMETRIC", + Self::Sysid => "SYSID", + Self::SystemP => "SYSTEM_P", + Self::SystemUser => "SYSTEM_USER", + Self::Table => "TABLE", + Self::Tables => "TABLES", + Self::Tablesample => "TABLESAMPLE", + Self::Tablespace => "TABLESPACE", + Self::Target => "TARGET", + Self::Temp => "TEMP", + Self::Template => "TEMPLATE", + Self::Temporary => "TEMPORARY", + Self::TextP => "TEXT_P", + Self::Then => "THEN", + Self::Ties => "TIES", + Self::Time => "TIME", + Self::Timestamp => "TIMESTAMP", + Self::To => "TO", + Self::Trailing => "TRAILING", + Self::Transaction => "TRANSACTION", + Self::Transform => "TRANSFORM", + Self::Treat => "TREAT", + Self::Trigger => "TRIGGER", + Self::Trim => "TRIM", + Self::TrueP => "TRUE_P", + Self::Truncate => "TRUNCATE", + Self::Trusted => "TRUSTED", + Self::TypeP => "TYPE_P", + Self::TypesP => "TYPES_P", + Self::Uescape => "UESCAPE", + Self::Unbounded => "UNBOUNDED", + Self::Unconditional => "UNCONDITIONAL", + Self::Uncommitted => "UNCOMMITTED", + Self::Unencrypted => "UNENCRYPTED", + Self::Union => "UNION", + Self::Unique => "UNIQUE", + Self::Unknown => "UNKNOWN", + Self::Unlisten => "UNLISTEN", + Self::Unlogged => "UNLOGGED", + Self::Until => "UNTIL", + Self::Update => "UPDATE", + Self::User => "USER", + Self::Using => "USING", + Self::Vacuum => "VACUUM", + Self::Valid => "VALID", + Self::Validate => "VALIDATE", + Self::Validator => "VALIDATOR", + Self::ValueP => "VALUE_P", + Self::Values => "VALUES", + Self::Varchar => "VARCHAR", + Self::Variadic => "VARIADIC", + Self::Varying => "VARYING", + Self::Verbose => "VERBOSE", + Self::VersionP => "VERSION_P", + Self::View => "VIEW", + Self::Views => "VIEWS", + Self::Volatile => "VOLATILE", + Self::When => "WHEN", + Self::Where => "WHERE", + Self::WhitespaceP => "WHITESPACE_P", + Self::Window => "WINDOW", + Self::With => "WITH", + Self::Within => "WITHIN", + Self::Without => "WITHOUT", + Self::Work => "WORK", + Self::Wrapper => "WRAPPER", + Self::Write => "WRITE", + Self::XmlP => "XML_P", + Self::Xmlattributes => "XMLATTRIBUTES", + Self::Xmlconcat => "XMLCONCAT", + Self::Xmlelement => "XMLELEMENT", + Self::Xmlexists => "XMLEXISTS", + Self::Xmlforest => "XMLFOREST", + Self::Xmlnamespaces => "XMLNAMESPACES", + Self::Xmlparse => "XMLPARSE", + Self::Xmlpi => "XMLPI", + Self::Xmlroot => "XMLROOT", + Self::Xmlserialize => "XMLSERIALIZE", + Self::Xmltable => "XMLTABLE", + Self::YearP => "YEAR_P", + Self::YesP => "YES_P", + Self::Zone => "ZONE", + Self::FormatLa => "FORMAT_LA", + Self::NotLa => "NOT_LA", + Self::NullsLa => "NULLS_LA", + Self::WithLa => "WITH_LA", + Self::WithoutLa => "WITHOUT_LA", + Self::ModeTypeName => "MODE_TYPE_NAME", + Self::ModePlpgsqlExpr => "MODE_PLPGSQL_EXPR", + Self::ModePlpgsqlAssign1 => "MODE_PLPGSQL_ASSIGN1", + Self::ModePlpgsqlAssign2 => "MODE_PLPGSQL_ASSIGN2", + Self::ModePlpgsqlAssign3 => "MODE_PLPGSQL_ASSIGN3", + Self::Uminus => "UMINUS", + } + } + /// Creates an enum from field names used in the ProtoBuf definition. + pub fn from_str_name(value: &str) -> ::core::option::Option { + match value { + "NUL" => Some(Self::Nul), + "ASCII_36" => Some(Self::Ascii36), + "ASCII_37" => Some(Self::Ascii37), + "ASCII_40" => Some(Self::Ascii40), + "ASCII_41" => Some(Self::Ascii41), + "ASCII_42" => Some(Self::Ascii42), + "ASCII_43" => Some(Self::Ascii43), + "ASCII_44" => Some(Self::Ascii44), + "ASCII_45" => Some(Self::Ascii45), + "ASCII_46" => Some(Self::Ascii46), + "ASCII_47" => Some(Self::Ascii47), + "ASCII_58" => Some(Self::Ascii58), + "ASCII_59" => Some(Self::Ascii59), + "ASCII_60" => Some(Self::Ascii60), + "ASCII_61" => Some(Self::Ascii61), + "ASCII_62" => Some(Self::Ascii62), + "ASCII_63" => Some(Self::Ascii63), + "ASCII_91" => Some(Self::Ascii91), + "ASCII_92" => Some(Self::Ascii92), + "ASCII_93" => Some(Self::Ascii93), + "ASCII_94" => Some(Self::Ascii94), + "IDENT" => Some(Self::Ident), + "UIDENT" => Some(Self::Uident), + "FCONST" => Some(Self::Fconst), + "SCONST" => Some(Self::Sconst), + "USCONST" => Some(Self::Usconst), + "BCONST" => Some(Self::Bconst), + "XCONST" => Some(Self::Xconst), + "Op" => Some(Self::Op), + "ICONST" => Some(Self::Iconst), + "PARAM" => Some(Self::Param), + "TYPECAST" => Some(Self::Typecast), + "DOT_DOT" => Some(Self::DotDot), + "COLON_EQUALS" => Some(Self::ColonEquals), + "EQUALS_GREATER" => Some(Self::EqualsGreater), + "LESS_EQUALS" => Some(Self::LessEquals), + "GREATER_EQUALS" => Some(Self::GreaterEquals), + "NOT_EQUALS" => Some(Self::NotEquals), + "SQL_COMMENT" => Some(Self::SqlComment), + "C_COMMENT" => Some(Self::CComment), + "ABORT_P" => Some(Self::AbortP), + "ABSENT" => Some(Self::Absent), + "ABSOLUTE_P" => Some(Self::AbsoluteP), + "ACCESS" => Some(Self::Access), + "ACTION" => Some(Self::Action), + "ADD_P" => Some(Self::AddP), + "ADMIN" => Some(Self::Admin), + "AFTER" => Some(Self::After), + "AGGREGATE" => Some(Self::Aggregate), + "ALL" => Some(Self::All), + "ALSO" => Some(Self::Also), + "ALTER" => Some(Self::Alter), + "ALWAYS" => Some(Self::Always), + "ANALYSE" => Some(Self::Analyse), + "ANALYZE" => Some(Self::Analyze), + "AND" => Some(Self::And), + "ANY" => Some(Self::Any), + "ARRAY" => Some(Self::Array), + "AS" => Some(Self::As), + "ASC" => Some(Self::Asc), + "ASENSITIVE" => Some(Self::Asensitive), + "ASSERTION" => Some(Self::Assertion), + "ASSIGNMENT" => Some(Self::Assignment), + "ASYMMETRIC" => Some(Self::Asymmetric), + "ATOMIC" => Some(Self::Atomic), + "AT" => Some(Self::At), + "ATTACH" => Some(Self::Attach), + "ATTRIBUTE" => Some(Self::Attribute), + "AUTHORIZATION" => Some(Self::Authorization), + "BACKWARD" => Some(Self::Backward), + "BEFORE" => Some(Self::Before), + "BEGIN_P" => Some(Self::BeginP), + "BETWEEN" => Some(Self::Between), + "BIGINT" => Some(Self::Bigint), + "BINARY" => Some(Self::Binary), + "BIT" => Some(Self::Bit), + "BOOLEAN_P" => Some(Self::BooleanP), + "BOTH" => Some(Self::Both), + "BREADTH" => Some(Self::Breadth), + "BY" => Some(Self::By), + "CACHE" => Some(Self::Cache), + "CALL" => Some(Self::Call), + "CALLED" => Some(Self::Called), + "CASCADE" => Some(Self::Cascade), + "CASCADED" => Some(Self::Cascaded), + "CASE" => Some(Self::Case), + "CAST" => Some(Self::Cast), + "CATALOG_P" => Some(Self::CatalogP), + "CHAIN" => Some(Self::Chain), + "CHAR_P" => Some(Self::CharP), + "CHARACTER" => Some(Self::Character), + "CHARACTERISTICS" => Some(Self::Characteristics), + "CHECK" => Some(Self::Check), + "CHECKPOINT" => Some(Self::Checkpoint), + "CLASS" => Some(Self::Class), + "CLOSE" => Some(Self::Close), + "CLUSTER" => Some(Self::Cluster), + "COALESCE" => Some(Self::Coalesce), + "COLLATE" => Some(Self::Collate), + "COLLATION" => Some(Self::Collation), + "COLUMN" => Some(Self::Column), + "COLUMNS" => Some(Self::Columns), + "COMMENT" => Some(Self::Comment), + "COMMENTS" => Some(Self::Comments), + "COMMIT" => Some(Self::Commit), + "COMMITTED" => Some(Self::Committed), + "COMPRESSION" => Some(Self::Compression), + "CONCURRENTLY" => Some(Self::Concurrently), + "CONDITIONAL" => Some(Self::Conditional), + "CONFIGURATION" => Some(Self::Configuration), + "CONFLICT" => Some(Self::Conflict), + "CONNECTION" => Some(Self::Connection), + "CONSTRAINT" => Some(Self::Constraint), + "CONSTRAINTS" => Some(Self::Constraints), + "CONTENT_P" => Some(Self::ContentP), + "CONTINUE_P" => Some(Self::ContinueP), + "CONVERSION_P" => Some(Self::ConversionP), + "COPY" => Some(Self::Copy), + "COST" => Some(Self::Cost), + "CREATE" => Some(Self::Create), + "CROSS" => Some(Self::Cross), + "CSV" => Some(Self::Csv), + "CUBE" => Some(Self::Cube), + "CURRENT_P" => Some(Self::CurrentP), + "CURRENT_CATALOG" => Some(Self::CurrentCatalog), + "CURRENT_DATE" => Some(Self::CurrentDate), + "CURRENT_ROLE" => Some(Self::CurrentRole), + "CURRENT_SCHEMA" => Some(Self::CurrentSchema), + "CURRENT_TIME" => Some(Self::CurrentTime), + "CURRENT_TIMESTAMP" => Some(Self::CurrentTimestamp), + "CURRENT_USER" => Some(Self::CurrentUser), + "CURSOR" => Some(Self::Cursor), + "CYCLE" => Some(Self::Cycle), + "DATA_P" => Some(Self::DataP), + "DATABASE" => Some(Self::Database), + "DAY_P" => Some(Self::DayP), + "DEALLOCATE" => Some(Self::Deallocate), + "DEC" => Some(Self::Dec), + "DECIMAL_P" => Some(Self::DecimalP), + "DECLARE" => Some(Self::Declare), + "DEFAULT" => Some(Self::Default), + "DEFAULTS" => Some(Self::Defaults), + "DEFERRABLE" => Some(Self::Deferrable), + "DEFERRED" => Some(Self::Deferred), + "DEFINER" => Some(Self::Definer), + "DELETE_P" => Some(Self::DeleteP), + "DELIMITER" => Some(Self::Delimiter), + "DELIMITERS" => Some(Self::Delimiters), + "DEPENDS" => Some(Self::Depends), + "DEPTH" => Some(Self::Depth), + "DESC" => Some(Self::Desc), + "DETACH" => Some(Self::Detach), + "DICTIONARY" => Some(Self::Dictionary), + "DISABLE_P" => Some(Self::DisableP), + "DISCARD" => Some(Self::Discard), + "DISTINCT" => Some(Self::Distinct), + "DO" => Some(Self::Do), + "DOCUMENT_P" => Some(Self::DocumentP), + "DOMAIN_P" => Some(Self::DomainP), + "DOUBLE_P" => Some(Self::DoubleP), + "DROP" => Some(Self::Drop), + "EACH" => Some(Self::Each), + "ELSE" => Some(Self::Else), + "EMPTY_P" => Some(Self::EmptyP), + "ENABLE_P" => Some(Self::EnableP), + "ENCODING" => Some(Self::Encoding), + "ENCRYPTED" => Some(Self::Encrypted), + "END_P" => Some(Self::EndP), + "ENUM_P" => Some(Self::EnumP), + "ERROR_P" => Some(Self::ErrorP), + "ESCAPE" => Some(Self::Escape), + "EVENT" => Some(Self::Event), + "EXCEPT" => Some(Self::Except), + "EXCLUDE" => Some(Self::Exclude), + "EXCLUDING" => Some(Self::Excluding), + "EXCLUSIVE" => Some(Self::Exclusive), + "EXECUTE" => Some(Self::Execute), + "EXISTS" => Some(Self::Exists), + "EXPLAIN" => Some(Self::Explain), + "EXPRESSION" => Some(Self::Expression), + "EXTENSION" => Some(Self::Extension), + "EXTERNAL" => Some(Self::External), + "EXTRACT" => Some(Self::Extract), + "FALSE_P" => Some(Self::FalseP), + "FAMILY" => Some(Self::Family), + "FETCH" => Some(Self::Fetch), + "FILTER" => Some(Self::Filter), + "FINALIZE" => Some(Self::Finalize), + "FIRST_P" => Some(Self::FirstP), + "FLOAT_P" => Some(Self::FloatP), + "FOLLOWING" => Some(Self::Following), + "FOR" => Some(Self::For), + "FORCE" => Some(Self::Force), + "FOREIGN" => Some(Self::Foreign), + "FORMAT" => Some(Self::Format), + "FORWARD" => Some(Self::Forward), + "FREEZE" => Some(Self::Freeze), + "FROM" => Some(Self::From), + "FULL" => Some(Self::Full), + "FUNCTION" => Some(Self::Function), + "FUNCTIONS" => Some(Self::Functions), + "GENERATED" => Some(Self::Generated), + "GLOBAL" => Some(Self::Global), + "GRANT" => Some(Self::Grant), + "GRANTED" => Some(Self::Granted), + "GREATEST" => Some(Self::Greatest), + "GROUP_P" => Some(Self::GroupP), + "GROUPING" => Some(Self::Grouping), + "GROUPS" => Some(Self::Groups), + "HANDLER" => Some(Self::Handler), + "HAVING" => Some(Self::Having), + "HEADER_P" => Some(Self::HeaderP), + "HOLD" => Some(Self::Hold), + "HOUR_P" => Some(Self::HourP), + "IDENTITY_P" => Some(Self::IdentityP), + "IF_P" => Some(Self::IfP), + "ILIKE" => Some(Self::Ilike), + "IMMEDIATE" => Some(Self::Immediate), + "IMMUTABLE" => Some(Self::Immutable), + "IMPLICIT_P" => Some(Self::ImplicitP), + "IMPORT_P" => Some(Self::ImportP), + "IN_P" => Some(Self::InP), + "INCLUDE" => Some(Self::Include), + "INCLUDING" => Some(Self::Including), + "INCREMENT" => Some(Self::Increment), + "INDENT" => Some(Self::Indent), + "INDEX" => Some(Self::Index), + "INDEXES" => Some(Self::Indexes), + "INHERIT" => Some(Self::Inherit), + "INHERITS" => Some(Self::Inherits), + "INITIALLY" => Some(Self::Initially), + "INLINE_P" => Some(Self::InlineP), + "INNER_P" => Some(Self::InnerP), + "INOUT" => Some(Self::Inout), + "INPUT_P" => Some(Self::InputP), + "INSENSITIVE" => Some(Self::Insensitive), + "INSERT" => Some(Self::Insert), + "INSTEAD" => Some(Self::Instead), + "INT_P" => Some(Self::IntP), + "INTEGER" => Some(Self::Integer), + "INTERSECT" => Some(Self::Intersect), + "INTERVAL" => Some(Self::Interval), + "INTO" => Some(Self::Into), + "INVOKER" => Some(Self::Invoker), + "IS" => Some(Self::Is), + "ISNULL" => Some(Self::Isnull), + "ISOLATION" => Some(Self::Isolation), + "JOIN" => Some(Self::Join), + "JSON" => Some(Self::Json), + "JSON_ARRAY" => Some(Self::JsonArray), + "JSON_ARRAYAGG" => Some(Self::JsonArrayagg), + "JSON_EXISTS" => Some(Self::JsonExists), + "JSON_OBJECT" => Some(Self::JsonObject), + "JSON_OBJECTAGG" => Some(Self::JsonObjectagg), + "JSON_QUERY" => Some(Self::JsonQuery), + "JSON_SCALAR" => Some(Self::JsonScalar), + "JSON_SERIALIZE" => Some(Self::JsonSerialize), + "JSON_TABLE" => Some(Self::JsonTable), + "JSON_VALUE" => Some(Self::JsonValue), + "KEEP" => Some(Self::Keep), + "KEY" => Some(Self::Key), + "KEYS" => Some(Self::Keys), + "LABEL" => Some(Self::Label), + "LANGUAGE" => Some(Self::Language), + "LARGE_P" => Some(Self::LargeP), + "LAST_P" => Some(Self::LastP), + "LATERAL_P" => Some(Self::LateralP), + "LEADING" => Some(Self::Leading), + "LEAKPROOF" => Some(Self::Leakproof), + "LEAST" => Some(Self::Least), + "LEFT" => Some(Self::Left), + "LEVEL" => Some(Self::Level), + "LIKE" => Some(Self::Like), + "LIMIT" => Some(Self::Limit), + "LISTEN" => Some(Self::Listen), + "LOAD" => Some(Self::Load), + "LOCAL" => Some(Self::Local), + "LOCALTIME" => Some(Self::Localtime), + "LOCALTIMESTAMP" => Some(Self::Localtimestamp), + "LOCATION" => Some(Self::Location), + "LOCK_P" => Some(Self::LockP), + "LOCKED" => Some(Self::Locked), + "LOGGED" => Some(Self::Logged), + "MAPPING" => Some(Self::Mapping), + "MATCH" => Some(Self::Match), + "MATCHED" => Some(Self::Matched), + "MATERIALIZED" => Some(Self::Materialized), + "MAXVALUE" => Some(Self::Maxvalue), + "MERGE" => Some(Self::Merge), + "MERGE_ACTION" => Some(Self::MergeAction), + "METHOD" => Some(Self::Method), + "MINUTE_P" => Some(Self::MinuteP), + "MINVALUE" => Some(Self::Minvalue), + "MODE" => Some(Self::Mode), + "MONTH_P" => Some(Self::MonthP), + "MOVE" => Some(Self::Move), + "NAME_P" => Some(Self::NameP), + "NAMES" => Some(Self::Names), + "NATIONAL" => Some(Self::National), + "NATURAL" => Some(Self::Natural), + "NCHAR" => Some(Self::Nchar), + "NESTED" => Some(Self::Nested), + "NEW" => Some(Self::New), + "NEXT" => Some(Self::Next), + "NFC" => Some(Self::Nfc), + "NFD" => Some(Self::Nfd), + "NFKC" => Some(Self::Nfkc), + "NFKD" => Some(Self::Nfkd), + "NO" => Some(Self::No), + "NONE" => Some(Self::None), + "NORMALIZE" => Some(Self::Normalize), + "NORMALIZED" => Some(Self::Normalized), + "NOT" => Some(Self::Not), + "NOTHING" => Some(Self::Nothing), + "NOTIFY" => Some(Self::Notify), + "NOTNULL" => Some(Self::Notnull), + "NOWAIT" => Some(Self::Nowait), + "NULL_P" => Some(Self::NullP), + "NULLIF" => Some(Self::Nullif), + "NULLS_P" => Some(Self::NullsP), + "NUMERIC" => Some(Self::Numeric), + "OBJECT_P" => Some(Self::ObjectP), + "OF" => Some(Self::Of), + "OFF" => Some(Self::Off), + "OFFSET" => Some(Self::Offset), + "OIDS" => Some(Self::Oids), + "OLD" => Some(Self::Old), + "OMIT" => Some(Self::Omit), + "ON" => Some(Self::On), + "ONLY" => Some(Self::Only), + "OPERATOR" => Some(Self::Operator), + "OPTION" => Some(Self::Option), + "OPTIONS" => Some(Self::Options), + "OR" => Some(Self::Or), + "ORDER" => Some(Self::Order), + "ORDINALITY" => Some(Self::Ordinality), + "OTHERS" => Some(Self::Others), + "OUT_P" => Some(Self::OutP), + "OUTER_P" => Some(Self::OuterP), + "OVER" => Some(Self::Over), + "OVERLAPS" => Some(Self::Overlaps), + "OVERLAY" => Some(Self::Overlay), + "OVERRIDING" => Some(Self::Overriding), + "OWNED" => Some(Self::Owned), + "OWNER" => Some(Self::Owner), + "PARALLEL" => Some(Self::Parallel), + "PARAMETER" => Some(Self::Parameter), + "PARSER" => Some(Self::Parser), + "PARTIAL" => Some(Self::Partial), + "PARTITION" => Some(Self::Partition), + "PASSING" => Some(Self::Passing), + "PASSWORD" => Some(Self::Password), + "PATH" => Some(Self::Path), + "PLACING" => Some(Self::Placing), + "PLAN" => Some(Self::Plan), + "PLANS" => Some(Self::Plans), + "POLICY" => Some(Self::Policy), + "POSITION" => Some(Self::Position), + "PRECEDING" => Some(Self::Preceding), + "PRECISION" => Some(Self::Precision), + "PRESERVE" => Some(Self::Preserve), + "PREPARE" => Some(Self::Prepare), + "PREPARED" => Some(Self::Prepared), + "PRIMARY" => Some(Self::Primary), + "PRIOR" => Some(Self::Prior), + "PRIVILEGES" => Some(Self::Privileges), + "PROCEDURAL" => Some(Self::Procedural), + "PROCEDURE" => Some(Self::Procedure), + "PROCEDURES" => Some(Self::Procedures), + "PROGRAM" => Some(Self::Program), + "PUBLICATION" => Some(Self::Publication), + "QUOTE" => Some(Self::Quote), + "QUOTES" => Some(Self::Quotes), + "RANGE" => Some(Self::Range), + "READ" => Some(Self::Read), + "REAL" => Some(Self::Real), + "REASSIGN" => Some(Self::Reassign), + "RECHECK" => Some(Self::Recheck), + "RECURSIVE" => Some(Self::Recursive), + "REF_P" => Some(Self::RefP), + "REFERENCES" => Some(Self::References), + "REFERENCING" => Some(Self::Referencing), + "REFRESH" => Some(Self::Refresh), + "REINDEX" => Some(Self::Reindex), + "RELATIVE_P" => Some(Self::RelativeP), + "RELEASE" => Some(Self::Release), + "RENAME" => Some(Self::Rename), + "REPEATABLE" => Some(Self::Repeatable), + "REPLACE" => Some(Self::Replace), + "REPLICA" => Some(Self::Replica), + "RESET" => Some(Self::Reset), + "RESTART" => Some(Self::Restart), + "RESTRICT" => Some(Self::Restrict), + "RETURN" => Some(Self::Return), + "RETURNING" => Some(Self::Returning), + "RETURNS" => Some(Self::Returns), + "REVOKE" => Some(Self::Revoke), + "RIGHT" => Some(Self::Right), + "ROLE" => Some(Self::Role), + "ROLLBACK" => Some(Self::Rollback), + "ROLLUP" => Some(Self::Rollup), + "ROUTINE" => Some(Self::Routine), + "ROUTINES" => Some(Self::Routines), + "ROW" => Some(Self::Row), + "ROWS" => Some(Self::Rows), + "RULE" => Some(Self::Rule), + "SAVEPOINT" => Some(Self::Savepoint), + "SCALAR" => Some(Self::Scalar), + "SCHEMA" => Some(Self::Schema), + "SCHEMAS" => Some(Self::Schemas), + "SCROLL" => Some(Self::Scroll), + "SEARCH" => Some(Self::Search), + "SECOND_P" => Some(Self::SecondP), + "SECURITY" => Some(Self::Security), + "SELECT" => Some(Self::Select), + "SEQUENCE" => Some(Self::Sequence), + "SEQUENCES" => Some(Self::Sequences), + "SERIALIZABLE" => Some(Self::Serializable), + "SERVER" => Some(Self::Server), + "SESSION" => Some(Self::Session), + "SESSION_USER" => Some(Self::SessionUser), + "SET" => Some(Self::Set), + "SETS" => Some(Self::Sets), + "SETOF" => Some(Self::Setof), + "SHARE" => Some(Self::Share), + "SHOW" => Some(Self::Show), + "SIMILAR" => Some(Self::Similar), + "SIMPLE" => Some(Self::Simple), + "SKIP" => Some(Self::Skip), + "SMALLINT" => Some(Self::Smallint), + "SNAPSHOT" => Some(Self::Snapshot), + "SOME" => Some(Self::Some), + "SOURCE" => Some(Self::Source), + "SQL_P" => Some(Self::SqlP), + "STABLE" => Some(Self::Stable), + "STANDALONE_P" => Some(Self::StandaloneP), + "START" => Some(Self::Start), + "STATEMENT" => Some(Self::Statement), + "STATISTICS" => Some(Self::Statistics), + "STDIN" => Some(Self::Stdin), + "STDOUT" => Some(Self::Stdout), + "STORAGE" => Some(Self::Storage), + "STORED" => Some(Self::Stored), + "STRICT_P" => Some(Self::StrictP), + "STRING_P" => Some(Self::StringP), + "STRIP_P" => Some(Self::StripP), + "SUBSCRIPTION" => Some(Self::Subscription), + "SUBSTRING" => Some(Self::Substring), + "SUPPORT" => Some(Self::Support), + "SYMMETRIC" => Some(Self::Symmetric), + "SYSID" => Some(Self::Sysid), + "SYSTEM_P" => Some(Self::SystemP), + "SYSTEM_USER" => Some(Self::SystemUser), + "TABLE" => Some(Self::Table), + "TABLES" => Some(Self::Tables), + "TABLESAMPLE" => Some(Self::Tablesample), + "TABLESPACE" => Some(Self::Tablespace), + "TARGET" => Some(Self::Target), + "TEMP" => Some(Self::Temp), + "TEMPLATE" => Some(Self::Template), + "TEMPORARY" => Some(Self::Temporary), + "TEXT_P" => Some(Self::TextP), + "THEN" => Some(Self::Then), + "TIES" => Some(Self::Ties), + "TIME" => Some(Self::Time), + "TIMESTAMP" => Some(Self::Timestamp), + "TO" => Some(Self::To), + "TRAILING" => Some(Self::Trailing), + "TRANSACTION" => Some(Self::Transaction), + "TRANSFORM" => Some(Self::Transform), + "TREAT" => Some(Self::Treat), + "TRIGGER" => Some(Self::Trigger), + "TRIM" => Some(Self::Trim), + "TRUE_P" => Some(Self::TrueP), + "TRUNCATE" => Some(Self::Truncate), + "TRUSTED" => Some(Self::Trusted), + "TYPE_P" => Some(Self::TypeP), + "TYPES_P" => Some(Self::TypesP), + "UESCAPE" => Some(Self::Uescape), + "UNBOUNDED" => Some(Self::Unbounded), + "UNCONDITIONAL" => Some(Self::Unconditional), + "UNCOMMITTED" => Some(Self::Uncommitted), + "UNENCRYPTED" => Some(Self::Unencrypted), + "UNION" => Some(Self::Union), + "UNIQUE" => Some(Self::Unique), + "UNKNOWN" => Some(Self::Unknown), + "UNLISTEN" => Some(Self::Unlisten), + "UNLOGGED" => Some(Self::Unlogged), + "UNTIL" => Some(Self::Until), + "UPDATE" => Some(Self::Update), + "USER" => Some(Self::User), + "USING" => Some(Self::Using), + "VACUUM" => Some(Self::Vacuum), + "VALID" => Some(Self::Valid), + "VALIDATE" => Some(Self::Validate), + "VALIDATOR" => Some(Self::Validator), + "VALUE_P" => Some(Self::ValueP), + "VALUES" => Some(Self::Values), + "VARCHAR" => Some(Self::Varchar), + "VARIADIC" => Some(Self::Variadic), + "VARYING" => Some(Self::Varying), + "VERBOSE" => Some(Self::Verbose), + "VERSION_P" => Some(Self::VersionP), + "VIEW" => Some(Self::View), + "VIEWS" => Some(Self::Views), + "VOLATILE" => Some(Self::Volatile), + "WHEN" => Some(Self::When), + "WHERE" => Some(Self::Where), + "WHITESPACE_P" => Some(Self::WhitespaceP), + "WINDOW" => Some(Self::Window), + "WITH" => Some(Self::With), + "WITHIN" => Some(Self::Within), + "WITHOUT" => Some(Self::Without), + "WORK" => Some(Self::Work), + "WRAPPER" => Some(Self::Wrapper), + "WRITE" => Some(Self::Write), + "XML_P" => Some(Self::XmlP), + "XMLATTRIBUTES" => Some(Self::Xmlattributes), + "XMLCONCAT" => Some(Self::Xmlconcat), + "XMLELEMENT" => Some(Self::Xmlelement), + "XMLEXISTS" => Some(Self::Xmlexists), + "XMLFOREST" => Some(Self::Xmlforest), + "XMLNAMESPACES" => Some(Self::Xmlnamespaces), + "XMLPARSE" => Some(Self::Xmlparse), + "XMLPI" => Some(Self::Xmlpi), + "XMLROOT" => Some(Self::Xmlroot), + "XMLSERIALIZE" => Some(Self::Xmlserialize), + "XMLTABLE" => Some(Self::Xmltable), + "YEAR_P" => Some(Self::YearP), + "YES_P" => Some(Self::YesP), + "ZONE" => Some(Self::Zone), + "FORMAT_LA" => Some(Self::FormatLa), + "NOT_LA" => Some(Self::NotLa), + "NULLS_LA" => Some(Self::NullsLa), + "WITH_LA" => Some(Self::WithLa), + "WITHOUT_LA" => Some(Self::WithoutLa), + "MODE_TYPE_NAME" => Some(Self::ModeTypeName), + "MODE_PLPGSQL_EXPR" => Some(Self::ModePlpgsqlExpr), + "MODE_PLPGSQL_ASSIGN1" => Some(Self::ModePlpgsqlAssign1), + "MODE_PLPGSQL_ASSIGN2" => Some(Self::ModePlpgsqlAssign2), + "MODE_PLPGSQL_ASSIGN3" => Some(Self::ModePlpgsqlAssign3), + "UMINUS" => Some(Self::Uminus), + _ => None, + } + } +} diff --git a/crates/pgt_query/src/scan.rs b/crates/pgt_query/src/scan.rs new file mode 100644 index 00000000..b12061e7 --- /dev/null +++ b/crates/pgt_query/src/scan.rs @@ -0,0 +1,33 @@ +use std::ffi::{CStr, CString}; + +use crate::bindings::*; +use crate::error::*; +use crate::protobuf; + +use prost::Message; + +/// Scans (lexes) the given SQL statement into tokens. +/// +/// # Example +/// +/// ```rust +/// let result = pgt_query::scan("SELECT * FROM contacts"); +/// assert!(result.is_ok()); +/// ``` +pub fn scan(sql: &str) -> Result { + let input = CString::new(sql)?; + let result = unsafe { pg_query_scan(input.as_ptr()) }; + let scan_result = if !result.error.is_null() { + let message = unsafe { CStr::from_ptr((*result.error).message) } + .to_string_lossy() + .to_string(); + Err(Error::Scan(message)) + } else { + let data = unsafe { + std::slice::from_raw_parts(result.pbuf.data as *const u8, result.pbuf.len as usize) + }; + protobuf::ScanResult::decode(data).map_err(Error::Decode) + }; + unsafe { pg_query_free_scan_result(result) }; + scan_result +} diff --git a/crates/pgt_query/src/split.rs b/crates/pgt_query/src/split.rs new file mode 100644 index 00000000..abb95eb8 --- /dev/null +++ b/crates/pgt_query/src/split.rs @@ -0,0 +1,86 @@ +use std::ffi::{CStr, CString}; + +use crate::bindings::*; +use crate::error::*; + +/// Split a well-formed query into separate statements. +/// +/// # Example +/// +/// ```rust +/// let query = r#"select /*;*/ 1; select "2;", (select 3);"#; +/// let statements = pgt_query::split_with_parser(query).unwrap(); +/// assert_eq!(statements, vec!["select /*;*/ 1", r#" select "2;", (select 3)"#]); +/// ``` +/// +/// However, `split_with_parser` will fail on malformed statements +/// +/// ```rust +/// let query = "select 1; this statement is not sql; select 2;"; +/// let result = pgt_query::split_with_parser(query); +/// let err = r#"syntax error at or near "this""#; +/// assert_eq!(result, Err(pgt_query::Error::Split(err.to_string()))); +/// ``` +pub fn split_with_parser(query: &str) -> Result> { + let input = CString::new(query)?; + let result = unsafe { pg_query_split_with_parser(input.as_ptr()) }; + let split_result = if !result.error.is_null() { + let message = unsafe { CStr::from_ptr((*result.error).message) } + .to_string_lossy() + .to_string(); + Err(Error::Split(message)) + } else { + let n_stmts = result.n_stmts as usize; + let mut statements = Vec::with_capacity(n_stmts); + for offset in 0..n_stmts { + let split_stmt = unsafe { *result.stmts.add(offset).read() }; + let start = split_stmt.stmt_location as usize; + let end = start + split_stmt.stmt_len as usize; + statements.push(&query[start..end]); + // not sure the start..end slice'll hold up for non-utf8 charsets + } + Ok(statements) + }; + unsafe { pg_query_free_split_result(result) }; + split_result +} + +/// Split a potentially-malformed query into separate statements. Note that +/// invalid tokens will be skipped +/// ```rust +/// let query = r#"select /*;*/ 1; asdf; select "2;", (select 3); asdf"#; +/// let statements = pgt_query::split_with_scanner(query).unwrap(); +/// assert_eq!(statements, vec![ +/// "select /*;*/ 1", +/// // skipped " asdf" since it was an invalid token +/// r#" select "2;", (select 3)"#, +/// ]); +/// ``` +pub fn split_with_scanner(query: &str) -> Result> { + let input = CString::new(query)?; + let result = unsafe { pg_query_split_with_scanner(input.as_ptr()) }; + let split_result = if !result.error.is_null() { + let message = unsafe { CStr::from_ptr((*result.error).message) } + .to_string_lossy() + .to_string(); + Err(Error::Split(message)) + } else { + // don't use result.stderr_buffer since it appears unused unless + // libpg_query is compiled with DEBUG defined. + let n_stmts = result.n_stmts as usize; + let mut start: usize; + let mut end: usize; + let mut statements = Vec::with_capacity(n_stmts); + for offset in 0..n_stmts { + let split_stmt = unsafe { *result.stmts.add(offset).read() }; + start = split_stmt.stmt_location as usize; + // TODO: consider comparing the new value of start to the old value + // of end to see if any region larger than a statement-separator got skipped + end = start + split_stmt.stmt_len as usize; + statements.push(&query[start..end]); + } + Ok(statements) + }; + unsafe { pg_query_free_split_result(result) }; + split_result +} diff --git a/crates/pgt_query_ext/Cargo.toml b/crates/pgt_query_ext/Cargo.toml index 3e6b57c1..9b4bfa1d 100644 --- a/crates/pgt_query_ext/Cargo.toml +++ b/crates/pgt_query_ext/Cargo.toml @@ -12,10 +12,8 @@ version = "0.0.0" [dependencies] -petgraph = "0.6.4" - -pg_query.workspace = true pgt_diagnostics.workspace = true +pgt_query.workspace = true pgt_text_size.workspace = true [lib] diff --git a/crates/pgt_query_ext/src/diagnostics.rs b/crates/pgt_query_ext/src/diagnostics.rs index 1a068dc0..4b5d92e9 100644 --- a/crates/pgt_query_ext/src/diagnostics.rs +++ b/crates/pgt_query_ext/src/diagnostics.rs @@ -30,8 +30,8 @@ impl SyntaxDiagnostic { } } -impl From for SyntaxDiagnostic { - fn from(err: pg_query::Error) -> Self { +impl From for SyntaxDiagnostic { + fn from(err: pgt_query::Error) -> Self { SyntaxDiagnostic { span: None, message: MessageAndDescription::from(err.to_string()), diff --git a/crates/pgt_query_ext/src/lib.rs b/crates/pgt_query_ext/src/lib.rs index 5882a778..4c630487 100644 --- a/crates/pgt_query_ext/src/lib.rs +++ b/crates/pgt_query_ext/src/lib.rs @@ -1,62 +1 @@ -//! Postgres Statement Parser -//! -//! Simple wrapper crate for `pg_query` to expose types and a function to get the root node for an -//! SQL statement. -//! -//! It also host any "extensions" to the `pg_query` crate that are not yet contributed upstream. -//! Extensions include -//! - `get_location` to get the location of a node -//! - `get_node_properties` to get the properties of a node -//! - `get_nodes` to get all the nodes in the AST as a petgraph tree -//! - `ChildrenIterator` to iterate over the children of a node pub mod diagnostics; - -pub use pg_query::protobuf; -pub use pg_query::{Error, NodeEnum, Result}; - -pub fn parse(sql: &str) -> Result { - pg_query::parse(sql).map(|parsed| { - parsed - .protobuf - .nodes() - .iter() - .find(|n| n.1 == 1) - .map(|n| n.0.to_enum()) - .ok_or_else(|| Error::Parse("Unable to find root node".to_string())) - })? -} - -/// This function parses a PL/pgSQL function. -/// -/// It expects the entire `CREATE FUNCTION` statement. -pub fn parse_plpgsql(sql: &str) -> Result<()> { - // we swallow the error until we have a proper binding - let _ = pg_query::parse_plpgsql(sql)?; - - Ok(()) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_parse_plpgsql_err() { - let input = " -create function test_organisation_id () - returns setof text - language plpgsql - security invoker - as $$ - -- syntax error here - decare - v_organisation_id uuid; -begin - select 1; -end -$$; - "; - - assert!(parse_plpgsql(input).is_err()); - } -} diff --git a/crates/pgt_query_proto_parser/Cargo.toml b/crates/pgt_query_macros/Cargo.toml similarity index 51% rename from crates/pgt_query_proto_parser/Cargo.toml rename to crates/pgt_query_macros/Cargo.toml index 729c94b4..0fcc52cf 100644 --- a/crates/pgt_query_proto_parser/Cargo.toml +++ b/crates/pgt_query_macros/Cargo.toml @@ -6,15 +6,19 @@ edition.workspace = true homepage.workspace = true keywords.workspace = true license.workspace = true -name = "pgt_query_proto_parser" +name = "pgt_query_macros" repository.workspace = true version = "0.0.0" - [dependencies] -convert_case = "0.6.0" -protobuf = "3.3.0" -protobuf-parse = "3.3.0" +convert_case = { workspace = true } +proc-macro2.workspace = true +prost-reflect = { workspace = true } +protox = { workspace = true } +quote.workspace = true [lib] -doctest = false +proc-macro = true + +[build-dependencies] +ureq = "2.9" diff --git a/crates/pgt_query_macros/build.rs b/crates/pgt_query_macros/build.rs new file mode 100644 index 00000000..db83ce86 --- /dev/null +++ b/crates/pgt_query_macros/build.rs @@ -0,0 +1,59 @@ +use std::env; +use std::fs; +use std::io::Write; +use std::path::PathBuf; + +// This should match the version used by pgt_query crate +// You can configure this via environment variable PG_QUERY_VERSION if needed +static LIBPG_QUERY_TAG: &str = "17-6.1.0"; + +fn main() -> Result<(), Box> { + // Allow version override via environment variable + let version = env::var("PG_QUERY_VERSION").unwrap_or_else(|_| LIBPG_QUERY_TAG.to_string()); + + // Get the manifest directory (source directory) + let manifest_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")?); + let postgres_dir = manifest_dir.join("postgres"); + let proto_filename = format!("{}.proto", version); + let proto_path = postgres_dir.join(&proto_filename); + + // Download proto file if not already present in source directory + if !proto_path.exists() { + println!( + "cargo:warning=Downloading pg_query.proto for libpg_query {} to source directory", + version + ); + + // Create postgres directory if it doesn't exist + fs::create_dir_all(&postgres_dir)?; + + // Download the proto file + let proto_url = format!( + "https://raw.githubusercontent.com/pganalyze/libpg_query/{}/protobuf/pg_query.proto", + version + ); + + let response = ureq::get(&proto_url).call()?; + let proto_content = response.into_string()?; + + // Write proto file to source directory + let mut file = fs::File::create(&proto_path)?; + file.write_all(proto_content.as_bytes())?; + + println!( + "cargo:warning=Successfully downloaded pg_query.proto to {}", + proto_path.display() + ); + } + + // Set environment variable for the proc macro + println!( + "cargo:rustc-env=PG_QUERY_PROTO_PATH={}", + proto_path.display() + ); + + // Tell cargo to rerun if the proto file changes + println!("cargo:rerun-if-changed={}", proto_path.display()); + + Ok(()) +} diff --git a/crates/pgt_query_macros/postgres/17-6.1.0.proto b/crates/pgt_query_macros/postgres/17-6.1.0.proto new file mode 100644 index 00000000..24a8f14c --- /dev/null +++ b/crates/pgt_query_macros/postgres/17-6.1.0.proto @@ -0,0 +1,4110 @@ +// This file is autogenerated by ./scripts/generate_protobuf_and_funcs.rb + +syntax = "proto3"; + +package pg_query; + +message ParseResult { + int32 version = 1; + repeated RawStmt stmts = 2; +} + +message ScanResult { + int32 version = 1; + repeated ScanToken tokens = 2; +} + +message Node { + oneof node { + Alias alias = 1 [json_name="Alias"]; + RangeVar range_var = 2 [json_name="RangeVar"]; + TableFunc table_func = 3 [json_name="TableFunc"]; + IntoClause into_clause = 4 [json_name="IntoClause"]; + Var var = 5 [json_name="Var"]; + Param param = 6 [json_name="Param"]; + Aggref aggref = 7 [json_name="Aggref"]; + GroupingFunc grouping_func = 8 [json_name="GroupingFunc"]; + WindowFunc window_func = 9 [json_name="WindowFunc"]; + WindowFuncRunCondition window_func_run_condition = 10 [json_name="WindowFuncRunCondition"]; + MergeSupportFunc merge_support_func = 11 [json_name="MergeSupportFunc"]; + SubscriptingRef subscripting_ref = 12 [json_name="SubscriptingRef"]; + FuncExpr func_expr = 13 [json_name="FuncExpr"]; + NamedArgExpr named_arg_expr = 14 [json_name="NamedArgExpr"]; + OpExpr op_expr = 15 [json_name="OpExpr"]; + DistinctExpr distinct_expr = 16 [json_name="DistinctExpr"]; + NullIfExpr null_if_expr = 17 [json_name="NullIfExpr"]; + ScalarArrayOpExpr scalar_array_op_expr = 18 [json_name="ScalarArrayOpExpr"]; + BoolExpr bool_expr = 19 [json_name="BoolExpr"]; + SubLink sub_link = 20 [json_name="SubLink"]; + SubPlan sub_plan = 21 [json_name="SubPlan"]; + AlternativeSubPlan alternative_sub_plan = 22 [json_name="AlternativeSubPlan"]; + FieldSelect field_select = 23 [json_name="FieldSelect"]; + FieldStore field_store = 24 [json_name="FieldStore"]; + RelabelType relabel_type = 25 [json_name="RelabelType"]; + CoerceViaIO coerce_via_io = 26 [json_name="CoerceViaIO"]; + ArrayCoerceExpr array_coerce_expr = 27 [json_name="ArrayCoerceExpr"]; + ConvertRowtypeExpr convert_rowtype_expr = 28 [json_name="ConvertRowtypeExpr"]; + CollateExpr collate_expr = 29 [json_name="CollateExpr"]; + CaseExpr case_expr = 30 [json_name="CaseExpr"]; + CaseWhen case_when = 31 [json_name="CaseWhen"]; + CaseTestExpr case_test_expr = 32 [json_name="CaseTestExpr"]; + ArrayExpr array_expr = 33 [json_name="ArrayExpr"]; + RowExpr row_expr = 34 [json_name="RowExpr"]; + RowCompareExpr row_compare_expr = 35 [json_name="RowCompareExpr"]; + CoalesceExpr coalesce_expr = 36 [json_name="CoalesceExpr"]; + MinMaxExpr min_max_expr = 37 [json_name="MinMaxExpr"]; + SQLValueFunction sqlvalue_function = 38 [json_name="SQLValueFunction"]; + XmlExpr xml_expr = 39 [json_name="XmlExpr"]; + JsonFormat json_format = 40 [json_name="JsonFormat"]; + JsonReturning json_returning = 41 [json_name="JsonReturning"]; + JsonValueExpr json_value_expr = 42 [json_name="JsonValueExpr"]; + JsonConstructorExpr json_constructor_expr = 43 [json_name="JsonConstructorExpr"]; + JsonIsPredicate json_is_predicate = 44 [json_name="JsonIsPredicate"]; + JsonBehavior json_behavior = 45 [json_name="JsonBehavior"]; + JsonExpr json_expr = 46 [json_name="JsonExpr"]; + JsonTablePath json_table_path = 47 [json_name="JsonTablePath"]; + JsonTablePathScan json_table_path_scan = 48 [json_name="JsonTablePathScan"]; + JsonTableSiblingJoin json_table_sibling_join = 49 [json_name="JsonTableSiblingJoin"]; + NullTest null_test = 50 [json_name="NullTest"]; + BooleanTest boolean_test = 51 [json_name="BooleanTest"]; + MergeAction merge_action = 52 [json_name="MergeAction"]; + CoerceToDomain coerce_to_domain = 53 [json_name="CoerceToDomain"]; + CoerceToDomainValue coerce_to_domain_value = 54 [json_name="CoerceToDomainValue"]; + SetToDefault set_to_default = 55 [json_name="SetToDefault"]; + CurrentOfExpr current_of_expr = 56 [json_name="CurrentOfExpr"]; + NextValueExpr next_value_expr = 57 [json_name="NextValueExpr"]; + InferenceElem inference_elem = 58 [json_name="InferenceElem"]; + TargetEntry target_entry = 59 [json_name="TargetEntry"]; + RangeTblRef range_tbl_ref = 60 [json_name="RangeTblRef"]; + JoinExpr join_expr = 61 [json_name="JoinExpr"]; + FromExpr from_expr = 62 [json_name="FromExpr"]; + OnConflictExpr on_conflict_expr = 63 [json_name="OnConflictExpr"]; + Query query = 64 [json_name="Query"]; + TypeName type_name = 65 [json_name="TypeName"]; + ColumnRef column_ref = 66 [json_name="ColumnRef"]; + ParamRef param_ref = 67 [json_name="ParamRef"]; + A_Expr a_expr = 68 [json_name="A_Expr"]; + TypeCast type_cast = 69 [json_name="TypeCast"]; + CollateClause collate_clause = 70 [json_name="CollateClause"]; + RoleSpec role_spec = 71 [json_name="RoleSpec"]; + FuncCall func_call = 72 [json_name="FuncCall"]; + A_Star a_star = 73 [json_name="A_Star"]; + A_Indices a_indices = 74 [json_name="A_Indices"]; + A_Indirection a_indirection = 75 [json_name="A_Indirection"]; + A_ArrayExpr a_array_expr = 76 [json_name="A_ArrayExpr"]; + ResTarget res_target = 77 [json_name="ResTarget"]; + MultiAssignRef multi_assign_ref = 78 [json_name="MultiAssignRef"]; + SortBy sort_by = 79 [json_name="SortBy"]; + WindowDef window_def = 80 [json_name="WindowDef"]; + RangeSubselect range_subselect = 81 [json_name="RangeSubselect"]; + RangeFunction range_function = 82 [json_name="RangeFunction"]; + RangeTableFunc range_table_func = 83 [json_name="RangeTableFunc"]; + RangeTableFuncCol range_table_func_col = 84 [json_name="RangeTableFuncCol"]; + RangeTableSample range_table_sample = 85 [json_name="RangeTableSample"]; + ColumnDef column_def = 86 [json_name="ColumnDef"]; + TableLikeClause table_like_clause = 87 [json_name="TableLikeClause"]; + IndexElem index_elem = 88 [json_name="IndexElem"]; + DefElem def_elem = 89 [json_name="DefElem"]; + LockingClause locking_clause = 90 [json_name="LockingClause"]; + XmlSerialize xml_serialize = 91 [json_name="XmlSerialize"]; + PartitionElem partition_elem = 92 [json_name="PartitionElem"]; + PartitionSpec partition_spec = 93 [json_name="PartitionSpec"]; + PartitionBoundSpec partition_bound_spec = 94 [json_name="PartitionBoundSpec"]; + PartitionRangeDatum partition_range_datum = 95 [json_name="PartitionRangeDatum"]; + SinglePartitionSpec single_partition_spec = 96 [json_name="SinglePartitionSpec"]; + PartitionCmd partition_cmd = 97 [json_name="PartitionCmd"]; + RangeTblEntry range_tbl_entry = 98 [json_name="RangeTblEntry"]; + RTEPermissionInfo rtepermission_info = 99 [json_name="RTEPermissionInfo"]; + RangeTblFunction range_tbl_function = 100 [json_name="RangeTblFunction"]; + TableSampleClause table_sample_clause = 101 [json_name="TableSampleClause"]; + WithCheckOption with_check_option = 102 [json_name="WithCheckOption"]; + SortGroupClause sort_group_clause = 103 [json_name="SortGroupClause"]; + GroupingSet grouping_set = 104 [json_name="GroupingSet"]; + WindowClause window_clause = 105 [json_name="WindowClause"]; + RowMarkClause row_mark_clause = 106 [json_name="RowMarkClause"]; + WithClause with_clause = 107 [json_name="WithClause"]; + InferClause infer_clause = 108 [json_name="InferClause"]; + OnConflictClause on_conflict_clause = 109 [json_name="OnConflictClause"]; + CTESearchClause ctesearch_clause = 110 [json_name="CTESearchClause"]; + CTECycleClause ctecycle_clause = 111 [json_name="CTECycleClause"]; + CommonTableExpr common_table_expr = 112 [json_name="CommonTableExpr"]; + MergeWhenClause merge_when_clause = 113 [json_name="MergeWhenClause"]; + TriggerTransition trigger_transition = 114 [json_name="TriggerTransition"]; + JsonOutput json_output = 115 [json_name="JsonOutput"]; + JsonArgument json_argument = 116 [json_name="JsonArgument"]; + JsonFuncExpr json_func_expr = 117 [json_name="JsonFuncExpr"]; + JsonTablePathSpec json_table_path_spec = 118 [json_name="JsonTablePathSpec"]; + JsonTable json_table = 119 [json_name="JsonTable"]; + JsonTableColumn json_table_column = 120 [json_name="JsonTableColumn"]; + JsonKeyValue json_key_value = 121 [json_name="JsonKeyValue"]; + JsonParseExpr json_parse_expr = 122 [json_name="JsonParseExpr"]; + JsonScalarExpr json_scalar_expr = 123 [json_name="JsonScalarExpr"]; + JsonSerializeExpr json_serialize_expr = 124 [json_name="JsonSerializeExpr"]; + JsonObjectConstructor json_object_constructor = 125 [json_name="JsonObjectConstructor"]; + JsonArrayConstructor json_array_constructor = 126 [json_name="JsonArrayConstructor"]; + JsonArrayQueryConstructor json_array_query_constructor = 127 [json_name="JsonArrayQueryConstructor"]; + JsonAggConstructor json_agg_constructor = 128 [json_name="JsonAggConstructor"]; + JsonObjectAgg json_object_agg = 129 [json_name="JsonObjectAgg"]; + JsonArrayAgg json_array_agg = 130 [json_name="JsonArrayAgg"]; + RawStmt raw_stmt = 131 [json_name="RawStmt"]; + InsertStmt insert_stmt = 132 [json_name="InsertStmt"]; + DeleteStmt delete_stmt = 133 [json_name="DeleteStmt"]; + UpdateStmt update_stmt = 134 [json_name="UpdateStmt"]; + MergeStmt merge_stmt = 135 [json_name="MergeStmt"]; + SelectStmt select_stmt = 136 [json_name="SelectStmt"]; + SetOperationStmt set_operation_stmt = 137 [json_name="SetOperationStmt"]; + ReturnStmt return_stmt = 138 [json_name="ReturnStmt"]; + PLAssignStmt plassign_stmt = 139 [json_name="PLAssignStmt"]; + CreateSchemaStmt create_schema_stmt = 140 [json_name="CreateSchemaStmt"]; + AlterTableStmt alter_table_stmt = 141 [json_name="AlterTableStmt"]; + ReplicaIdentityStmt replica_identity_stmt = 142 [json_name="ReplicaIdentityStmt"]; + AlterTableCmd alter_table_cmd = 143 [json_name="AlterTableCmd"]; + AlterCollationStmt alter_collation_stmt = 144 [json_name="AlterCollationStmt"]; + AlterDomainStmt alter_domain_stmt = 145 [json_name="AlterDomainStmt"]; + GrantStmt grant_stmt = 146 [json_name="GrantStmt"]; + ObjectWithArgs object_with_args = 147 [json_name="ObjectWithArgs"]; + AccessPriv access_priv = 148 [json_name="AccessPriv"]; + GrantRoleStmt grant_role_stmt = 149 [json_name="GrantRoleStmt"]; + AlterDefaultPrivilegesStmt alter_default_privileges_stmt = 150 [json_name="AlterDefaultPrivilegesStmt"]; + CopyStmt copy_stmt = 151 [json_name="CopyStmt"]; + VariableSetStmt variable_set_stmt = 152 [json_name="VariableSetStmt"]; + VariableShowStmt variable_show_stmt = 153 [json_name="VariableShowStmt"]; + CreateStmt create_stmt = 154 [json_name="CreateStmt"]; + Constraint constraint = 155 [json_name="Constraint"]; + CreateTableSpaceStmt create_table_space_stmt = 156 [json_name="CreateTableSpaceStmt"]; + DropTableSpaceStmt drop_table_space_stmt = 157 [json_name="DropTableSpaceStmt"]; + AlterTableSpaceOptionsStmt alter_table_space_options_stmt = 158 [json_name="AlterTableSpaceOptionsStmt"]; + AlterTableMoveAllStmt alter_table_move_all_stmt = 159 [json_name="AlterTableMoveAllStmt"]; + CreateExtensionStmt create_extension_stmt = 160 [json_name="CreateExtensionStmt"]; + AlterExtensionStmt alter_extension_stmt = 161 [json_name="AlterExtensionStmt"]; + AlterExtensionContentsStmt alter_extension_contents_stmt = 162 [json_name="AlterExtensionContentsStmt"]; + CreateFdwStmt create_fdw_stmt = 163 [json_name="CreateFdwStmt"]; + AlterFdwStmt alter_fdw_stmt = 164 [json_name="AlterFdwStmt"]; + CreateForeignServerStmt create_foreign_server_stmt = 165 [json_name="CreateForeignServerStmt"]; + AlterForeignServerStmt alter_foreign_server_stmt = 166 [json_name="AlterForeignServerStmt"]; + CreateForeignTableStmt create_foreign_table_stmt = 167 [json_name="CreateForeignTableStmt"]; + CreateUserMappingStmt create_user_mapping_stmt = 168 [json_name="CreateUserMappingStmt"]; + AlterUserMappingStmt alter_user_mapping_stmt = 169 [json_name="AlterUserMappingStmt"]; + DropUserMappingStmt drop_user_mapping_stmt = 170 [json_name="DropUserMappingStmt"]; + ImportForeignSchemaStmt import_foreign_schema_stmt = 171 [json_name="ImportForeignSchemaStmt"]; + CreatePolicyStmt create_policy_stmt = 172 [json_name="CreatePolicyStmt"]; + AlterPolicyStmt alter_policy_stmt = 173 [json_name="AlterPolicyStmt"]; + CreateAmStmt create_am_stmt = 174 [json_name="CreateAmStmt"]; + CreateTrigStmt create_trig_stmt = 175 [json_name="CreateTrigStmt"]; + CreateEventTrigStmt create_event_trig_stmt = 176 [json_name="CreateEventTrigStmt"]; + AlterEventTrigStmt alter_event_trig_stmt = 177 [json_name="AlterEventTrigStmt"]; + CreatePLangStmt create_plang_stmt = 178 [json_name="CreatePLangStmt"]; + CreateRoleStmt create_role_stmt = 179 [json_name="CreateRoleStmt"]; + AlterRoleStmt alter_role_stmt = 180 [json_name="AlterRoleStmt"]; + AlterRoleSetStmt alter_role_set_stmt = 181 [json_name="AlterRoleSetStmt"]; + DropRoleStmt drop_role_stmt = 182 [json_name="DropRoleStmt"]; + CreateSeqStmt create_seq_stmt = 183 [json_name="CreateSeqStmt"]; + AlterSeqStmt alter_seq_stmt = 184 [json_name="AlterSeqStmt"]; + DefineStmt define_stmt = 185 [json_name="DefineStmt"]; + CreateDomainStmt create_domain_stmt = 186 [json_name="CreateDomainStmt"]; + CreateOpClassStmt create_op_class_stmt = 187 [json_name="CreateOpClassStmt"]; + CreateOpClassItem create_op_class_item = 188 [json_name="CreateOpClassItem"]; + CreateOpFamilyStmt create_op_family_stmt = 189 [json_name="CreateOpFamilyStmt"]; + AlterOpFamilyStmt alter_op_family_stmt = 190 [json_name="AlterOpFamilyStmt"]; + DropStmt drop_stmt = 191 [json_name="DropStmt"]; + TruncateStmt truncate_stmt = 192 [json_name="TruncateStmt"]; + CommentStmt comment_stmt = 193 [json_name="CommentStmt"]; + SecLabelStmt sec_label_stmt = 194 [json_name="SecLabelStmt"]; + DeclareCursorStmt declare_cursor_stmt = 195 [json_name="DeclareCursorStmt"]; + ClosePortalStmt close_portal_stmt = 196 [json_name="ClosePortalStmt"]; + FetchStmt fetch_stmt = 197 [json_name="FetchStmt"]; + IndexStmt index_stmt = 198 [json_name="IndexStmt"]; + CreateStatsStmt create_stats_stmt = 199 [json_name="CreateStatsStmt"]; + StatsElem stats_elem = 200 [json_name="StatsElem"]; + AlterStatsStmt alter_stats_stmt = 201 [json_name="AlterStatsStmt"]; + CreateFunctionStmt create_function_stmt = 202 [json_name="CreateFunctionStmt"]; + FunctionParameter function_parameter = 203 [json_name="FunctionParameter"]; + AlterFunctionStmt alter_function_stmt = 204 [json_name="AlterFunctionStmt"]; + DoStmt do_stmt = 205 [json_name="DoStmt"]; + InlineCodeBlock inline_code_block = 206 [json_name="InlineCodeBlock"]; + CallStmt call_stmt = 207 [json_name="CallStmt"]; + CallContext call_context = 208 [json_name="CallContext"]; + RenameStmt rename_stmt = 209 [json_name="RenameStmt"]; + AlterObjectDependsStmt alter_object_depends_stmt = 210 [json_name="AlterObjectDependsStmt"]; + AlterObjectSchemaStmt alter_object_schema_stmt = 211 [json_name="AlterObjectSchemaStmt"]; + AlterOwnerStmt alter_owner_stmt = 212 [json_name="AlterOwnerStmt"]; + AlterOperatorStmt alter_operator_stmt = 213 [json_name="AlterOperatorStmt"]; + AlterTypeStmt alter_type_stmt = 214 [json_name="AlterTypeStmt"]; + RuleStmt rule_stmt = 215 [json_name="RuleStmt"]; + NotifyStmt notify_stmt = 216 [json_name="NotifyStmt"]; + ListenStmt listen_stmt = 217 [json_name="ListenStmt"]; + UnlistenStmt unlisten_stmt = 218 [json_name="UnlistenStmt"]; + TransactionStmt transaction_stmt = 219 [json_name="TransactionStmt"]; + CompositeTypeStmt composite_type_stmt = 220 [json_name="CompositeTypeStmt"]; + CreateEnumStmt create_enum_stmt = 221 [json_name="CreateEnumStmt"]; + CreateRangeStmt create_range_stmt = 222 [json_name="CreateRangeStmt"]; + AlterEnumStmt alter_enum_stmt = 223 [json_name="AlterEnumStmt"]; + ViewStmt view_stmt = 224 [json_name="ViewStmt"]; + LoadStmt load_stmt = 225 [json_name="LoadStmt"]; + CreatedbStmt createdb_stmt = 226 [json_name="CreatedbStmt"]; + AlterDatabaseStmt alter_database_stmt = 227 [json_name="AlterDatabaseStmt"]; + AlterDatabaseRefreshCollStmt alter_database_refresh_coll_stmt = 228 [json_name="AlterDatabaseRefreshCollStmt"]; + AlterDatabaseSetStmt alter_database_set_stmt = 229 [json_name="AlterDatabaseSetStmt"]; + DropdbStmt dropdb_stmt = 230 [json_name="DropdbStmt"]; + AlterSystemStmt alter_system_stmt = 231 [json_name="AlterSystemStmt"]; + ClusterStmt cluster_stmt = 232 [json_name="ClusterStmt"]; + VacuumStmt vacuum_stmt = 233 [json_name="VacuumStmt"]; + VacuumRelation vacuum_relation = 234 [json_name="VacuumRelation"]; + ExplainStmt explain_stmt = 235 [json_name="ExplainStmt"]; + CreateTableAsStmt create_table_as_stmt = 236 [json_name="CreateTableAsStmt"]; + RefreshMatViewStmt refresh_mat_view_stmt = 237 [json_name="RefreshMatViewStmt"]; + CheckPointStmt check_point_stmt = 238 [json_name="CheckPointStmt"]; + DiscardStmt discard_stmt = 239 [json_name="DiscardStmt"]; + LockStmt lock_stmt = 240 [json_name="LockStmt"]; + ConstraintsSetStmt constraints_set_stmt = 241 [json_name="ConstraintsSetStmt"]; + ReindexStmt reindex_stmt = 242 [json_name="ReindexStmt"]; + CreateConversionStmt create_conversion_stmt = 243 [json_name="CreateConversionStmt"]; + CreateCastStmt create_cast_stmt = 244 [json_name="CreateCastStmt"]; + CreateTransformStmt create_transform_stmt = 245 [json_name="CreateTransformStmt"]; + PrepareStmt prepare_stmt = 246 [json_name="PrepareStmt"]; + ExecuteStmt execute_stmt = 247 [json_name="ExecuteStmt"]; + DeallocateStmt deallocate_stmt = 248 [json_name="DeallocateStmt"]; + DropOwnedStmt drop_owned_stmt = 249 [json_name="DropOwnedStmt"]; + ReassignOwnedStmt reassign_owned_stmt = 250 [json_name="ReassignOwnedStmt"]; + AlterTSDictionaryStmt alter_tsdictionary_stmt = 251 [json_name="AlterTSDictionaryStmt"]; + AlterTSConfigurationStmt alter_tsconfiguration_stmt = 252 [json_name="AlterTSConfigurationStmt"]; + PublicationTable publication_table = 253 [json_name="PublicationTable"]; + PublicationObjSpec publication_obj_spec = 254 [json_name="PublicationObjSpec"]; + CreatePublicationStmt create_publication_stmt = 255 [json_name="CreatePublicationStmt"]; + AlterPublicationStmt alter_publication_stmt = 256 [json_name="AlterPublicationStmt"]; + CreateSubscriptionStmt create_subscription_stmt = 257 [json_name="CreateSubscriptionStmt"]; + AlterSubscriptionStmt alter_subscription_stmt = 258 [json_name="AlterSubscriptionStmt"]; + DropSubscriptionStmt drop_subscription_stmt = 259 [json_name="DropSubscriptionStmt"]; + Integer integer = 260 [json_name="Integer"]; + Float float = 261 [json_name="Float"]; + Boolean boolean = 262 [json_name="Boolean"]; + String string = 263 [json_name="String"]; + BitString bit_string = 264 [json_name="BitString"]; + List list = 265 [json_name="List"]; + IntList int_list = 266 [json_name="IntList"]; + OidList oid_list = 267 [json_name="OidList"]; + A_Const a_const = 268 [json_name="A_Const"]; + } +} + +message Integer +{ + int32 ival = 1; /* machine integer */ +} + +message Float +{ + string fval = 1; /* string */ +} + +message Boolean +{ + bool boolval = 1; +} + +message String +{ + string sval = 1; /* string */ +} + +message BitString +{ + string bsval = 1; /* string */ +} + +message List +{ + repeated Node items = 1; +} + +message OidList +{ + repeated Node items = 1; +} + +message IntList +{ + repeated Node items = 1; +} + +message A_Const +{ + oneof val { + Integer ival = 1; + Float fval = 2; + Boolean boolval = 3; + String sval = 4; + BitString bsval = 5; + } + bool isnull = 10; + int32 location = 11; +} + +message Alias +{ + string aliasname = 1 [json_name="aliasname"]; + repeated Node colnames = 2 [json_name="colnames"]; +} + +message RangeVar +{ + string catalogname = 1 [json_name="catalogname"]; + string schemaname = 2 [json_name="schemaname"]; + string relname = 3 [json_name="relname"]; + bool inh = 4 [json_name="inh"]; + string relpersistence = 5 [json_name="relpersistence"]; + Alias alias = 6 [json_name="alias"]; + int32 location = 7 [json_name="location"]; +} + +message TableFunc +{ + TableFuncType functype = 1 [json_name="functype"]; + repeated Node ns_uris = 2 [json_name="ns_uris"]; + repeated Node ns_names = 3 [json_name="ns_names"]; + Node docexpr = 4 [json_name="docexpr"]; + Node rowexpr = 5 [json_name="rowexpr"]; + repeated Node colnames = 6 [json_name="colnames"]; + repeated Node coltypes = 7 [json_name="coltypes"]; + repeated Node coltypmods = 8 [json_name="coltypmods"]; + repeated Node colcollations = 9 [json_name="colcollations"]; + repeated Node colexprs = 10 [json_name="colexprs"]; + repeated Node coldefexprs = 11 [json_name="coldefexprs"]; + repeated Node colvalexprs = 12 [json_name="colvalexprs"]; + repeated Node passingvalexprs = 13 [json_name="passingvalexprs"]; + repeated uint64 notnulls = 14 [json_name="notnulls"]; + Node plan = 15 [json_name="plan"]; + int32 ordinalitycol = 16 [json_name="ordinalitycol"]; + int32 location = 17 [json_name="location"]; +} + +message IntoClause +{ + RangeVar rel = 1 [json_name="rel"]; + repeated Node col_names = 2 [json_name="colNames"]; + string access_method = 3 [json_name="accessMethod"]; + repeated Node options = 4 [json_name="options"]; + OnCommitAction on_commit = 5 [json_name="onCommit"]; + string table_space_name = 6 [json_name="tableSpaceName"]; + Node view_query = 7 [json_name="viewQuery"]; + bool skip_data = 8 [json_name="skipData"]; +} + +message Var +{ + Node xpr = 1 [json_name="xpr"]; + int32 varno = 2 [json_name="varno"]; + int32 varattno = 3 [json_name="varattno"]; + uint32 vartype = 4 [json_name="vartype"]; + int32 vartypmod = 5 [json_name="vartypmod"]; + uint32 varcollid = 6 [json_name="varcollid"]; + repeated uint64 varnullingrels = 7 [json_name="varnullingrels"]; + uint32 varlevelsup = 8 [json_name="varlevelsup"]; + int32 location = 9 [json_name="location"]; +} + +message Param +{ + Node xpr = 1 [json_name="xpr"]; + ParamKind paramkind = 2 [json_name="paramkind"]; + int32 paramid = 3 [json_name="paramid"]; + uint32 paramtype = 4 [json_name="paramtype"]; + int32 paramtypmod = 5 [json_name="paramtypmod"]; + uint32 paramcollid = 6 [json_name="paramcollid"]; + int32 location = 7 [json_name="location"]; +} + +message Aggref +{ + Node xpr = 1 [json_name="xpr"]; + uint32 aggfnoid = 2 [json_name="aggfnoid"]; + uint32 aggtype = 3 [json_name="aggtype"]; + uint32 aggcollid = 4 [json_name="aggcollid"]; + uint32 inputcollid = 5 [json_name="inputcollid"]; + repeated Node aggargtypes = 6 [json_name="aggargtypes"]; + repeated Node aggdirectargs = 7 [json_name="aggdirectargs"]; + repeated Node args = 8 [json_name="args"]; + repeated Node aggorder = 9 [json_name="aggorder"]; + repeated Node aggdistinct = 10 [json_name="aggdistinct"]; + Node aggfilter = 11 [json_name="aggfilter"]; + bool aggstar = 12 [json_name="aggstar"]; + bool aggvariadic = 13 [json_name="aggvariadic"]; + string aggkind = 14 [json_name="aggkind"]; + uint32 agglevelsup = 15 [json_name="agglevelsup"]; + AggSplit aggsplit = 16 [json_name="aggsplit"]; + int32 aggno = 17 [json_name="aggno"]; + int32 aggtransno = 18 [json_name="aggtransno"]; + int32 location = 19 [json_name="location"]; +} + +message GroupingFunc +{ + Node xpr = 1 [json_name="xpr"]; + repeated Node args = 2 [json_name="args"]; + repeated Node refs = 3 [json_name="refs"]; + uint32 agglevelsup = 4 [json_name="agglevelsup"]; + int32 location = 5 [json_name="location"]; +} + +message WindowFunc +{ + Node xpr = 1 [json_name="xpr"]; + uint32 winfnoid = 2 [json_name="winfnoid"]; + uint32 wintype = 3 [json_name="wintype"]; + uint32 wincollid = 4 [json_name="wincollid"]; + uint32 inputcollid = 5 [json_name="inputcollid"]; + repeated Node args = 6 [json_name="args"]; + Node aggfilter = 7 [json_name="aggfilter"]; + repeated Node run_condition = 8 [json_name="runCondition"]; + uint32 winref = 9 [json_name="winref"]; + bool winstar = 10 [json_name="winstar"]; + bool winagg = 11 [json_name="winagg"]; + int32 location = 12 [json_name="location"]; +} + +message WindowFuncRunCondition +{ + Node xpr = 1 [json_name="xpr"]; + uint32 opno = 2 [json_name="opno"]; + uint32 inputcollid = 3 [json_name="inputcollid"]; + bool wfunc_left = 4 [json_name="wfunc_left"]; + Node arg = 5 [json_name="arg"]; +} + +message MergeSupportFunc +{ + Node xpr = 1 [json_name="xpr"]; + uint32 msftype = 2 [json_name="msftype"]; + uint32 msfcollid = 3 [json_name="msfcollid"]; + int32 location = 4 [json_name="location"]; +} + +message SubscriptingRef +{ + Node xpr = 1 [json_name="xpr"]; + uint32 refcontainertype = 2 [json_name="refcontainertype"]; + uint32 refelemtype = 3 [json_name="refelemtype"]; + uint32 refrestype = 4 [json_name="refrestype"]; + int32 reftypmod = 5 [json_name="reftypmod"]; + uint32 refcollid = 6 [json_name="refcollid"]; + repeated Node refupperindexpr = 7 [json_name="refupperindexpr"]; + repeated Node reflowerindexpr = 8 [json_name="reflowerindexpr"]; + Node refexpr = 9 [json_name="refexpr"]; + Node refassgnexpr = 10 [json_name="refassgnexpr"]; +} + +message FuncExpr +{ + Node xpr = 1 [json_name="xpr"]; + uint32 funcid = 2 [json_name="funcid"]; + uint32 funcresulttype = 3 [json_name="funcresulttype"]; + bool funcretset = 4 [json_name="funcretset"]; + bool funcvariadic = 5 [json_name="funcvariadic"]; + CoercionForm funcformat = 6 [json_name="funcformat"]; + uint32 funccollid = 7 [json_name="funccollid"]; + uint32 inputcollid = 8 [json_name="inputcollid"]; + repeated Node args = 9 [json_name="args"]; + int32 location = 10 [json_name="location"]; +} + +message NamedArgExpr +{ + Node xpr = 1 [json_name="xpr"]; + Node arg = 2 [json_name="arg"]; + string name = 3 [json_name="name"]; + int32 argnumber = 4 [json_name="argnumber"]; + int32 location = 5 [json_name="location"]; +} + +message OpExpr +{ + Node xpr = 1 [json_name="xpr"]; + uint32 opno = 2 [json_name="opno"]; + uint32 opresulttype = 3 [json_name="opresulttype"]; + bool opretset = 4 [json_name="opretset"]; + uint32 opcollid = 5 [json_name="opcollid"]; + uint32 inputcollid = 6 [json_name="inputcollid"]; + repeated Node args = 7 [json_name="args"]; + int32 location = 8 [json_name="location"]; +} + +message DistinctExpr +{ + Node xpr = 1 [json_name="xpr"]; + uint32 opno = 2 [json_name="opno"]; + uint32 opresulttype = 3 [json_name="opresulttype"]; + bool opretset = 4 [json_name="opretset"]; + uint32 opcollid = 5 [json_name="opcollid"]; + uint32 inputcollid = 6 [json_name="inputcollid"]; + repeated Node args = 7 [json_name="args"]; + int32 location = 8 [json_name="location"]; +} + +message NullIfExpr +{ + Node xpr = 1 [json_name="xpr"]; + uint32 opno = 2 [json_name="opno"]; + uint32 opresulttype = 3 [json_name="opresulttype"]; + bool opretset = 4 [json_name="opretset"]; + uint32 opcollid = 5 [json_name="opcollid"]; + uint32 inputcollid = 6 [json_name="inputcollid"]; + repeated Node args = 7 [json_name="args"]; + int32 location = 8 [json_name="location"]; +} + +message ScalarArrayOpExpr +{ + Node xpr = 1 [json_name="xpr"]; + uint32 opno = 2 [json_name="opno"]; + bool use_or = 3 [json_name="useOr"]; + uint32 inputcollid = 4 [json_name="inputcollid"]; + repeated Node args = 5 [json_name="args"]; + int32 location = 6 [json_name="location"]; +} + +message BoolExpr +{ + Node xpr = 1 [json_name="xpr"]; + BoolExprType boolop = 2 [json_name="boolop"]; + repeated Node args = 3 [json_name="args"]; + int32 location = 4 [json_name="location"]; +} + +message SubLink +{ + Node xpr = 1 [json_name="xpr"]; + SubLinkType sub_link_type = 2 [json_name="subLinkType"]; + int32 sub_link_id = 3 [json_name="subLinkId"]; + Node testexpr = 4 [json_name="testexpr"]; + repeated Node oper_name = 5 [json_name="operName"]; + Node subselect = 6 [json_name="subselect"]; + int32 location = 7 [json_name="location"]; +} + +message SubPlan +{ + Node xpr = 1 [json_name="xpr"]; + SubLinkType sub_link_type = 2 [json_name="subLinkType"]; + Node testexpr = 3 [json_name="testexpr"]; + repeated Node param_ids = 4 [json_name="paramIds"]; + int32 plan_id = 5 [json_name="plan_id"]; + string plan_name = 6 [json_name="plan_name"]; + uint32 first_col_type = 7 [json_name="firstColType"]; + int32 first_col_typmod = 8 [json_name="firstColTypmod"]; + uint32 first_col_collation = 9 [json_name="firstColCollation"]; + bool use_hash_table = 10 [json_name="useHashTable"]; + bool unknown_eq_false = 11 [json_name="unknownEqFalse"]; + bool parallel_safe = 12 [json_name="parallel_safe"]; + repeated Node set_param = 13 [json_name="setParam"]; + repeated Node par_param = 14 [json_name="parParam"]; + repeated Node args = 15 [json_name="args"]; + double startup_cost = 16 [json_name="startup_cost"]; + double per_call_cost = 17 [json_name="per_call_cost"]; +} + +message AlternativeSubPlan +{ + Node xpr = 1 [json_name="xpr"]; + repeated Node subplans = 2 [json_name="subplans"]; +} + +message FieldSelect +{ + Node xpr = 1 [json_name="xpr"]; + Node arg = 2 [json_name="arg"]; + int32 fieldnum = 3 [json_name="fieldnum"]; + uint32 resulttype = 4 [json_name="resulttype"]; + int32 resulttypmod = 5 [json_name="resulttypmod"]; + uint32 resultcollid = 6 [json_name="resultcollid"]; +} + +message FieldStore +{ + Node xpr = 1 [json_name="xpr"]; + Node arg = 2 [json_name="arg"]; + repeated Node newvals = 3 [json_name="newvals"]; + repeated Node fieldnums = 4 [json_name="fieldnums"]; + uint32 resulttype = 5 [json_name="resulttype"]; +} + +message RelabelType +{ + Node xpr = 1 [json_name="xpr"]; + Node arg = 2 [json_name="arg"]; + uint32 resulttype = 3 [json_name="resulttype"]; + int32 resulttypmod = 4 [json_name="resulttypmod"]; + uint32 resultcollid = 5 [json_name="resultcollid"]; + CoercionForm relabelformat = 6 [json_name="relabelformat"]; + int32 location = 7 [json_name="location"]; +} + +message CoerceViaIO +{ + Node xpr = 1 [json_name="xpr"]; + Node arg = 2 [json_name="arg"]; + uint32 resulttype = 3 [json_name="resulttype"]; + uint32 resultcollid = 4 [json_name="resultcollid"]; + CoercionForm coerceformat = 5 [json_name="coerceformat"]; + int32 location = 6 [json_name="location"]; +} + +message ArrayCoerceExpr +{ + Node xpr = 1 [json_name="xpr"]; + Node arg = 2 [json_name="arg"]; + Node elemexpr = 3 [json_name="elemexpr"]; + uint32 resulttype = 4 [json_name="resulttype"]; + int32 resulttypmod = 5 [json_name="resulttypmod"]; + uint32 resultcollid = 6 [json_name="resultcollid"]; + CoercionForm coerceformat = 7 [json_name="coerceformat"]; + int32 location = 8 [json_name="location"]; +} + +message ConvertRowtypeExpr +{ + Node xpr = 1 [json_name="xpr"]; + Node arg = 2 [json_name="arg"]; + uint32 resulttype = 3 [json_name="resulttype"]; + CoercionForm convertformat = 4 [json_name="convertformat"]; + int32 location = 5 [json_name="location"]; +} + +message CollateExpr +{ + Node xpr = 1 [json_name="xpr"]; + Node arg = 2 [json_name="arg"]; + uint32 coll_oid = 3 [json_name="collOid"]; + int32 location = 4 [json_name="location"]; +} + +message CaseExpr +{ + Node xpr = 1 [json_name="xpr"]; + uint32 casetype = 2 [json_name="casetype"]; + uint32 casecollid = 3 [json_name="casecollid"]; + Node arg = 4 [json_name="arg"]; + repeated Node args = 5 [json_name="args"]; + Node defresult = 6 [json_name="defresult"]; + int32 location = 7 [json_name="location"]; +} + +message CaseWhen +{ + Node xpr = 1 [json_name="xpr"]; + Node expr = 2 [json_name="expr"]; + Node result = 3 [json_name="result"]; + int32 location = 4 [json_name="location"]; +} + +message CaseTestExpr +{ + Node xpr = 1 [json_name="xpr"]; + uint32 type_id = 2 [json_name="typeId"]; + int32 type_mod = 3 [json_name="typeMod"]; + uint32 collation = 4 [json_name="collation"]; +} + +message ArrayExpr +{ + Node xpr = 1 [json_name="xpr"]; + uint32 array_typeid = 2 [json_name="array_typeid"]; + uint32 array_collid = 3 [json_name="array_collid"]; + uint32 element_typeid = 4 [json_name="element_typeid"]; + repeated Node elements = 5 [json_name="elements"]; + bool multidims = 6 [json_name="multidims"]; + int32 location = 7 [json_name="location"]; +} + +message RowExpr +{ + Node xpr = 1 [json_name="xpr"]; + repeated Node args = 2 [json_name="args"]; + uint32 row_typeid = 3 [json_name="row_typeid"]; + CoercionForm row_format = 4 [json_name="row_format"]; + repeated Node colnames = 5 [json_name="colnames"]; + int32 location = 6 [json_name="location"]; +} + +message RowCompareExpr +{ + Node xpr = 1 [json_name="xpr"]; + RowCompareType rctype = 2 [json_name="rctype"]; + repeated Node opnos = 3 [json_name="opnos"]; + repeated Node opfamilies = 4 [json_name="opfamilies"]; + repeated Node inputcollids = 5 [json_name="inputcollids"]; + repeated Node largs = 6 [json_name="largs"]; + repeated Node rargs = 7 [json_name="rargs"]; +} + +message CoalesceExpr +{ + Node xpr = 1 [json_name="xpr"]; + uint32 coalescetype = 2 [json_name="coalescetype"]; + uint32 coalescecollid = 3 [json_name="coalescecollid"]; + repeated Node args = 4 [json_name="args"]; + int32 location = 5 [json_name="location"]; +} + +message MinMaxExpr +{ + Node xpr = 1 [json_name="xpr"]; + uint32 minmaxtype = 2 [json_name="minmaxtype"]; + uint32 minmaxcollid = 3 [json_name="minmaxcollid"]; + uint32 inputcollid = 4 [json_name="inputcollid"]; + MinMaxOp op = 5 [json_name="op"]; + repeated Node args = 6 [json_name="args"]; + int32 location = 7 [json_name="location"]; +} + +message SQLValueFunction +{ + Node xpr = 1 [json_name="xpr"]; + SQLValueFunctionOp op = 2 [json_name="op"]; + uint32 type = 3 [json_name="type"]; + int32 typmod = 4 [json_name="typmod"]; + int32 location = 5 [json_name="location"]; +} + +message XmlExpr +{ + Node xpr = 1 [json_name="xpr"]; + XmlExprOp op = 2 [json_name="op"]; + string name = 3 [json_name="name"]; + repeated Node named_args = 4 [json_name="named_args"]; + repeated Node arg_names = 5 [json_name="arg_names"]; + repeated Node args = 6 [json_name="args"]; + XmlOptionType xmloption = 7 [json_name="xmloption"]; + bool indent = 8 [json_name="indent"]; + uint32 type = 9 [json_name="type"]; + int32 typmod = 10 [json_name="typmod"]; + int32 location = 11 [json_name="location"]; +} + +message JsonFormat +{ + JsonFormatType format_type = 1 [json_name="format_type"]; + JsonEncoding encoding = 2 [json_name="encoding"]; + int32 location = 3 [json_name="location"]; +} + +message JsonReturning +{ + JsonFormat format = 1 [json_name="format"]; + uint32 typid = 2 [json_name="typid"]; + int32 typmod = 3 [json_name="typmod"]; +} + +message JsonValueExpr +{ + Node raw_expr = 1 [json_name="raw_expr"]; + Node formatted_expr = 2 [json_name="formatted_expr"]; + JsonFormat format = 3 [json_name="format"]; +} + +message JsonConstructorExpr +{ + Node xpr = 1 [json_name="xpr"]; + JsonConstructorType type = 2 [json_name="type"]; + repeated Node args = 3 [json_name="args"]; + Node func = 4 [json_name="func"]; + Node coercion = 5 [json_name="coercion"]; + JsonReturning returning = 6 [json_name="returning"]; + bool absent_on_null = 7 [json_name="absent_on_null"]; + bool unique = 8 [json_name="unique"]; + int32 location = 9 [json_name="location"]; +} + +message JsonIsPredicate +{ + Node expr = 1 [json_name="expr"]; + JsonFormat format = 2 [json_name="format"]; + JsonValueType item_type = 3 [json_name="item_type"]; + bool unique_keys = 4 [json_name="unique_keys"]; + int32 location = 5 [json_name="location"]; +} + +message JsonBehavior +{ + JsonBehaviorType btype = 1 [json_name="btype"]; + Node expr = 2 [json_name="expr"]; + bool coerce = 3 [json_name="coerce"]; + int32 location = 4 [json_name="location"]; +} + +message JsonExpr +{ + Node xpr = 1 [json_name="xpr"]; + JsonExprOp op = 2 [json_name="op"]; + string column_name = 3 [json_name="column_name"]; + Node formatted_expr = 4 [json_name="formatted_expr"]; + JsonFormat format = 5 [json_name="format"]; + Node path_spec = 6 [json_name="path_spec"]; + JsonReturning returning = 7 [json_name="returning"]; + repeated Node passing_names = 8 [json_name="passing_names"]; + repeated Node passing_values = 9 [json_name="passing_values"]; + JsonBehavior on_empty = 10 [json_name="on_empty"]; + JsonBehavior on_error = 11 [json_name="on_error"]; + bool use_io_coercion = 12 [json_name="use_io_coercion"]; + bool use_json_coercion = 13 [json_name="use_json_coercion"]; + JsonWrapper wrapper = 14 [json_name="wrapper"]; + bool omit_quotes = 15 [json_name="omit_quotes"]; + uint32 collation = 16 [json_name="collation"]; + int32 location = 17 [json_name="location"]; +} + +message JsonTablePath +{ + string name = 1 [json_name="name"]; +} + +message JsonTablePathScan +{ + Node plan = 1 [json_name="plan"]; + JsonTablePath path = 2 [json_name="path"]; + bool error_on_error = 3 [json_name="errorOnError"]; + Node child = 4 [json_name="child"]; + int32 col_min = 5 [json_name="colMin"]; + int32 col_max = 6 [json_name="colMax"]; +} + +message JsonTableSiblingJoin +{ + Node plan = 1 [json_name="plan"]; + Node lplan = 2 [json_name="lplan"]; + Node rplan = 3 [json_name="rplan"]; +} + +message NullTest +{ + Node xpr = 1 [json_name="xpr"]; + Node arg = 2 [json_name="arg"]; + NullTestType nulltesttype = 3 [json_name="nulltesttype"]; + bool argisrow = 4 [json_name="argisrow"]; + int32 location = 5 [json_name="location"]; +} + +message BooleanTest +{ + Node xpr = 1 [json_name="xpr"]; + Node arg = 2 [json_name="arg"]; + BoolTestType booltesttype = 3 [json_name="booltesttype"]; + int32 location = 4 [json_name="location"]; +} + +message MergeAction +{ + MergeMatchKind match_kind = 1 [json_name="matchKind"]; + CmdType command_type = 2 [json_name="commandType"]; + OverridingKind override = 3 [json_name="override"]; + Node qual = 4 [json_name="qual"]; + repeated Node target_list = 5 [json_name="targetList"]; + repeated Node update_colnos = 6 [json_name="updateColnos"]; +} + +message CoerceToDomain +{ + Node xpr = 1 [json_name="xpr"]; + Node arg = 2 [json_name="arg"]; + uint32 resulttype = 3 [json_name="resulttype"]; + int32 resulttypmod = 4 [json_name="resulttypmod"]; + uint32 resultcollid = 5 [json_name="resultcollid"]; + CoercionForm coercionformat = 6 [json_name="coercionformat"]; + int32 location = 7 [json_name="location"]; +} + +message CoerceToDomainValue +{ + Node xpr = 1 [json_name="xpr"]; + uint32 type_id = 2 [json_name="typeId"]; + int32 type_mod = 3 [json_name="typeMod"]; + uint32 collation = 4 [json_name="collation"]; + int32 location = 5 [json_name="location"]; +} + +message SetToDefault +{ + Node xpr = 1 [json_name="xpr"]; + uint32 type_id = 2 [json_name="typeId"]; + int32 type_mod = 3 [json_name="typeMod"]; + uint32 collation = 4 [json_name="collation"]; + int32 location = 5 [json_name="location"]; +} + +message CurrentOfExpr +{ + Node xpr = 1 [json_name="xpr"]; + uint32 cvarno = 2 [json_name="cvarno"]; + string cursor_name = 3 [json_name="cursor_name"]; + int32 cursor_param = 4 [json_name="cursor_param"]; +} + +message NextValueExpr +{ + Node xpr = 1 [json_name="xpr"]; + uint32 seqid = 2 [json_name="seqid"]; + uint32 type_id = 3 [json_name="typeId"]; +} + +message InferenceElem +{ + Node xpr = 1 [json_name="xpr"]; + Node expr = 2 [json_name="expr"]; + uint32 infercollid = 3 [json_name="infercollid"]; + uint32 inferopclass = 4 [json_name="inferopclass"]; +} + +message TargetEntry +{ + Node xpr = 1 [json_name="xpr"]; + Node expr = 2 [json_name="expr"]; + int32 resno = 3 [json_name="resno"]; + string resname = 4 [json_name="resname"]; + uint32 ressortgroupref = 5 [json_name="ressortgroupref"]; + uint32 resorigtbl = 6 [json_name="resorigtbl"]; + int32 resorigcol = 7 [json_name="resorigcol"]; + bool resjunk = 8 [json_name="resjunk"]; +} + +message RangeTblRef +{ + int32 rtindex = 1 [json_name="rtindex"]; +} + +message JoinExpr +{ + JoinType jointype = 1 [json_name="jointype"]; + bool is_natural = 2 [json_name="isNatural"]; + Node larg = 3 [json_name="larg"]; + Node rarg = 4 [json_name="rarg"]; + repeated Node using_clause = 5 [json_name="usingClause"]; + Alias join_using_alias = 6 [json_name="join_using_alias"]; + Node quals = 7 [json_name="quals"]; + Alias alias = 8 [json_name="alias"]; + int32 rtindex = 9 [json_name="rtindex"]; +} + +message FromExpr +{ + repeated Node fromlist = 1 [json_name="fromlist"]; + Node quals = 2 [json_name="quals"]; +} + +message OnConflictExpr +{ + OnConflictAction action = 1 [json_name="action"]; + repeated Node arbiter_elems = 2 [json_name="arbiterElems"]; + Node arbiter_where = 3 [json_name="arbiterWhere"]; + uint32 constraint = 4 [json_name="constraint"]; + repeated Node on_conflict_set = 5 [json_name="onConflictSet"]; + Node on_conflict_where = 6 [json_name="onConflictWhere"]; + int32 excl_rel_index = 7 [json_name="exclRelIndex"]; + repeated Node excl_rel_tlist = 8 [json_name="exclRelTlist"]; +} + +message Query +{ + CmdType command_type = 1 [json_name="commandType"]; + QuerySource query_source = 2 [json_name="querySource"]; + bool can_set_tag = 3 [json_name="canSetTag"]; + Node utility_stmt = 4 [json_name="utilityStmt"]; + int32 result_relation = 5 [json_name="resultRelation"]; + bool has_aggs = 6 [json_name="hasAggs"]; + bool has_window_funcs = 7 [json_name="hasWindowFuncs"]; + bool has_target_srfs = 8 [json_name="hasTargetSRFs"]; + bool has_sub_links = 9 [json_name="hasSubLinks"]; + bool has_distinct_on = 10 [json_name="hasDistinctOn"]; + bool has_recursive = 11 [json_name="hasRecursive"]; + bool has_modifying_cte = 12 [json_name="hasModifyingCTE"]; + bool has_for_update = 13 [json_name="hasForUpdate"]; + bool has_row_security = 14 [json_name="hasRowSecurity"]; + bool is_return = 15 [json_name="isReturn"]; + repeated Node cte_list = 16 [json_name="cteList"]; + repeated Node rtable = 17 [json_name="rtable"]; + repeated Node rteperminfos = 18 [json_name="rteperminfos"]; + FromExpr jointree = 19 [json_name="jointree"]; + repeated Node merge_action_list = 20 [json_name="mergeActionList"]; + int32 merge_target_relation = 21 [json_name="mergeTargetRelation"]; + Node merge_join_condition = 22 [json_name="mergeJoinCondition"]; + repeated Node target_list = 23 [json_name="targetList"]; + OverridingKind override = 24 [json_name="override"]; + OnConflictExpr on_conflict = 25 [json_name="onConflict"]; + repeated Node returning_list = 26 [json_name="returningList"]; + repeated Node group_clause = 27 [json_name="groupClause"]; + bool group_distinct = 28 [json_name="groupDistinct"]; + repeated Node grouping_sets = 29 [json_name="groupingSets"]; + Node having_qual = 30 [json_name="havingQual"]; + repeated Node window_clause = 31 [json_name="windowClause"]; + repeated Node distinct_clause = 32 [json_name="distinctClause"]; + repeated Node sort_clause = 33 [json_name="sortClause"]; + Node limit_offset = 34 [json_name="limitOffset"]; + Node limit_count = 35 [json_name="limitCount"]; + LimitOption limit_option = 36 [json_name="limitOption"]; + repeated Node row_marks = 37 [json_name="rowMarks"]; + Node set_operations = 38 [json_name="setOperations"]; + repeated Node constraint_deps = 39 [json_name="constraintDeps"]; + repeated Node with_check_options = 40 [json_name="withCheckOptions"]; + int32 stmt_location = 41 [json_name="stmt_location"]; + int32 stmt_len = 42 [json_name="stmt_len"]; +} + +message TypeName +{ + repeated Node names = 1 [json_name="names"]; + uint32 type_oid = 2 [json_name="typeOid"]; + bool setof = 3 [json_name="setof"]; + bool pct_type = 4 [json_name="pct_type"]; + repeated Node typmods = 5 [json_name="typmods"]; + int32 typemod = 6 [json_name="typemod"]; + repeated Node array_bounds = 7 [json_name="arrayBounds"]; + int32 location = 8 [json_name="location"]; +} + +message ColumnRef +{ + repeated Node fields = 1 [json_name="fields"]; + int32 location = 2 [json_name="location"]; +} + +message ParamRef +{ + int32 number = 1 [json_name="number"]; + int32 location = 2 [json_name="location"]; +} + +message A_Expr +{ + A_Expr_Kind kind = 1 [json_name="kind"]; + repeated Node name = 2 [json_name="name"]; + Node lexpr = 3 [json_name="lexpr"]; + Node rexpr = 4 [json_name="rexpr"]; + int32 location = 5 [json_name="location"]; +} + +message TypeCast +{ + Node arg = 1 [json_name="arg"]; + TypeName type_name = 2 [json_name="typeName"]; + int32 location = 3 [json_name="location"]; +} + +message CollateClause +{ + Node arg = 1 [json_name="arg"]; + repeated Node collname = 2 [json_name="collname"]; + int32 location = 3 [json_name="location"]; +} + +message RoleSpec +{ + RoleSpecType roletype = 1 [json_name="roletype"]; + string rolename = 2 [json_name="rolename"]; + int32 location = 3 [json_name="location"]; +} + +message FuncCall +{ + repeated Node funcname = 1 [json_name="funcname"]; + repeated Node args = 2 [json_name="args"]; + repeated Node agg_order = 3 [json_name="agg_order"]; + Node agg_filter = 4 [json_name="agg_filter"]; + WindowDef over = 5 [json_name="over"]; + bool agg_within_group = 6 [json_name="agg_within_group"]; + bool agg_star = 7 [json_name="agg_star"]; + bool agg_distinct = 8 [json_name="agg_distinct"]; + bool func_variadic = 9 [json_name="func_variadic"]; + CoercionForm funcformat = 10 [json_name="funcformat"]; + int32 location = 11 [json_name="location"]; +} + +message A_Star +{ +} + +message A_Indices +{ + bool is_slice = 1 [json_name="is_slice"]; + Node lidx = 2 [json_name="lidx"]; + Node uidx = 3 [json_name="uidx"]; +} + +message A_Indirection +{ + Node arg = 1 [json_name="arg"]; + repeated Node indirection = 2 [json_name="indirection"]; +} + +message A_ArrayExpr +{ + repeated Node elements = 1 [json_name="elements"]; + int32 location = 2 [json_name="location"]; +} + +message ResTarget +{ + string name = 1 [json_name="name"]; + repeated Node indirection = 2 [json_name="indirection"]; + Node val = 3 [json_name="val"]; + int32 location = 4 [json_name="location"]; +} + +message MultiAssignRef +{ + Node source = 1 [json_name="source"]; + int32 colno = 2 [json_name="colno"]; + int32 ncolumns = 3 [json_name="ncolumns"]; +} + +message SortBy +{ + Node node = 1 [json_name="node"]; + SortByDir sortby_dir = 2 [json_name="sortby_dir"]; + SortByNulls sortby_nulls = 3 [json_name="sortby_nulls"]; + repeated Node use_op = 4 [json_name="useOp"]; + int32 location = 5 [json_name="location"]; +} + +message WindowDef +{ + string name = 1 [json_name="name"]; + string refname = 2 [json_name="refname"]; + repeated Node partition_clause = 3 [json_name="partitionClause"]; + repeated Node order_clause = 4 [json_name="orderClause"]; + int32 frame_options = 5 [json_name="frameOptions"]; + Node start_offset = 6 [json_name="startOffset"]; + Node end_offset = 7 [json_name="endOffset"]; + int32 location = 8 [json_name="location"]; +} + +message RangeSubselect +{ + bool lateral = 1 [json_name="lateral"]; + Node subquery = 2 [json_name="subquery"]; + Alias alias = 3 [json_name="alias"]; +} + +message RangeFunction +{ + bool lateral = 1 [json_name="lateral"]; + bool ordinality = 2 [json_name="ordinality"]; + bool is_rowsfrom = 3 [json_name="is_rowsfrom"]; + repeated Node functions = 4 [json_name="functions"]; + Alias alias = 5 [json_name="alias"]; + repeated Node coldeflist = 6 [json_name="coldeflist"]; +} + +message RangeTableFunc +{ + bool lateral = 1 [json_name="lateral"]; + Node docexpr = 2 [json_name="docexpr"]; + Node rowexpr = 3 [json_name="rowexpr"]; + repeated Node namespaces = 4 [json_name="namespaces"]; + repeated Node columns = 5 [json_name="columns"]; + Alias alias = 6 [json_name="alias"]; + int32 location = 7 [json_name="location"]; +} + +message RangeTableFuncCol +{ + string colname = 1 [json_name="colname"]; + TypeName type_name = 2 [json_name="typeName"]; + bool for_ordinality = 3 [json_name="for_ordinality"]; + bool is_not_null = 4 [json_name="is_not_null"]; + Node colexpr = 5 [json_name="colexpr"]; + Node coldefexpr = 6 [json_name="coldefexpr"]; + int32 location = 7 [json_name="location"]; +} + +message RangeTableSample +{ + Node relation = 1 [json_name="relation"]; + repeated Node method = 2 [json_name="method"]; + repeated Node args = 3 [json_name="args"]; + Node repeatable = 4 [json_name="repeatable"]; + int32 location = 5 [json_name="location"]; +} + +message ColumnDef +{ + string colname = 1 [json_name="colname"]; + TypeName type_name = 2 [json_name="typeName"]; + string compression = 3 [json_name="compression"]; + int32 inhcount = 4 [json_name="inhcount"]; + bool is_local = 5 [json_name="is_local"]; + bool is_not_null = 6 [json_name="is_not_null"]; + bool is_from_type = 7 [json_name="is_from_type"]; + string storage = 8 [json_name="storage"]; + string storage_name = 9 [json_name="storage_name"]; + Node raw_default = 10 [json_name="raw_default"]; + Node cooked_default = 11 [json_name="cooked_default"]; + string identity = 12 [json_name="identity"]; + RangeVar identity_sequence = 13 [json_name="identitySequence"]; + string generated = 14 [json_name="generated"]; + CollateClause coll_clause = 15 [json_name="collClause"]; + uint32 coll_oid = 16 [json_name="collOid"]; + repeated Node constraints = 17 [json_name="constraints"]; + repeated Node fdwoptions = 18 [json_name="fdwoptions"]; + int32 location = 19 [json_name="location"]; +} + +message TableLikeClause +{ + RangeVar relation = 1 [json_name="relation"]; + uint32 options = 2 [json_name="options"]; + uint32 relation_oid = 3 [json_name="relationOid"]; +} + +message IndexElem +{ + string name = 1 [json_name="name"]; + Node expr = 2 [json_name="expr"]; + string indexcolname = 3 [json_name="indexcolname"]; + repeated Node collation = 4 [json_name="collation"]; + repeated Node opclass = 5 [json_name="opclass"]; + repeated Node opclassopts = 6 [json_name="opclassopts"]; + SortByDir ordering = 7 [json_name="ordering"]; + SortByNulls nulls_ordering = 8 [json_name="nulls_ordering"]; +} + +message DefElem +{ + string defnamespace = 1 [json_name="defnamespace"]; + string defname = 2 [json_name="defname"]; + Node arg = 3 [json_name="arg"]; + DefElemAction defaction = 4 [json_name="defaction"]; + int32 location = 5 [json_name="location"]; +} + +message LockingClause +{ + repeated Node locked_rels = 1 [json_name="lockedRels"]; + LockClauseStrength strength = 2 [json_name="strength"]; + LockWaitPolicy wait_policy = 3 [json_name="waitPolicy"]; +} + +message XmlSerialize +{ + XmlOptionType xmloption = 1 [json_name="xmloption"]; + Node expr = 2 [json_name="expr"]; + TypeName type_name = 3 [json_name="typeName"]; + bool indent = 4 [json_name="indent"]; + int32 location = 5 [json_name="location"]; +} + +message PartitionElem +{ + string name = 1 [json_name="name"]; + Node expr = 2 [json_name="expr"]; + repeated Node collation = 3 [json_name="collation"]; + repeated Node opclass = 4 [json_name="opclass"]; + int32 location = 5 [json_name="location"]; +} + +message PartitionSpec +{ + PartitionStrategy strategy = 1 [json_name="strategy"]; + repeated Node part_params = 2 [json_name="partParams"]; + int32 location = 3 [json_name="location"]; +} + +message PartitionBoundSpec +{ + string strategy = 1 [json_name="strategy"]; + bool is_default = 2 [json_name="is_default"]; + int32 modulus = 3 [json_name="modulus"]; + int32 remainder = 4 [json_name="remainder"]; + repeated Node listdatums = 5 [json_name="listdatums"]; + repeated Node lowerdatums = 6 [json_name="lowerdatums"]; + repeated Node upperdatums = 7 [json_name="upperdatums"]; + int32 location = 8 [json_name="location"]; +} + +message PartitionRangeDatum +{ + PartitionRangeDatumKind kind = 1 [json_name="kind"]; + Node value = 2 [json_name="value"]; + int32 location = 3 [json_name="location"]; +} + +message SinglePartitionSpec +{ +} + +message PartitionCmd +{ + RangeVar name = 1 [json_name="name"]; + PartitionBoundSpec bound = 2 [json_name="bound"]; + bool concurrent = 3 [json_name="concurrent"]; +} + +message RangeTblEntry +{ + Alias alias = 1 [json_name="alias"]; + Alias eref = 2 [json_name="eref"]; + RTEKind rtekind = 3 [json_name="rtekind"]; + uint32 relid = 4 [json_name="relid"]; + bool inh = 5 [json_name="inh"]; + string relkind = 6 [json_name="relkind"]; + int32 rellockmode = 7 [json_name="rellockmode"]; + uint32 perminfoindex = 8 [json_name="perminfoindex"]; + TableSampleClause tablesample = 9 [json_name="tablesample"]; + Query subquery = 10 [json_name="subquery"]; + bool security_barrier = 11 [json_name="security_barrier"]; + JoinType jointype = 12 [json_name="jointype"]; + int32 joinmergedcols = 13 [json_name="joinmergedcols"]; + repeated Node joinaliasvars = 14 [json_name="joinaliasvars"]; + repeated Node joinleftcols = 15 [json_name="joinleftcols"]; + repeated Node joinrightcols = 16 [json_name="joinrightcols"]; + Alias join_using_alias = 17 [json_name="join_using_alias"]; + repeated Node functions = 18 [json_name="functions"]; + bool funcordinality = 19 [json_name="funcordinality"]; + TableFunc tablefunc = 20 [json_name="tablefunc"]; + repeated Node values_lists = 21 [json_name="values_lists"]; + string ctename = 22 [json_name="ctename"]; + uint32 ctelevelsup = 23 [json_name="ctelevelsup"]; + bool self_reference = 24 [json_name="self_reference"]; + repeated Node coltypes = 25 [json_name="coltypes"]; + repeated Node coltypmods = 26 [json_name="coltypmods"]; + repeated Node colcollations = 27 [json_name="colcollations"]; + string enrname = 28 [json_name="enrname"]; + double enrtuples = 29 [json_name="enrtuples"]; + bool lateral = 30 [json_name="lateral"]; + bool in_from_cl = 31 [json_name="inFromCl"]; + repeated Node security_quals = 32 [json_name="securityQuals"]; +} + +message RTEPermissionInfo +{ + uint32 relid = 1 [json_name="relid"]; + bool inh = 2 [json_name="inh"]; + uint64 required_perms = 3 [json_name="requiredPerms"]; + uint32 check_as_user = 4 [json_name="checkAsUser"]; + repeated uint64 selected_cols = 5 [json_name="selectedCols"]; + repeated uint64 inserted_cols = 6 [json_name="insertedCols"]; + repeated uint64 updated_cols = 7 [json_name="updatedCols"]; +} + +message RangeTblFunction +{ + Node funcexpr = 1 [json_name="funcexpr"]; + int32 funccolcount = 2 [json_name="funccolcount"]; + repeated Node funccolnames = 3 [json_name="funccolnames"]; + repeated Node funccoltypes = 4 [json_name="funccoltypes"]; + repeated Node funccoltypmods = 5 [json_name="funccoltypmods"]; + repeated Node funccolcollations = 6 [json_name="funccolcollations"]; + repeated uint64 funcparams = 7 [json_name="funcparams"]; +} + +message TableSampleClause +{ + uint32 tsmhandler = 1 [json_name="tsmhandler"]; + repeated Node args = 2 [json_name="args"]; + Node repeatable = 3 [json_name="repeatable"]; +} + +message WithCheckOption +{ + WCOKind kind = 1 [json_name="kind"]; + string relname = 2 [json_name="relname"]; + string polname = 3 [json_name="polname"]; + Node qual = 4 [json_name="qual"]; + bool cascaded = 5 [json_name="cascaded"]; +} + +message SortGroupClause +{ + uint32 tle_sort_group_ref = 1 [json_name="tleSortGroupRef"]; + uint32 eqop = 2 [json_name="eqop"]; + uint32 sortop = 3 [json_name="sortop"]; + bool nulls_first = 4 [json_name="nulls_first"]; + bool hashable = 5 [json_name="hashable"]; +} + +message GroupingSet +{ + GroupingSetKind kind = 1 [json_name="kind"]; + repeated Node content = 2 [json_name="content"]; + int32 location = 3 [json_name="location"]; +} + +message WindowClause +{ + string name = 1 [json_name="name"]; + string refname = 2 [json_name="refname"]; + repeated Node partition_clause = 3 [json_name="partitionClause"]; + repeated Node order_clause = 4 [json_name="orderClause"]; + int32 frame_options = 5 [json_name="frameOptions"]; + Node start_offset = 6 [json_name="startOffset"]; + Node end_offset = 7 [json_name="endOffset"]; + uint32 start_in_range_func = 8 [json_name="startInRangeFunc"]; + uint32 end_in_range_func = 9 [json_name="endInRangeFunc"]; + uint32 in_range_coll = 10 [json_name="inRangeColl"]; + bool in_range_asc = 11 [json_name="inRangeAsc"]; + bool in_range_nulls_first = 12 [json_name="inRangeNullsFirst"]; + uint32 winref = 13 [json_name="winref"]; + bool copied_order = 14 [json_name="copiedOrder"]; +} + +message RowMarkClause +{ + uint32 rti = 1 [json_name="rti"]; + LockClauseStrength strength = 2 [json_name="strength"]; + LockWaitPolicy wait_policy = 3 [json_name="waitPolicy"]; + bool pushed_down = 4 [json_name="pushedDown"]; +} + +message WithClause +{ + repeated Node ctes = 1 [json_name="ctes"]; + bool recursive = 2 [json_name="recursive"]; + int32 location = 3 [json_name="location"]; +} + +message InferClause +{ + repeated Node index_elems = 1 [json_name="indexElems"]; + Node where_clause = 2 [json_name="whereClause"]; + string conname = 3 [json_name="conname"]; + int32 location = 4 [json_name="location"]; +} + +message OnConflictClause +{ + OnConflictAction action = 1 [json_name="action"]; + InferClause infer = 2 [json_name="infer"]; + repeated Node target_list = 3 [json_name="targetList"]; + Node where_clause = 4 [json_name="whereClause"]; + int32 location = 5 [json_name="location"]; +} + +message CTESearchClause +{ + repeated Node search_col_list = 1 [json_name="search_col_list"]; + bool search_breadth_first = 2 [json_name="search_breadth_first"]; + string search_seq_column = 3 [json_name="search_seq_column"]; + int32 location = 4 [json_name="location"]; +} + +message CTECycleClause +{ + repeated Node cycle_col_list = 1 [json_name="cycle_col_list"]; + string cycle_mark_column = 2 [json_name="cycle_mark_column"]; + Node cycle_mark_value = 3 [json_name="cycle_mark_value"]; + Node cycle_mark_default = 4 [json_name="cycle_mark_default"]; + string cycle_path_column = 5 [json_name="cycle_path_column"]; + int32 location = 6 [json_name="location"]; + uint32 cycle_mark_type = 7 [json_name="cycle_mark_type"]; + int32 cycle_mark_typmod = 8 [json_name="cycle_mark_typmod"]; + uint32 cycle_mark_collation = 9 [json_name="cycle_mark_collation"]; + uint32 cycle_mark_neop = 10 [json_name="cycle_mark_neop"]; +} + +message CommonTableExpr +{ + string ctename = 1 [json_name="ctename"]; + repeated Node aliascolnames = 2 [json_name="aliascolnames"]; + CTEMaterialize ctematerialized = 3 [json_name="ctematerialized"]; + Node ctequery = 4 [json_name="ctequery"]; + CTESearchClause search_clause = 5 [json_name="search_clause"]; + CTECycleClause cycle_clause = 6 [json_name="cycle_clause"]; + int32 location = 7 [json_name="location"]; + bool cterecursive = 8 [json_name="cterecursive"]; + int32 cterefcount = 9 [json_name="cterefcount"]; + repeated Node ctecolnames = 10 [json_name="ctecolnames"]; + repeated Node ctecoltypes = 11 [json_name="ctecoltypes"]; + repeated Node ctecoltypmods = 12 [json_name="ctecoltypmods"]; + repeated Node ctecolcollations = 13 [json_name="ctecolcollations"]; +} + +message MergeWhenClause +{ + MergeMatchKind match_kind = 1 [json_name="matchKind"]; + CmdType command_type = 2 [json_name="commandType"]; + OverridingKind override = 3 [json_name="override"]; + Node condition = 4 [json_name="condition"]; + repeated Node target_list = 5 [json_name="targetList"]; + repeated Node values = 6 [json_name="values"]; +} + +message TriggerTransition +{ + string name = 1 [json_name="name"]; + bool is_new = 2 [json_name="isNew"]; + bool is_table = 3 [json_name="isTable"]; +} + +message JsonOutput +{ + TypeName type_name = 1 [json_name="typeName"]; + JsonReturning returning = 2 [json_name="returning"]; +} + +message JsonArgument +{ + JsonValueExpr val = 1 [json_name="val"]; + string name = 2 [json_name="name"]; +} + +message JsonFuncExpr +{ + JsonExprOp op = 1 [json_name="op"]; + string column_name = 2 [json_name="column_name"]; + JsonValueExpr context_item = 3 [json_name="context_item"]; + Node pathspec = 4 [json_name="pathspec"]; + repeated Node passing = 5 [json_name="passing"]; + JsonOutput output = 6 [json_name="output"]; + JsonBehavior on_empty = 7 [json_name="on_empty"]; + JsonBehavior on_error = 8 [json_name="on_error"]; + JsonWrapper wrapper = 9 [json_name="wrapper"]; + JsonQuotes quotes = 10 [json_name="quotes"]; + int32 location = 11 [json_name="location"]; +} + +message JsonTablePathSpec +{ + Node string = 1 [json_name="string"]; + string name = 2 [json_name="name"]; + int32 name_location = 3 [json_name="name_location"]; + int32 location = 4 [json_name="location"]; +} + +message JsonTable +{ + JsonValueExpr context_item = 1 [json_name="context_item"]; + JsonTablePathSpec pathspec = 2 [json_name="pathspec"]; + repeated Node passing = 3 [json_name="passing"]; + repeated Node columns = 4 [json_name="columns"]; + JsonBehavior on_error = 5 [json_name="on_error"]; + Alias alias = 6 [json_name="alias"]; + bool lateral = 7 [json_name="lateral"]; + int32 location = 8 [json_name="location"]; +} + +message JsonTableColumn +{ + JsonTableColumnType coltype = 1 [json_name="coltype"]; + string name = 2 [json_name="name"]; + TypeName type_name = 3 [json_name="typeName"]; + JsonTablePathSpec pathspec = 4 [json_name="pathspec"]; + JsonFormat format = 5 [json_name="format"]; + JsonWrapper wrapper = 6 [json_name="wrapper"]; + JsonQuotes quotes = 7 [json_name="quotes"]; + repeated Node columns = 8 [json_name="columns"]; + JsonBehavior on_empty = 9 [json_name="on_empty"]; + JsonBehavior on_error = 10 [json_name="on_error"]; + int32 location = 11 [json_name="location"]; +} + +message JsonKeyValue +{ + Node key = 1 [json_name="key"]; + JsonValueExpr value = 2 [json_name="value"]; +} + +message JsonParseExpr +{ + JsonValueExpr expr = 1 [json_name="expr"]; + JsonOutput output = 2 [json_name="output"]; + bool unique_keys = 3 [json_name="unique_keys"]; + int32 location = 4 [json_name="location"]; +} + +message JsonScalarExpr +{ + Node expr = 1 [json_name="expr"]; + JsonOutput output = 2 [json_name="output"]; + int32 location = 3 [json_name="location"]; +} + +message JsonSerializeExpr +{ + JsonValueExpr expr = 1 [json_name="expr"]; + JsonOutput output = 2 [json_name="output"]; + int32 location = 3 [json_name="location"]; +} + +message JsonObjectConstructor +{ + repeated Node exprs = 1 [json_name="exprs"]; + JsonOutput output = 2 [json_name="output"]; + bool absent_on_null = 3 [json_name="absent_on_null"]; + bool unique = 4 [json_name="unique"]; + int32 location = 5 [json_name="location"]; +} + +message JsonArrayConstructor +{ + repeated Node exprs = 1 [json_name="exprs"]; + JsonOutput output = 2 [json_name="output"]; + bool absent_on_null = 3 [json_name="absent_on_null"]; + int32 location = 4 [json_name="location"]; +} + +message JsonArrayQueryConstructor +{ + Node query = 1 [json_name="query"]; + JsonOutput output = 2 [json_name="output"]; + JsonFormat format = 3 [json_name="format"]; + bool absent_on_null = 4 [json_name="absent_on_null"]; + int32 location = 5 [json_name="location"]; +} + +message JsonAggConstructor +{ + JsonOutput output = 1 [json_name="output"]; + Node agg_filter = 2 [json_name="agg_filter"]; + repeated Node agg_order = 3 [json_name="agg_order"]; + WindowDef over = 4 [json_name="over"]; + int32 location = 5 [json_name="location"]; +} + +message JsonObjectAgg +{ + JsonAggConstructor constructor = 1 [json_name="constructor"]; + JsonKeyValue arg = 2 [json_name="arg"]; + bool absent_on_null = 3 [json_name="absent_on_null"]; + bool unique = 4 [json_name="unique"]; +} + +message JsonArrayAgg +{ + JsonAggConstructor constructor = 1 [json_name="constructor"]; + JsonValueExpr arg = 2 [json_name="arg"]; + bool absent_on_null = 3 [json_name="absent_on_null"]; +} + +message RawStmt +{ + Node stmt = 1 [json_name="stmt"]; + int32 stmt_location = 2 [json_name="stmt_location"]; + int32 stmt_len = 3 [json_name="stmt_len"]; +} + +message InsertStmt +{ + RangeVar relation = 1 [json_name="relation"]; + repeated Node cols = 2 [json_name="cols"]; + Node select_stmt = 3 [json_name="selectStmt"]; + OnConflictClause on_conflict_clause = 4 [json_name="onConflictClause"]; + repeated Node returning_list = 5 [json_name="returningList"]; + WithClause with_clause = 6 [json_name="withClause"]; + OverridingKind override = 7 [json_name="override"]; +} + +message DeleteStmt +{ + RangeVar relation = 1 [json_name="relation"]; + repeated Node using_clause = 2 [json_name="usingClause"]; + Node where_clause = 3 [json_name="whereClause"]; + repeated Node returning_list = 4 [json_name="returningList"]; + WithClause with_clause = 5 [json_name="withClause"]; +} + +message UpdateStmt +{ + RangeVar relation = 1 [json_name="relation"]; + repeated Node target_list = 2 [json_name="targetList"]; + Node where_clause = 3 [json_name="whereClause"]; + repeated Node from_clause = 4 [json_name="fromClause"]; + repeated Node returning_list = 5 [json_name="returningList"]; + WithClause with_clause = 6 [json_name="withClause"]; +} + +message MergeStmt +{ + RangeVar relation = 1 [json_name="relation"]; + Node source_relation = 2 [json_name="sourceRelation"]; + Node join_condition = 3 [json_name="joinCondition"]; + repeated Node merge_when_clauses = 4 [json_name="mergeWhenClauses"]; + repeated Node returning_list = 5 [json_name="returningList"]; + WithClause with_clause = 6 [json_name="withClause"]; +} + +message SelectStmt +{ + repeated Node distinct_clause = 1 [json_name="distinctClause"]; + IntoClause into_clause = 2 [json_name="intoClause"]; + repeated Node target_list = 3 [json_name="targetList"]; + repeated Node from_clause = 4 [json_name="fromClause"]; + Node where_clause = 5 [json_name="whereClause"]; + repeated Node group_clause = 6 [json_name="groupClause"]; + bool group_distinct = 7 [json_name="groupDistinct"]; + Node having_clause = 8 [json_name="havingClause"]; + repeated Node window_clause = 9 [json_name="windowClause"]; + repeated Node values_lists = 10 [json_name="valuesLists"]; + repeated Node sort_clause = 11 [json_name="sortClause"]; + Node limit_offset = 12 [json_name="limitOffset"]; + Node limit_count = 13 [json_name="limitCount"]; + LimitOption limit_option = 14 [json_name="limitOption"]; + repeated Node locking_clause = 15 [json_name="lockingClause"]; + WithClause with_clause = 16 [json_name="withClause"]; + SetOperation op = 17 [json_name="op"]; + bool all = 18 [json_name="all"]; + SelectStmt larg = 19 [json_name="larg"]; + SelectStmt rarg = 20 [json_name="rarg"]; +} + +message SetOperationStmt +{ + SetOperation op = 1 [json_name="op"]; + bool all = 2 [json_name="all"]; + Node larg = 3 [json_name="larg"]; + Node rarg = 4 [json_name="rarg"]; + repeated Node col_types = 5 [json_name="colTypes"]; + repeated Node col_typmods = 6 [json_name="colTypmods"]; + repeated Node col_collations = 7 [json_name="colCollations"]; + repeated Node group_clauses = 8 [json_name="groupClauses"]; +} + +message ReturnStmt +{ + Node returnval = 1 [json_name="returnval"]; +} + +message PLAssignStmt +{ + string name = 1 [json_name="name"]; + repeated Node indirection = 2 [json_name="indirection"]; + int32 nnames = 3 [json_name="nnames"]; + SelectStmt val = 4 [json_name="val"]; + int32 location = 5 [json_name="location"]; +} + +message CreateSchemaStmt +{ + string schemaname = 1 [json_name="schemaname"]; + RoleSpec authrole = 2 [json_name="authrole"]; + repeated Node schema_elts = 3 [json_name="schemaElts"]; + bool if_not_exists = 4 [json_name="if_not_exists"]; +} + +message AlterTableStmt +{ + RangeVar relation = 1 [json_name="relation"]; + repeated Node cmds = 2 [json_name="cmds"]; + ObjectType objtype = 3 [json_name="objtype"]; + bool missing_ok = 4 [json_name="missing_ok"]; +} + +message ReplicaIdentityStmt +{ + string identity_type = 1 [json_name="identity_type"]; + string name = 2 [json_name="name"]; +} + +message AlterTableCmd +{ + AlterTableType subtype = 1 [json_name="subtype"]; + string name = 2 [json_name="name"]; + int32 num = 3 [json_name="num"]; + RoleSpec newowner = 4 [json_name="newowner"]; + Node def = 5 [json_name="def"]; + DropBehavior behavior = 6 [json_name="behavior"]; + bool missing_ok = 7 [json_name="missing_ok"]; + bool recurse = 8 [json_name="recurse"]; +} + +message AlterCollationStmt +{ + repeated Node collname = 1 [json_name="collname"]; +} + +message AlterDomainStmt +{ + string subtype = 1 [json_name="subtype"]; + repeated Node type_name = 2 [json_name="typeName"]; + string name = 3 [json_name="name"]; + Node def = 4 [json_name="def"]; + DropBehavior behavior = 5 [json_name="behavior"]; + bool missing_ok = 6 [json_name="missing_ok"]; +} + +message GrantStmt +{ + bool is_grant = 1 [json_name="is_grant"]; + GrantTargetType targtype = 2 [json_name="targtype"]; + ObjectType objtype = 3 [json_name="objtype"]; + repeated Node objects = 4 [json_name="objects"]; + repeated Node privileges = 5 [json_name="privileges"]; + repeated Node grantees = 6 [json_name="grantees"]; + bool grant_option = 7 [json_name="grant_option"]; + RoleSpec grantor = 8 [json_name="grantor"]; + DropBehavior behavior = 9 [json_name="behavior"]; +} + +message ObjectWithArgs +{ + repeated Node objname = 1 [json_name="objname"]; + repeated Node objargs = 2 [json_name="objargs"]; + repeated Node objfuncargs = 3 [json_name="objfuncargs"]; + bool args_unspecified = 4 [json_name="args_unspecified"]; +} + +message AccessPriv +{ + string priv_name = 1 [json_name="priv_name"]; + repeated Node cols = 2 [json_name="cols"]; +} + +message GrantRoleStmt +{ + repeated Node granted_roles = 1 [json_name="granted_roles"]; + repeated Node grantee_roles = 2 [json_name="grantee_roles"]; + bool is_grant = 3 [json_name="is_grant"]; + repeated Node opt = 4 [json_name="opt"]; + RoleSpec grantor = 5 [json_name="grantor"]; + DropBehavior behavior = 6 [json_name="behavior"]; +} + +message AlterDefaultPrivilegesStmt +{ + repeated Node options = 1 [json_name="options"]; + GrantStmt action = 2 [json_name="action"]; +} + +message CopyStmt +{ + RangeVar relation = 1 [json_name="relation"]; + Node query = 2 [json_name="query"]; + repeated Node attlist = 3 [json_name="attlist"]; + bool is_from = 4 [json_name="is_from"]; + bool is_program = 5 [json_name="is_program"]; + string filename = 6 [json_name="filename"]; + repeated Node options = 7 [json_name="options"]; + Node where_clause = 8 [json_name="whereClause"]; +} + +message VariableSetStmt +{ + VariableSetKind kind = 1 [json_name="kind"]; + string name = 2 [json_name="name"]; + repeated Node args = 3 [json_name="args"]; + bool is_local = 4 [json_name="is_local"]; +} + +message VariableShowStmt +{ + string name = 1 [json_name="name"]; +} + +message CreateStmt +{ + RangeVar relation = 1 [json_name="relation"]; + repeated Node table_elts = 2 [json_name="tableElts"]; + repeated Node inh_relations = 3 [json_name="inhRelations"]; + PartitionBoundSpec partbound = 4 [json_name="partbound"]; + PartitionSpec partspec = 5 [json_name="partspec"]; + TypeName of_typename = 6 [json_name="ofTypename"]; + repeated Node constraints = 7 [json_name="constraints"]; + repeated Node options = 8 [json_name="options"]; + OnCommitAction oncommit = 9 [json_name="oncommit"]; + string tablespacename = 10 [json_name="tablespacename"]; + string access_method = 11 [json_name="accessMethod"]; + bool if_not_exists = 12 [json_name="if_not_exists"]; +} + +message Constraint +{ + ConstrType contype = 1 [json_name="contype"]; + string conname = 2 [json_name="conname"]; + bool deferrable = 3 [json_name="deferrable"]; + bool initdeferred = 4 [json_name="initdeferred"]; + bool skip_validation = 5 [json_name="skip_validation"]; + bool initially_valid = 6 [json_name="initially_valid"]; + bool is_no_inherit = 7 [json_name="is_no_inherit"]; + Node raw_expr = 8 [json_name="raw_expr"]; + string cooked_expr = 9 [json_name="cooked_expr"]; + string generated_when = 10 [json_name="generated_when"]; + int32 inhcount = 11 [json_name="inhcount"]; + bool nulls_not_distinct = 12 [json_name="nulls_not_distinct"]; + repeated Node keys = 13 [json_name="keys"]; + repeated Node including = 14 [json_name="including"]; + repeated Node exclusions = 15 [json_name="exclusions"]; + repeated Node options = 16 [json_name="options"]; + string indexname = 17 [json_name="indexname"]; + string indexspace = 18 [json_name="indexspace"]; + bool reset_default_tblspc = 19 [json_name="reset_default_tblspc"]; + string access_method = 20 [json_name="access_method"]; + Node where_clause = 21 [json_name="where_clause"]; + RangeVar pktable = 22 [json_name="pktable"]; + repeated Node fk_attrs = 23 [json_name="fk_attrs"]; + repeated Node pk_attrs = 24 [json_name="pk_attrs"]; + string fk_matchtype = 25 [json_name="fk_matchtype"]; + string fk_upd_action = 26 [json_name="fk_upd_action"]; + string fk_del_action = 27 [json_name="fk_del_action"]; + repeated Node fk_del_set_cols = 28 [json_name="fk_del_set_cols"]; + repeated Node old_conpfeqop = 29 [json_name="old_conpfeqop"]; + uint32 old_pktable_oid = 30 [json_name="old_pktable_oid"]; + int32 location = 31 [json_name="location"]; +} + +message CreateTableSpaceStmt +{ + string tablespacename = 1 [json_name="tablespacename"]; + RoleSpec owner = 2 [json_name="owner"]; + string location = 3 [json_name="location"]; + repeated Node options = 4 [json_name="options"]; +} + +message DropTableSpaceStmt +{ + string tablespacename = 1 [json_name="tablespacename"]; + bool missing_ok = 2 [json_name="missing_ok"]; +} + +message AlterTableSpaceOptionsStmt +{ + string tablespacename = 1 [json_name="tablespacename"]; + repeated Node options = 2 [json_name="options"]; + bool is_reset = 3 [json_name="isReset"]; +} + +message AlterTableMoveAllStmt +{ + string orig_tablespacename = 1 [json_name="orig_tablespacename"]; + ObjectType objtype = 2 [json_name="objtype"]; + repeated Node roles = 3 [json_name="roles"]; + string new_tablespacename = 4 [json_name="new_tablespacename"]; + bool nowait = 5 [json_name="nowait"]; +} + +message CreateExtensionStmt +{ + string extname = 1 [json_name="extname"]; + bool if_not_exists = 2 [json_name="if_not_exists"]; + repeated Node options = 3 [json_name="options"]; +} + +message AlterExtensionStmt +{ + string extname = 1 [json_name="extname"]; + repeated Node options = 2 [json_name="options"]; +} + +message AlterExtensionContentsStmt +{ + string extname = 1 [json_name="extname"]; + int32 action = 2 [json_name="action"]; + ObjectType objtype = 3 [json_name="objtype"]; + Node object = 4 [json_name="object"]; +} + +message CreateFdwStmt +{ + string fdwname = 1 [json_name="fdwname"]; + repeated Node func_options = 2 [json_name="func_options"]; + repeated Node options = 3 [json_name="options"]; +} + +message AlterFdwStmt +{ + string fdwname = 1 [json_name="fdwname"]; + repeated Node func_options = 2 [json_name="func_options"]; + repeated Node options = 3 [json_name="options"]; +} + +message CreateForeignServerStmt +{ + string servername = 1 [json_name="servername"]; + string servertype = 2 [json_name="servertype"]; + string version = 3 [json_name="version"]; + string fdwname = 4 [json_name="fdwname"]; + bool if_not_exists = 5 [json_name="if_not_exists"]; + repeated Node options = 6 [json_name="options"]; +} + +message AlterForeignServerStmt +{ + string servername = 1 [json_name="servername"]; + string version = 2 [json_name="version"]; + repeated Node options = 3 [json_name="options"]; + bool has_version = 4 [json_name="has_version"]; +} + +message CreateForeignTableStmt +{ + CreateStmt base_stmt = 1 [json_name="base"]; + string servername = 2 [json_name="servername"]; + repeated Node options = 3 [json_name="options"]; +} + +message CreateUserMappingStmt +{ + RoleSpec user = 1 [json_name="user"]; + string servername = 2 [json_name="servername"]; + bool if_not_exists = 3 [json_name="if_not_exists"]; + repeated Node options = 4 [json_name="options"]; +} + +message AlterUserMappingStmt +{ + RoleSpec user = 1 [json_name="user"]; + string servername = 2 [json_name="servername"]; + repeated Node options = 3 [json_name="options"]; +} + +message DropUserMappingStmt +{ + RoleSpec user = 1 [json_name="user"]; + string servername = 2 [json_name="servername"]; + bool missing_ok = 3 [json_name="missing_ok"]; +} + +message ImportForeignSchemaStmt +{ + string server_name = 1 [json_name="server_name"]; + string remote_schema = 2 [json_name="remote_schema"]; + string local_schema = 3 [json_name="local_schema"]; + ImportForeignSchemaType list_type = 4 [json_name="list_type"]; + repeated Node table_list = 5 [json_name="table_list"]; + repeated Node options = 6 [json_name="options"]; +} + +message CreatePolicyStmt +{ + string policy_name = 1 [json_name="policy_name"]; + RangeVar table = 2 [json_name="table"]; + string cmd_name = 3 [json_name="cmd_name"]; + bool permissive = 4 [json_name="permissive"]; + repeated Node roles = 5 [json_name="roles"]; + Node qual = 6 [json_name="qual"]; + Node with_check = 7 [json_name="with_check"]; +} + +message AlterPolicyStmt +{ + string policy_name = 1 [json_name="policy_name"]; + RangeVar table = 2 [json_name="table"]; + repeated Node roles = 3 [json_name="roles"]; + Node qual = 4 [json_name="qual"]; + Node with_check = 5 [json_name="with_check"]; +} + +message CreateAmStmt +{ + string amname = 1 [json_name="amname"]; + repeated Node handler_name = 2 [json_name="handler_name"]; + string amtype = 3 [json_name="amtype"]; +} + +message CreateTrigStmt +{ + bool replace = 1 [json_name="replace"]; + bool isconstraint = 2 [json_name="isconstraint"]; + string trigname = 3 [json_name="trigname"]; + RangeVar relation = 4 [json_name="relation"]; + repeated Node funcname = 5 [json_name="funcname"]; + repeated Node args = 6 [json_name="args"]; + bool row = 7 [json_name="row"]; + int32 timing = 8 [json_name="timing"]; + int32 events = 9 [json_name="events"]; + repeated Node columns = 10 [json_name="columns"]; + Node when_clause = 11 [json_name="whenClause"]; + repeated Node transition_rels = 12 [json_name="transitionRels"]; + bool deferrable = 13 [json_name="deferrable"]; + bool initdeferred = 14 [json_name="initdeferred"]; + RangeVar constrrel = 15 [json_name="constrrel"]; +} + +message CreateEventTrigStmt +{ + string trigname = 1 [json_name="trigname"]; + string eventname = 2 [json_name="eventname"]; + repeated Node whenclause = 3 [json_name="whenclause"]; + repeated Node funcname = 4 [json_name="funcname"]; +} + +message AlterEventTrigStmt +{ + string trigname = 1 [json_name="trigname"]; + string tgenabled = 2 [json_name="tgenabled"]; +} + +message CreatePLangStmt +{ + bool replace = 1 [json_name="replace"]; + string plname = 2 [json_name="plname"]; + repeated Node plhandler = 3 [json_name="plhandler"]; + repeated Node plinline = 4 [json_name="plinline"]; + repeated Node plvalidator = 5 [json_name="plvalidator"]; + bool pltrusted = 6 [json_name="pltrusted"]; +} + +message CreateRoleStmt +{ + RoleStmtType stmt_type = 1 [json_name="stmt_type"]; + string role = 2 [json_name="role"]; + repeated Node options = 3 [json_name="options"]; +} + +message AlterRoleStmt +{ + RoleSpec role = 1 [json_name="role"]; + repeated Node options = 2 [json_name="options"]; + int32 action = 3 [json_name="action"]; +} + +message AlterRoleSetStmt +{ + RoleSpec role = 1 [json_name="role"]; + string database = 2 [json_name="database"]; + VariableSetStmt setstmt = 3 [json_name="setstmt"]; +} + +message DropRoleStmt +{ + repeated Node roles = 1 [json_name="roles"]; + bool missing_ok = 2 [json_name="missing_ok"]; +} + +message CreateSeqStmt +{ + RangeVar sequence = 1 [json_name="sequence"]; + repeated Node options = 2 [json_name="options"]; + uint32 owner_id = 3 [json_name="ownerId"]; + bool for_identity = 4 [json_name="for_identity"]; + bool if_not_exists = 5 [json_name="if_not_exists"]; +} + +message AlterSeqStmt +{ + RangeVar sequence = 1 [json_name="sequence"]; + repeated Node options = 2 [json_name="options"]; + bool for_identity = 3 [json_name="for_identity"]; + bool missing_ok = 4 [json_name="missing_ok"]; +} + +message DefineStmt +{ + ObjectType kind = 1 [json_name="kind"]; + bool oldstyle = 2 [json_name="oldstyle"]; + repeated Node defnames = 3 [json_name="defnames"]; + repeated Node args = 4 [json_name="args"]; + repeated Node definition = 5 [json_name="definition"]; + bool if_not_exists = 6 [json_name="if_not_exists"]; + bool replace = 7 [json_name="replace"]; +} + +message CreateDomainStmt +{ + repeated Node domainname = 1 [json_name="domainname"]; + TypeName type_name = 2 [json_name="typeName"]; + CollateClause coll_clause = 3 [json_name="collClause"]; + repeated Node constraints = 4 [json_name="constraints"]; +} + +message CreateOpClassStmt +{ + repeated Node opclassname = 1 [json_name="opclassname"]; + repeated Node opfamilyname = 2 [json_name="opfamilyname"]; + string amname = 3 [json_name="amname"]; + TypeName datatype = 4 [json_name="datatype"]; + repeated Node items = 5 [json_name="items"]; + bool is_default = 6 [json_name="isDefault"]; +} + +message CreateOpClassItem +{ + int32 itemtype = 1 [json_name="itemtype"]; + ObjectWithArgs name = 2 [json_name="name"]; + int32 number = 3 [json_name="number"]; + repeated Node order_family = 4 [json_name="order_family"]; + repeated Node class_args = 5 [json_name="class_args"]; + TypeName storedtype = 6 [json_name="storedtype"]; +} + +message CreateOpFamilyStmt +{ + repeated Node opfamilyname = 1 [json_name="opfamilyname"]; + string amname = 2 [json_name="amname"]; +} + +message AlterOpFamilyStmt +{ + repeated Node opfamilyname = 1 [json_name="opfamilyname"]; + string amname = 2 [json_name="amname"]; + bool is_drop = 3 [json_name="isDrop"]; + repeated Node items = 4 [json_name="items"]; +} + +message DropStmt +{ + repeated Node objects = 1 [json_name="objects"]; + ObjectType remove_type = 2 [json_name="removeType"]; + DropBehavior behavior = 3 [json_name="behavior"]; + bool missing_ok = 4 [json_name="missing_ok"]; + bool concurrent = 5 [json_name="concurrent"]; +} + +message TruncateStmt +{ + repeated Node relations = 1 [json_name="relations"]; + bool restart_seqs = 2 [json_name="restart_seqs"]; + DropBehavior behavior = 3 [json_name="behavior"]; +} + +message CommentStmt +{ + ObjectType objtype = 1 [json_name="objtype"]; + Node object = 2 [json_name="object"]; + string comment = 3 [json_name="comment"]; +} + +message SecLabelStmt +{ + ObjectType objtype = 1 [json_name="objtype"]; + Node object = 2 [json_name="object"]; + string provider = 3 [json_name="provider"]; + string label = 4 [json_name="label"]; +} + +message DeclareCursorStmt +{ + string portalname = 1 [json_name="portalname"]; + int32 options = 2 [json_name="options"]; + Node query = 3 [json_name="query"]; +} + +message ClosePortalStmt +{ + string portalname = 1 [json_name="portalname"]; +} + +message FetchStmt +{ + FetchDirection direction = 1 [json_name="direction"]; + int64 how_many = 2 [json_name="howMany"]; + string portalname = 3 [json_name="portalname"]; + bool ismove = 4 [json_name="ismove"]; +} + +message IndexStmt +{ + string idxname = 1 [json_name="idxname"]; + RangeVar relation = 2 [json_name="relation"]; + string access_method = 3 [json_name="accessMethod"]; + string table_space = 4 [json_name="tableSpace"]; + repeated Node index_params = 5 [json_name="indexParams"]; + repeated Node index_including_params = 6 [json_name="indexIncludingParams"]; + repeated Node options = 7 [json_name="options"]; + Node where_clause = 8 [json_name="whereClause"]; + repeated Node exclude_op_names = 9 [json_name="excludeOpNames"]; + string idxcomment = 10 [json_name="idxcomment"]; + uint32 index_oid = 11 [json_name="indexOid"]; + uint32 old_number = 12 [json_name="oldNumber"]; + uint32 old_create_subid = 13 [json_name="oldCreateSubid"]; + uint32 old_first_relfilelocator_subid = 14 [json_name="oldFirstRelfilelocatorSubid"]; + bool unique = 15 [json_name="unique"]; + bool nulls_not_distinct = 16 [json_name="nulls_not_distinct"]; + bool primary = 17 [json_name="primary"]; + bool isconstraint = 18 [json_name="isconstraint"]; + bool deferrable = 19 [json_name="deferrable"]; + bool initdeferred = 20 [json_name="initdeferred"]; + bool transformed = 21 [json_name="transformed"]; + bool concurrent = 22 [json_name="concurrent"]; + bool if_not_exists = 23 [json_name="if_not_exists"]; + bool reset_default_tblspc = 24 [json_name="reset_default_tblspc"]; +} + +message CreateStatsStmt +{ + repeated Node defnames = 1 [json_name="defnames"]; + repeated Node stat_types = 2 [json_name="stat_types"]; + repeated Node exprs = 3 [json_name="exprs"]; + repeated Node relations = 4 [json_name="relations"]; + string stxcomment = 5 [json_name="stxcomment"]; + bool transformed = 6 [json_name="transformed"]; + bool if_not_exists = 7 [json_name="if_not_exists"]; +} + +message StatsElem +{ + string name = 1 [json_name="name"]; + Node expr = 2 [json_name="expr"]; +} + +message AlterStatsStmt +{ + repeated Node defnames = 1 [json_name="defnames"]; + Node stxstattarget = 2 [json_name="stxstattarget"]; + bool missing_ok = 3 [json_name="missing_ok"]; +} + +message CreateFunctionStmt +{ + bool is_procedure = 1 [json_name="is_procedure"]; + bool replace = 2 [json_name="replace"]; + repeated Node funcname = 3 [json_name="funcname"]; + repeated Node parameters = 4 [json_name="parameters"]; + TypeName return_type = 5 [json_name="returnType"]; + repeated Node options = 6 [json_name="options"]; + Node sql_body = 7 [json_name="sql_body"]; +} + +message FunctionParameter +{ + string name = 1 [json_name="name"]; + TypeName arg_type = 2 [json_name="argType"]; + FunctionParameterMode mode = 3 [json_name="mode"]; + Node defexpr = 4 [json_name="defexpr"]; +} + +message AlterFunctionStmt +{ + ObjectType objtype = 1 [json_name="objtype"]; + ObjectWithArgs func = 2 [json_name="func"]; + repeated Node actions = 3 [json_name="actions"]; +} + +message DoStmt +{ + repeated Node args = 1 [json_name="args"]; +} + +message InlineCodeBlock +{ + string source_text = 1 [json_name="source_text"]; + uint32 lang_oid = 2 [json_name="langOid"]; + bool lang_is_trusted = 3 [json_name="langIsTrusted"]; + bool atomic = 4 [json_name="atomic"]; +} + +message CallStmt +{ + FuncCall funccall = 1 [json_name="funccall"]; + FuncExpr funcexpr = 2 [json_name="funcexpr"]; + repeated Node outargs = 3 [json_name="outargs"]; +} + +message CallContext +{ + bool atomic = 1 [json_name="atomic"]; +} + +message RenameStmt +{ + ObjectType rename_type = 1 [json_name="renameType"]; + ObjectType relation_type = 2 [json_name="relationType"]; + RangeVar relation = 3 [json_name="relation"]; + Node object = 4 [json_name="object"]; + string subname = 5 [json_name="subname"]; + string newname = 6 [json_name="newname"]; + DropBehavior behavior = 7 [json_name="behavior"]; + bool missing_ok = 8 [json_name="missing_ok"]; +} + +message AlterObjectDependsStmt +{ + ObjectType object_type = 1 [json_name="objectType"]; + RangeVar relation = 2 [json_name="relation"]; + Node object = 3 [json_name="object"]; + String extname = 4 [json_name="extname"]; + bool remove = 5 [json_name="remove"]; +} + +message AlterObjectSchemaStmt +{ + ObjectType object_type = 1 [json_name="objectType"]; + RangeVar relation = 2 [json_name="relation"]; + Node object = 3 [json_name="object"]; + string newschema = 4 [json_name="newschema"]; + bool missing_ok = 5 [json_name="missing_ok"]; +} + +message AlterOwnerStmt +{ + ObjectType object_type = 1 [json_name="objectType"]; + RangeVar relation = 2 [json_name="relation"]; + Node object = 3 [json_name="object"]; + RoleSpec newowner = 4 [json_name="newowner"]; +} + +message AlterOperatorStmt +{ + ObjectWithArgs opername = 1 [json_name="opername"]; + repeated Node options = 2 [json_name="options"]; +} + +message AlterTypeStmt +{ + repeated Node type_name = 1 [json_name="typeName"]; + repeated Node options = 2 [json_name="options"]; +} + +message RuleStmt +{ + RangeVar relation = 1 [json_name="relation"]; + string rulename = 2 [json_name="rulename"]; + Node where_clause = 3 [json_name="whereClause"]; + CmdType event = 4 [json_name="event"]; + bool instead = 5 [json_name="instead"]; + repeated Node actions = 6 [json_name="actions"]; + bool replace = 7 [json_name="replace"]; +} + +message NotifyStmt +{ + string conditionname = 1 [json_name="conditionname"]; + string payload = 2 [json_name="payload"]; +} + +message ListenStmt +{ + string conditionname = 1 [json_name="conditionname"]; +} + +message UnlistenStmt +{ + string conditionname = 1 [json_name="conditionname"]; +} + +message TransactionStmt +{ + TransactionStmtKind kind = 1 [json_name="kind"]; + repeated Node options = 2 [json_name="options"]; + string savepoint_name = 3 [json_name="savepoint_name"]; + string gid = 4 [json_name="gid"]; + bool chain = 5 [json_name="chain"]; + int32 location = 6 [json_name="location"]; +} + +message CompositeTypeStmt +{ + RangeVar typevar = 1 [json_name="typevar"]; + repeated Node coldeflist = 2 [json_name="coldeflist"]; +} + +message CreateEnumStmt +{ + repeated Node type_name = 1 [json_name="typeName"]; + repeated Node vals = 2 [json_name="vals"]; +} + +message CreateRangeStmt +{ + repeated Node type_name = 1 [json_name="typeName"]; + repeated Node params = 2 [json_name="params"]; +} + +message AlterEnumStmt +{ + repeated Node type_name = 1 [json_name="typeName"]; + string old_val = 2 [json_name="oldVal"]; + string new_val = 3 [json_name="newVal"]; + string new_val_neighbor = 4 [json_name="newValNeighbor"]; + bool new_val_is_after = 5 [json_name="newValIsAfter"]; + bool skip_if_new_val_exists = 6 [json_name="skipIfNewValExists"]; +} + +message ViewStmt +{ + RangeVar view = 1 [json_name="view"]; + repeated Node aliases = 2 [json_name="aliases"]; + Node query = 3 [json_name="query"]; + bool replace = 4 [json_name="replace"]; + repeated Node options = 5 [json_name="options"]; + ViewCheckOption with_check_option = 6 [json_name="withCheckOption"]; +} + +message LoadStmt +{ + string filename = 1 [json_name="filename"]; +} + +message CreatedbStmt +{ + string dbname = 1 [json_name="dbname"]; + repeated Node options = 2 [json_name="options"]; +} + +message AlterDatabaseStmt +{ + string dbname = 1 [json_name="dbname"]; + repeated Node options = 2 [json_name="options"]; +} + +message AlterDatabaseRefreshCollStmt +{ + string dbname = 1 [json_name="dbname"]; +} + +message AlterDatabaseSetStmt +{ + string dbname = 1 [json_name="dbname"]; + VariableSetStmt setstmt = 2 [json_name="setstmt"]; +} + +message DropdbStmt +{ + string dbname = 1 [json_name="dbname"]; + bool missing_ok = 2 [json_name="missing_ok"]; + repeated Node options = 3 [json_name="options"]; +} + +message AlterSystemStmt +{ + VariableSetStmt setstmt = 1 [json_name="setstmt"]; +} + +message ClusterStmt +{ + RangeVar relation = 1 [json_name="relation"]; + string indexname = 2 [json_name="indexname"]; + repeated Node params = 3 [json_name="params"]; +} + +message VacuumStmt +{ + repeated Node options = 1 [json_name="options"]; + repeated Node rels = 2 [json_name="rels"]; + bool is_vacuumcmd = 3 [json_name="is_vacuumcmd"]; +} + +message VacuumRelation +{ + RangeVar relation = 1 [json_name="relation"]; + uint32 oid = 2 [json_name="oid"]; + repeated Node va_cols = 3 [json_name="va_cols"]; +} + +message ExplainStmt +{ + Node query = 1 [json_name="query"]; + repeated Node options = 2 [json_name="options"]; +} + +message CreateTableAsStmt +{ + Node query = 1 [json_name="query"]; + IntoClause into = 2 [json_name="into"]; + ObjectType objtype = 3 [json_name="objtype"]; + bool is_select_into = 4 [json_name="is_select_into"]; + bool if_not_exists = 5 [json_name="if_not_exists"]; +} + +message RefreshMatViewStmt +{ + bool concurrent = 1 [json_name="concurrent"]; + bool skip_data = 2 [json_name="skipData"]; + RangeVar relation = 3 [json_name="relation"]; +} + +message CheckPointStmt +{ +} + +message DiscardStmt +{ + DiscardMode target = 1 [json_name="target"]; +} + +message LockStmt +{ + repeated Node relations = 1 [json_name="relations"]; + int32 mode = 2 [json_name="mode"]; + bool nowait = 3 [json_name="nowait"]; +} + +message ConstraintsSetStmt +{ + repeated Node constraints = 1 [json_name="constraints"]; + bool deferred = 2 [json_name="deferred"]; +} + +message ReindexStmt +{ + ReindexObjectType kind = 1 [json_name="kind"]; + RangeVar relation = 2 [json_name="relation"]; + string name = 3 [json_name="name"]; + repeated Node params = 4 [json_name="params"]; +} + +message CreateConversionStmt +{ + repeated Node conversion_name = 1 [json_name="conversion_name"]; + string for_encoding_name = 2 [json_name="for_encoding_name"]; + string to_encoding_name = 3 [json_name="to_encoding_name"]; + repeated Node func_name = 4 [json_name="func_name"]; + bool def = 5 [json_name="def"]; +} + +message CreateCastStmt +{ + TypeName sourcetype = 1 [json_name="sourcetype"]; + TypeName targettype = 2 [json_name="targettype"]; + ObjectWithArgs func = 3 [json_name="func"]; + CoercionContext context = 4 [json_name="context"]; + bool inout = 5 [json_name="inout"]; +} + +message CreateTransformStmt +{ + bool replace = 1 [json_name="replace"]; + TypeName type_name = 2 [json_name="type_name"]; + string lang = 3 [json_name="lang"]; + ObjectWithArgs fromsql = 4 [json_name="fromsql"]; + ObjectWithArgs tosql = 5 [json_name="tosql"]; +} + +message PrepareStmt +{ + string name = 1 [json_name="name"]; + repeated Node argtypes = 2 [json_name="argtypes"]; + Node query = 3 [json_name="query"]; +} + +message ExecuteStmt +{ + string name = 1 [json_name="name"]; + repeated Node params = 2 [json_name="params"]; +} + +message DeallocateStmt +{ + string name = 1 [json_name="name"]; + bool isall = 2 [json_name="isall"]; + int32 location = 3 [json_name="location"]; +} + +message DropOwnedStmt +{ + repeated Node roles = 1 [json_name="roles"]; + DropBehavior behavior = 2 [json_name="behavior"]; +} + +message ReassignOwnedStmt +{ + repeated Node roles = 1 [json_name="roles"]; + RoleSpec newrole = 2 [json_name="newrole"]; +} + +message AlterTSDictionaryStmt +{ + repeated Node dictname = 1 [json_name="dictname"]; + repeated Node options = 2 [json_name="options"]; +} + +message AlterTSConfigurationStmt +{ + AlterTSConfigType kind = 1 [json_name="kind"]; + repeated Node cfgname = 2 [json_name="cfgname"]; + repeated Node tokentype = 3 [json_name="tokentype"]; + repeated Node dicts = 4 [json_name="dicts"]; + bool override = 5 [json_name="override"]; + bool replace = 6 [json_name="replace"]; + bool missing_ok = 7 [json_name="missing_ok"]; +} + +message PublicationTable +{ + RangeVar relation = 1 [json_name="relation"]; + Node where_clause = 2 [json_name="whereClause"]; + repeated Node columns = 3 [json_name="columns"]; +} + +message PublicationObjSpec +{ + PublicationObjSpecType pubobjtype = 1 [json_name="pubobjtype"]; + string name = 2 [json_name="name"]; + PublicationTable pubtable = 3 [json_name="pubtable"]; + int32 location = 4 [json_name="location"]; +} + +message CreatePublicationStmt +{ + string pubname = 1 [json_name="pubname"]; + repeated Node options = 2 [json_name="options"]; + repeated Node pubobjects = 3 [json_name="pubobjects"]; + bool for_all_tables = 4 [json_name="for_all_tables"]; +} + +message AlterPublicationStmt +{ + string pubname = 1 [json_name="pubname"]; + repeated Node options = 2 [json_name="options"]; + repeated Node pubobjects = 3 [json_name="pubobjects"]; + bool for_all_tables = 4 [json_name="for_all_tables"]; + AlterPublicationAction action = 5 [json_name="action"]; +} + +message CreateSubscriptionStmt +{ + string subname = 1 [json_name="subname"]; + string conninfo = 2 [json_name="conninfo"]; + repeated Node publication = 3 [json_name="publication"]; + repeated Node options = 4 [json_name="options"]; +} + +message AlterSubscriptionStmt +{ + AlterSubscriptionType kind = 1 [json_name="kind"]; + string subname = 2 [json_name="subname"]; + string conninfo = 3 [json_name="conninfo"]; + repeated Node publication = 4 [json_name="publication"]; + repeated Node options = 5 [json_name="options"]; +} + +message DropSubscriptionStmt +{ + string subname = 1 [json_name="subname"]; + bool missing_ok = 2 [json_name="missing_ok"]; + DropBehavior behavior = 3 [json_name="behavior"]; +} + +enum QuerySource +{ + QUERY_SOURCE_UNDEFINED = 0; + QSRC_ORIGINAL = 1; + QSRC_PARSER = 2; + QSRC_INSTEAD_RULE = 3; + QSRC_QUAL_INSTEAD_RULE = 4; + QSRC_NON_INSTEAD_RULE = 5; +} + +enum SortByDir +{ + SORT_BY_DIR_UNDEFINED = 0; + SORTBY_DEFAULT = 1; + SORTBY_ASC = 2; + SORTBY_DESC = 3; + SORTBY_USING = 4; +} + +enum SortByNulls +{ + SORT_BY_NULLS_UNDEFINED = 0; + SORTBY_NULLS_DEFAULT = 1; + SORTBY_NULLS_FIRST = 2; + SORTBY_NULLS_LAST = 3; +} + +enum SetQuantifier +{ + SET_QUANTIFIER_UNDEFINED = 0; + SET_QUANTIFIER_DEFAULT = 1; + SET_QUANTIFIER_ALL = 2; + SET_QUANTIFIER_DISTINCT = 3; +} + +enum A_Expr_Kind +{ + A_EXPR_KIND_UNDEFINED = 0; + AEXPR_OP = 1; + AEXPR_OP_ANY = 2; + AEXPR_OP_ALL = 3; + AEXPR_DISTINCT = 4; + AEXPR_NOT_DISTINCT = 5; + AEXPR_NULLIF = 6; + AEXPR_IN = 7; + AEXPR_LIKE = 8; + AEXPR_ILIKE = 9; + AEXPR_SIMILAR = 10; + AEXPR_BETWEEN = 11; + AEXPR_NOT_BETWEEN = 12; + AEXPR_BETWEEN_SYM = 13; + AEXPR_NOT_BETWEEN_SYM = 14; +} + +enum RoleSpecType +{ + ROLE_SPEC_TYPE_UNDEFINED = 0; + ROLESPEC_CSTRING = 1; + ROLESPEC_CURRENT_ROLE = 2; + ROLESPEC_CURRENT_USER = 3; + ROLESPEC_SESSION_USER = 4; + ROLESPEC_PUBLIC = 5; +} + +enum TableLikeOption +{ + TABLE_LIKE_OPTION_UNDEFINED = 0; + CREATE_TABLE_LIKE_COMMENTS = 1; + CREATE_TABLE_LIKE_COMPRESSION = 2; + CREATE_TABLE_LIKE_CONSTRAINTS = 3; + CREATE_TABLE_LIKE_DEFAULTS = 4; + CREATE_TABLE_LIKE_GENERATED = 5; + CREATE_TABLE_LIKE_IDENTITY = 6; + CREATE_TABLE_LIKE_INDEXES = 7; + CREATE_TABLE_LIKE_STATISTICS = 8; + CREATE_TABLE_LIKE_STORAGE = 9; + CREATE_TABLE_LIKE_ALL = 10; +} + +enum DefElemAction +{ + DEF_ELEM_ACTION_UNDEFINED = 0; + DEFELEM_UNSPEC = 1; + DEFELEM_SET = 2; + DEFELEM_ADD = 3; + DEFELEM_DROP = 4; +} + +enum PartitionStrategy +{ + PARTITION_STRATEGY_UNDEFINED = 0; + PARTITION_STRATEGY_LIST = 1; + PARTITION_STRATEGY_RANGE = 2; + PARTITION_STRATEGY_HASH = 3; +} + +enum PartitionRangeDatumKind +{ + PARTITION_RANGE_DATUM_KIND_UNDEFINED = 0; + PARTITION_RANGE_DATUM_MINVALUE = 1; + PARTITION_RANGE_DATUM_VALUE = 2; + PARTITION_RANGE_DATUM_MAXVALUE = 3; +} + +enum RTEKind +{ + RTEKIND_UNDEFINED = 0; + RTE_RELATION = 1; + RTE_SUBQUERY = 2; + RTE_JOIN = 3; + RTE_FUNCTION = 4; + RTE_TABLEFUNC = 5; + RTE_VALUES = 6; + RTE_CTE = 7; + RTE_NAMEDTUPLESTORE = 8; + RTE_RESULT = 9; +} + +enum WCOKind +{ + WCOKIND_UNDEFINED = 0; + WCO_VIEW_CHECK = 1; + WCO_RLS_INSERT_CHECK = 2; + WCO_RLS_UPDATE_CHECK = 3; + WCO_RLS_CONFLICT_CHECK = 4; + WCO_RLS_MERGE_UPDATE_CHECK = 5; + WCO_RLS_MERGE_DELETE_CHECK = 6; +} + +enum GroupingSetKind +{ + GROUPING_SET_KIND_UNDEFINED = 0; + GROUPING_SET_EMPTY = 1; + GROUPING_SET_SIMPLE = 2; + GROUPING_SET_ROLLUP = 3; + GROUPING_SET_CUBE = 4; + GROUPING_SET_SETS = 5; +} + +enum CTEMaterialize +{ + CTEMATERIALIZE_UNDEFINED = 0; + CTEMaterializeDefault = 1; + CTEMaterializeAlways = 2; + CTEMaterializeNever = 3; +} + +enum JsonQuotes +{ + JSON_QUOTES_UNDEFINED = 0; + JS_QUOTES_UNSPEC = 1; + JS_QUOTES_KEEP = 2; + JS_QUOTES_OMIT = 3; +} + +enum JsonTableColumnType +{ + JSON_TABLE_COLUMN_TYPE_UNDEFINED = 0; + JTC_FOR_ORDINALITY = 1; + JTC_REGULAR = 2; + JTC_EXISTS = 3; + JTC_FORMATTED = 4; + JTC_NESTED = 5; +} + +enum SetOperation +{ + SET_OPERATION_UNDEFINED = 0; + SETOP_NONE = 1; + SETOP_UNION = 2; + SETOP_INTERSECT = 3; + SETOP_EXCEPT = 4; +} + +enum ObjectType +{ + OBJECT_TYPE_UNDEFINED = 0; + OBJECT_ACCESS_METHOD = 1; + OBJECT_AGGREGATE = 2; + OBJECT_AMOP = 3; + OBJECT_AMPROC = 4; + OBJECT_ATTRIBUTE = 5; + OBJECT_CAST = 6; + OBJECT_COLUMN = 7; + OBJECT_COLLATION = 8; + OBJECT_CONVERSION = 9; + OBJECT_DATABASE = 10; + OBJECT_DEFAULT = 11; + OBJECT_DEFACL = 12; + OBJECT_DOMAIN = 13; + OBJECT_DOMCONSTRAINT = 14; + OBJECT_EVENT_TRIGGER = 15; + OBJECT_EXTENSION = 16; + OBJECT_FDW = 17; + OBJECT_FOREIGN_SERVER = 18; + OBJECT_FOREIGN_TABLE = 19; + OBJECT_FUNCTION = 20; + OBJECT_INDEX = 21; + OBJECT_LANGUAGE = 22; + OBJECT_LARGEOBJECT = 23; + OBJECT_MATVIEW = 24; + OBJECT_OPCLASS = 25; + OBJECT_OPERATOR = 26; + OBJECT_OPFAMILY = 27; + OBJECT_PARAMETER_ACL = 28; + OBJECT_POLICY = 29; + OBJECT_PROCEDURE = 30; + OBJECT_PUBLICATION = 31; + OBJECT_PUBLICATION_NAMESPACE = 32; + OBJECT_PUBLICATION_REL = 33; + OBJECT_ROLE = 34; + OBJECT_ROUTINE = 35; + OBJECT_RULE = 36; + OBJECT_SCHEMA = 37; + OBJECT_SEQUENCE = 38; + OBJECT_SUBSCRIPTION = 39; + OBJECT_STATISTIC_EXT = 40; + OBJECT_TABCONSTRAINT = 41; + OBJECT_TABLE = 42; + OBJECT_TABLESPACE = 43; + OBJECT_TRANSFORM = 44; + OBJECT_TRIGGER = 45; + OBJECT_TSCONFIGURATION = 46; + OBJECT_TSDICTIONARY = 47; + OBJECT_TSPARSER = 48; + OBJECT_TSTEMPLATE = 49; + OBJECT_TYPE = 50; + OBJECT_USER_MAPPING = 51; + OBJECT_VIEW = 52; +} + +enum DropBehavior +{ + DROP_BEHAVIOR_UNDEFINED = 0; + DROP_RESTRICT = 1; + DROP_CASCADE = 2; +} + +enum AlterTableType +{ + ALTER_TABLE_TYPE_UNDEFINED = 0; + AT_AddColumn = 1; + AT_AddColumnToView = 2; + AT_ColumnDefault = 3; + AT_CookedColumnDefault = 4; + AT_DropNotNull = 5; + AT_SetNotNull = 6; + AT_SetExpression = 7; + AT_DropExpression = 8; + AT_CheckNotNull = 9; + AT_SetStatistics = 10; + AT_SetOptions = 11; + AT_ResetOptions = 12; + AT_SetStorage = 13; + AT_SetCompression = 14; + AT_DropColumn = 15; + AT_AddIndex = 16; + AT_ReAddIndex = 17; + AT_AddConstraint = 18; + AT_ReAddConstraint = 19; + AT_ReAddDomainConstraint = 20; + AT_AlterConstraint = 21; + AT_ValidateConstraint = 22; + AT_AddIndexConstraint = 23; + AT_DropConstraint = 24; + AT_ReAddComment = 25; + AT_AlterColumnType = 26; + AT_AlterColumnGenericOptions = 27; + AT_ChangeOwner = 28; + AT_ClusterOn = 29; + AT_DropCluster = 30; + AT_SetLogged = 31; + AT_SetUnLogged = 32; + AT_DropOids = 33; + AT_SetAccessMethod = 34; + AT_SetTableSpace = 35; + AT_SetRelOptions = 36; + AT_ResetRelOptions = 37; + AT_ReplaceRelOptions = 38; + AT_EnableTrig = 39; + AT_EnableAlwaysTrig = 40; + AT_EnableReplicaTrig = 41; + AT_DisableTrig = 42; + AT_EnableTrigAll = 43; + AT_DisableTrigAll = 44; + AT_EnableTrigUser = 45; + AT_DisableTrigUser = 46; + AT_EnableRule = 47; + AT_EnableAlwaysRule = 48; + AT_EnableReplicaRule = 49; + AT_DisableRule = 50; + AT_AddInherit = 51; + AT_DropInherit = 52; + AT_AddOf = 53; + AT_DropOf = 54; + AT_ReplicaIdentity = 55; + AT_EnableRowSecurity = 56; + AT_DisableRowSecurity = 57; + AT_ForceRowSecurity = 58; + AT_NoForceRowSecurity = 59; + AT_GenericOptions = 60; + AT_AttachPartition = 61; + AT_DetachPartition = 62; + AT_DetachPartitionFinalize = 63; + AT_AddIdentity = 64; + AT_SetIdentity = 65; + AT_DropIdentity = 66; + AT_ReAddStatistics = 67; +} + +enum GrantTargetType +{ + GRANT_TARGET_TYPE_UNDEFINED = 0; + ACL_TARGET_OBJECT = 1; + ACL_TARGET_ALL_IN_SCHEMA = 2; + ACL_TARGET_DEFAULTS = 3; +} + +enum VariableSetKind +{ + VARIABLE_SET_KIND_UNDEFINED = 0; + VAR_SET_VALUE = 1; + VAR_SET_DEFAULT = 2; + VAR_SET_CURRENT = 3; + VAR_SET_MULTI = 4; + VAR_RESET = 5; + VAR_RESET_ALL = 6; +} + +enum ConstrType +{ + CONSTR_TYPE_UNDEFINED = 0; + CONSTR_NULL = 1; + CONSTR_NOTNULL = 2; + CONSTR_DEFAULT = 3; + CONSTR_IDENTITY = 4; + CONSTR_GENERATED = 5; + CONSTR_CHECK = 6; + CONSTR_PRIMARY = 7; + CONSTR_UNIQUE = 8; + CONSTR_EXCLUSION = 9; + CONSTR_FOREIGN = 10; + CONSTR_ATTR_DEFERRABLE = 11; + CONSTR_ATTR_NOT_DEFERRABLE = 12; + CONSTR_ATTR_DEFERRED = 13; + CONSTR_ATTR_IMMEDIATE = 14; +} + +enum ImportForeignSchemaType +{ + IMPORT_FOREIGN_SCHEMA_TYPE_UNDEFINED = 0; + FDW_IMPORT_SCHEMA_ALL = 1; + FDW_IMPORT_SCHEMA_LIMIT_TO = 2; + FDW_IMPORT_SCHEMA_EXCEPT = 3; +} + +enum RoleStmtType +{ + ROLE_STMT_TYPE_UNDEFINED = 0; + ROLESTMT_ROLE = 1; + ROLESTMT_USER = 2; + ROLESTMT_GROUP = 3; +} + +enum FetchDirection +{ + FETCH_DIRECTION_UNDEFINED = 0; + FETCH_FORWARD = 1; + FETCH_BACKWARD = 2; + FETCH_ABSOLUTE = 3; + FETCH_RELATIVE = 4; +} + +enum FunctionParameterMode +{ + FUNCTION_PARAMETER_MODE_UNDEFINED = 0; + FUNC_PARAM_IN = 1; + FUNC_PARAM_OUT = 2; + FUNC_PARAM_INOUT = 3; + FUNC_PARAM_VARIADIC = 4; + FUNC_PARAM_TABLE = 5; + FUNC_PARAM_DEFAULT = 6; +} + +enum TransactionStmtKind +{ + TRANSACTION_STMT_KIND_UNDEFINED = 0; + TRANS_STMT_BEGIN = 1; + TRANS_STMT_START = 2; + TRANS_STMT_COMMIT = 3; + TRANS_STMT_ROLLBACK = 4; + TRANS_STMT_SAVEPOINT = 5; + TRANS_STMT_RELEASE = 6; + TRANS_STMT_ROLLBACK_TO = 7; + TRANS_STMT_PREPARE = 8; + TRANS_STMT_COMMIT_PREPARED = 9; + TRANS_STMT_ROLLBACK_PREPARED = 10; +} + +enum ViewCheckOption +{ + VIEW_CHECK_OPTION_UNDEFINED = 0; + NO_CHECK_OPTION = 1; + LOCAL_CHECK_OPTION = 2; + CASCADED_CHECK_OPTION = 3; +} + +enum DiscardMode +{ + DISCARD_MODE_UNDEFINED = 0; + DISCARD_ALL = 1; + DISCARD_PLANS = 2; + DISCARD_SEQUENCES = 3; + DISCARD_TEMP = 4; +} + +enum ReindexObjectType +{ + REINDEX_OBJECT_TYPE_UNDEFINED = 0; + REINDEX_OBJECT_INDEX = 1; + REINDEX_OBJECT_TABLE = 2; + REINDEX_OBJECT_SCHEMA = 3; + REINDEX_OBJECT_SYSTEM = 4; + REINDEX_OBJECT_DATABASE = 5; +} + +enum AlterTSConfigType +{ + ALTER_TSCONFIG_TYPE_UNDEFINED = 0; + ALTER_TSCONFIG_ADD_MAPPING = 1; + ALTER_TSCONFIG_ALTER_MAPPING_FOR_TOKEN = 2; + ALTER_TSCONFIG_REPLACE_DICT = 3; + ALTER_TSCONFIG_REPLACE_DICT_FOR_TOKEN = 4; + ALTER_TSCONFIG_DROP_MAPPING = 5; +} + +enum PublicationObjSpecType +{ + PUBLICATION_OBJ_SPEC_TYPE_UNDEFINED = 0; + PUBLICATIONOBJ_TABLE = 1; + PUBLICATIONOBJ_TABLES_IN_SCHEMA = 2; + PUBLICATIONOBJ_TABLES_IN_CUR_SCHEMA = 3; + PUBLICATIONOBJ_CONTINUATION = 4; +} + +enum AlterPublicationAction +{ + ALTER_PUBLICATION_ACTION_UNDEFINED = 0; + AP_AddObjects = 1; + AP_DropObjects = 2; + AP_SetObjects = 3; +} + +enum AlterSubscriptionType +{ + ALTER_SUBSCRIPTION_TYPE_UNDEFINED = 0; + ALTER_SUBSCRIPTION_OPTIONS = 1; + ALTER_SUBSCRIPTION_CONNECTION = 2; + ALTER_SUBSCRIPTION_SET_PUBLICATION = 3; + ALTER_SUBSCRIPTION_ADD_PUBLICATION = 4; + ALTER_SUBSCRIPTION_DROP_PUBLICATION = 5; + ALTER_SUBSCRIPTION_REFRESH = 6; + ALTER_SUBSCRIPTION_ENABLED = 7; + ALTER_SUBSCRIPTION_SKIP = 8; +} + +enum OverridingKind +{ + OVERRIDING_KIND_UNDEFINED = 0; + OVERRIDING_NOT_SET = 1; + OVERRIDING_USER_VALUE = 2; + OVERRIDING_SYSTEM_VALUE = 3; +} + +enum OnCommitAction +{ + ON_COMMIT_ACTION_UNDEFINED = 0; + ONCOMMIT_NOOP = 1; + ONCOMMIT_PRESERVE_ROWS = 2; + ONCOMMIT_DELETE_ROWS = 3; + ONCOMMIT_DROP = 4; +} + +enum TableFuncType +{ + TABLE_FUNC_TYPE_UNDEFINED = 0; + TFT_XMLTABLE = 1; + TFT_JSON_TABLE = 2; +} + +enum ParamKind +{ + PARAM_KIND_UNDEFINED = 0; + PARAM_EXTERN = 1; + PARAM_EXEC = 2; + PARAM_SUBLINK = 3; + PARAM_MULTIEXPR = 4; +} + +enum CoercionContext +{ + COERCION_CONTEXT_UNDEFINED = 0; + COERCION_IMPLICIT = 1; + COERCION_ASSIGNMENT = 2; + COERCION_PLPGSQL = 3; + COERCION_EXPLICIT = 4; +} + +enum CoercionForm +{ + COERCION_FORM_UNDEFINED = 0; + COERCE_EXPLICIT_CALL = 1; + COERCE_EXPLICIT_CAST = 2; + COERCE_IMPLICIT_CAST = 3; + COERCE_SQL_SYNTAX = 4; +} + +enum BoolExprType +{ + BOOL_EXPR_TYPE_UNDEFINED = 0; + AND_EXPR = 1; + OR_EXPR = 2; + NOT_EXPR = 3; +} + +enum SubLinkType +{ + SUB_LINK_TYPE_UNDEFINED = 0; + EXISTS_SUBLINK = 1; + ALL_SUBLINK = 2; + ANY_SUBLINK = 3; + ROWCOMPARE_SUBLINK = 4; + EXPR_SUBLINK = 5; + MULTIEXPR_SUBLINK = 6; + ARRAY_SUBLINK = 7; + CTE_SUBLINK = 8; +} + +enum RowCompareType +{ + ROW_COMPARE_TYPE_UNDEFINED = 0; + ROWCOMPARE_LT = 1; + ROWCOMPARE_LE = 2; + ROWCOMPARE_EQ = 3; + ROWCOMPARE_GE = 4; + ROWCOMPARE_GT = 5; + ROWCOMPARE_NE = 6; +} + +enum MinMaxOp +{ + MIN_MAX_OP_UNDEFINED = 0; + IS_GREATEST = 1; + IS_LEAST = 2; +} + +enum SQLValueFunctionOp +{ + SQLVALUE_FUNCTION_OP_UNDEFINED = 0; + SVFOP_CURRENT_DATE = 1; + SVFOP_CURRENT_TIME = 2; + SVFOP_CURRENT_TIME_N = 3; + SVFOP_CURRENT_TIMESTAMP = 4; + SVFOP_CURRENT_TIMESTAMP_N = 5; + SVFOP_LOCALTIME = 6; + SVFOP_LOCALTIME_N = 7; + SVFOP_LOCALTIMESTAMP = 8; + SVFOP_LOCALTIMESTAMP_N = 9; + SVFOP_CURRENT_ROLE = 10; + SVFOP_CURRENT_USER = 11; + SVFOP_USER = 12; + SVFOP_SESSION_USER = 13; + SVFOP_CURRENT_CATALOG = 14; + SVFOP_CURRENT_SCHEMA = 15; +} + +enum XmlExprOp +{ + XML_EXPR_OP_UNDEFINED = 0; + IS_XMLCONCAT = 1; + IS_XMLELEMENT = 2; + IS_XMLFOREST = 3; + IS_XMLPARSE = 4; + IS_XMLPI = 5; + IS_XMLROOT = 6; + IS_XMLSERIALIZE = 7; + IS_DOCUMENT = 8; +} + +enum XmlOptionType +{ + XML_OPTION_TYPE_UNDEFINED = 0; + XMLOPTION_DOCUMENT = 1; + XMLOPTION_CONTENT = 2; +} + +enum JsonEncoding +{ + JSON_ENCODING_UNDEFINED = 0; + JS_ENC_DEFAULT = 1; + JS_ENC_UTF8 = 2; + JS_ENC_UTF16 = 3; + JS_ENC_UTF32 = 4; +} + +enum JsonFormatType +{ + JSON_FORMAT_TYPE_UNDEFINED = 0; + JS_FORMAT_DEFAULT = 1; + JS_FORMAT_JSON = 2; + JS_FORMAT_JSONB = 3; +} + +enum JsonConstructorType +{ + JSON_CONSTRUCTOR_TYPE_UNDEFINED = 0; + JSCTOR_JSON_OBJECT = 1; + JSCTOR_JSON_ARRAY = 2; + JSCTOR_JSON_OBJECTAGG = 3; + JSCTOR_JSON_ARRAYAGG = 4; + JSCTOR_JSON_PARSE = 5; + JSCTOR_JSON_SCALAR = 6; + JSCTOR_JSON_SERIALIZE = 7; +} + +enum JsonValueType +{ + JSON_VALUE_TYPE_UNDEFINED = 0; + JS_TYPE_ANY = 1; + JS_TYPE_OBJECT = 2; + JS_TYPE_ARRAY = 3; + JS_TYPE_SCALAR = 4; +} + +enum JsonWrapper +{ + JSON_WRAPPER_UNDEFINED = 0; + JSW_UNSPEC = 1; + JSW_NONE = 2; + JSW_CONDITIONAL = 3; + JSW_UNCONDITIONAL = 4; +} + +enum JsonBehaviorType +{ + JSON_BEHAVIOR_TYPE_UNDEFINED = 0; + JSON_BEHAVIOR_NULL = 1; + JSON_BEHAVIOR_ERROR = 2; + JSON_BEHAVIOR_EMPTY = 3; + JSON_BEHAVIOR_TRUE = 4; + JSON_BEHAVIOR_FALSE = 5; + JSON_BEHAVIOR_UNKNOWN = 6; + JSON_BEHAVIOR_EMPTY_ARRAY = 7; + JSON_BEHAVIOR_EMPTY_OBJECT = 8; + JSON_BEHAVIOR_DEFAULT = 9; +} + +enum JsonExprOp +{ + JSON_EXPR_OP_UNDEFINED = 0; + JSON_EXISTS_OP = 1; + JSON_QUERY_OP = 2; + JSON_VALUE_OP = 3; + JSON_TABLE_OP = 4; +} + +enum NullTestType +{ + NULL_TEST_TYPE_UNDEFINED = 0; + IS_NULL = 1; + IS_NOT_NULL = 2; +} + +enum BoolTestType +{ + BOOL_TEST_TYPE_UNDEFINED = 0; + IS_TRUE = 1; + IS_NOT_TRUE = 2; + IS_FALSE = 3; + IS_NOT_FALSE = 4; + IS_UNKNOWN = 5; + IS_NOT_UNKNOWN = 6; +} + +enum MergeMatchKind +{ + MERGE_MATCH_KIND_UNDEFINED = 0; + MERGE_WHEN_MATCHED = 1; + MERGE_WHEN_NOT_MATCHED_BY_SOURCE = 2; + MERGE_WHEN_NOT_MATCHED_BY_TARGET = 3; +} + +enum CmdType +{ + CMD_TYPE_UNDEFINED = 0; + CMD_UNKNOWN = 1; + CMD_SELECT = 2; + CMD_UPDATE = 3; + CMD_INSERT = 4; + CMD_DELETE = 5; + CMD_MERGE = 6; + CMD_UTILITY = 7; + CMD_NOTHING = 8; +} + +enum JoinType +{ + JOIN_TYPE_UNDEFINED = 0; + JOIN_INNER = 1; + JOIN_LEFT = 2; + JOIN_FULL = 3; + JOIN_RIGHT = 4; + JOIN_SEMI = 5; + JOIN_ANTI = 6; + JOIN_RIGHT_ANTI = 7; + JOIN_UNIQUE_OUTER = 8; + JOIN_UNIQUE_INNER = 9; +} + +enum AggStrategy +{ + AGG_STRATEGY_UNDEFINED = 0; + AGG_PLAIN = 1; + AGG_SORTED = 2; + AGG_HASHED = 3; + AGG_MIXED = 4; +} + +enum AggSplit +{ + AGG_SPLIT_UNDEFINED = 0; + AGGSPLIT_SIMPLE = 1; + AGGSPLIT_INITIAL_SERIAL = 2; + AGGSPLIT_FINAL_DESERIAL = 3; +} + +enum SetOpCmd +{ + SET_OP_CMD_UNDEFINED = 0; + SETOPCMD_INTERSECT = 1; + SETOPCMD_INTERSECT_ALL = 2; + SETOPCMD_EXCEPT = 3; + SETOPCMD_EXCEPT_ALL = 4; +} + +enum SetOpStrategy +{ + SET_OP_STRATEGY_UNDEFINED = 0; + SETOP_SORTED = 1; + SETOP_HASHED = 2; +} + +enum OnConflictAction +{ + ON_CONFLICT_ACTION_UNDEFINED = 0; + ONCONFLICT_NONE = 1; + ONCONFLICT_NOTHING = 2; + ONCONFLICT_UPDATE = 3; +} + +enum LimitOption +{ + LIMIT_OPTION_UNDEFINED = 0; + LIMIT_OPTION_DEFAULT = 1; + LIMIT_OPTION_COUNT = 2; + LIMIT_OPTION_WITH_TIES = 3; +} + +enum LockClauseStrength +{ + LOCK_CLAUSE_STRENGTH_UNDEFINED = 0; + LCS_NONE = 1; + LCS_FORKEYSHARE = 2; + LCS_FORSHARE = 3; + LCS_FORNOKEYUPDATE = 4; + LCS_FORUPDATE = 5; +} + +enum LockWaitPolicy +{ + LOCK_WAIT_POLICY_UNDEFINED = 0; + LockWaitBlock = 1; + LockWaitSkip = 2; + LockWaitError = 3; +} + +enum LockTupleMode +{ + LOCK_TUPLE_MODE_UNDEFINED = 0; + LockTupleKeyShare = 1; + LockTupleShare = 2; + LockTupleNoKeyExclusive = 3; + LockTupleExclusive = 4; +} + +message ScanToken { + int32 start = 1; + int32 end = 2; + Token token = 4; + KeywordKind keyword_kind = 5; +} + +enum KeywordKind { + NO_KEYWORD = 0; + UNRESERVED_KEYWORD = 1; + COL_NAME_KEYWORD = 2; + TYPE_FUNC_NAME_KEYWORD = 3; + RESERVED_KEYWORD = 4; +} + +enum Token { + NUL = 0; + // Single-character tokens that are returned 1:1 (identical with "self" list in scan.l) + // Either supporting syntax, or single-character operators (some can be both) + // Also see https://www.postgresql.org/docs/12/sql-syntax-lexical.html#SQL-SYNTAX-SPECIAL-CHARS + ASCII_36 = 36; // "$" + ASCII_37 = 37; // "%" + ASCII_40 = 40; // "(" + ASCII_41 = 41; // ")" + ASCII_42 = 42; // "*" + ASCII_43 = 43; // "+" + ASCII_44 = 44; // "," + ASCII_45 = 45; // "-" + ASCII_46 = 46; // "." + ASCII_47 = 47; // "/" + ASCII_58 = 58; // ":" + ASCII_59 = 59; // ";" + ASCII_60 = 60; // "<" + ASCII_61 = 61; // "=" + ASCII_62 = 62; // ">" + ASCII_63 = 63; // "?" + ASCII_91 = 91; // "[" + ASCII_92 = 92; // "\" + ASCII_93 = 93; // "]" + ASCII_94 = 94; // "^" + // Named tokens in scan.l + IDENT = 258; + UIDENT = 259; + FCONST = 260; + SCONST = 261; + USCONST = 262; + BCONST = 263; + XCONST = 264; + Op = 265; + ICONST = 266; + PARAM = 267; + TYPECAST = 268; + DOT_DOT = 269; + COLON_EQUALS = 270; + EQUALS_GREATER = 271; + LESS_EQUALS = 272; + GREATER_EQUALS = 273; + NOT_EQUALS = 274; + SQL_COMMENT = 275; + C_COMMENT = 276; + ABORT_P = 277; + ABSENT = 278; + ABSOLUTE_P = 279; + ACCESS = 280; + ACTION = 281; + ADD_P = 282; + ADMIN = 283; + AFTER = 284; + AGGREGATE = 285; + ALL = 286; + ALSO = 287; + ALTER = 288; + ALWAYS = 289; + ANALYSE = 290; + ANALYZE = 291; + AND = 292; + ANY = 293; + ARRAY = 294; + AS = 295; + ASC = 296; + ASENSITIVE = 297; + ASSERTION = 298; + ASSIGNMENT = 299; + ASYMMETRIC = 300; + ATOMIC = 301; + AT = 302; + ATTACH = 303; + ATTRIBUTE = 304; + AUTHORIZATION = 305; + BACKWARD = 306; + BEFORE = 307; + BEGIN_P = 308; + BETWEEN = 309; + BIGINT = 310; + BINARY = 311; + BIT = 312; + BOOLEAN_P = 313; + BOTH = 314; + BREADTH = 315; + BY = 316; + CACHE = 317; + CALL = 318; + CALLED = 319; + CASCADE = 320; + CASCADED = 321; + CASE = 322; + CAST = 323; + CATALOG_P = 324; + CHAIN = 325; + CHAR_P = 326; + CHARACTER = 327; + CHARACTERISTICS = 328; + CHECK = 329; + CHECKPOINT = 330; + CLASS = 331; + CLOSE = 332; + CLUSTER = 333; + COALESCE = 334; + COLLATE = 335; + COLLATION = 336; + COLUMN = 337; + COLUMNS = 338; + COMMENT = 339; + COMMENTS = 340; + COMMIT = 341; + COMMITTED = 342; + COMPRESSION = 343; + CONCURRENTLY = 344; + CONDITIONAL = 345; + CONFIGURATION = 346; + CONFLICT = 347; + CONNECTION = 348; + CONSTRAINT = 349; + CONSTRAINTS = 350; + CONTENT_P = 351; + CONTINUE_P = 352; + CONVERSION_P = 353; + COPY = 354; + COST = 355; + CREATE = 356; + CROSS = 357; + CSV = 358; + CUBE = 359; + CURRENT_P = 360; + CURRENT_CATALOG = 361; + CURRENT_DATE = 362; + CURRENT_ROLE = 363; + CURRENT_SCHEMA = 364; + CURRENT_TIME = 365; + CURRENT_TIMESTAMP = 366; + CURRENT_USER = 367; + CURSOR = 368; + CYCLE = 369; + DATA_P = 370; + DATABASE = 371; + DAY_P = 372; + DEALLOCATE = 373; + DEC = 374; + DECIMAL_P = 375; + DECLARE = 376; + DEFAULT = 377; + DEFAULTS = 378; + DEFERRABLE = 379; + DEFERRED = 380; + DEFINER = 381; + DELETE_P = 382; + DELIMITER = 383; + DELIMITERS = 384; + DEPENDS = 385; + DEPTH = 386; + DESC = 387; + DETACH = 388; + DICTIONARY = 389; + DISABLE_P = 390; + DISCARD = 391; + DISTINCT = 392; + DO = 393; + DOCUMENT_P = 394; + DOMAIN_P = 395; + DOUBLE_P = 396; + DROP = 397; + EACH = 398; + ELSE = 399; + EMPTY_P = 400; + ENABLE_P = 401; + ENCODING = 402; + ENCRYPTED = 403; + END_P = 404; + ENUM_P = 405; + ERROR_P = 406; + ESCAPE = 407; + EVENT = 408; + EXCEPT = 409; + EXCLUDE = 410; + EXCLUDING = 411; + EXCLUSIVE = 412; + EXECUTE = 413; + EXISTS = 414; + EXPLAIN = 415; + EXPRESSION = 416; + EXTENSION = 417; + EXTERNAL = 418; + EXTRACT = 419; + FALSE_P = 420; + FAMILY = 421; + FETCH = 422; + FILTER = 423; + FINALIZE = 424; + FIRST_P = 425; + FLOAT_P = 426; + FOLLOWING = 427; + FOR = 428; + FORCE = 429; + FOREIGN = 430; + FORMAT = 431; + FORWARD = 432; + FREEZE = 433; + FROM = 434; + FULL = 435; + FUNCTION = 436; + FUNCTIONS = 437; + GENERATED = 438; + GLOBAL = 439; + GRANT = 440; + GRANTED = 441; + GREATEST = 442; + GROUP_P = 443; + GROUPING = 444; + GROUPS = 445; + HANDLER = 446; + HAVING = 447; + HEADER_P = 448; + HOLD = 449; + HOUR_P = 450; + IDENTITY_P = 451; + IF_P = 452; + ILIKE = 453; + IMMEDIATE = 454; + IMMUTABLE = 455; + IMPLICIT_P = 456; + IMPORT_P = 457; + IN_P = 458; + INCLUDE = 459; + INCLUDING = 460; + INCREMENT = 461; + INDENT = 462; + INDEX = 463; + INDEXES = 464; + INHERIT = 465; + INHERITS = 466; + INITIALLY = 467; + INLINE_P = 468; + INNER_P = 469; + INOUT = 470; + INPUT_P = 471; + INSENSITIVE = 472; + INSERT = 473; + INSTEAD = 474; + INT_P = 475; + INTEGER = 476; + INTERSECT = 477; + INTERVAL = 478; + INTO = 479; + INVOKER = 480; + IS = 481; + ISNULL = 482; + ISOLATION = 483; + JOIN = 484; + JSON = 485; + JSON_ARRAY = 486; + JSON_ARRAYAGG = 487; + JSON_EXISTS = 488; + JSON_OBJECT = 489; + JSON_OBJECTAGG = 490; + JSON_QUERY = 491; + JSON_SCALAR = 492; + JSON_SERIALIZE = 493; + JSON_TABLE = 494; + JSON_VALUE = 495; + KEEP = 496; + KEY = 497; + KEYS = 498; + LABEL = 499; + LANGUAGE = 500; + LARGE_P = 501; + LAST_P = 502; + LATERAL_P = 503; + LEADING = 504; + LEAKPROOF = 505; + LEAST = 506; + LEFT = 507; + LEVEL = 508; + LIKE = 509; + LIMIT = 510; + LISTEN = 511; + LOAD = 512; + LOCAL = 513; + LOCALTIME = 514; + LOCALTIMESTAMP = 515; + LOCATION = 516; + LOCK_P = 517; + LOCKED = 518; + LOGGED = 519; + MAPPING = 520; + MATCH = 521; + MATCHED = 522; + MATERIALIZED = 523; + MAXVALUE = 524; + MERGE = 525; + MERGE_ACTION = 526; + METHOD = 527; + MINUTE_P = 528; + MINVALUE = 529; + MODE = 530; + MONTH_P = 531; + MOVE = 532; + NAME_P = 533; + NAMES = 534; + NATIONAL = 535; + NATURAL = 536; + NCHAR = 537; + NESTED = 538; + NEW = 539; + NEXT = 540; + NFC = 541; + NFD = 542; + NFKC = 543; + NFKD = 544; + NO = 545; + NONE = 546; + NORMALIZE = 547; + NORMALIZED = 548; + NOT = 549; + NOTHING = 550; + NOTIFY = 551; + NOTNULL = 552; + NOWAIT = 553; + NULL_P = 554; + NULLIF = 555; + NULLS_P = 556; + NUMERIC = 557; + OBJECT_P = 558; + OF = 559; + OFF = 560; + OFFSET = 561; + OIDS = 562; + OLD = 563; + OMIT = 564; + ON = 565; + ONLY = 566; + OPERATOR = 567; + OPTION = 568; + OPTIONS = 569; + OR = 570; + ORDER = 571; + ORDINALITY = 572; + OTHERS = 573; + OUT_P = 574; + OUTER_P = 575; + OVER = 576; + OVERLAPS = 577; + OVERLAY = 578; + OVERRIDING = 579; + OWNED = 580; + OWNER = 581; + PARALLEL = 582; + PARAMETER = 583; + PARSER = 584; + PARTIAL = 585; + PARTITION = 586; + PASSING = 587; + PASSWORD = 588; + PATH = 589; + PLACING = 590; + PLAN = 591; + PLANS = 592; + POLICY = 593; + POSITION = 594; + PRECEDING = 595; + PRECISION = 596; + PRESERVE = 597; + PREPARE = 598; + PREPARED = 599; + PRIMARY = 600; + PRIOR = 601; + PRIVILEGES = 602; + PROCEDURAL = 603; + PROCEDURE = 604; + PROCEDURES = 605; + PROGRAM = 606; + PUBLICATION = 607; + QUOTE = 608; + QUOTES = 609; + RANGE = 610; + READ = 611; + REAL = 612; + REASSIGN = 613; + RECHECK = 614; + RECURSIVE = 615; + REF_P = 616; + REFERENCES = 617; + REFERENCING = 618; + REFRESH = 619; + REINDEX = 620; + RELATIVE_P = 621; + RELEASE = 622; + RENAME = 623; + REPEATABLE = 624; + REPLACE = 625; + REPLICA = 626; + RESET = 627; + RESTART = 628; + RESTRICT = 629; + RETURN = 630; + RETURNING = 631; + RETURNS = 632; + REVOKE = 633; + RIGHT = 634; + ROLE = 635; + ROLLBACK = 636; + ROLLUP = 637; + ROUTINE = 638; + ROUTINES = 639; + ROW = 640; + ROWS = 641; + RULE = 642; + SAVEPOINT = 643; + SCALAR = 644; + SCHEMA = 645; + SCHEMAS = 646; + SCROLL = 647; + SEARCH = 648; + SECOND_P = 649; + SECURITY = 650; + SELECT = 651; + SEQUENCE = 652; + SEQUENCES = 653; + SERIALIZABLE = 654; + SERVER = 655; + SESSION = 656; + SESSION_USER = 657; + SET = 658; + SETS = 659; + SETOF = 660; + SHARE = 661; + SHOW = 662; + SIMILAR = 663; + SIMPLE = 664; + SKIP = 665; + SMALLINT = 666; + SNAPSHOT = 667; + SOME = 668; + SOURCE = 669; + SQL_P = 670; + STABLE = 671; + STANDALONE_P = 672; + START = 673; + STATEMENT = 674; + STATISTICS = 675; + STDIN = 676; + STDOUT = 677; + STORAGE = 678; + STORED = 679; + STRICT_P = 680; + STRING_P = 681; + STRIP_P = 682; + SUBSCRIPTION = 683; + SUBSTRING = 684; + SUPPORT = 685; + SYMMETRIC = 686; + SYSID = 687; + SYSTEM_P = 688; + SYSTEM_USER = 689; + TABLE = 690; + TABLES = 691; + TABLESAMPLE = 692; + TABLESPACE = 693; + TARGET = 694; + TEMP = 695; + TEMPLATE = 696; + TEMPORARY = 697; + TEXT_P = 698; + THEN = 699; + TIES = 700; + TIME = 701; + TIMESTAMP = 702; + TO = 703; + TRAILING = 704; + TRANSACTION = 705; + TRANSFORM = 706; + TREAT = 707; + TRIGGER = 708; + TRIM = 709; + TRUE_P = 710; + TRUNCATE = 711; + TRUSTED = 712; + TYPE_P = 713; + TYPES_P = 714; + UESCAPE = 715; + UNBOUNDED = 716; + UNCONDITIONAL = 717; + UNCOMMITTED = 718; + UNENCRYPTED = 719; + UNION = 720; + UNIQUE = 721; + UNKNOWN = 722; + UNLISTEN = 723; + UNLOGGED = 724; + UNTIL = 725; + UPDATE = 726; + USER = 727; + USING = 728; + VACUUM = 729; + VALID = 730; + VALIDATE = 731; + VALIDATOR = 732; + VALUE_P = 733; + VALUES = 734; + VARCHAR = 735; + VARIADIC = 736; + VARYING = 737; + VERBOSE = 738; + VERSION_P = 739; + VIEW = 740; + VIEWS = 741; + VOLATILE = 742; + WHEN = 743; + WHERE = 744; + WHITESPACE_P = 745; + WINDOW = 746; + WITH = 747; + WITHIN = 748; + WITHOUT = 749; + WORK = 750; + WRAPPER = 751; + WRITE = 752; + XML_P = 753; + XMLATTRIBUTES = 754; + XMLCONCAT = 755; + XMLELEMENT = 756; + XMLEXISTS = 757; + XMLFOREST = 758; + XMLNAMESPACES = 759; + XMLPARSE = 760; + XMLPI = 761; + XMLROOT = 762; + XMLSERIALIZE = 763; + XMLTABLE = 764; + YEAR_P = 765; + YES_P = 766; + ZONE = 767; + FORMAT_LA = 768; + NOT_LA = 769; + NULLS_LA = 770; + WITH_LA = 771; + WITHOUT_LA = 772; + MODE_TYPE_NAME = 773; + MODE_PLPGSQL_EXPR = 774; + MODE_PLPGSQL_ASSIGN1 = 775; + MODE_PLPGSQL_ASSIGN2 = 776; + MODE_PLPGSQL_ASSIGN3 = 777; + UMINUS = 778; +} diff --git a/crates/pgt_query_macros/src/iter_mut.rs b/crates/pgt_query_macros/src/iter_mut.rs new file mode 100644 index 00000000..b0bc10de --- /dev/null +++ b/crates/pgt_query_macros/src/iter_mut.rs @@ -0,0 +1,142 @@ +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; + +use crate::proto_analyser::{FieldType, Node, ProtoAnalyzer}; + +pub fn iter_mut_mod(analyser: ProtoAnalyzer) -> proc_macro2::TokenStream { + let enum_variants = analyser.enum_variants(); + let nodes = analyser.nodes(); + + let mut node_variant_names = Vec::new(); + let mut node_property_handlers = Vec::new(); + + // Create a map from type name to enum variant name + let mut type_to_variant: std::collections::HashMap = + std::collections::HashMap::new(); + for variant in &enum_variants { + type_to_variant.insert(variant.type_name.clone(), variant.name.clone()); + } + + for node in &nodes { + // Use the enum variant name from the Node enum + if let Some(variant_name) = type_to_variant.get(&node.name) { + let variant_ident = format_ident!("{}", variant_name); + node_variant_names.push(variant_ident); + + let property_handlers = property_handlers(node); + node_property_handlers.push(property_handlers); + } + } + + quote! { + use std::collections::VecDeque; + + /// An iterator that provides mutable access to all nodes in an AST tree. + /// + /// This iterator performs a depth-first traversal of the AST, yielding mutable + /// references to each node. It uses unsafe operations internally to work with + /// raw pointers in the AST structure. + /// + /// # Safety Requirements + /// + /// Users of this iterator must ensure: + /// + /// - The root `NodeMut` passed to `new()` must point to a valid, properly + /// constructed AST that remains alive for the iterator's lifetime + /// - No other code concurrently accesses or modifies the AST while this + /// iterator is in use (exclusive access required) + /// - The AST structure must not be modified through other means while + /// iterating (e.g., don't modify parent nodes while iterating children) + /// + /// # Panics + /// + /// This iterator may panic or cause undefined behavior if the safety + /// requirements above are violated. + /// ``` + pub struct NodeMutIterator { + stack: VecDeque, + } + + impl NodeMutIterator { + /// Creates a new iterator starting from the given root node. + /// + /// # Safety + /// + /// The caller must ensure that `roots` points to valid AST nodes + /// and that the safety requirements documented on `NodeMutIterator` + /// are met throughout the iterator's lifetime. + pub fn new(root: NodeMut) -> Self { + Self { + stack: VecDeque::from([root]), + } + } + } + + impl Iterator for NodeMutIterator { + type Item = NodeMut; + + fn next(&mut self) -> Option { + if self.stack.is_empty() { + return None; + } + + let node = self.stack.pop_front().unwrap(); + + unsafe { + match node { + #(NodeMut::#node_variant_names(n) => {#node_property_handlers}),*, + _ => { + // Some node types don't have any child nodes to traverse + } + }; + } + + Some(node) + } + } + } +} + +fn property_handlers(node: &Node) -> TokenStream { + let handlers: Vec = node + .fields + .iter() + .filter_map(|field| { + let field_name = format_ident!("{}", field.name.as_str()); + if matches!(field.r#type, FieldType::Node(_)) && field.repeated { + Some(quote! { + n.#field_name + .iter_mut() + .for_each(|x| { + if let Some(n) = x.node.as_mut() { + self.stack.push_back(n.to_mut()); + } + }); + }) + } else if matches!(field.r#type, FieldType::Node(_)) && !field.is_one_of { + if field.r#type == FieldType::Node(None) { + Some(quote! { + if let Some(n) = n.#field_name.as_mut() { + if let Some(n) = n.node.as_mut() { + self.stack.push_back(n.to_mut()); + } + } + }) + } else { + Some(quote! { + if let Some(field_node) = n.#field_name.as_mut() { + self.stack.push_back(field_node.to_mut()); + } + }) + } + } else { + None // Filter out non-node fields + } + }) + .collect(); + + quote! { + let n = n.as_mut().unwrap(); + #(#handlers)* + } +} diff --git a/crates/pgt_query_macros/src/iter_ref.rs b/crates/pgt_query_macros/src/iter_ref.rs new file mode 100644 index 00000000..8e232340 --- /dev/null +++ b/crates/pgt_query_macros/src/iter_ref.rs @@ -0,0 +1,105 @@ +use proc_macro2::TokenStream; +use quote::{format_ident, quote}; + +use crate::proto_analyser::{FieldType, Node, ProtoAnalyzer}; + +pub fn iter_ref_mod(analyser: ProtoAnalyzer) -> proc_macro2::TokenStream { + let enum_variants = analyser.enum_variants(); + let nodes = analyser.nodes(); + + let mut node_variant_names = Vec::new(); + let mut node_property_handlers = Vec::new(); + + let mut type_to_variant: std::collections::HashMap = + std::collections::HashMap::new(); + for variant in &enum_variants { + type_to_variant.insert(variant.type_name.clone(), variant.name.clone()); + } + + for node in &nodes { + if let Some(variant_name) = type_to_variant.get(&node.name) { + let variant_ident = format_ident!("{}", variant_name); + node_variant_names.push(variant_ident); + + let property_handlers = property_handlers(node); + node_property_handlers.push(quote! { + #(#property_handlers)* + }); + } + } + + quote! { + use std::collections::VecDeque; + + pub struct NodeRefIterator<'a>{ + stack: VecDeque>, + } + + impl<'a> NodeRefIterator<'a> { + pub fn new(root: NodeRef<'a>) -> Self { + Self { + stack: VecDeque::from([root]), + } + } + } + + impl<'a> Iterator for NodeRefIterator<'a> { + type Item = NodeRef<'a>; + + fn next(&mut self) -> Option { + if self.stack.is_empty() { + return None; + } + + let node = self.stack.pop_front().unwrap(); + + match &node { + #(NodeRef::#node_variant_names(n) => {#node_property_handlers}),*, + _ => { + // Some node types don't have any child nodes to traverse + } + }; + + Some(node) + } + } + } +} + +fn property_handlers(node: &Node) -> Vec { + node.fields + .iter() + .filter_map(|field| { + let field_name = format_ident!("{}", field.name.as_str()); + if matches!(field.r#type, FieldType::Node(_)) && field.repeated { + Some(quote! { + n.#field_name + .iter() + .for_each(|x| { + if let Some(n) = x.node.as_ref() { + self.stack.push_back(n.to_ref()); + } + }); + }) + } else if matches!(field.r#type, FieldType::Node(_)) && !field.is_one_of { + if field.r#type == FieldType::Node(None) { + Some(quote! { + if let Some(n) = &n.#field_name { + if let Some(n) = n.node.as_ref() { + self.stack.push_back(n.to_ref()); + } + } + }) + } else { + Some(quote! { + if let Some(field_node) = &n.#field_name { + self.stack.push_back(field_node.to_ref()); + } + }) + } + } else { + None // Filter out non-node fields + } + }) + .collect() +} diff --git a/crates/pgt_query_macros/src/lib.rs b/crates/pgt_query_macros/src/lib.rs new file mode 100644 index 00000000..718da161 --- /dev/null +++ b/crates/pgt_query_macros/src/lib.rs @@ -0,0 +1,106 @@ +use iter_mut::iter_mut_mod; +use iter_ref::iter_ref_mod; +use node_enum::node_enum_mod; +use node_mut::node_mut_mod; +use node_ref::node_ref_mod; +use node_structs::node_structs_mod; +use proto_analyser::ProtoAnalyzer; +use quote::quote; +use std::path; + +mod iter_mut; +mod iter_ref; +mod node_enum; +mod node_mut; +mod node_ref; +mod node_structs; +mod proto_analyser; + +#[proc_macro] +pub fn node_ref_codegen(_input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let analyser = ProtoAnalyzer::from(&proto_file_path()).unwrap(); + + let node_ref = node_ref_mod(analyser); + + quote! { + use crate::*; + + #node_ref + } + .into() +} + +#[proc_macro] +pub fn node_mut_codegen(_input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let analyser = ProtoAnalyzer::from(&proto_file_path()).unwrap(); + + let node_mut = node_mut_mod(analyser); + + quote! { + use crate::*; + + #node_mut + } + .into() +} + +#[proc_macro] +pub fn node_structs_codegen(_input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let analyser = ProtoAnalyzer::from(&proto_file_path()).unwrap(); + + let conversions = node_structs_mod(analyser); + + quote! { + use crate::*; + + #conversions + } + .into() +} + +#[proc_macro] +pub fn node_enum_codegen(_input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let analyser = ProtoAnalyzer::from(&proto_file_path()).unwrap(); + + let node_enum = node_enum_mod(analyser); + + quote! { + use crate::*; + + #node_enum + } + .into() +} + +#[proc_macro] +pub fn iter_ref_codegen(_input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let analyser = ProtoAnalyzer::from(&proto_file_path()).unwrap(); + + let iterator = iter_ref_mod(analyser); + + quote! { + use crate::*; + + #iterator + } + .into() +} + +#[proc_macro] +pub fn iter_mut_codegen(_input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let analyser = ProtoAnalyzer::from(&proto_file_path()).unwrap(); + + let iterator = iter_mut_mod(analyser); + + quote! { + use crate::*; + + #iterator + } + .into() +} + +fn proto_file_path() -> path::PathBuf { + // Use the path set by the build script + path::PathBuf::from(env!("PG_QUERY_PROTO_PATH")) +} diff --git a/crates/pgt_query_macros/src/node_enum.rs b/crates/pgt_query_macros/src/node_enum.rs new file mode 100644 index 00000000..0801bbab --- /dev/null +++ b/crates/pgt_query_macros/src/node_enum.rs @@ -0,0 +1,44 @@ +use quote::{format_ident, quote}; + +use crate::proto_analyser::ProtoAnalyzer; + +pub fn node_enum_mod(analyser: ProtoAnalyzer) -> proc_macro2::TokenStream { + let node_variants = analyser.enum_variants(); + + let mut to_ref_matches = Vec::new(); + let mut to_mut_matches = Vec::new(); + + for variant in &node_variants { + let variant_ident = format_ident!("{}", &variant.name); + + to_ref_matches.push(quote! { + NodeEnum::#variant_ident(n) => NodeRef::#variant_ident(&n) + }); + + if variant.boxed { + to_mut_matches.push(quote! { + NodeEnum::#variant_ident(n) => NodeMut::#variant_ident(&mut **n as *mut _) + }); + } else { + to_mut_matches.push(quote! { + NodeEnum::#variant_ident(n) => NodeMut::#variant_ident(n as *mut _) + }); + } + } + + quote! { + impl NodeEnum { + pub fn to_ref(&self) -> NodeRef { + match self { + #(#to_ref_matches,)* + } + } + + pub fn to_mut(&mut self) -> NodeMut { + match self { + #(#to_mut_matches,)* + } + } + } + } +} diff --git a/crates/pgt_query_macros/src/node_mut.rs b/crates/pgt_query_macros/src/node_mut.rs new file mode 100644 index 00000000..52120c1a --- /dev/null +++ b/crates/pgt_query_macros/src/node_mut.rs @@ -0,0 +1,50 @@ +use quote::{format_ident, quote}; + +use crate::proto_analyser::ProtoAnalyzer; + +pub fn node_mut_mod(analyser: ProtoAnalyzer) -> proc_macro2::TokenStream { + let node_variants = analyser.enum_variants(); + + let mut to_enum_matches = Vec::new(); + let mut node_enum_variants = Vec::new(); + + for variant in &node_variants { + let variant_ident = format_ident!("{}", &variant.name); + let type_ident = format_ident!("{}", &variant.type_name); + + if variant.boxed { + // For boxed variants, we need to box the cloned value + to_enum_matches.push(quote! { + NodeMut::#variant_ident(n) => Ok(NodeEnum::#variant_ident(Box::new(n.as_ref().ok_or(err)?.clone()))) + }); + } else { + // For non-boxed variants, clone directly + to_enum_matches.push(quote! { + NodeMut::#variant_ident(n) => Ok(NodeEnum::#variant_ident(n.as_ref().ok_or(err)?.clone())) + }); + } + + node_enum_variants.push(quote! { + #variant_ident(*mut protobuf::#type_ident) + }); + } + + quote! { + #[derive(Debug, Copy, Clone)] + pub enum NodeMut { + #(#node_enum_variants, )* + } + + impl NodeMut { + pub fn to_enum(self) -> Result { + unsafe { + let err = Error::InvalidPointer; + match self { + #(#to_enum_matches,)* + _ => Err(Error::InvalidPointer), + } + } + } + } + } +} diff --git a/crates/pgt_query_macros/src/node_ref.rs b/crates/pgt_query_macros/src/node_ref.rs new file mode 100644 index 00000000..64f9b7c4 --- /dev/null +++ b/crates/pgt_query_macros/src/node_ref.rs @@ -0,0 +1,46 @@ +use quote::{format_ident, quote}; + +use crate::proto_analyser::ProtoAnalyzer; + +pub fn node_ref_mod(analyser: ProtoAnalyzer) -> proc_macro2::TokenStream { + let node_variants = analyser.enum_variants(); + + let mut to_enum_matches = Vec::new(); + let mut node_enum_variants = Vec::new(); + + for variant in &node_variants { + let variant_ident = format_ident!("{}", &variant.name); + let type_ident = format_ident!("{}", &variant.type_name); + + if variant.boxed { + // For boxed variants, we need to box the cloned value + to_enum_matches.push(quote! { + NodeRef::#variant_ident(n) => NodeEnum::#variant_ident(::prost::alloc::boxed::Box::new((*n).clone())) + }); + } else { + // For non-boxed variants, clone directly + to_enum_matches.push(quote! { + NodeRef::#variant_ident(n) => NodeEnum::#variant_ident((*n).clone()) + }); + } + + node_enum_variants.push(quote! { + #variant_ident(&'a protobuf::#type_ident) + }); + } + + quote! { + #[derive(Debug, Copy, Clone)] + pub enum NodeRef<'a> { + #(#node_enum_variants,)* + } + + impl<'a> NodeRef<'a> { + pub fn to_enum(self) -> NodeEnum { + match self { + #(#to_enum_matches,)* + } + } + } + } +} diff --git a/crates/pgt_query_macros/src/node_structs.rs b/crates/pgt_query_macros/src/node_structs.rs new file mode 100644 index 00000000..52fca2e0 --- /dev/null +++ b/crates/pgt_query_macros/src/node_structs.rs @@ -0,0 +1,30 @@ +use quote::{format_ident, quote}; + +use crate::proto_analyser::ProtoAnalyzer; + +pub fn node_structs_mod(analyser: ProtoAnalyzer) -> proc_macro2::TokenStream { + let node_variants = analyser.enum_variants(); + + let mut impls = Vec::new(); + + for variant in &node_variants { + let node_ident = format_ident!("{}", &variant.name); + let type_ident = format_ident!("{}", &variant.type_name); + + impls.push(quote! { + impl protobuf::#type_ident { + pub fn to_ref(&self) -> NodeRef { + NodeRef::#node_ident(self) + } + + pub fn to_mut(&mut self) -> NodeMut { + NodeMut::#node_ident(self) + } + } + }); + } + + quote! { + #(#impls)* + } +} diff --git a/crates/pgt_query_macros/src/proto_analyser.rs b/crates/pgt_query_macros/src/proto_analyser.rs new file mode 100644 index 00000000..26a18b60 --- /dev/null +++ b/crates/pgt_query_macros/src/proto_analyser.rs @@ -0,0 +1,252 @@ +use std::{ + collections::{HashMap, HashSet}, + path::Path, +}; + +use convert_case::{Case, Casing}; +use prost_reflect::{ + DescriptorError, DescriptorPool, FieldDescriptor, MessageDescriptor, + prost_types::{ + FieldDescriptorProto, + field_descriptor_proto::{Label, Type}, + }, +}; + +pub(crate) struct ProtoAnalyzer { + pool: DescriptorPool, + message_graph: HashMap>, +} + +pub(crate) struct EnumVariant { + pub name: String, + pub type_name: String, + pub boxed: bool, +} + +#[derive(Debug, PartialEq, Eq)] +pub(crate) enum FieldType { + Node(Option), + Enum(String), + Literal, +} + +pub(crate) struct Field { + pub name: String, + pub r#type: FieldType, + pub repeated: bool, + pub is_one_of: bool, +} + +pub(crate) struct Node { + pub name: String, + #[allow(dead_code)] + pub enum_variant_name: String, + pub fields: Vec, +} + +impl ProtoAnalyzer { + pub fn from(proto_file: &Path) -> Result { + let include_path = proto_file + .parent() + .expect("Proto file must have a parent directory"); + + // protox::compile expects the proto file to be relative to the include path + let file_name = proto_file + .file_name() + .expect("Proto file must have a file name"); + + let pool = DescriptorPool::from_file_descriptor_set( + protox::compile([file_name], [include_path]).expect("unable to parse"), + )?; + + let mut analyzer = ProtoAnalyzer { + pool, + message_graph: HashMap::new(), + }; + + // Build the message graph + analyzer.build_message_graph(); + + Ok(analyzer) + } + + pub fn nodes(&self) -> Vec { + let mut nodes = Vec::new(); + + for msg in self.pool.all_messages() { + if ["ParseResult", "ScanResult", "Node", "ScanToken"].contains(&msg.name()) { + continue; + } + let fields = msg + .fields() + .map(|f| { + let field_type = match f.field_descriptor_proto().r#type() { + Type::Message => match f.field_descriptor_proto().type_name() { + ".pg_query.Node" => FieldType::Node(None), + name => { + FieldType::Node(Some(name.to_string().replace(".pg_query.", ""))) + } + }, + Type::Enum => FieldType::Enum( + f.field_descriptor_proto() + .type_name() + .to_string() + .replace(".pg_query.", ""), + ), + _ => FieldType::Literal, + }; + + Field { + name: f.name().to_string(), + r#type: field_type, + repeated: f.is_list(), + is_one_of: f.containing_oneof().is_some(), + } + }) + .collect(); + + nodes.push(Node { + name: msg.name().to_string(), + enum_variant_name: msg.name().to_case(Case::Pascal), // Convert to PascalCase for enum variant name + fields, + }); + } + + nodes + } + + pub fn enum_variants(&self) -> Vec { + let node = self + .pool + .get_message_by_name(".pg_query.Node") + .expect("Node message not found"); + + let mut variants = Vec::new(); + for field in node.fields() { + // The prost-generated variant name is derived from the field name using snake_case to PascalCase conversion + // For example: ctesearch_clause -> CtesearchClause + let field_name = field.name(); + let variant_name = field_name.to_case(Case::Pascal); + + // Get the actual proto type name (the message type) + let proto_type_name = field + .field_descriptor_proto() + .type_name() + .split('.') + .next_back() + .unwrap_or(&variant_name); + + // The Rust type name is the proto type name converted to PascalCase + // For example: CTESearchClause -> CteSearchClause + let type_name = proto_type_name.to_case(Case::Pascal); + + let boxed = self.is_field_boxed(&field, &node); + + variants.push(EnumVariant { + name: variant_name, + type_name, + boxed, + }); + } + + variants + } + + /// Build a graph of message dependencies for cycle detection + fn build_message_graph(&mut self) { + // Collect all messages first to avoid borrowing issues + let mut all_messages = Vec::new(); + for file in self.pool.files() { + for message in file.messages() { + all_messages.push(message); + } + } + + // Now add them to the graph + for message in all_messages { + self.add_message_to_graph(&message); + } + } + + /// Add a message and its dependencies to the graph + fn add_message_to_graph(&mut self, message: &MessageDescriptor) { + let msg_fq_name = format!(".{}", message.full_name()); + let mut dependencies = Vec::new(); + + // Check all fields for message type dependencies + for field in message.fields() { + if let Some(field_message) = field.kind().as_message() { + // Only add non-repeated message fields as dependencies + // since repeated fields are already heap allocated in Vec + if !field.is_list() { + let field_fq_name = format!(".{}", field_message.full_name()); + dependencies.push(field_fq_name); + } + } + } + + self.message_graph.insert(msg_fq_name, dependencies); + + // Recursively add nested messages + for nested in message.child_messages() { + self.add_message_to_graph(&nested); + } + } + + /// Detect if a field will be boxed by prost due to recursive nesting + fn is_field_boxed(&self, field: &FieldDescriptor, parent_message: &MessageDescriptor) -> bool { + // Check if this is a message field that should be boxed + let parent_fq_name = format!(".{}", parent_message.full_name()); + self.is_boxed(&parent_fq_name, field.field_descriptor_proto()) + } + + /// Check if there's a path from parent_message to field_type in the message graph + /// This indicates that field_type is transitively contained within parent_message + fn is_nested(&self, parent_message_name: &str, field_type_name: &str) -> bool { + self.has_path(parent_message_name, field_type_name, &mut HashSet::new()) + } + + /// Recursive helper to find if there's a path from 'from' to 'to' in the message graph + fn has_path(&self, from: &str, to: &str, visited: &mut HashSet) -> bool { + // If we've already visited this node, return false to avoid cycles + if visited.contains(from) { + return false; + } + + // If we've reached the target, we found a path + if from == to { + return true; + } + + visited.insert(from.to_string()); + + // Check all dependencies of the current message + if let Some(dependencies) = self.message_graph.get(from) { + for dep in dependencies { + if self.has_path(dep, to, visited) { + return true; + } + } + } + + false + } + + /// Returns whether the Rust type for this message field is `Box<_>`. + fn is_boxed(&self, fq_message_name: &str, field: &FieldDescriptorProto) -> bool { + if field.label() == Label::Repeated { + // Repeated field are stored in Vec, therefore it is already heap allocated + return false; + } + let fd_type = field.r#type(); + if fd_type == Type::Message || fd_type == Type::Group { + // The field should be boxed if the field type transitively contains the parent message + // This prevents infinitely sized type definitions + if let Some(field_type_name) = field.type_name.as_ref() { + // IMPORTANT: Check if field_type_name contains fq_message_name (not the other way around) + return self.is_nested(field_type_name, fq_message_name); + } + } + false + } +} diff --git a/crates/pgt_query_proto_parser/src/lib.rs b/crates/pgt_query_proto_parser/src/lib.rs deleted file mode 100644 index 12f8cf9c..00000000 --- a/crates/pgt_query_proto_parser/src/lib.rs +++ /dev/null @@ -1,9 +0,0 @@ -//! A parser for the libg_query proto file -//! -//! This crate provides a parser for the libg_query proto file, and a struct to represent and interact with the parsed file. - -mod proto_file; -mod proto_parser; - -pub use crate::proto_file::{Field, FieldType, Node, ProtoFile, Token}; -pub use crate::proto_parser::ProtoParser; diff --git a/crates/pgt_query_proto_parser/src/proto_file.rs b/crates/pgt_query_proto_parser/src/proto_file.rs deleted file mode 100644 index 2cc32798..00000000 --- a/crates/pgt_query_proto_parser/src/proto_file.rs +++ /dev/null @@ -1,60 +0,0 @@ -/// The FieldTypes of a protobuf message -#[derive(Debug, Eq, PartialEq)] -pub enum FieldType { - Node, - Double, - Float, - Int64, - Uint64, - Int32, - Fixed64, - Fixed32, - Bool, - String, - Group, - Message, - Bytes, - Uint32, - Enum, - Sfixed32, - Sfixed64, - Sint32, - Sint64, -} - -/// A libg_query token -#[derive(Debug)] -pub struct Token { - pub name: String, - pub value: i32, -} - -/// A libg_query field -#[derive(Debug)] -pub struct Field { - pub name: String, - pub node_name: Option, - pub enum_variant_name: Option, - pub field_type: FieldType, - pub repeated: bool, - pub is_one_of: bool, -} - -/// A libg_query node -#[derive(Debug)] -pub struct Node { - pub name: String, - pub fields: Vec, -} - -/// The libg_query proto file -pub struct ProtoFile { - pub tokens: Vec, - pub nodes: Vec, -} - -impl ProtoFile { - pub fn node(&self, name: &str) -> Option<&Node> { - self.nodes.iter().find(|n| n.name == name) - } -} diff --git a/crates/pgt_query_proto_parser/src/proto_parser.rs b/crates/pgt_query_proto_parser/src/proto_parser.rs deleted file mode 100644 index 56f93c6e..00000000 --- a/crates/pgt_query_proto_parser/src/proto_parser.rs +++ /dev/null @@ -1,179 +0,0 @@ -use convert_case::{Case, Casing}; -use protobuf::descriptor::{FileDescriptorProto, field_descriptor_proto::Label}; -use protobuf_parse::Parser; -use std::{ffi::OsStr, path::Path}; - -use crate::proto_file::{Field, FieldType, Node, ProtoFile, Token}; - -/// The parser for the libg_query proto file -pub struct ProtoParser { - inner: FileDescriptorProto, -} - -impl ProtoParser { - pub fn new(file_path: &impl AsRef) -> Self { - let proto_file = Path::new(file_path); - let proto_dir = proto_file.parent().unwrap(); - - let result = Parser::new() - .pure() - .include(proto_dir) - .input(proto_file) - .parse_and_typecheck() - .unwrap(); - - ProtoParser { - inner: result.file_descriptors[0].clone(), - } - } - - pub fn parse(&self) -> ProtoFile { - ProtoFile { - tokens: self.tokens(), - nodes: self.nodes(), - } - } - - fn tokens(&self) -> Vec { - self.inner - .enum_type - .iter() - .find(|e| e.name == Some("Token".into())) - .unwrap() - .value - .iter() - .map(|e| Token { - // token names in proto are UPPERCASE_SNAKE_CASE - name: e.name.clone().unwrap().to_case(Case::UpperCamel), - value: e.number.unwrap(), - }) - .collect() - } - - fn get_enum_variant_name(&self, type_name: &str) -> Option { - let variant = self - .inner - .message_type - .iter() - .find(|e| e.name == Some("Node".into())) - .unwrap() - .field - .iter() - .find(|e| e.type_name().split(".").last().unwrap() == type_name); - variant.map(|v| v.name.clone().unwrap().to_case(Case::UpperCamel)) - } - - fn nodes(&self) -> Vec { - self.inner - .message_type - .iter() - .find(|e| e.name == Some("Node".into())) - .unwrap() - .field - .iter() - .map(|e| { - let name: String = e.name.to_owned().unwrap().to_case(Case::UpperCamel); - let node = self - .inner - .message_type - .iter() - .find(|n| { - n.name.clone().unwrap().to_case(Case::UpperCamel) - == e.json_name.as_ref().unwrap().to_case(Case::UpperCamel) - }) - .unwrap(); - - let mut fields: Vec = Vec::new(); - // from node fields - fields.append(&mut - node - .field - .iter() - .filter_map(|e| { - // skip one of fields, they are handled separately - if e.has_oneof_index() { - return None; - } - // use label and type to get the field type - let type_name: FieldType = match e.type_name() { - "" => match e.type_() { - protobuf::descriptor::field_descriptor_proto::Type::TYPE_DOUBLE => FieldType::Double, - protobuf::descriptor::field_descriptor_proto::Type::TYPE_FLOAT => FieldType::Float, - protobuf::descriptor::field_descriptor_proto::Type::TYPE_INT64 => FieldType::Int64, - protobuf::descriptor::field_descriptor_proto::Type::TYPE_UINT64 => FieldType::Uint64, - protobuf::descriptor::field_descriptor_proto::Type::TYPE_INT32 => FieldType::Int32, - protobuf::descriptor::field_descriptor_proto::Type::TYPE_FIXED64 => FieldType::Fixed64, - protobuf::descriptor::field_descriptor_proto::Type::TYPE_FIXED32 => FieldType::Fixed32, - protobuf::descriptor::field_descriptor_proto::Type::TYPE_BOOL => FieldType::Bool, - protobuf::descriptor::field_descriptor_proto::Type::TYPE_STRING => FieldType::String, - protobuf::descriptor::field_descriptor_proto::Type::TYPE_GROUP => FieldType::Group, - protobuf::descriptor::field_descriptor_proto::Type::TYPE_MESSAGE => FieldType::Message, - protobuf::descriptor::field_descriptor_proto::Type::TYPE_BYTES => FieldType::Bytes, - protobuf::descriptor::field_descriptor_proto::Type::TYPE_UINT32 => FieldType::Uint32, - protobuf::descriptor::field_descriptor_proto::Type::TYPE_ENUM => FieldType::Enum, - protobuf::descriptor::field_descriptor_proto::Type::TYPE_SFIXED32 => FieldType::Sfixed32, - protobuf::descriptor::field_descriptor_proto::Type::TYPE_SFIXED64 => FieldType::Sfixed64, - protobuf::descriptor::field_descriptor_proto::Type::TYPE_SINT32 => FieldType::Sint32, - protobuf::descriptor::field_descriptor_proto::Type::TYPE_SINT64 => FieldType::Sint64, - }, - _ => { - if !e.type_name().starts_with(".pg_query") { - panic!("Unknown type: {}", e.type_name()); - - } - if e.type_() == protobuf::descriptor::field_descriptor_proto::Type::TYPE_ENUM { - FieldType::Enum - } else { - FieldType::Node - } - }, - }; - let mut node_name = None; - let mut enum_variant_name = None; - if e.type_name().starts_with(".pg_query") { - let n = e.type_name().split(".").last().unwrap().to_string(); - node_name = Some(n.clone()); - if n != "Node" { - enum_variant_name = self.get_enum_variant_name(e.type_name().split(".").last().unwrap().to_string().as_str()); - } - } - // TODO: node name must be derived from the property name in the node - // enum - Some(Field { - name: e.name.clone().unwrap(), - node_name, - enum_variant_name, - field_type: type_name, - repeated: e.label() == Label::LABEL_REPEATED, - is_one_of: false, - }) - }) - .collect() - ); - - // one of declarations - fields.append(&mut - node - .oneof_decl - .iter() - .map(|e| { - Field { - name: e.name.clone().unwrap(), - node_name: Some("Node".to_string()), - enum_variant_name: None, - field_type: FieldType::Node, - repeated: false, - is_one_of: true, - } - }) - .collect() - ); - Node { - // token names in proto are UPPERCASE_SNAKE_CASE - name: name.clone(), - fields, - } - }) - .collect() - } -} diff --git a/crates/pgt_statement_splitter/Cargo.toml b/crates/pgt_statement_splitter/Cargo.toml index bdd892a6..45a42ebc 100644 --- a/crates/pgt_statement_splitter/Cargo.toml +++ b/crates/pgt_statement_splitter/Cargo.toml @@ -14,7 +14,7 @@ version = "0.0.0" [dependencies] pgt_diagnostics = { workspace = true } pgt_lexer.workspace = true -pgt_query_ext.workspace = true +pgt_query.workspace = true pgt_text_size.workspace = true regex.workspace = true diff --git a/crates/pgt_type_resolver/Cargo.toml b/crates/pgt_type_resolver/Cargo.toml index 5d2a8eb1..9a190fdf 100644 --- a/crates/pgt_type_resolver/Cargo.toml +++ b/crates/pgt_type_resolver/Cargo.toml @@ -12,7 +12,7 @@ version = "0.0.0" [dependencies] -pgt_query_ext.workspace = true +pgt_query.workspace = true pgt_schema_cache.workspace = true [dev-dependencies] diff --git a/crates/pgt_type_resolver/src/functions.rs b/crates/pgt_type_resolver/src/functions.rs index 1b0036b5..208af30d 100644 --- a/crates/pgt_type_resolver/src/functions.rs +++ b/crates/pgt_type_resolver/src/functions.rs @@ -6,7 +6,7 @@ use crate::{ }; pub fn resolve_func_call<'b>( - node: &pgt_query_ext::protobuf::FuncCall, + node: &pgt_query::protobuf::FuncCall, schema_cache: &'b SchemaCache, ) -> Option<&'b Function> { let (schema, name) = resolve_func_identifier(node); @@ -30,7 +30,7 @@ pub fn resolve_func_call<'b>( if fns.len() == 1 { Some(fns[0]) } else { None } } -fn resolve_func_identifier(node: &pgt_query_ext::protobuf::FuncCall) -> (Option, String) { +fn resolve_func_identifier(node: &pgt_query::protobuf::FuncCall) -> (Option, String) { match node.funcname.as_slice() { [name] => (None, get_string_from_node(name)), [schema, name] => ( diff --git a/crates/pgt_type_resolver/src/types.rs b/crates/pgt_type_resolver/src/types.rs index b5560114..85e1d8d2 100644 --- a/crates/pgt_type_resolver/src/types.rs +++ b/crates/pgt_type_resolver/src/types.rs @@ -5,9 +5,9 @@ pub(crate) enum PossibleType { AnyOf(Vec), } -pub fn resolve_type(node: &pgt_query_ext::NodeEnum, schema_cache: &SchemaCache) -> PossibleType { +pub fn resolve_type(node: &pgt_query::NodeEnum, schema_cache: &SchemaCache) -> PossibleType { match node { - pgt_query_ext::NodeEnum::AConst(n) => { + pgt_query::NodeEnum::AConst(n) => { if n.isnull { PossibleType::Null } else { @@ -16,7 +16,7 @@ pub fn resolve_type(node: &pgt_query_ext::NodeEnum, schema_cache: &SchemaCache) .as_ref() .expect("expected non-nullable AConst to have a value") { - pgt_query_ext::protobuf::a_const::Val::Ival(_) => { + pgt_query::protobuf::a_const::Val::Ival(_) => { let types: Vec = ["int2", "int4", "int8"] .iter() .map(|s| s.to_string()) @@ -33,7 +33,7 @@ pub fn resolve_type(node: &pgt_query_ext::NodeEnum, schema_cache: &SchemaCache) .collect(), ) } - pgt_query_ext::protobuf::a_const::Val::Fval(_) => { + pgt_query::protobuf::a_const::Val::Fval(_) => { let types: Vec = ["float4", "float8"].iter().map(|s| s.to_string()).collect(); @@ -46,7 +46,7 @@ pub fn resolve_type(node: &pgt_query_ext::NodeEnum, schema_cache: &SchemaCache) .collect(), ) } - pgt_query_ext::protobuf::a_const::Val::Boolval(_) => PossibleType::AnyOf( + pgt_query::protobuf::a_const::Val::Boolval(_) => PossibleType::AnyOf( schema_cache .types .iter() @@ -54,7 +54,7 @@ pub fn resolve_type(node: &pgt_query_ext::NodeEnum, schema_cache: &SchemaCache) .map(|t| t.id) .collect(), ), - pgt_query_ext::protobuf::a_const::Val::Sval(v) => { + pgt_query::protobuf::a_const::Val::Sval(v) => { let types: Vec = ["text", "varchar"].iter().map(|s| s.to_string()).collect(); @@ -70,7 +70,7 @@ pub fn resolve_type(node: &pgt_query_ext::NodeEnum, schema_cache: &SchemaCache) .collect(), ) } - pgt_query_ext::protobuf::a_const::Val::Bsval(_) => todo!(), + pgt_query::protobuf::a_const::Val::Bsval(_) => todo!(), } } } diff --git a/crates/pgt_type_resolver/src/util.rs b/crates/pgt_type_resolver/src/util.rs index f10cf5bb..d31d1fa8 100644 --- a/crates/pgt_type_resolver/src/util.rs +++ b/crates/pgt_type_resolver/src/util.rs @@ -1,6 +1,6 @@ -pub(crate) fn get_string_from_node(node: &pgt_query_ext::protobuf::Node) -> String { +pub(crate) fn get_string_from_node(node: &pgt_query::protobuf::Node) -> String { match &node.node { - Some(pgt_query_ext::NodeEnum::String(s)) => s.sval.to_string(), + Some(pgt_query::NodeEnum::String(s)) => s.sval.to_string(), _ => "".to_string(), } } diff --git a/crates/pgt_typecheck/Cargo.toml b/crates/pgt_typecheck/Cargo.toml index 175ecd59..f61f6a37 100644 --- a/crates/pgt_typecheck/Cargo.toml +++ b/crates/pgt_typecheck/Cargo.toml @@ -14,7 +14,7 @@ version = "0.0.0" [dependencies] pgt_console.workspace = true pgt_diagnostics.workspace = true -pgt_query_ext.workspace = true +pgt_query.workspace = true pgt_schema_cache.workspace = true pgt_text_size.workspace = true pgt_treesitter.workspace = true diff --git a/crates/pgt_typecheck/src/lib.rs b/crates/pgt_typecheck/src/lib.rs index e1dcd259..a3dde01d 100644 --- a/crates/pgt_typecheck/src/lib.rs +++ b/crates/pgt_typecheck/src/lib.rs @@ -14,7 +14,7 @@ pub use typed_identifier::{IdentifierType, TypedIdentifier}; pub struct TypecheckParams<'a> { pub conn: &'a PgPool, pub sql: &'a str, - pub ast: &'a pgt_query_ext::NodeEnum, + pub ast: &'a pgt_query::NodeEnum, pub tree: &'a tree_sitter::Tree, pub schema_cache: &'a pgt_schema_cache::SchemaCache, pub identifiers: Vec, @@ -39,11 +39,11 @@ pub async fn check_sql( // Check if the AST is not a supported statement type if !matches!( params.ast, - pgt_query_ext::NodeEnum::SelectStmt(_) - | pgt_query_ext::NodeEnum::InsertStmt(_) - | pgt_query_ext::NodeEnum::UpdateStmt(_) - | pgt_query_ext::NodeEnum::DeleteStmt(_) - | pgt_query_ext::NodeEnum::CommonTableExpr(_) + pgt_query::NodeEnum::SelectStmt(_) + | pgt_query::NodeEnum::InsertStmt(_) + | pgt_query::NodeEnum::UpdateStmt(_) + | pgt_query::NodeEnum::DeleteStmt(_) + | pgt_query::NodeEnum::CommonTableExpr(_) ) { return Ok(None); } diff --git a/crates/pgt_typecheck/tests/diagnostics.rs b/crates/pgt_typecheck/tests/diagnostics.rs index a7448503..f21d9ef9 100644 --- a/crates/pgt_typecheck/tests/diagnostics.rs +++ b/crates/pgt_typecheck/tests/diagnostics.rs @@ -23,7 +23,10 @@ async fn test(name: &str, query: &str, setup: Option<&str>, test_db: &PgPool) { .await .expect("Failed to load Schema Cache"); - let root = pgt_query_ext::parse(query).unwrap(); + let root = pgt_query::parse(query) + .unwrap() + .into_root() + .expect("Failed to parse query"); let tree = parser.parse(query, None).unwrap(); let conn = &test_db; diff --git a/crates/pgt_workspace/Cargo.toml b/crates/pgt_workspace/Cargo.toml index e78f4391..4acc0600 100644 --- a/crates/pgt_workspace/Cargo.toml +++ b/crates/pgt_workspace/Cargo.toml @@ -26,6 +26,7 @@ pgt_console = { workspace = true } pgt_diagnostics = { workspace = true } pgt_fs = { workspace = true, features = ["serde"] } pgt_lexer = { workspace = true } +pgt_query = { workspace = true } pgt_query_ext = { workspace = true } pgt_schema_cache = { workspace = true } pgt_statement_splitter = { workspace = true } diff --git a/crates/pgt_workspace/src/workspace/server/document.rs b/crates/pgt_workspace/src/workspace/server/document.rs index c9f880ec..c8cdc1f1 100644 --- a/crates/pgt_workspace/src/workspace/server/document.rs +++ b/crates/pgt_workspace/src/workspace/server/document.rs @@ -191,12 +191,7 @@ impl<'a> StatementMapper<'a> for DefaultMapper { pub struct ExecuteStatementMapper; impl<'a> StatementMapper<'a> for ExecuteStatementMapper { - type Output = ( - StatementId, - TextRange, - String, - Option, - ); + type Output = (StatementId, TextRange, String, Option); fn map(&self, parser: &'a Document, id: StatementId, range: TextRange) -> Self::Output { let ast_result = parser.ast_db.get_or_cache_ast(&id); @@ -214,7 +209,7 @@ impl<'a> StatementMapper<'a> for TypecheckDiagnosticsMapper { type Output = ( StatementId, TextRange, - Option, + Option, Arc, Option, ); diff --git a/crates/pgt_workspace/src/workspace/server/function_utils.rs b/crates/pgt_workspace/src/workspace/server/function_utils.rs index cf02ceb1..74e76ff2 100644 --- a/crates/pgt_workspace/src/workspace/server/function_utils.rs +++ b/crates/pgt_workspace/src/workspace/server/function_utils.rs @@ -1,6 +1,6 @@ /// Helper function to find a specific option value from function options pub fn find_option_value( - create_fn: &pgt_query_ext::protobuf::CreateFunctionStmt, + create_fn: &pgt_query::protobuf::CreateFunctionStmt, option_name: &str, ) -> Option { create_fn @@ -8,18 +8,18 @@ pub fn find_option_value( .iter() .filter_map(|opt_wrapper| opt_wrapper.node.as_ref()) .find_map(|opt| { - if let pgt_query_ext::NodeEnum::DefElem(def_elem) = opt { + if let pgt_query::NodeEnum::DefElem(def_elem) = opt { if def_elem.defname == option_name { def_elem .arg .iter() .filter_map(|arg_wrapper| arg_wrapper.node.as_ref()) .find_map(|arg| { - if let pgt_query_ext::NodeEnum::String(s) = arg { + if let pgt_query::NodeEnum::String(s) = arg { Some(s.sval.clone()) - } else if let pgt_query_ext::NodeEnum::List(l) = arg { + } else if let pgt_query::NodeEnum::List(l) = arg { l.items.iter().find_map(|item_wrapper| { - if let Some(pgt_query_ext::NodeEnum::String(s)) = + if let Some(pgt_query::NodeEnum::String(s)) = item_wrapper.node.as_ref() { Some(s.sval.clone()) @@ -40,11 +40,11 @@ pub fn find_option_value( }) } -pub fn parse_name(nodes: &[pgt_query_ext::protobuf::Node]) -> Option<(Option, String)> { +pub fn parse_name(nodes: &[pgt_query::protobuf::Node]) -> Option<(Option, String)> { let names = nodes .iter() .map(|n| match &n.node { - Some(pgt_query_ext::NodeEnum::String(s)) => Some(s.sval.clone()), + Some(pgt_query::NodeEnum::String(s)) => Some(s.sval.clone()), _ => None, }) .collect::>(); diff --git a/crates/pgt_workspace/src/workspace/server/pg_query.rs b/crates/pgt_workspace/src/workspace/server/pg_query.rs index ba471dfa..e90dd41b 100644 --- a/crates/pgt_workspace/src/workspace/server/pg_query.rs +++ b/crates/pgt_workspace/src/workspace/server/pg_query.rs @@ -11,7 +11,7 @@ use super::statement_identifier::StatementId; const DEFAULT_CACHE_SIZE: usize = 1000; pub struct PgQueryStore { - ast_db: Mutex>>>, + ast_db: Mutex>>>, plpgsql_db: Mutex>>, } @@ -30,14 +30,22 @@ impl PgQueryStore { pub fn get_or_cache_ast( &self, statement: &StatementId, - ) -> Arc> { + ) -> Arc> { let mut cache = self.ast_db.lock().unwrap(); if let Some(existing) = cache.get(statement) { return existing.clone(); } - let r = Arc::new(pgt_query_ext::parse(statement.content()).map_err(SyntaxDiagnostic::from)); + let r = Arc::new( + pgt_query::parse(statement.content()) + .map_err(SyntaxDiagnostic::from) + .and_then(|ast| { + ast.into_root().ok_or_else(|| { + SyntaxDiagnostic::new("No root node found in parse result", None) + }) + }), + ); cache.put(statement.clone(), r.clone()); r } @@ -49,7 +57,7 @@ impl PgQueryStore { let ast = self.get_or_cache_ast(statement); let create_fn = match ast.as_ref() { - Ok(pgt_query_ext::NodeEnum::CreateFunctionStmt(node)) => node, + Ok(pgt_query::NodeEnum::CreateFunctionStmt(node)) => node, _ => return None, }; @@ -72,7 +80,7 @@ impl PgQueryStore { let range = TextRange::new(start.try_into().unwrap(), end.try_into().unwrap()); - let r = pgt_query_ext::parse_plpgsql(statement.content()) + let r = pgt_query::parse_plpgsql(statement.content()) .map_err(|err| SyntaxDiagnostic::new(err.to_string(), Some(range))); cache.put(statement.clone(), r.clone()); diff --git a/crates/pgt_workspace/src/workspace/server/sql_function.rs b/crates/pgt_workspace/src/workspace/server/sql_function.rs index 6161dda7..0b230edc 100644 --- a/crates/pgt_workspace/src/workspace/server/sql_function.rs +++ b/crates/pgt_workspace/src/workspace/server/sql_function.rs @@ -30,9 +30,9 @@ pub struct SQLFunctionBody { } /// Extracts the function signature from a SQL function definition -pub fn get_sql_fn_signature(ast: &pgt_query_ext::NodeEnum) -> Option { +pub fn get_sql_fn_signature(ast: &pgt_query::NodeEnum) -> Option { let create_fn = match ast { - pgt_query_ext::NodeEnum::CreateFunctionStmt(cf) => cf, + pgt_query::NodeEnum::CreateFunctionStmt(cf) => cf, _ => return None, }; @@ -49,7 +49,7 @@ pub fn get_sql_fn_signature(ast: &pgt_query_ext::NodeEnum) -> Option Option Option { +pub fn get_sql_fn_body(ast: &pgt_query::NodeEnum, content: &str) -> Option { let create_fn = match ast { - pgt_query_ext::NodeEnum::CreateFunctionStmt(cf) => cf, + pgt_query::NodeEnum::CreateFunctionStmt(cf) => cf, _ => return None, }; @@ -120,7 +120,7 @@ mod tests { IMMUTABLE RETURNS NULL ON NULL INPUT;"; - let ast = pgt_query_ext::parse(input).unwrap(); + let ast = pgt_query::parse(input).unwrap().into_root().unwrap(); let sig = get_sql_fn_signature(&ast); @@ -146,7 +146,7 @@ mod tests { IMMUTABLE RETURNS NULL ON NULL INPUT;"; - let ast = pgt_query_ext::parse(input).unwrap(); + let ast = pgt_query::parse(input).unwrap().into_root().unwrap(); let sig = get_sql_fn_signature(&ast); diff --git a/docs/codegen/Cargo.toml b/docs/codegen/Cargo.toml index 96092a7a..bf650ac9 100644 --- a/docs/codegen/Cargo.toml +++ b/docs/codegen/Cargo.toml @@ -26,6 +26,7 @@ pgt_cli = { workspace = true } pgt_analyse = { workspace = true } pgt_analyser = { workspace = true } pgt_diagnostics = { workspace = true } +pgt_query = { workspace = true } pgt_query_ext = { workspace = true } pgt_workspace = { workspace = true } pgt_statement_splitter = { workspace = true } diff --git a/docs/codegen/src/rules_docs.rs b/docs/codegen/src/rules_docs.rs index 1d4b86a9..67626237 100644 --- a/docs/codegen/src/rules_docs.rs +++ b/docs/codegen/src/rules_docs.rs @@ -444,28 +444,30 @@ fn print_diagnostics( // split and parse each statement let stmts = pgt_statement_splitter::split(code); for stmt_range in stmts.ranges { - match pgt_query_ext::parse(&code[stmt_range]) { + match pgt_query::parse(&code[stmt_range]) { Ok(ast) => { - for rule_diag in analyser.run(pgt_analyser::AnalyserParams { - schema_cache: None, - stmts: vec![AnalysableStatement { - range: stmt_range, - root: ast, - }], - }) { - let diag = pgt_diagnostics::serde::Diagnostic::new(rule_diag); - - let category = diag.category().expect("linter diagnostic has no code"); - let severity = settings.get_severity_from_rule_code(category).expect( + if let Some(root) = ast.into_root() { + for rule_diag in analyser.run(pgt_analyser::AnalyserParams { + schema_cache: None, + stmts: vec![AnalysableStatement { + range: stmt_range, + root, + }], + }) { + let diag = pgt_diagnostics::serde::Diagnostic::new(rule_diag); + + let category = diag.category().expect("linter diagnostic has no code"); + let severity = settings.get_severity_from_rule_code(category).expect( "If you see this error, it means you need to run cargo codegen-configuration", ); - let error = diag - .with_severity(severity) - .with_file_path(&file_path) - .with_file_source_code(code); + let error = diag + .with_severity(severity) + .with_file_path(&file_path) + .with_file_source_code(code); - write_diagnostic(code, error)?; + write_diagnostic(code, error)?; + } } } Err(e) => { diff --git a/libpg_query b/libpg_query deleted file mode 160000 index 1c1a32ed..00000000 --- a/libpg_query +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1c1a32ed2f4c7799830d50bf4cb159222aafec48 diff --git a/xtask/rules_check/Cargo.toml b/xtask/rules_check/Cargo.toml index 3f0198d1..abd02a05 100644 --- a/xtask/rules_check/Cargo.toml +++ b/xtask/rules_check/Cargo.toml @@ -11,6 +11,7 @@ pgt_analyse = { workspace = true } pgt_analyser = { workspace = true } pgt_console = { workspace = true } pgt_diagnostics = { workspace = true } +pgt_query = { workspace = true } pgt_query_ext = { workspace = true } pgt_statement_splitter = { workspace = true } pgt_workspace = { workspace = true } diff --git a/xtask/rules_check/src/lib.rs b/xtask/rules_check/src/lib.rs index 0c57d06f..dfdd24ba 100644 --- a/xtask/rules_check/src/lib.rs +++ b/xtask/rules_check/src/lib.rs @@ -128,28 +128,30 @@ fn assert_lint( let result = pgt_statement_splitter::split(code); for stmt_range in result.ranges { - match pgt_query_ext::parse(&code[stmt_range]) { + match pgt_query::parse(&code[stmt_range]) { Ok(ast) => { - for rule_diag in analyser.run(pgt_analyser::AnalyserParams { - schema_cache: None, - stmts: vec![AnalysableStatement { - range: stmt_range, - root: ast, - }], - }) { - let diag = pgt_diagnostics::serde::Diagnostic::new(rule_diag); - - let category = diag.category().expect("linter diagnostic has no code"); - let severity = settings.get_severity_from_rule_code(category).expect( + if let Some(root) = ast.into_root() { + for rule_diag in analyser.run(pgt_analyser::AnalyserParams { + schema_cache: None, + stmts: vec![AnalysableStatement { + range: stmt_range, + root, + }], + }) { + let diag = pgt_diagnostics::serde::Diagnostic::new(rule_diag); + + let category = diag.category().expect("linter diagnostic has no code"); + let severity = settings.get_severity_from_rule_code(category).expect( "If you see this error, it means you need to run cargo codegen-configuration", ); - let error = diag - .with_severity(severity) - .with_file_path(&file_path) - .with_file_source_code(code); + let error = diag + .with_severity(severity) + .with_file_path(&file_path) + .with_file_source_code(code); - write_diagnostic(code, error)?; + write_diagnostic(code, error)?; + } } } Err(e) => { From b651c75726b58ae1dd3a52571ff76ac6ac907f70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Steinr=C3=B6tter?= Date: Wed, 23 Jul 2025 10:38:39 +0200 Subject: [PATCH 20/29] chore: add extensions to schema cache (#468) --- ...a23f1440c250590de976c5c46c5edf6746faf.json | 44 +++++++++++++++++++ crates/pgt_schema_cache/src/extensions.rs | 22 ++++++++++ crates/pgt_schema_cache/src/lib.rs | 2 + .../src/queries/extensions.sql | 10 +++++ crates/pgt_schema_cache/src/schema_cache.rs | 8 ---- 5 files changed, 78 insertions(+), 8 deletions(-) create mode 100644 .sqlx/query-3ebf3d74eb9d0448d675882c7f8a23f1440c250590de976c5c46c5edf6746faf.json create mode 100644 crates/pgt_schema_cache/src/extensions.rs create mode 100644 crates/pgt_schema_cache/src/queries/extensions.sql diff --git a/.sqlx/query-3ebf3d74eb9d0448d675882c7f8a23f1440c250590de976c5c46c5edf6746faf.json b/.sqlx/query-3ebf3d74eb9d0448d675882c7f8a23f1440c250590de976c5c46c5edf6746faf.json new file mode 100644 index 00000000..1b922062 --- /dev/null +++ b/.sqlx/query-3ebf3d74eb9d0448d675882c7f8a23f1440c250590de976c5c46c5edf6746faf.json @@ -0,0 +1,44 @@ +{ + "db_name": "PostgreSQL", + "query": "SELECT\n e.name as \"name!\",\n n.nspname AS schema,\n e.default_version as \"default_version!\",\n x.extversion AS installed_version,\n e.comment\nFROM\n pg_available_extensions() e(name, default_version, comment)\n LEFT JOIN pg_extension x ON e.name = x.extname\n LEFT JOIN pg_namespace n ON x.extnamespace = n.oid\n", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "name!", + "type_info": "Name" + }, + { + "ordinal": 1, + "name": "schema", + "type_info": "Name" + }, + { + "ordinal": 2, + "name": "default_version!", + "type_info": "Text" + }, + { + "ordinal": 3, + "name": "installed_version", + "type_info": "Text" + }, + { + "ordinal": 4, + "name": "comment", + "type_info": "Text" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + null, + true, + null, + true, + null + ] + }, + "hash": "3ebf3d74eb9d0448d675882c7f8a23f1440c250590de976c5c46c5edf6746faf" +} diff --git a/crates/pgt_schema_cache/src/extensions.rs b/crates/pgt_schema_cache/src/extensions.rs new file mode 100644 index 00000000..8494397c --- /dev/null +++ b/crates/pgt_schema_cache/src/extensions.rs @@ -0,0 +1,22 @@ +use sqlx::PgPool; + +use crate::schema_cache::SchemaCacheItem; + +#[derive(Debug, Default)] +pub struct Extension { + pub name: String, + pub schema: Option, + pub default_version: String, + pub installed_version: Option, + pub comment: Option, +} + +impl SchemaCacheItem for Extension { + type Item = Extension; + + async fn load(pool: &PgPool) -> Result, sqlx::Error> { + sqlx::query_file_as!(Extension, "src/queries/extensions.sql") + .fetch_all(pool) + .await + } +} diff --git a/crates/pgt_schema_cache/src/lib.rs b/crates/pgt_schema_cache/src/lib.rs index b67f9412..6440cd01 100644 --- a/crates/pgt_schema_cache/src/lib.rs +++ b/crates/pgt_schema_cache/src/lib.rs @@ -3,6 +3,7 @@ #![allow(dead_code)] mod columns; +mod extensions; mod functions; mod policies; mod roles; @@ -14,6 +15,7 @@ mod types; mod versions; pub use columns::*; +pub use extensions::Extension; pub use functions::{Behavior, Function, FunctionArg, FunctionArgs, ProcKind}; pub use policies::{Policy, PolicyCommand}; pub use roles::*; diff --git a/crates/pgt_schema_cache/src/queries/extensions.sql b/crates/pgt_schema_cache/src/queries/extensions.sql new file mode 100644 index 00000000..aedc71b2 --- /dev/null +++ b/crates/pgt_schema_cache/src/queries/extensions.sql @@ -0,0 +1,10 @@ +SELECT + e.name as "name!", + n.nspname AS schema, + e.default_version as "default_version!", + x.extversion AS installed_version, + e.comment +FROM + pg_available_extensions() e(name, default_version, comment) + LEFT JOIN pg_extension x ON e.name = x.extname + LEFT JOIN pg_namespace n ON x.extnamespace = n.oid diff --git a/crates/pgt_schema_cache/src/schema_cache.rs b/crates/pgt_schema_cache/src/schema_cache.rs index 8fb9683b..df7239ea 100644 --- a/crates/pgt_schema_cache/src/schema_cache.rs +++ b/crates/pgt_schema_cache/src/schema_cache.rs @@ -49,14 +49,6 @@ impl SchemaCache { }) } - /// Applies an AST node to the repository - /// - /// For example, alter table add column will add the column to the table if it does not exist - /// yet - pub fn mutate(&mut self) { - unimplemented!(); - } - pub fn find_table(&self, name: &str, schema: Option<&str>) -> Option<&Table> { self.tables .iter() From bb686b96e6ea78eebd576d6a992935925820f990 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Steinr=C3=B6tter?= Date: Fri, 1 Aug 2025 08:29:16 +0200 Subject: [PATCH 21/29] feat: plpgsql check (#469) adds support for plpgsql_check. the approach is even simpler as described in the issue: if we encounter a create function statement, we start a transaction, run the statement, then run plpgsql_check on it, and then rollback the transaction. the remaining code is just about translating the location information we get from the extension into a good range. todo: - [x] integrate into workspace api - [x] to get span, move from the first word / occurrence in the line to the next semicolon - [x] handle return trigger by checking for all variations - [x] make sure we do not report create fn syntax errors if plpgsql_check is enabled - [x] check why "not a known variable" is not getting the right span closes #190 --- .github/workflows/pull_request.yml | 59 +- ...ae6f3075c4f754cd379b0555c205fff95a95c.json | 50 ++ ...117910b19f540f19393b76aa6434e9d1d8502.json | 8 +- ...75ba8951faa1be2ea6b2bf6714b1aa9127a6f.json | 44 - Cargo.lock | 19 + Cargo.toml | 1 + Dockerfile | 16 + .../src/categories.rs | 1 + crates/pgt_plpgsql_check/Cargo.toml | 30 + crates/pgt_plpgsql_check/src/diagnostics.rs | 245 ++++++ crates/pgt_plpgsql_check/src/lib.rs | 794 ++++++++++++++++++ crates/pgt_query_ext/src/lib.rs | 1 + .../src/utils.rs} | 43 + .../pgt_schema_cache/src/queries/triggers.sql | 33 +- crates/pgt_schema_cache/src/schema_cache.rs | 20 +- crates/pgt_schema_cache/src/triggers.rs | 39 +- crates/pgt_typecheck/src/lib.rs | 14 - crates/pgt_workspace/Cargo.toml | 1 + crates/pgt_workspace/src/workspace/server.rs | 65 +- .../src/workspace/server.tests.rs | 73 +- .../src/workspace/server/pg_query.rs | 5 +- .../src/workspace/server/sql_function.rs | 12 +- docker-compose.yml | 2 +- test.sql | 8 + 24 files changed, 1446 insertions(+), 137 deletions(-) create mode 100644 .sqlx/query-277e47bf46f8331549f55c8a0ebae6f3075c4f754cd379b0555c205fff95a95c.json delete mode 100644 .sqlx/query-df57cc22f7d63847abce1d0d15675ba8951faa1be2ea6b2bf6714b1aa9127a6f.json create mode 100644 Dockerfile create mode 100644 crates/pgt_plpgsql_check/Cargo.toml create mode 100644 crates/pgt_plpgsql_check/src/diagnostics.rs create mode 100644 crates/pgt_plpgsql_check/src/lib.rs rename crates/{pgt_workspace/src/workspace/server/function_utils.rs => pgt_query_ext/src/utils.rs} (63%) diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 8aa24265..20218378 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -79,15 +79,6 @@ jobs: lint: name: Lint Project runs-on: ubuntu-latest - services: - postgres: - image: postgres:latest - env: - POSTGRES_USER: postgres - POSTGRES_PASSWORD: postgres - POSTGRES_DB: postgres - ports: - - 5432:5432 steps: - name: Checkout PR Branch uses: actions/checkout@v4 @@ -103,6 +94,24 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + # we need to use the same database as we do locally for sqlx prepare to output the same hashes + - name: Build and start PostgreSQL with plpgsql_check + run: | + docker build -t postgres-plpgsql-check:latest . + docker run -d --name postgres \ + -e POSTGRES_USER=postgres \ + -e POSTGRES_PASSWORD=postgres \ + -e POSTGRES_DB=postgres \ + -p 5432:5432 \ + postgres-plpgsql-check:latest + # Wait for postgres to be ready + for _ in {1..30}; do + if docker exec postgres pg_isready -U postgres; then + break + fi + sleep 1 + done + - name: Setup sqlx-cli run: cargo install sqlx-cli @@ -154,13 +163,37 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # running containers via `services` only works on linux - # https://github.com/actions/runner/issues/1866 - - name: Setup postgres + # For Linux, use custom Docker image with plpgsql_check + - name: Build and start PostgreSQL with plpgsql_check + if: runner.os == 'Linux' + run: | + docker build -t postgres-plpgsql-check:latest . + docker run -d --name postgres \ + -e POSTGRES_USER=postgres \ + -e POSTGRES_PASSWORD=postgres \ + -e POSTGRES_DB=postgres \ + -p 5432:5432 \ + postgres-plpgsql-check:latest + # Wait for postgres to be ready + for _ in {1..30}; do + if docker exec postgres pg_isready -U postgres; then + break + fi + sleep 1 + done + # For Windows, use the action since PostgreSQL Docker image doesn't support Windows containers + - name: Setup postgres (Windows) + if: runner.os == 'Windows' id: postgres uses: ikalnytskyi/action-setup-postgres@v7 - name: Print Roles - run: psql ${{ steps.postgres.outputs.connection-uri }} -c "select rolname from pg_roles;" + run: | + if [[ "$RUNNER_OS" == "Linux" ]]; then + docker exec postgres psql -U postgres -c "select rolname from pg_roles;" + else + psql ${{ steps.postgres.outputs.connection-uri }} -c "select rolname from pg_roles;" + fi + shell: bash - name: Run tests run: cargo test --workspace diff --git a/.sqlx/query-277e47bf46f8331549f55c8a0ebae6f3075c4f754cd379b0555c205fff95a95c.json b/.sqlx/query-277e47bf46f8331549f55c8a0ebae6f3075c4f754cd379b0555c205fff95a95c.json new file mode 100644 index 00000000..db3f4a73 --- /dev/null +++ b/.sqlx/query-277e47bf46f8331549f55c8a0ebae6f3075c4f754cd379b0555c205fff95a95c.json @@ -0,0 +1,50 @@ +{ + "db_name": "PostgreSQL", + "query": "-- we need to join tables from the pg_catalog since \"TRUNCATE\" triggers are\n-- not available in the information_schema.trigger table.\nselect\n t.tgname as \"name!\",\n c.relname as \"table_name!\",\n p.proname as \"proc_name!\",\n proc_ns.nspname as \"proc_schema!\",\n table_ns.nspname as \"table_schema!\",\n t.tgtype as \"details_bitmask!\"\nfrom\n pg_catalog.pg_trigger t\nleft join pg_catalog.pg_proc p on t.tgfoid = p.oid\nleft join pg_catalog.pg_class c on t.tgrelid = c.oid\nleft join pg_catalog.pg_namespace table_ns on c.relnamespace = table_ns.oid\nleft join pg_catalog.pg_namespace proc_ns on p.pronamespace = proc_ns.oid\nwhere\n t.tgisinternal = false and\n t.tgconstraint = 0;\n", + "describe": { + "columns": [ + { + "ordinal": 0, + "name": "name!", + "type_info": "Name" + }, + { + "ordinal": 1, + "name": "table_name!", + "type_info": "Name" + }, + { + "ordinal": 2, + "name": "proc_name!", + "type_info": "Name" + }, + { + "ordinal": 3, + "name": "proc_schema!", + "type_info": "Name" + }, + { + "ordinal": 4, + "name": "table_schema!", + "type_info": "Name" + }, + { + "ordinal": 5, + "name": "details_bitmask!", + "type_info": "Int2" + } + ], + "parameters": { + "Left": [] + }, + "nullable": [ + false, + true, + true, + true, + true, + false + ] + }, + "hash": "277e47bf46f8331549f55c8a0ebae6f3075c4f754cd379b0555c205fff95a95c" +} diff --git a/.sqlx/query-4ea19fee016f1daeafdc466647d117910b19f540f19393b76aa6434e9d1d8502.json b/.sqlx/query-4ea19fee016f1daeafdc466647d117910b19f540f19393b76aa6434e9d1d8502.json index 4980f4f3..400f031d 100644 --- a/.sqlx/query-4ea19fee016f1daeafdc466647d117910b19f540f19393b76aa6434e9d1d8502.json +++ b/.sqlx/query-4ea19fee016f1daeafdc466647d117910b19f540f19393b76aa6434e9d1d8502.json @@ -90,9 +90,9 @@ "nullable": [ null, true, + false, true, - true, - true, + false, null, null, null, @@ -101,9 +101,9 @@ null, null, null, - true, + false, null, - true + false ] }, "hash": "4ea19fee016f1daeafdc466647d117910b19f540f19393b76aa6434e9d1d8502" diff --git a/.sqlx/query-df57cc22f7d63847abce1d0d15675ba8951faa1be2ea6b2bf6714b1aa9127a6f.json b/.sqlx/query-df57cc22f7d63847abce1d0d15675ba8951faa1be2ea6b2bf6714b1aa9127a6f.json deleted file mode 100644 index b6fd2fc8..00000000 --- a/.sqlx/query-df57cc22f7d63847abce1d0d15675ba8951faa1be2ea6b2bf6714b1aa9127a6f.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "db_name": "PostgreSQL", - "query": "-- we need to join tables from the pg_catalog since \"TRUNCATE\" triggers are \n-- not available in the information_schema.trigger table.\nselect \n t.tgname as \"name!\",\n c.relname as \"table_name!\",\n p.proname as \"proc_name!\",\n n.nspname as \"schema_name!\",\n t.tgtype as \"details_bitmask!\"\nfrom \n pg_catalog.pg_trigger t \n left join pg_catalog.pg_proc p on t.tgfoid = p.oid\n left join pg_catalog.pg_class c on t.tgrelid = c.oid\n left join pg_catalog.pg_namespace n on c.relnamespace = n.oid\nwhere \n -- triggers enforcing constraints (e.g. unique fields) should not be included.\n t.tgisinternal = false and \n t.tgconstraint = 0;\n", - "describe": { - "columns": [ - { - "ordinal": 0, - "name": "name!", - "type_info": "Name" - }, - { - "ordinal": 1, - "name": "table_name!", - "type_info": "Name" - }, - { - "ordinal": 2, - "name": "proc_name!", - "type_info": "Name" - }, - { - "ordinal": 3, - "name": "schema_name!", - "type_info": "Name" - }, - { - "ordinal": 4, - "name": "details_bitmask!", - "type_info": "Int2" - } - ], - "parameters": { - "Left": [] - }, - "nullable": [ - false, - true, - true, - true, - false - ] - }, - "hash": "df57cc22f7d63847abce1d0d15675ba8951faa1be2ea6b2bf6714b1aa9127a6f" -} diff --git a/Cargo.lock b/Cargo.lock index d76baca3..49143908 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2976,6 +2976,24 @@ dependencies = [ "quote", ] +[[package]] +name = "pgt_plpgsql_check" +version = "0.0.0" +dependencies = [ + "pgt_console", + "pgt_diagnostics", + "pgt_query", + "pgt_query_ext", + "pgt_schema_cache", + "pgt_test_utils", + "pgt_text_size", + "regex", + "serde", + "serde_json", + "sqlx", + "tree-sitter", +] + [[package]] name = "pgt_query" version = "0.0.0" @@ -3163,6 +3181,7 @@ dependencies = [ "pgt_diagnostics", "pgt_fs", "pgt_lexer", + "pgt_plpgsql_check", "pgt_query", "pgt_query_ext", "pgt_schema_cache", diff --git a/Cargo.toml b/Cargo.toml index e243ab3e..d68aafe0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -76,6 +76,7 @@ pgt_lexer = { path = "./crates/pgt_lexer", version = "0.0.0" } pgt_lexer_codegen = { path = "./crates/pgt_lexer_codegen", version = "0.0.0" } pgt_lsp = { path = "./crates/pgt_lsp", version = "0.0.0" } pgt_markup = { path = "./crates/pgt_markup", version = "0.0.0" } +pgt_plpgsql_check = { path = "./crates/pgt_plpgsql_check", version = "0.0.0" } pgt_query = { path = "./crates/pgt_query", version = "0.0.0" } pgt_query_ext = { path = "./crates/pgt_query_ext", version = "0.0.0" } pgt_query_macros = { path = "./crates/pgt_query_macros", version = "0.0.0" } diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..10353bb2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,16 @@ +FROM postgres:15 + +# Install build dependencies +RUN apt-get update && \ + apt-get install -y postgresql-server-dev-15 gcc make git && \ + cd /tmp && \ + git clone https://github.com/okbob/plpgsql_check.git && \ + cd plpgsql_check && \ + make && \ + make install && \ + apt-get remove -y postgresql-server-dev-15 gcc make git && \ + apt-get autoremove -y && \ + rm -rf /tmp/plpgsql_check /var/lib/apt/lists/* + +# Add initialization script directly +RUN echo "CREATE EXTENSION IF NOT EXISTS plpgsql_check;" > /docker-entrypoint-initdb.d/01-create-extension.sql \ No newline at end of file diff --git a/crates/pgt_diagnostics_categories/src/categories.rs b/crates/pgt_diagnostics_categories/src/categories.rs index b9d29698..14df90b9 100644 --- a/crates/pgt_diagnostics_categories/src/categories.rs +++ b/crates/pgt_diagnostics_categories/src/categories.rs @@ -32,6 +32,7 @@ define_categories! { "flags/invalid", "project", "typecheck", + "plpgsql_check", "internalError/panic", "syntax", "dummy", diff --git a/crates/pgt_plpgsql_check/Cargo.toml b/crates/pgt_plpgsql_check/Cargo.toml new file mode 100644 index 00000000..75d1a52b --- /dev/null +++ b/crates/pgt_plpgsql_check/Cargo.toml @@ -0,0 +1,30 @@ +[package] +authors.workspace = true +categories.workspace = true +description = "" +edition.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +name = "pgt_plpgsql_check" +repository.workspace = true +version = "0.0.0" + + +[dependencies] +pgt_console = { workspace = true } +pgt_diagnostics = { workspace = true } +pgt_query = { workspace = true } +pgt_query_ext = { workspace = true } +pgt_schema_cache = { workspace = true } +pgt_text_size = { workspace = true } +regex = { workspace = true } +serde = { workspace = true } +serde_json = { workspace = true } +sqlx = { workspace = true } +tree-sitter = { workspace = true } + +[dev-dependencies] +pgt_test_utils = { workspace = true } + +[lib] diff --git a/crates/pgt_plpgsql_check/src/diagnostics.rs b/crates/pgt_plpgsql_check/src/diagnostics.rs new file mode 100644 index 00000000..a0daec13 --- /dev/null +++ b/crates/pgt_plpgsql_check/src/diagnostics.rs @@ -0,0 +1,245 @@ +use std::io; + +use pgt_console::markup; +use pgt_diagnostics::{Advices, Diagnostic, LogCategory, MessageAndDescription, Severity, Visit}; +use pgt_text_size::TextRange; + +use crate::{PlpgSqlCheckIssue, PlpgSqlCheckResult}; + +/// Find the first occurrence of target text that is not within string literals +fn find_text_outside_strings(text: &str, target: &str) -> Option { + let text_lower = text.to_lowercase(); + let target_lower = target.to_lowercase(); + let mut in_string = false; + let mut quote_char = '\0'; + let bytes = text_lower.as_bytes(); + let mut i = 0; + + while i < bytes.len() { + let ch = bytes[i] as char; + + if !in_string { + // Check if we're starting a string literal + if ch == '\'' || ch == '"' { + in_string = true; + quote_char = ch; + } else { + // Check if we found our target at this position + if text_lower[i..].starts_with(&target_lower) { + // Check if this is a complete word (not part of another identifier) + let is_word_start = + i == 0 || !bytes[i - 1].is_ascii_alphanumeric() && bytes[i - 1] != b'_'; + let target_end = i + target_lower.len(); + let is_word_end = target_end >= bytes.len() + || (!bytes[target_end].is_ascii_alphanumeric() + && bytes[target_end] != b'_'); + + if is_word_start && is_word_end { + return Some(i); + } + } + } + } else { + // We're inside a string literal + if ch == quote_char { + // Check if it's escaped (look for double quotes/apostrophes) + if i + 1 < bytes.len() && bytes[i + 1] as char == quote_char { + // Skip the escaped quote + i += 1; + } else { + // End of string literal + in_string = false; + quote_char = '\0'; + } + } + } + + i += 1; + } + + None +} + +/// A specialized diagnostic for plpgsql_check. +#[derive(Clone, Debug, Diagnostic)] +#[diagnostic(category = "plpgsql_check")] +pub struct PlPgSqlCheckDiagnostic { + #[location(span)] + pub span: Option, + #[description] + #[message] + pub message: MessageAndDescription, + #[advice] + pub advices: PlPgSqlCheckAdvices, + #[severity] + pub severity: Severity, +} + +#[derive(Debug, Clone)] +pub struct PlPgSqlCheckAdvices { + pub code: Option, + /// the relation (table or view) where the issue was found, if applicable + /// only applicable for trigger functions + pub relation: Option, +} + +impl Advices for PlPgSqlCheckAdvices { + fn record(&self, visitor: &mut dyn Visit) -> io::Result<()> { + // Show the error code if available + if let Some(code) = &self.code { + visitor.record_log( + LogCategory::Error, + &markup! { "SQL State: " {code} }, + )?; + } + + // Show relation information if available + if let Some(relation) = &self.relation { + visitor.record_log( + LogCategory::Info, + &markup! { "Relation: " {relation} }, + )?; + } + + Ok(()) + } +} + +/// Convert plpgsql_check results into diagnostics with optional relation info for triggers +pub fn create_diagnostics_from_check_result( + result: &PlpgSqlCheckResult, + fn_body: &str, + offset: usize, + relation: Option, +) -> Vec { + result + .issues + .iter() + .map(|issue| { + let severity = match issue.level.as_str() { + "error" => Severity::Error, + "warning" => Severity::Warning, + "notice" => Severity::Hint, + _ => Severity::Information, + }; + + PlPgSqlCheckDiagnostic { + message: issue.message.clone().into(), + severity, + span: resolve_span(issue, fn_body, offset), + advices: PlPgSqlCheckAdvices { + code: issue.sql_state.clone(), + relation: relation.clone(), + }, + } + }) + .collect() +} + +fn resolve_span(issue: &PlpgSqlCheckIssue, fn_body: &str, offset: usize) -> Option { + let stmt = match issue.statement.as_ref() { + Some(s) => s, + None => { + return Some(TextRange::new( + (offset as u32).into(), + ((offset + fn_body.len()) as u32).into(), + )); + } + }; + + let line_number = stmt + .line_number + .parse::() + .expect("Expected line number to be a valid usize"); + + let text = &stmt.text; + + // calculate the offset to the target line + let line_offset: usize = fn_body + .lines() + .take(line_number - 1) + .map(|line| line.len() + 1) // +1 for newline + .sum(); + + // find the position within the target line + let line = fn_body.lines().nth(line_number - 1)?; + let start = line + .to_lowercase() + .find(&text.to_lowercase()) + .unwrap_or_else(|| { + line.char_indices() + .find_map(|(i, c)| if !c.is_whitespace() { Some(i) } else { None }) + .unwrap_or(0) + }); + + let stmt_offset = line_offset + start; + + if let Some(q) = &issue.query { + // first find the query within the fn body *after* stmt_offset, ignoring string literals + let query_start = find_text_outside_strings(&fn_body[stmt_offset..], &q.text) + .map(|pos| pos + stmt_offset); + + // the position is *within* the query text + let pos = q + .position + .parse::() + .expect("Expected query position to be a valid usize") + - 1; // -1 because the position is 1-based + + let start = query_start? + pos; + + // the range of the diagnostics is the token that `pos` is on + // Find the end of the current token by looking for whitespace or SQL delimiters + let remaining = &fn_body[start..]; + let end = remaining + .char_indices() + .find(|(_, c)| { + c.is_whitespace() || matches!(c, ',' | ';' | ')' | '(' | '=' | '<' | '>') + }) + .map(|(i, _c)| { + i // just the token end, don't include delimiters + }) + .unwrap_or(remaining.len()); + + return Some(TextRange::new( + ((offset + start) as u32).into(), + ((offset + start + end) as u32).into(), + )); + } + + // if no query is present, the end range covers + // - if text is "IF" or "ELSIF", then until the next "THEN" + // - TODO: check "LOOP", "CASE", "WHILE", "EXPECTION" and others + // - else: until the next semicolon or end of line + + if text.to_uppercase() == "IF" || text.to_uppercase() == "ELSIF" { + // Find the position of the next "THEN" after the statement + let remaining = &fn_body[stmt_offset..]; + if let Some(then_pos) = remaining.to_uppercase().find("THEN") { + let end = then_pos + "THEN".len(); + return Some(TextRange::new( + ((offset + stmt_offset) as u32).into(), + ((offset + stmt_offset + end) as u32).into(), + )); + } + } + + // if no specific end is found, use the next semicolon or the end of the line + let remaining = &fn_body[stmt_offset..]; + let end = remaining + .char_indices() + .find(|(_, c)| matches!(c, ';' | '\n' | '\r')) + .map(|(i, c)| { + if c == ';' { + i + 1 // include the semicolon + } else { + i // just the end of the line + } + }) + .unwrap_or(remaining.len()); + + Some(TextRange::new( + ((offset + stmt_offset) as u32).into(), + ((offset + stmt_offset + end) as u32).into(), + )) +} diff --git a/crates/pgt_plpgsql_check/src/lib.rs b/crates/pgt_plpgsql_check/src/lib.rs new file mode 100644 index 00000000..05e2f570 --- /dev/null +++ b/crates/pgt_plpgsql_check/src/lib.rs @@ -0,0 +1,794 @@ +mod diagnostics; + +pub use diagnostics::PlPgSqlCheckDiagnostic; +use diagnostics::create_diagnostics_from_check_result; +use pgt_query::protobuf::CreateFunctionStmt; +use regex::Regex; +use serde::Deserialize; +pub use sqlx::postgres::PgSeverity; +use sqlx::{Acquire, PgPool, Postgres, Transaction}; + +#[derive(Debug)] +pub struct PlPgSqlCheckParams<'a> { + pub conn: &'a PgPool, + pub sql: &'a str, + pub ast: &'a pgt_query::NodeEnum, + pub schema_cache: &'a pgt_schema_cache::SchemaCache, +} + +#[derive(Debug, Deserialize)] +pub struct PlpgSqlCheckResult { + pub function: String, + pub issues: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct PlpgSqlCheckIssue { + pub level: String, + pub message: String, + pub statement: Option, + pub query: Option, + #[serde(rename = "sqlState")] + pub sql_state: Option, +} + +#[derive(Debug, Deserialize)] +pub struct Statement { + #[serde(rename = "lineNumber")] + pub line_number: String, + pub text: String, +} + +#[derive(Debug, Deserialize)] +pub struct Query { + pub position: String, + pub text: String, +} + +/// check if the given node is a plpgsql function that should be checked +fn should_check_function<'a>( + ast: &'a pgt_query::NodeEnum, + schema_cache: &pgt_schema_cache::SchemaCache, +) -> Option<&'a CreateFunctionStmt> { + let create_fn = match ast { + pgt_query::NodeEnum::CreateFunctionStmt(stmt) => stmt, + _ => return None, + }; + + if pgt_query_ext::utils::find_option_value(create_fn, "language") != Some("plpgsql".to_string()) + { + return None; + } + + if !schema_cache + .extensions + .iter() + .any(|e| e.name == "plpgsql_check") + { + return None; + } + + Some(create_fn) +} + +/// check if a function is a trigger function +fn is_trigger_function(create_fn: &CreateFunctionStmt) -> bool { + create_fn + .return_type + .as_ref() + .map(|n| { + matches!( + pgt_query_ext::utils::parse_name(&n.names), + Some((None, name)) if name == "trigger" + ) + }) + .unwrap_or(false) +} + +/// build the function identifier string used by plpgsql_check +fn build_function_identifier( + create_fn: &CreateFunctionStmt, + fn_schema: &Option, + fn_name: &str, +) -> String { + let args = create_fn + .parameters + .iter() + .filter_map(|arg| { + let node = match &arg.node { + Some(pgt_query::NodeEnum::FunctionParameter(n)) => n, + _ => return None, + }; + let type_name_node = node.arg_type.as_ref()?; + let type_name = match pgt_query_ext::utils::parse_name(&type_name_node.names) { + Some((schema, name)) => match schema { + Some(s) => format!("{}.{}", s, name), + None => name, + }, + None => return None, + }; + + if !type_name_node.array_bounds.is_empty() { + Some(format!("{}[]", type_name)) + } else { + Some(type_name) + } + }) + .collect::>(); + + let fn_qualified_name = match fn_schema { + Some(schema) => format!("{}.{}", schema, fn_name), + None => fn_name.to_string(), + }; + + if args.is_empty() { + fn_qualified_name + } else { + format!("{}({})", fn_qualified_name, args.join(", ")) + } +} + +pub async fn check_plpgsql( + params: PlPgSqlCheckParams<'_>, +) -> Result, sqlx::Error> { + let create_fn = match should_check_function(params.ast, params.schema_cache) { + Some(stmt) => stmt, + None => return Ok(vec![]), + }; + + let (fn_schema, fn_name) = match pgt_query_ext::utils::parse_name(&create_fn.funcname) { + Some(n) => n, + None => return Ok(vec![]), + }; + + let fn_identifier = build_function_identifier(create_fn, &fn_schema, &fn_name); + + let fn_body = pgt_query_ext::utils::find_option_value(create_fn, "as") + .ok_or_else(|| sqlx::Error::Protocol("Failed to find function body".to_string()))?; + let offset = params + .sql + .find(&fn_body) + .ok_or_else(|| sqlx::Error::Protocol("Failed to find function body in SQL".to_string()))?; + let is_trigger = is_trigger_function(create_fn); + + let mut conn = params.conn.acquire().await?; + conn.close_on_drop(); + + let mut tx: Transaction<'_, Postgres> = conn.begin().await?; + + // disable function body checking to rely on plpgsql_check + sqlx::query("SET LOCAL check_function_bodies = off") + .execute(&mut *tx) + .await?; + + // make sure we run with "or replace" + let sql_with_replace = if !create_fn.replace { + let re = Regex::new(r"(?i)\bCREATE\s+FUNCTION\b").unwrap(); + re.replace(params.sql, "CREATE OR REPLACE FUNCTION") + .to_string() + } else { + params.sql.to_string() + }; + + // create the function - this should always succeed + sqlx::query(&sql_with_replace).execute(&mut *tx).await?; + + // run plpgsql_check and collect results with their relations + let results_with_relations: Vec<(String, Option)> = if is_trigger { + let mut results = Vec::new(); + + for trigger in params.schema_cache.triggers.iter() { + if trigger.proc_name == fn_name + && (fn_schema.is_none() || fn_schema.as_deref() == Some(&trigger.proc_schema)) + { + let relation = format!("{}.{}", trigger.table_schema, trigger.table_name); + + let result: Option = sqlx::query_scalar(&format!( + "select plpgsql_check_function('{}', '{}', format := 'json')", + fn_identifier, relation + )) + .fetch_optional(&mut *tx) + .await? + .flatten(); + + if let Some(result) = result { + results.push((result, Some(relation))); + } + } + } + + results + } else { + let result: Option = sqlx::query_scalar(&format!( + "select plpgsql_check_function('{}', format := 'json')", + fn_identifier + )) + .fetch_optional(&mut *tx) + .await? + .flatten(); + + if let Some(result) = result { + vec![(result, None)] + } else { + vec![] + } + }; + + tx.rollback().await?; + + // Parse results and create diagnostics + let mut diagnostics = Vec::new(); + for (result_json, relation) in results_with_relations { + let check_result: PlpgSqlCheckResult = serde_json::from_str(&result_json).map_err(|e| { + sqlx::Error::Protocol(format!("Failed to parse plpgsql_check result: {}", e)) + })?; + + let mut result_diagnostics = + create_diagnostics_from_check_result(&check_result, &fn_body, offset, relation); + diagnostics.append(&mut result_diagnostics); + } + + Ok(diagnostics) +} + +#[cfg(all(test, not(target_os = "windows")))] +mod tests { + use sqlx::{Executor, PgPool}; + + /// Test helper to run plpgsql_check and return diagnostics with span text + async fn run_plpgsql_check_test( + test_db: &PgPool, + setup_sql: &str, + create_fn_sql: &str, + ) -> Result<(Vec, Vec>), Box> + { + test_db.execute(setup_sql).await?; + + let ast = pgt_query::parse(create_fn_sql)? + .into_root() + .ok_or("Failed to parse SQL root")?; + let schema_cache = pgt_schema_cache::SchemaCache::load(test_db).await?; + + let diagnostics = super::check_plpgsql(super::PlPgSqlCheckParams { + conn: test_db, + sql: create_fn_sql, + ast: &ast, + schema_cache: &schema_cache, + }) + .await?; + + let span_texts = diagnostics + .iter() + .map(|diag| { + diag.span.as_ref().map(|s| { + let start = usize::from(s.start()); + let end = usize::from(s.end()); + create_fn_sql[start..end].to_string() + }) + }) + .collect(); + + Ok((diagnostics, span_texts)) + } + + #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] + async fn test_plpgsql_check_if_expr(test_db: PgPool) { + let setup = r#" + create extension if not exists plpgsql_check; + + CREATE TABLE t1(a int, b int); + "#; + + let create_fn_sql = r#" + CREATE OR REPLACE FUNCTION public.f1() + RETURNS void + LANGUAGE plpgsql + AS $function$ + declare r t1 := (select t1 from t1 where a = 1); + BEGIN + if r.c is null or + true is false + then -- there is bug - table t1 missing "c" column + RAISE NOTICE 'c is null'; + end if; + END; + $function$; + "#; + + let (diagnostics, span_texts) = run_plpgsql_check_test(&test_db, setup, create_fn_sql) + .await + .expect("Failed to run plpgsql_check test"); + + assert_eq!(diagnostics.len(), 1); + assert!(matches!( + diagnostics[0].severity, + pgt_diagnostics::Severity::Error + )); + assert_eq!( + span_texts[0].as_deref(), + Some("if r.c is null or\n true is false\n then") + ); + } + + #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] + async fn test_plpgsql_check_missing_var(test_db: PgPool) { + let setup = r#" + create extension if not exists plpgsql_check; + + CREATE TABLE t1(a int, b int); + "#; + + let create_fn_sql = r#" + CREATE OR REPLACE FUNCTION public.f1() + RETURNS void + LANGUAGE plpgsql + AS $function$ + BEGIN + SELECT 1 from t1 where a = v_c; + END; + $function$; + "#; + + let (diagnostics, span_texts) = run_plpgsql_check_test(&test_db, setup, create_fn_sql) + .await + .expect("Failed to run plpgsql_check test"); + assert_eq!(diagnostics.len(), 1); + assert!(matches!( + diagnostics[0].severity, + pgt_diagnostics::Severity::Error + )); + assert_eq!(span_texts[0].as_deref(), Some("v_c")); + } + + #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] + async fn test_plpgsql_check_missing_col_if_stmt(test_db: PgPool) { + let setup = r#" + create extension if not exists plpgsql_check; + + CREATE TABLE t1(a int, b int); + "#; + + let create_fn_sql = r#" + CREATE OR REPLACE FUNCTION public.f1() + RETURNS void + LANGUAGE plpgsql + AS $function$ + BEGIN + if (select c from t1 where id = 1) is null then -- there is bug - table t1 missing "c" column + RAISE NOTICE 'c is null'; + end if; + END; + $function$; + "#; + + let (diagnostics, span_texts) = run_plpgsql_check_test(&test_db, setup, create_fn_sql) + .await + .expect("Failed to run plpgsql_check test"); + assert_eq!(diagnostics.len(), 1); + assert!(matches!( + diagnostics[0].severity, + pgt_diagnostics::Severity::Error + )); + assert_eq!(span_texts[0].as_deref(), Some("c")); + } + + #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] + async fn test_plpgsql_check(test_db: PgPool) { + let setup = r#" + create extension if not exists plpgsql_check; + + CREATE TABLE t1(a int, b int); + "#; + + let create_fn_sql = r#" + CREATE OR REPLACE FUNCTION public.f1() + RETURNS void + LANGUAGE plpgsql + AS $function$ + DECLARE r record; + BEGIN + FOR r IN SELECT * FROM t1 + LOOP + RAISE NOTICE '%', r.c; -- there is bug - table t1 missing "c" column + END LOOP; + END; + $function$; + "#; + + let (diagnostics, span_texts) = run_plpgsql_check_test(&test_db, setup, create_fn_sql) + .await + .expect("Failed to run plpgsql_check test"); + + assert_eq!(diagnostics.len(), 1); + assert!(matches!( + diagnostics[0].severity, + pgt_diagnostics::Severity::Error + )); + assert_eq!(span_texts[0].as_deref(), Some("RAISE NOTICE '%', r.c;")); + } + + #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] + async fn test_plpgsql_check_stacked_diagnostics(test_db: PgPool) { + let setup = r#" + create extension if not exists plpgsql_check; + "#; + + let create_fn_sql = r#" + create or replace function fxtest() + returns void as $$ + declare + v_sqlstate text; + v_message text; + v_context text; + begin + get stacked diagnostics + v_sqlstate = returned_sqlstate, + v_message = message_text, + v_context = pg_exception_context; + end; + $$ language plpgsql; + "#; + + let (diagnostics, span_texts) = run_plpgsql_check_test(&test_db, setup, create_fn_sql) + .await + .expect("Failed to run plpgsql_check test"); + + assert!(!diagnostics.is_empty()); + assert!(matches!( + diagnostics[0].severity, + pgt_diagnostics::Severity::Error + )); + assert_eq!(span_texts[0].as_deref(), Some("get stacked diagnostics")); + } + + #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] + async fn test_plpgsql_check_constant_refcursor(test_db: PgPool) { + let setup = r#" + create extension if not exists plpgsql_check; + create table rc_test(a int); + "#; + + let create_fn_sql = r#" + create function return_constant_refcursor() returns refcursor as $$ + declare + rc constant refcursor; + begin + open rc for select a from rc_test; + return rc; + end + $$ language plpgsql; + "#; + + let (diagnostics, span_texts) = run_plpgsql_check_test(&test_db, setup, create_fn_sql) + .await + .expect("Failed to run plpgsql_check test"); + + assert!(!diagnostics.is_empty()); + assert!(matches!( + diagnostics[0].severity, + pgt_diagnostics::Severity::Error + )); + assert_eq!( + span_texts[0].as_deref(), + Some("open rc for select a from rc_test;") + ); + } + + #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] + async fn test_plpgsql_check_constant_assignment(test_db: PgPool) { + let setup = r#" + create extension if not exists plpgsql_check; + + create procedure p1(a int, out b int) + as $$ + begin + b := a + 10; + end; + $$ language plpgsql; + "#; + + let create_fn_sql = r#" + create function f1() + returns void as $$ + declare b constant int; + begin + call p1(10, b); + end; + $$ language plpgsql; + "#; + + let (diagnostics, span_texts) = run_plpgsql_check_test(&test_db, setup, create_fn_sql) + .await + .expect("Failed to run plpgsql_check test"); + + assert!(!diagnostics.is_empty()); + assert!(matches!( + diagnostics[0].severity, + pgt_diagnostics::Severity::Error + )); + assert_eq!(span_texts[0].as_deref(), Some("call p1(10, b);")); + } + + #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] + async fn test_plpgsql_check_missing_procedure(test_db: PgPool) { + let setup = r#" + create extension if not exists plpgsql_check; + "#; + + let create_fn_sql = r#" + create function f1() + returns void as $$ + declare b constant int; + begin + call p1(10, b); + end; + $$ language plpgsql; + "#; + + let (diagnostics, span_texts) = run_plpgsql_check_test(&test_db, setup, create_fn_sql) + .await + .expect("Failed to run plpgsql_check test"); + + assert!(!diagnostics.is_empty()); + assert!(matches!( + diagnostics[0].severity, + pgt_diagnostics::Severity::Error + )); + assert_eq!(span_texts[0].as_deref(), Some("p1")); + } + + #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] + async fn test_plpgsql_check_dml_in_stable_function(test_db: PgPool) { + let setup = r#" + create extension if not exists plpgsql_check; + create table t1(a int, b int); + "#; + + let create_fn_sql = r#" + create function f1() + returns void as $$ + begin + if false then + insert into t1 values(10,20); + update t1 set a = 10; + delete from t1; + end if; + end; + $$ language plpgsql stable; + "#; + + let (diagnostics, span_texts) = run_plpgsql_check_test(&test_db, setup, create_fn_sql) + .await + .expect("Failed to run plpgsql_check test"); + + assert_eq!(diagnostics.len(), 1); + assert!(span_texts[0].is_some()); + + assert_eq!(diagnostics[0].advices.code.as_deref(), Some("0A000")); + } + + #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] + async fn test_plpgsql_check_record_field_assignment(test_db: PgPool) { + let setup = r#" + create extension if not exists plpgsql_check; + + create function g1() returns table(a int, b int) as $$ + begin + return query select 1, 2; + end; + $$ language plpgsql; + "#; + + let create_fn_sql = r#" + create or replace function f1() + returns void as $$ + declare r record; + begin + for r in select * from g1() + loop + r.c := 20; + end loop; + end; + $$ language plpgsql; + "#; + + let (diagnostics, span_texts) = run_plpgsql_check_test(&test_db, setup, create_fn_sql) + .await + .expect("Failed to run plpgsql_check test"); + + assert!(!diagnostics.is_empty()); + assert!(matches!( + diagnostics[0].severity, + pgt_diagnostics::Severity::Error + )); + assert!(span_texts[0].is_some()); + } + + #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] + async fn test_plpgsql_check_trigger_basic(test_db: PgPool) { + let setup = r#" + create extension if not exists plpgsql_check; + + CREATE TABLE users( + id serial primary key, + name text not null, + email text + ); + + CREATE OR REPLACE FUNCTION public.log_user_changes() + RETURNS trigger + LANGUAGE plpgsql + AS $function$ + BEGIN + -- Intentional error: referencing non-existent column + INSERT INTO audit_log(table_name, changed_id, old_email, new_email) + VALUES ('users', NEW.id, OLD.email, NEW.email); + RETURN NEW; + END; + $function$; + + CREATE TRIGGER trg_users_audit + AFTER UPDATE ON users + FOR EACH ROW + EXECUTE FUNCTION public.log_user_changes(); + "#; + + let create_fn_sql = r#" + CREATE OR REPLACE FUNCTION public.log_user_changes() + RETURNS trigger + LANGUAGE plpgsql + AS $function$ + BEGIN + -- Intentional error: referencing non-existent column + INSERT INTO audit_log(table_name, changed_id, old_email, new_email) + VALUES ('users', NEW.id, OLD.email, NEW.email); + RETURN NEW; + END; + $function$; + "#; + + let (diagnostics, span_texts) = run_plpgsql_check_test(&test_db, setup, create_fn_sql) + .await + .expect("Failed to run plpgsql_check test"); + + assert!(!diagnostics.is_empty()); + assert!(matches!( + diagnostics[0].severity, + pgt_diagnostics::Severity::Error + )); + assert!(diagnostics[0].advices.relation.is_some()); + assert_eq!( + diagnostics[0].advices.relation.as_deref(), + Some("public.users") + ); + assert_eq!(span_texts[0].as_deref(), Some("audit_log")); + } + + #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] + async fn test_plpgsql_check_trigger_missing_column(test_db: PgPool) { + let setup = r#" + create extension if not exists plpgsql_check; + + CREATE TABLE products( + id serial primary key, + name text not null, + price numeric(10,2) + ); + + CREATE OR REPLACE FUNCTION public.validate_product() + RETURNS trigger + LANGUAGE plpgsql + AS $function$ + BEGIN + -- Error: referencing non-existent column + IF NEW.category IS NULL THEN + RAISE EXCEPTION 'Category is required'; + END IF; + RETURN NEW; + END; + $function$; + + CREATE TRIGGER trg_product_validation + BEFORE INSERT OR UPDATE ON products + FOR EACH ROW + EXECUTE FUNCTION public.validate_product(); + "#; + + let create_fn_sql = r#" + CREATE OR REPLACE FUNCTION public.validate_product() + RETURNS trigger + LANGUAGE plpgsql + AS $function$ + BEGIN + -- Error: referencing non-existent column + IF NEW.category IS NULL THEN + RAISE EXCEPTION 'Category is required'; + END IF; + RETURN NEW; + END; + $function$; + "#; + + let (diagnostics, span_texts) = run_plpgsql_check_test(&test_db, setup, create_fn_sql) + .await + .expect("Failed to run plpgsql_check test"); + + assert!(!diagnostics.is_empty()); + assert!(matches!( + diagnostics[0].severity, + pgt_diagnostics::Severity::Error + )); + assert!(span_texts[0].as_deref().unwrap().contains("category")); + assert_eq!( + diagnostics[0].advices.relation.as_deref(), + Some("public.products") + ); + } + + #[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] + async fn test_plpgsql_check_trigger_multiple_tables(test_db: PgPool) { + let setup = r#" + create extension if not exists plpgsql_check; + + CREATE TABLE table_a( + id serial primary key, + name text + ); + + CREATE TABLE table_b( + id serial primary key, + description text + ); + + CREATE OR REPLACE FUNCTION public.generic_audit() + RETURNS trigger + LANGUAGE plpgsql + AS $function$ + BEGIN + -- Error: referencing column that doesn't exist in both tables + INSERT INTO audit_log(table_name, record_id, old_status) + VALUES (TG_TABLE_NAME, NEW.id, OLD.status); + RETURN NEW; + END; + $function$; + + CREATE TRIGGER trg_audit_a + AFTER UPDATE ON table_a + FOR EACH ROW + EXECUTE FUNCTION public.generic_audit(); + + CREATE TRIGGER trg_audit_b + AFTER UPDATE ON table_b + FOR EACH ROW + EXECUTE FUNCTION public.generic_audit(); + "#; + + let create_fn_sql = r#" + CREATE OR REPLACE FUNCTION public.generic_audit() + RETURNS trigger + LANGUAGE plpgsql + AS $function$ + BEGIN + -- Error: referencing column that doesn't exist in both tables + INSERT INTO audit_log(table_name, record_id, old_status) + VALUES (TG_TABLE_NAME, NEW.id, OLD.status); + RETURN NEW; + END; + $function$; + "#; + + let (diagnostics, _span_texts) = run_plpgsql_check_test(&test_db, setup, create_fn_sql) + .await + .expect("Failed to run plpgsql_check test"); + + assert!(!diagnostics.is_empty()); + assert!(diagnostics.len() >= 2); + + let relations: Vec<_> = diagnostics + .iter() + .filter_map(|d| d.advices.relation.as_ref()) + .collect(); + assert!(relations.contains(&&"public.table_a".to_string())); + assert!(relations.contains(&&"public.table_b".to_string())); + } +} diff --git a/crates/pgt_query_ext/src/lib.rs b/crates/pgt_query_ext/src/lib.rs index 4c630487..b0288da8 100644 --- a/crates/pgt_query_ext/src/lib.rs +++ b/crates/pgt_query_ext/src/lib.rs @@ -1 +1,2 @@ pub mod diagnostics; +pub mod utils; diff --git a/crates/pgt_workspace/src/workspace/server/function_utils.rs b/crates/pgt_query_ext/src/utils.rs similarity index 63% rename from crates/pgt_workspace/src/workspace/server/function_utils.rs rename to crates/pgt_query_ext/src/utils.rs index 74e76ff2..6dedebea 100644 --- a/crates/pgt_workspace/src/workspace/server/function_utils.rs +++ b/crates/pgt_query_ext/src/utils.rs @@ -55,3 +55,46 @@ pub fn parse_name(nodes: &[pgt_query::protobuf::Node]) -> Option<(Option _ => None, } } + +#[cfg(test)] +mod tests { + use crate::utils::{find_option_value, parse_name}; + + #[test] + fn test_find_option_value() { + let input = " + CREATE OR REPLACE FUNCTION public.f1() + RETURNS boolean + LANGUAGE plpgsql + AS $function$ + declare r t1 := (select t1 from t1 where a = 1); + BEGIN + if r.c is null or + true is false + then -- there is bug - table t1 missing \"c\" column + RAISE NOTICE 'c is null'; + end if; + END; + $function$; +" + .trim(); + + let ast = pgt_query::parse(input).unwrap().into_root().unwrap(); + let create_fn = match &ast { + pgt_query::NodeEnum::CreateFunctionStmt(stmt) => stmt, + _ => panic!("Expected CreateFunctionStmt"), + }; + + assert_eq!( + find_option_value(create_fn, "language"), + Some("plpgsql".to_string()) + ); + + assert!(find_option_value(create_fn, "as").is_some(),); + + assert_eq!( + parse_name(&create_fn.return_type.as_ref().unwrap().names), + Some((Some("pg_catalog".to_string()), "bool".to_string())) + ); + } +} diff --git a/crates/pgt_schema_cache/src/queries/triggers.sql b/crates/pgt_schema_cache/src/queries/triggers.sql index c28cc39f..895d1be0 100644 --- a/crates/pgt_schema_cache/src/queries/triggers.sql +++ b/crates/pgt_schema_cache/src/queries/triggers.sql @@ -1,17 +1,18 @@ --- we need to join tables from the pg_catalog since "TRUNCATE" triggers are +-- we need to join tables from the pg_catalog since "TRUNCATE" triggers are -- not available in the information_schema.trigger table. -select - t.tgname as "name!", - c.relname as "table_name!", - p.proname as "proc_name!", - n.nspname as "schema_name!", - t.tgtype as "details_bitmask!" -from - pg_catalog.pg_trigger t - left join pg_catalog.pg_proc p on t.tgfoid = p.oid - left join pg_catalog.pg_class c on t.tgrelid = c.oid - left join pg_catalog.pg_namespace n on c.relnamespace = n.oid -where - -- triggers enforcing constraints (e.g. unique fields) should not be included. - t.tgisinternal = false and - t.tgconstraint = 0; +select + t.tgname as "name!", + c.relname as "table_name!", + p.proname as "proc_name!", + proc_ns.nspname as "proc_schema!", + table_ns.nspname as "table_schema!", + t.tgtype as "details_bitmask!" +from + pg_catalog.pg_trigger t +left join pg_catalog.pg_proc p on t.tgfoid = p.oid +left join pg_catalog.pg_class c on t.tgrelid = c.oid +left join pg_catalog.pg_namespace table_ns on c.relnamespace = table_ns.oid +left join pg_catalog.pg_namespace proc_ns on p.pronamespace = proc_ns.oid +where + t.tgisinternal = false and + t.tgconstraint = 0; diff --git a/crates/pgt_schema_cache/src/schema_cache.rs b/crates/pgt_schema_cache/src/schema_cache.rs index df7239ea..84bcd77c 100644 --- a/crates/pgt_schema_cache/src/schema_cache.rs +++ b/crates/pgt_schema_cache/src/schema_cache.rs @@ -7,7 +7,7 @@ use crate::schemas::Schema; use crate::tables::Table; use crate::types::PostgresType; use crate::versions::Version; -use crate::{Role, Trigger}; +use crate::{Extension, Role, Trigger}; #[derive(Debug, Default)] pub struct SchemaCache { @@ -18,13 +18,25 @@ pub struct SchemaCache { pub versions: Vec, pub columns: Vec, pub policies: Vec, + pub extensions: Vec, pub triggers: Vec, pub roles: Vec, } impl SchemaCache { pub async fn load(pool: &PgPool) -> Result { - let (schemas, tables, functions, types, versions, columns, policies, triggers, roles) = futures_util::try_join!( + let ( + schemas, + tables, + functions, + types, + versions, + columns, + policies, + triggers, + roles, + extensions, + ) = futures_util::try_join!( Schema::load(pool), Table::load(pool), Function::load(pool), @@ -33,7 +45,8 @@ impl SchemaCache { Column::load(pool), Policy::load(pool), Trigger::load(pool), - Role::load(pool) + Role::load(pool), + Extension::load(pool), )?; Ok(SchemaCache { @@ -46,6 +59,7 @@ impl SchemaCache { policies, triggers, roles, + extensions, }) } diff --git a/crates/pgt_schema_cache/src/triggers.rs b/crates/pgt_schema_cache/src/triggers.rs index 2b2a3aff..d0a4788a 100644 --- a/crates/pgt_schema_cache/src/triggers.rs +++ b/crates/pgt_schema_cache/src/triggers.rs @@ -82,20 +82,22 @@ impl TryFrom for TriggerTiming { pub struct TriggerQueried { name: String, table_name: String, - schema_name: String, + table_schema: String, proc_name: String, + proc_schema: String, details_bitmask: i16, } #[derive(Debug, PartialEq, Eq)] pub struct Trigger { - name: String, - table_name: String, - schema_name: String, - proc_name: String, - affected: TriggerAffected, - timing: TriggerTiming, - events: Vec, + pub name: String, + pub table_name: String, + pub table_schema: String, + pub proc_name: String, + pub proc_schema: String, + pub affected: TriggerAffected, + pub timing: TriggerTiming, + pub events: Vec, } impl From for Trigger { @@ -104,7 +106,8 @@ impl From for Trigger { name: value.name, table_name: value.table_name, proc_name: value.proc_name, - schema_name: value.schema_name, + proc_schema: value.proc_schema, + table_schema: value.table_schema, affected: value.details_bitmask.into(), timing: value.details_bitmask.try_into().unwrap(), events: TriggerEvents::from(value.details_bitmask).0, @@ -141,7 +144,7 @@ mod tests { id serial primary key, name text ); - + create or replace function public.log_user_insert() returns trigger as $$ begin @@ -149,17 +152,17 @@ mod tests { return new; end; $$ language plpgsql; - + create trigger trg_users_insert before insert on public.users for each row execute function public.log_user_insert(); - + create trigger trg_users_update after update or insert on public.users for each statement execute function public.log_user_insert(); - + create trigger trg_users_delete before delete on public.users for each row @@ -186,7 +189,7 @@ mod tests { .iter() .find(|t| t.name == "trg_users_insert") .unwrap(); - assert_eq!(insert_trigger.schema_name, "public"); + assert_eq!(insert_trigger.table_schema, "public"); assert_eq!(insert_trigger.table_name, "users"); assert_eq!(insert_trigger.timing, TriggerTiming::Before); assert_eq!(insert_trigger.affected, TriggerAffected::Row); @@ -197,7 +200,7 @@ mod tests { .iter() .find(|t| t.name == "trg_users_update") .unwrap(); - assert_eq!(insert_trigger.schema_name, "public"); + assert_eq!(insert_trigger.table_schema, "public"); assert_eq!(insert_trigger.table_name, "users"); assert_eq!(update_trigger.timing, TriggerTiming::After); assert_eq!(update_trigger.affected, TriggerAffected::Statement); @@ -209,7 +212,7 @@ mod tests { .iter() .find(|t| t.name == "trg_users_delete") .unwrap(); - assert_eq!(insert_trigger.schema_name, "public"); + assert_eq!(insert_trigger.table_schema, "public"); assert_eq!(insert_trigger.table_name, "users"); assert_eq!(delete_trigger.timing, TriggerTiming::Before); assert_eq!(delete_trigger.affected, TriggerAffected::Row); @@ -275,7 +278,7 @@ mod tests { .iter() .find(|t| t.name == "trg_docs_instead_update") .unwrap(); - assert_eq!(instead_trigger.schema_name, "public"); + assert_eq!(instead_trigger.table_schema, "public"); assert_eq!(instead_trigger.table_name, "docs_view"); assert_eq!(instead_trigger.timing, TriggerTiming::Instead); assert_eq!(instead_trigger.affected, TriggerAffected::Row); @@ -286,7 +289,7 @@ mod tests { .iter() .find(|t| t.name == "trg_docs_truncate") .unwrap(); - assert_eq!(truncate_trigger.schema_name, "public"); + assert_eq!(truncate_trigger.table_schema, "public"); assert_eq!(truncate_trigger.table_name, "docs"); assert_eq!(truncate_trigger.timing, TriggerTiming::After); assert_eq!(truncate_trigger.affected, TriggerAffected::Statement); diff --git a/crates/pgt_typecheck/src/lib.rs b/crates/pgt_typecheck/src/lib.rs index a3dde01d..ceb36b94 100644 --- a/crates/pgt_typecheck/src/lib.rs +++ b/crates/pgt_typecheck/src/lib.rs @@ -3,7 +3,6 @@ mod typed_identifier; pub use diagnostics::TypecheckDiagnostic; use diagnostics::create_type_error; -use pgt_text_size::TextRange; use sqlx::postgres::PgDatabaseError; pub use sqlx::postgres::PgSeverity; use sqlx::{Executor, PgPool}; @@ -20,19 +19,6 @@ pub struct TypecheckParams<'a> { pub identifiers: Vec, } -#[derive(Debug, Clone)] -pub struct TypeError { - pub message: String, - pub code: String, - pub severity: PgSeverity, - pub position: Option, - pub range: Option, - pub table: Option, - pub column: Option, - pub data_type: Option, - pub constraint: Option, -} - pub async fn check_sql( params: TypecheckParams<'_>, ) -> Result, sqlx::Error> { diff --git a/crates/pgt_workspace/Cargo.toml b/crates/pgt_workspace/Cargo.toml index 4acc0600..efded47c 100644 --- a/crates/pgt_workspace/Cargo.toml +++ b/crates/pgt_workspace/Cargo.toml @@ -26,6 +26,7 @@ pgt_console = { workspace = true } pgt_diagnostics = { workspace = true } pgt_fs = { workspace = true, features = ["serde"] } pgt_lexer = { workspace = true } +pgt_plpgsql_check = { workspace = true } pgt_query = { workspace = true } pgt_query_ext = { workspace = true } pgt_schema_cache = { workspace = true } diff --git a/crates/pgt_workspace/src/workspace/server.rs b/crates/pgt_workspace/src/workspace/server.rs index c6ed0827..f4a9561f 100644 --- a/crates/pgt_workspace/src/workspace/server.rs +++ b/crates/pgt_workspace/src/workspace/server.rs @@ -55,7 +55,6 @@ mod async_helper; mod connection_key; mod connection_manager; pub(crate) mod document; -mod function_utils; mod migration; mod pg_query; mod schema_cache_manager; @@ -454,7 +453,8 @@ impl Workspace for WorkspaceServer { let path_clone = params.path.clone(); let schema_cache = self.schema_cache.load(pool.clone())?; let input = doc.iter(TypecheckDiagnosticsMapper).collect::>(); - // sorry for the ugly code :( + + // Combined async context for both typecheck and plpgsql_check let async_results = run_async(async move { stream::iter(input) .map(|(id, range, ast, cst, sign)| { @@ -462,8 +462,11 @@ impl Workspace for WorkspaceServer { let path = path_clone.clone(); let schema_cache = Arc::clone(&schema_cache); async move { + let mut diagnostics = Vec::new(); + if let Some(ast) = ast { - pgt_typecheck::check_sql(TypecheckParams { + // Type checking + let typecheck_result = pgt_typecheck::check_sql(TypecheckParams { conn: &pool, sql: id.content(), ast: &ast, @@ -486,18 +489,40 @@ impl Workspace for WorkspaceServer { }) .unwrap_or_default(), }) + .await; + + if let Ok(Some(diag)) = typecheck_result { + let r = diag.location().span.map(|span| span + range.start()); + diagnostics.push( + diag.with_file_path(path.as_path().display().to_string()) + .with_file_span(r.unwrap_or(range)), + ); + } + + // plpgsql_check + let plpgsql_check_results = pgt_plpgsql_check::check_plpgsql( + pgt_plpgsql_check::PlPgSqlCheckParams { + conn: &pool, + sql: id.content(), + ast: &ast, + schema_cache: schema_cache.as_ref(), + }, + ) .await - .map(|d| { - d.map(|d| { - let r = d.location().span.map(|span| span + range.start()); + .unwrap_or_else(|_| vec![]); + println!("{:#?}", plpgsql_check_results); + + for d in plpgsql_check_results { + let r = d.span.map(|span| span + range.start()); + diagnostics.push( d.with_file_path(path.as_path().display().to_string()) - .with_file_span(r.unwrap_or(range)) - }) - }) - } else { - Ok(None) + .with_file_span(r.unwrap_or(range)), + ); + } } + + Ok::, sqlx::Error>(diagnostics) } }) .buffer_unordered(10) @@ -506,8 +531,8 @@ impl Workspace for WorkspaceServer { })?; for result in async_results.into_iter() { - let result = result?; - if let Some(diag) = result { + let diagnostics_batch = result?; + for diag in diagnostics_batch { diagnostics.push(SDiagnostic::new(diag)); } } @@ -548,6 +573,20 @@ impl Workspace for WorkspaceServer { analysable_stmts.push(node); } if let Some(diag) = diagnostic { + // ignore the syntax error if we already have more specialized diagnostics for the + // same statement. + // this is important for create function statements, where we might already have detailed + // diagnostics from plpgsql_check. + if diagnostics.iter().any(|d| { + d.location().span.is_some_and(|async_loc| { + diag.location() + .span + .is_some_and(|syntax_loc| syntax_loc.contains_range(async_loc)) + }) + }) { + continue; + } + diagnostics.push(SDiagnostic::new( diag.with_file_path(path.clone()) .with_severity(Severity::Error), diff --git a/crates/pgt_workspace/src/workspace/server.tests.rs b/crates/pgt_workspace/src/workspace/server.tests.rs index 0578f90f..ef5ba267 100644 --- a/crates/pgt_workspace/src/workspace/server.tests.rs +++ b/crates/pgt_workspace/src/workspace/server.tests.rs @@ -8,7 +8,7 @@ use pgt_configuration::{ use pgt_diagnostics::Diagnostic; use pgt_fs::PgTPath; use pgt_text_size::TextRange; -use sqlx::PgPool; +use sqlx::{Executor, PgPool}; use crate::{ Workspace, WorkspaceError, @@ -206,3 +206,74 @@ async fn correctly_ignores_files() { assert!(execute_statement_result.is_ok_and(|res| res == ExecuteStatementResult::default())); } + +#[cfg(all(test, not(target_os = "windows")))] +#[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] +async fn test_dedupe_diagnostics(test_db: PgPool) { + let mut conf = PartialConfiguration::init(); + conf.merge_with(PartialConfiguration { + db: Some(PartialDatabaseConfiguration { + database: Some( + test_db + .connect_options() + .get_database() + .unwrap() + .to_string(), + ), + ..Default::default() + }), + ..Default::default() + }); + + let workspace = get_test_workspace(Some(conf)).expect("Unable to create test workspace"); + + let path = PgTPath::new("test.sql"); + + let setup_sql = "CREATE EXTENSION IF NOT EXISTS plpgsql_check;"; + test_db.execute(setup_sql).await.expect("setup sql failed"); + + let content = r#" + CREATE OR REPLACE FUNCTION public.f1() + RETURNS void + LANGUAGE plpgsql + AS $function$ + decare r text; + BEGIN + select '1' into into r; + END; + $function$; + "#; + + workspace + .open_file(OpenFileParams { + path: path.clone(), + content: content.into(), + version: 1, + }) + .expect("Unable to open test file"); + + let diagnostics = workspace + .pull_diagnostics(crate::workspace::PullDiagnosticsParams { + path: path.clone(), + categories: RuleCategories::all(), + max_diagnostics: 100, + only: vec![], + skip: vec![], + }) + .expect("Unable to pull diagnostics") + .diagnostics; + + assert_eq!(diagnostics.len(), 1, "Expected one diagnostic"); + + let diagnostic = &diagnostics[0]; + + assert_eq!( + diagnostic.category().map(|c| c.name()), + Some("plpgsql_check") + ); + + assert_eq!( + diagnostic.location().span, + Some(TextRange::new(115.into(), 210.into())) + ); +} diff --git a/crates/pgt_workspace/src/workspace/server/pg_query.rs b/crates/pgt_workspace/src/workspace/server/pg_query.rs index e90dd41b..05f1425d 100644 --- a/crates/pgt_workspace/src/workspace/server/pg_query.rs +++ b/crates/pgt_workspace/src/workspace/server/pg_query.rs @@ -5,7 +5,6 @@ use lru::LruCache; use pgt_query_ext::diagnostics::*; use pgt_text_size::TextRange; -use super::function_utils::find_option_value; use super::statement_identifier::StatementId; const DEFAULT_CACHE_SIZE: usize = 1000; @@ -61,7 +60,7 @@ impl PgQueryStore { _ => return None, }; - let language = find_option_value(create_fn, "language")?; + let language = pgt_query_ext::utils::find_option_value(create_fn, "language")?; if language != "plpgsql" { return None; @@ -73,7 +72,7 @@ impl PgQueryStore { return Some(existing.clone()); } - let sql_body = find_option_value(create_fn, "as")?; + let sql_body = pgt_query_ext::utils::find_option_value(create_fn, "as")?; let start = statement.content().find(&sql_body)?; let end = start + sql_body.len(); diff --git a/crates/pgt_workspace/src/workspace/server/sql_function.rs b/crates/pgt_workspace/src/workspace/server/sql_function.rs index 0b230edc..4a1463b7 100644 --- a/crates/pgt_workspace/src/workspace/server/sql_function.rs +++ b/crates/pgt_workspace/src/workspace/server/sql_function.rs @@ -1,7 +1,5 @@ use pgt_text_size::TextRange; -use super::function_utils::{find_option_value, parse_name}; - #[derive(Debug, Clone)] pub struct ArgType { pub schema: Option, @@ -37,14 +35,14 @@ pub fn get_sql_fn_signature(ast: &pgt_query::NodeEnum) -> Option Option Option Option Date: Thu, 14 Aug 2025 12:37:34 +0200 Subject: [PATCH 22/29] fix: add support for named param type `$name` (#475) --- crates/pgt_tokenizer/src/lib.rs | 47 ++++++++++++------- ...enizer__tests__named_param_dollar_raw.snap | 23 +++++++++ crates/pgt_tokenizer/src/token.rs | 3 ++ crates/pgt_workspace/src/workspace/server.rs | 2 - 4 files changed, 55 insertions(+), 20 deletions(-) create mode 100644 crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_dollar_raw.snap diff --git a/crates/pgt_tokenizer/src/lib.rs b/crates/pgt_tokenizer/src/lib.rs index 80b66363..83b9ba44 100644 --- a/crates/pgt_tokenizer/src/lib.rs +++ b/crates/pgt_tokenizer/src/lib.rs @@ -186,9 +186,29 @@ impl Cursor<'_> { '$' => { // Dollar quoted strings if is_ident_start(self.first()) || self.first() == '$' { - self.dollar_quoted_string() + // Get the start sequence of the dollar quote, i.e., 'foo' in $foo$hello$foo$ + // if ident does not continue and there is no terminating dollar + // sign, we have a positional param `$name` + let mut start = vec![]; + loop { + match self.first() { + '$' => { + self.bump(); + break self.dollar_quoted_string(start); + } + c if is_ident_cont(c) => { + self.bump(); + start.push(c); + } + _ => { + break TokenKind::NamedParam { + kind: NamedParamKind::DollarRaw, + }; + } + } + } } else { - // Parameters + // positional parameter, e.g. `$1` while self.first().is_ascii_digit() { self.bump(); } @@ -490,22 +510,7 @@ impl Cursor<'_> { } // https://www.postgresql.org/docs/16/sql-syntax-lexical.html#SQL-SYNTAX-DOLLAR-QUOTING - fn dollar_quoted_string(&mut self) -> TokenKind { - // Get the start sequence of the dollar quote, i.e., 'foo' in - // $foo$hello$foo$ - let mut start = vec![]; - while let Some(c) = self.bump() { - match c { - '$' => { - self.bump(); - break; - } - _ => { - start.push(c); - } - } - } - + fn dollar_quoted_string(&mut self, start: Vec) -> TokenKind { // we have a dollar quoted string deliminated with `$$` if start.is_empty() { loop { @@ -658,6 +663,12 @@ mod tests { assert_debug_snapshot!(result); } + #[test] + fn named_param_dollar_raw() { + let result = lex("select 1 from c where id = $id;"); + assert_debug_snapshot!(result); + } + #[test] fn named_param_colon_raw() { let result = lex("select 1 from c where id = :id;"); diff --git a/crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_dollar_raw.snap b/crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_dollar_raw.snap new file mode 100644 index 00000000..db0f9412 --- /dev/null +++ b/crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_dollar_raw.snap @@ -0,0 +1,23 @@ +--- +source: crates/pgt_tokenizer/src/lib.rs +expression: result +snapshot_kind: text +--- +[ + "select" @ Ident, + " " @ Space, + "1" @ Literal { kind: Int { base: Decimal, empty_int: false } }, + " " @ Space, + "from" @ Ident, + " " @ Space, + "c" @ Ident, + " " @ Space, + "where" @ Ident, + " " @ Space, + "id" @ Ident, + " " @ Space, + "=" @ Eq, + " " @ Space, + "$id" @ NamedParam { kind: DollarRaw }, + ";" @ Semi, +] diff --git a/crates/pgt_tokenizer/src/token.rs b/crates/pgt_tokenizer/src/token.rs index e3dbaee2..da98a229 100644 --- a/crates/pgt_tokenizer/src/token.rs +++ b/crates/pgt_tokenizer/src/token.rs @@ -132,6 +132,9 @@ pub enum NamedParamKind { /// /// Used in: psql ColonIdentifier { terminated: bool }, + + /// e.g. `$name` + DollarRaw, } /// Parsed token. diff --git a/crates/pgt_workspace/src/workspace/server.rs b/crates/pgt_workspace/src/workspace/server.rs index f4a9561f..f0a39dbf 100644 --- a/crates/pgt_workspace/src/workspace/server.rs +++ b/crates/pgt_workspace/src/workspace/server.rs @@ -511,8 +511,6 @@ impl Workspace for WorkspaceServer { .await .unwrap_or_else(|_| vec![]); - println!("{:#?}", plpgsql_check_results); - for d in plpgsql_check_results { let r = d.span.map(|span| span + range.start()); diagnostics.push( From 6b0a9f864468852e714d875d15128a7d45efc7a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Steinr=C3=B6tter?= Date: Sun, 17 Aug 2025 15:10:15 +0200 Subject: [PATCH 23/29] fix: positional params (#473) pre-processes the query string to replace named params like `@name` with positional params like `$1`, because only the latter is supported by the postgres parser. not super happy with the way it is used for the type checker but also did not want to put that function in another crate. also removed a `println!` statement leftover from a previous pr of me. also fixed a bug in the lexer that tokenized a cast as a named parameter. fixes #405, #454 --- Cargo.lock | 1 + crates/pgt_lexer/src/lexer.rs | 1 + crates/pgt_lexer_codegen/src/syntax_kind.rs | 1 + crates/pgt_tokenizer/src/lib.rs | 70 ++++++++++++------ ..._tests__named_param_colon_raw_vs_cast.snap | 25 +++++++ crates/pgt_tokenizer/src/token.rs | 2 + crates/pgt_workspace/Cargo.toml | 1 + crates/pgt_workspace/src/workspace/server.rs | 3 +- .../src/workspace/server.tests.rs | 54 ++++++++++++++ .../src/workspace/server/pg_query.rs | 73 ++++++++++++++++++- 10 files changed, 205 insertions(+), 26 deletions(-) create mode 100644 crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_colon_raw_vs_cast.snap diff --git a/Cargo.lock b/Cargo.lock index 49143908..94b591f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3189,6 +3189,7 @@ dependencies = [ "pgt_suppressions", "pgt_test_utils", "pgt_text_size", + "pgt_tokenizer", "pgt_typecheck", "pgt_workspace_macros", "rustc-hash 2.1.0", diff --git a/crates/pgt_lexer/src/lexer.rs b/crates/pgt_lexer/src/lexer.rs index ad6db297..3e691229 100644 --- a/crates/pgt_lexer/src/lexer.rs +++ b/crates/pgt_lexer/src/lexer.rs @@ -111,6 +111,7 @@ impl<'a> Lexer<'a> { pgt_tokenizer::TokenKind::Tilde => SyntaxKind::TILDE, pgt_tokenizer::TokenKind::Question => SyntaxKind::QUESTION, pgt_tokenizer::TokenKind::Colon => SyntaxKind::COLON, + pgt_tokenizer::TokenKind::DoubleColon => SyntaxKind::DOUBLE_COLON, pgt_tokenizer::TokenKind::Eq => SyntaxKind::EQ, pgt_tokenizer::TokenKind::Bang => SyntaxKind::BANG, pgt_tokenizer::TokenKind::Lt => SyntaxKind::L_ANGLE, diff --git a/crates/pgt_lexer_codegen/src/syntax_kind.rs b/crates/pgt_lexer_codegen/src/syntax_kind.rs index c671e451..3a005437 100644 --- a/crates/pgt_lexer_codegen/src/syntax_kind.rs +++ b/crates/pgt_lexer_codegen/src/syntax_kind.rs @@ -37,6 +37,7 @@ const PUNCT: &[(&str, &str)] = &[ ("_", "UNDERSCORE"), (".", "DOT"), (":", "COLON"), + ("::", "DOUBLE_COLON"), ("=", "EQ"), ("!", "BANG"), ("-", "MINUS"), diff --git a/crates/pgt_tokenizer/src/lib.rs b/crates/pgt_tokenizer/src/lib.rs index 83b9ba44..16093db8 100644 --- a/crates/pgt_tokenizer/src/lib.rs +++ b/crates/pgt_tokenizer/src/lib.rs @@ -144,32 +144,37 @@ impl Cursor<'_> { } } ':' => { - // Named parameters in psql with different substitution styles. - // - // https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-INTERPOLATION - match self.first() { - '\'' => { - // Named parameter with colon prefix and single quotes. - self.bump(); - let terminated = self.single_quoted_string(); - let kind = NamedParamKind::ColonString { terminated }; - TokenKind::NamedParam { kind } - } - '"' => { - // Named parameter with colon prefix and double quotes. - self.bump(); - let terminated = self.double_quoted_string(); - let kind = NamedParamKind::ColonIdentifier { terminated }; - TokenKind::NamedParam { kind } - } - c if is_ident_start(c) => { - // Named parameter with colon prefix. - self.eat_while(is_ident_cont); - TokenKind::NamedParam { - kind: NamedParamKind::ColonRaw, + if self.first() == ':' { + self.bump(); + TokenKind::DoubleColon + } else { + // Named parameters in psql with different substitution styles. + // + // https://www.postgresql.org/docs/current/app-psql.html#APP-PSQL-INTERPOLATION + match self.first() { + '\'' => { + // Named parameter with colon prefix and single quotes. + self.bump(); + let terminated = self.single_quoted_string(); + let kind = NamedParamKind::ColonString { terminated }; + TokenKind::NamedParam { kind } + } + '"' => { + // Named parameter with colon prefix and double quotes. + self.bump(); + let terminated = self.double_quoted_string(); + let kind = NamedParamKind::ColonIdentifier { terminated }; + TokenKind::NamedParam { kind } + } + c if is_ident_start(c) => { + // Named parameter with colon prefix. + self.eat_while(is_ident_cont); + TokenKind::NamedParam { + kind: NamedParamKind::ColonRaw, + } } + _ => TokenKind::Colon, } - _ => TokenKind::Colon, } } // One-symbol tokens. @@ -675,6 +680,23 @@ mod tests { assert_debug_snapshot!(result); } + #[test] + fn debug_simple_cast() { + let result = lex("::test"); + assert_debug_snapshot!(result, @r###" + [ + "::" @ DoubleColon, + "test" @ Ident, + ] + "###); + } + + #[test] + fn named_param_colon_raw_vs_cast() { + let result = lex("select 1 from c where id::test = :id;"); + assert_debug_snapshot!(result); + } + #[test] fn named_param_colon_string() { let result = lex("select 1 from c where id = :'id';"); diff --git a/crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_colon_raw_vs_cast.snap b/crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_colon_raw_vs_cast.snap new file mode 100644 index 00000000..ecfd4821 --- /dev/null +++ b/crates/pgt_tokenizer/src/snapshots/pgt_tokenizer__tests__named_param_colon_raw_vs_cast.snap @@ -0,0 +1,25 @@ +--- +source: crates/pgt_tokenizer/src/lib.rs +expression: result +snapshot_kind: text +--- +[ + "select" @ Ident, + " " @ Space, + "1" @ Literal { kind: Int { base: Decimal, empty_int: false } }, + " " @ Space, + "from" @ Ident, + " " @ Space, + "c" @ Ident, + " " @ Space, + "where" @ Ident, + " " @ Space, + "id" @ Ident, + "::" @ DoubleColon, + "test" @ Ident, + " " @ Space, + "=" @ Eq, + " " @ Space, + ":id" @ NamedParam { kind: ColonRaw }, + ";" @ Semi, +] diff --git a/crates/pgt_tokenizer/src/token.rs b/crates/pgt_tokenizer/src/token.rs index da98a229..1312773d 100644 --- a/crates/pgt_tokenizer/src/token.rs +++ b/crates/pgt_tokenizer/src/token.rs @@ -46,6 +46,8 @@ pub enum TokenKind { Minus, /// `:` Colon, + /// `::` + DoubleColon, /// `.` Dot, /// `=` diff --git a/crates/pgt_workspace/Cargo.toml b/crates/pgt_workspace/Cargo.toml index efded47c..860b5133 100644 --- a/crates/pgt_workspace/Cargo.toml +++ b/crates/pgt_workspace/Cargo.toml @@ -33,6 +33,7 @@ pgt_schema_cache = { workspace = true } pgt_statement_splitter = { workspace = true } pgt_suppressions = { workspace = true } pgt_text_size.workspace = true +pgt_tokenizer = { workspace = true } pgt_typecheck = { workspace = true } pgt_workspace_macros = { workspace = true } rustc-hash = { workspace = true } diff --git a/crates/pgt_workspace/src/workspace/server.rs b/crates/pgt_workspace/src/workspace/server.rs index f0a39dbf..49c306f2 100644 --- a/crates/pgt_workspace/src/workspace/server.rs +++ b/crates/pgt_workspace/src/workspace/server.rs @@ -14,6 +14,7 @@ use document::{ TypecheckDiagnosticsMapper, }; use futures::{StreamExt, stream}; +use pg_query::convert_to_positional_params; use pgt_analyse::{AnalyserOptions, AnalysisFilter}; use pgt_analyser::{Analyser, AnalyserConfig, AnalyserParams}; use pgt_diagnostics::{ @@ -468,7 +469,7 @@ impl Workspace for WorkspaceServer { // Type checking let typecheck_result = pgt_typecheck::check_sql(TypecheckParams { conn: &pool, - sql: id.content(), + sql: convert_to_positional_params(id.content()).as_str(), ast: &ast, tree: &cst, schema_cache: schema_cache.as_ref(), diff --git a/crates/pgt_workspace/src/workspace/server.tests.rs b/crates/pgt_workspace/src/workspace/server.tests.rs index ef5ba267..894d1042 100644 --- a/crates/pgt_workspace/src/workspace/server.tests.rs +++ b/crates/pgt_workspace/src/workspace/server.tests.rs @@ -277,3 +277,57 @@ async fn test_dedupe_diagnostics(test_db: PgPool) { Some(TextRange::new(115.into(), 210.into())) ); } + +#[sqlx::test(migrator = "pgt_test_utils::MIGRATIONS")] +async fn test_positional_params(test_db: PgPool) { + let mut conf = PartialConfiguration::init(); + conf.merge_with(PartialConfiguration { + db: Some(PartialDatabaseConfiguration { + database: Some( + test_db + .connect_options() + .get_database() + .unwrap() + .to_string(), + ), + ..Default::default() + }), + ..Default::default() + }); + + let workspace = get_test_workspace(Some(conf)).expect("Unable to create test workspace"); + + let path = PgTPath::new("test.sql"); + + let setup_sql = r" + create table users ( + id serial primary key, + name text not null, + email text not null + ); + "; + test_db.execute(setup_sql).await.expect("setup sql failed"); + + let content = r#"select * from users where id = @one and name = :two and email = :'three';"#; + + workspace + .open_file(OpenFileParams { + path: path.clone(), + content: content.into(), + version: 1, + }) + .expect("Unable to open test file"); + + let diagnostics = workspace + .pull_diagnostics(crate::workspace::PullDiagnosticsParams { + path: path.clone(), + categories: RuleCategories::all(), + max_diagnostics: 100, + only: vec![], + skip: vec![], + }) + .expect("Unable to pull diagnostics") + .diagnostics; + + assert_eq!(diagnostics.len(), 0, "Expected no diagnostic"); +} diff --git a/crates/pgt_workspace/src/workspace/server/pg_query.rs b/crates/pgt_workspace/src/workspace/server/pg_query.rs index 05f1425d..bd9ffdfc 100644 --- a/crates/pgt_workspace/src/workspace/server/pg_query.rs +++ b/crates/pgt_workspace/src/workspace/server/pg_query.rs @@ -1,9 +1,11 @@ +use std::collections::HashMap; use std::num::NonZeroUsize; use std::sync::{Arc, Mutex}; use lru::LruCache; use pgt_query_ext::diagnostics::*; use pgt_text_size::TextRange; +use pgt_tokenizer::tokenize; use super::statement_identifier::StatementId; @@ -37,7 +39,7 @@ impl PgQueryStore { } let r = Arc::new( - pgt_query::parse(statement.content()) + pgt_query::parse(&convert_to_positional_params(statement.content())) .map_err(SyntaxDiagnostic::from) .and_then(|ast| { ast.into_root().ok_or_else(|| { @@ -87,10 +89,79 @@ impl PgQueryStore { } } +/// Converts named parameters in a SQL query string to positional parameters. +/// +/// This function scans the input SQL string for named parameters (e.g., `@param`, `:param`, `:'param'`) +/// and replaces them with positional parameters (e.g., `$1`, `$2`, etc.). +/// +/// It maintains the original spacing of the named parameters in the output string. +/// +/// Useful for preparing SQL queries for parsing or execution where named paramters are not supported. +pub fn convert_to_positional_params(text: &str) -> String { + let mut result = String::with_capacity(text.len()); + let mut param_mapping: HashMap<&str, usize> = HashMap::new(); + let mut param_index = 1; + let mut position = 0; + + for token in tokenize(text) { + let token_len = token.len as usize; + let token_text = &text[position..position + token_len]; + + if matches!(token.kind, pgt_tokenizer::TokenKind::NamedParam { .. }) { + let idx = match param_mapping.get(token_text) { + Some(&index) => index, + None => { + let index = param_index; + param_mapping.insert(token_text, index); + param_index += 1; + index + } + }; + + let replacement = format!("${}", idx); + let original_len = token_text.len(); + let replacement_len = replacement.len(); + + result.push_str(&replacement); + + // maintain original spacing + if replacement_len < original_len { + result.push_str(&" ".repeat(original_len - replacement_len)); + } + } else { + result.push_str(token_text); + } + + position += token_len; + } + + result +} + #[cfg(test)] mod tests { use super::*; + #[test] + fn test_convert_to_positional_params() { + let input = "select * from users where id = @one and name = :two and email = :'three';"; + let result = convert_to_positional_params(input); + assert_eq!( + result, + "select * from users where id = $1 and name = $2 and email = $3 ;" + ); + } + + #[test] + fn test_convert_to_positional_params_with_duplicates() { + let input = "select * from users where first_name = @one and starts_with(email, @one) and created_at > @two;"; + let result = convert_to_positional_params(input); + assert_eq!( + result, + "select * from users where first_name = $1 and starts_with(email, $1 ) and created_at > $2 ;" + ); + } + #[test] fn test_plpgsql_syntax_error() { let input = " From a1e34a9ab3195cae6a340a62888531317dbde507 Mon Sep 17 00:00:00 2001 From: Julian Domke <68325451+juleswritescode@users.noreply.github.com> Date: Mon, 18 Aug 2025 09:06:10 +0200 Subject: [PATCH 24/29] feat: basic hover (#463) --- Cargo.lock | 30 +++++++ Cargo.toml | 1 + crates/pgt_hover/Cargo.toml | 36 ++++++++ crates/pgt_hover/src/hovered_node.rs | 50 +++++++++++ crates/pgt_hover/src/lib.rs | 47 ++++++++++ crates/pgt_hover/src/to_markdown.rs | 90 +++++++++++++++++++ crates/pgt_lsp/src/capabilities.rs | 8 +- crates/pgt_lsp/src/handlers.rs | 1 + crates/pgt_lsp/src/handlers/hover.rs | 42 +++++++++ crates/pgt_lsp/src/server.rs | 11 +++ crates/pgt_schema_cache/src/schema_cache.rs | 6 +- crates/pgt_treesitter/Cargo.toml | 5 +- crates/pgt_workspace/Cargo.toml | 1 + .../pgt_workspace/src/features/completions.rs | 24 ++--- crates/pgt_workspace/src/features/mod.rs | 1 + crates/pgt_workspace/src/features/on_hover.rs | 25 ++++++ crates/pgt_workspace/src/workspace.rs | 3 + crates/pgt_workspace/src/workspace/client.rs | 7 ++ crates/pgt_workspace/src/workspace/server.rs | 46 +++++++++- .../src/workspace/server/document.rs | 35 ++++++-- .../backend-jsonrpc/src/workspace.ts | 1 + 21 files changed, 439 insertions(+), 31 deletions(-) create mode 100644 crates/pgt_hover/Cargo.toml create mode 100644 crates/pgt_hover/src/hovered_node.rs create mode 100644 crates/pgt_hover/src/lib.rs create mode 100644 crates/pgt_hover/src/to_markdown.rs create mode 100644 crates/pgt_lsp/src/handlers/hover.rs create mode 100644 crates/pgt_workspace/src/features/on_hover.rs diff --git a/Cargo.lock b/Cargo.lock index 94b591f3..be7eacb7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1904,6 +1904,15 @@ version = "1.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" +[[package]] +name = "humansize" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb51c9a029ddc91b07a787f1d86b53ccfa49b0e86688c946ebe8d3555685dd7" +dependencies = [ + "libm", +] + [[package]] name = "icu_collections" version = "1.5.0" @@ -2914,6 +2923,26 @@ dependencies = [ "tracing", ] +[[package]] +name = "pgt_hover" +version = "0.0.0" +dependencies = [ + "humansize", + "pgt_query", + "pgt_schema_cache", + "pgt_test_utils", + "pgt_text_size", + "pgt_treesitter", + "schemars", + "serde", + "serde_json", + "sqlx", + "tokio", + "tracing", + "tree-sitter", + "tree_sitter_sql", +] + [[package]] name = "pgt_lexer" version = "0.0.0" @@ -3180,6 +3209,7 @@ dependencies = [ "pgt_console", "pgt_diagnostics", "pgt_fs", + "pgt_hover", "pgt_lexer", "pgt_plpgsql_check", "pgt_query", diff --git a/Cargo.toml b/Cargo.toml index d68aafe0..84fed3d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -72,6 +72,7 @@ pgt_diagnostics_categories = { path = "./crates/pgt_diagnostics_categories", ver pgt_diagnostics_macros = { path = "./crates/pgt_diagnostics_macros", version = "0.0.0" } pgt_flags = { path = "./crates/pgt_flags", version = "0.0.0" } pgt_fs = { path = "./crates/pgt_fs", version = "0.0.0" } +pgt_hover = { path = "./crates/pgt_hover", version = "0.0.0" } pgt_lexer = { path = "./crates/pgt_lexer", version = "0.0.0" } pgt_lexer_codegen = { path = "./crates/pgt_lexer_codegen", version = "0.0.0" } pgt_lsp = { path = "./crates/pgt_lsp", version = "0.0.0" } diff --git a/crates/pgt_hover/Cargo.toml b/crates/pgt_hover/Cargo.toml new file mode 100644 index 00000000..5b3a9714 --- /dev/null +++ b/crates/pgt_hover/Cargo.toml @@ -0,0 +1,36 @@ +[package] +authors.workspace = true +categories.workspace = true +description = "" +edition.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +name = "pgt_hover" +repository.workspace = true +version = "0.0.0" + + +[dependencies] +humansize = { version = "2.1.3" } +pgt_query.workspace = true +pgt_schema_cache.workspace = true +pgt_text_size.workspace = true +pgt_treesitter.workspace = true +schemars = { workspace = true, optional = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +sqlx.workspace = true +tokio = { version = "1.41.1", features = ["full"] } +tracing = { workspace = true } +tree-sitter.workspace = true +tree_sitter_sql.workspace = true + +[dev-dependencies] +pgt_test_utils.workspace = true + +[lib] +doctest = false + +[features] +schema = ["dep:schemars"] diff --git a/crates/pgt_hover/src/hovered_node.rs b/crates/pgt_hover/src/hovered_node.rs new file mode 100644 index 00000000..2f1905e7 --- /dev/null +++ b/crates/pgt_hover/src/hovered_node.rs @@ -0,0 +1,50 @@ +use pgt_text_size::TextSize; +use pgt_treesitter::TreeSitterContextParams; + +#[derive(Debug)] +pub(crate) enum NodeIdentification { + Name(String), + SchemaAndName((String, String)), + #[allow(unused)] + SchemaAndTableAndName((String, String, String)), +} + +#[allow(unused)] +#[derive(Debug)] +pub(crate) enum HoveredNode { + Schema(NodeIdentification), + Table(NodeIdentification), + Function(NodeIdentification), + Column(NodeIdentification), + Policy(NodeIdentification), + Trigger(NodeIdentification), + Role(NodeIdentification), +} + +impl HoveredNode { + pub(crate) fn get(position: TextSize, text: &str, tree: &tree_sitter::Tree) -> Option { + let ctx = pgt_treesitter::context::TreesitterContext::new(TreeSitterContextParams { + position, + text, + tree, + }); + + let node_content = ctx.get_node_under_cursor_content()?; + + let under_node = ctx.node_under_cursor.as_ref()?; + + match under_node.kind() { + "identifier" if ctx.parent_matches_one_of_kind(&["object_reference", "relation"]) => { + if let Some(schema) = ctx.schema_or_alias_name { + Some(HoveredNode::Table(NodeIdentification::SchemaAndName(( + schema, + node_content, + )))) + } else { + Some(HoveredNode::Table(NodeIdentification::Name(node_content))) + } + } + _ => None, + } + } +} diff --git a/crates/pgt_hover/src/lib.rs b/crates/pgt_hover/src/lib.rs new file mode 100644 index 00000000..c2f5c213 --- /dev/null +++ b/crates/pgt_hover/src/lib.rs @@ -0,0 +1,47 @@ +use pgt_schema_cache::SchemaCache; +use pgt_text_size::TextSize; + +use crate::{hovered_node::HoveredNode, to_markdown::ToHoverMarkdown}; + +mod hovered_node; +mod to_markdown; + +pub struct OnHoverParams<'a> { + pub position: TextSize, + pub schema_cache: &'a SchemaCache, + pub stmt_sql: &'a str, + pub ast: Option<&'a pgt_query::NodeEnum>, + pub ts_tree: &'a tree_sitter::Tree, +} + +pub fn on_hover(params: OnHoverParams) -> Vec { + if let Some(hovered_node) = HoveredNode::get(params.position, params.stmt_sql, params.ts_tree) { + match hovered_node { + HoveredNode::Table(node_identification) => { + let table = match node_identification { + hovered_node::NodeIdentification::Name(n) => { + params.schema_cache.find_table(n.as_str(), None) + } + hovered_node::NodeIdentification::SchemaAndName((s, n)) => { + params.schema_cache.find_table(n.as_str(), Some(s.as_str())) + } + hovered_node::NodeIdentification::SchemaAndTableAndName(_) => None, + }; + + table + .map(|t| { + let mut markdown = String::new(); + match t.to_hover_markdown(&mut markdown) { + Ok(_) => vec![markdown], + Err(_) => vec![], + } + }) + .unwrap_or(vec![]) + } + + _ => todo!(), + } + } else { + Default::default() + } +} diff --git a/crates/pgt_hover/src/to_markdown.rs b/crates/pgt_hover/src/to_markdown.rs new file mode 100644 index 00000000..7ea66160 --- /dev/null +++ b/crates/pgt_hover/src/to_markdown.rs @@ -0,0 +1,90 @@ +use std::fmt::Write; + +use humansize::DECIMAL; + +pub(crate) trait ToHoverMarkdown { + fn to_hover_markdown(&self, writer: &mut W) -> Result<(), std::fmt::Error>; +} + +impl ToHoverMarkdown for pgt_schema_cache::Table { + fn to_hover_markdown(&self, writer: &mut W) -> Result<(), std::fmt::Error> { + HeadlineWriter::for_table(writer, self)?; + BodyWriter::for_table(writer, self)?; + FooterWriter::for_table(writer, self)?; + + Ok(()) + } +} + +struct HeadlineWriter; + +impl HeadlineWriter { + fn for_table( + writer: &mut W, + table: &pgt_schema_cache::Table, + ) -> Result<(), std::fmt::Error> { + let table_kind = match table.table_kind { + pgt_schema_cache::TableKind::View => " (View)", + pgt_schema_cache::TableKind::MaterializedView => " (M.View)", + pgt_schema_cache::TableKind::Partitioned => " (Partitioned)", + pgt_schema_cache::TableKind::Ordinary => "", + }; + + let locked_txt = if table.rls_enabled { + " - 🔒 RLS enabled" + } else { + " - 🔓 RLS disabled" + }; + + write!( + writer, + "### {}.{}{}{}", + table.schema, table.name, table_kind, locked_txt + )?; + + markdown_newline(writer)?; + + Ok(()) + } +} + +struct BodyWriter; + +impl BodyWriter { + fn for_table( + writer: &mut W, + table: &pgt_schema_cache::Table, + ) -> Result<(), std::fmt::Error> { + if let Some(c) = table.comment.as_ref() { + write!(writer, "{}", c)?; + markdown_newline(writer)?; + } + + Ok(()) + } +} + +struct FooterWriter; + +impl FooterWriter { + fn for_table( + writer: &mut W, + table: &pgt_schema_cache::Table, + ) -> Result<(), std::fmt::Error> { + write!( + writer, + "~{} rows, ~{} dead rows, {}", + table.live_rows_estimate, + table.dead_rows_estimate, + humansize::format_size(table.bytes as u64, DECIMAL) + )?; + + Ok(()) + } +} + +fn markdown_newline(writer: &mut W) -> Result<(), std::fmt::Error> { + write!(writer, " ")?; + writeln!(writer)?; + Ok(()) +} diff --git a/crates/pgt_lsp/src/capabilities.rs b/crates/pgt_lsp/src/capabilities.rs index 3b473eb7..8c8ff6d9 100644 --- a/crates/pgt_lsp/src/capabilities.rs +++ b/crates/pgt_lsp/src/capabilities.rs @@ -3,9 +3,10 @@ use crate::handlers::code_actions::command_id; use pgt_workspace::features::code_actions::CommandActionCategory; use strum::IntoEnumIterator; use tower_lsp::lsp_types::{ - ClientCapabilities, CompletionOptions, ExecuteCommandOptions, PositionEncodingKind, - SaveOptions, ServerCapabilities, TextDocumentSyncCapability, TextDocumentSyncKind, - TextDocumentSyncOptions, TextDocumentSyncSaveOptions, WorkDoneProgressOptions, + ClientCapabilities, CompletionOptions, ExecuteCommandOptions, HoverProviderCapability, + PositionEncodingKind, SaveOptions, ServerCapabilities, TextDocumentSyncCapability, + TextDocumentSyncKind, TextDocumentSyncOptions, TextDocumentSyncSaveOptions, + WorkDoneProgressOptions, }; /// The capabilities to send from server as part of [`InitializeResult`] @@ -62,6 +63,7 @@ pub(crate) fn server_capabilities(capabilities: &ClientCapabilities) -> ServerCa true, )), rename_provider: None, + hover_provider: Some(HoverProviderCapability::Simple(true)), ..Default::default() } } diff --git a/crates/pgt_lsp/src/handlers.rs b/crates/pgt_lsp/src/handlers.rs index 103bef2f..113e3fcc 100644 --- a/crates/pgt_lsp/src/handlers.rs +++ b/crates/pgt_lsp/src/handlers.rs @@ -1,3 +1,4 @@ pub(crate) mod code_actions; pub(crate) mod completions; +pub(crate) mod hover; pub(crate) mod text_document; diff --git a/crates/pgt_lsp/src/handlers/hover.rs b/crates/pgt_lsp/src/handlers/hover.rs new file mode 100644 index 00000000..59d70ca7 --- /dev/null +++ b/crates/pgt_lsp/src/handlers/hover.rs @@ -0,0 +1,42 @@ +use pgt_workspace::{WorkspaceError, features::on_hover::OnHoverParams}; +use tower_lsp::lsp_types::{self, MarkedString, MarkupContent}; + +use crate::{adapters::get_cursor_position, diagnostics::LspError, session::Session}; + +pub(crate) fn on_hover( + session: &Session, + params: lsp_types::HoverParams, +) -> Result { + let url = params.text_document_position_params.text_document.uri; + let position = params.text_document_position_params.position; + let path = session.file_path(&url)?; + + match session.workspace.on_hover(OnHoverParams { + path, + position: get_cursor_position(session, &url, position)?, + }) { + Ok(result) => { + tracing::debug!("Found hover items: {:#?}", result); + + Ok(lsp_types::HoverContents::Array( + result + .into_iter() + .map(MarkedString::from_markdown) + .collect(), + )) + } + + Err(e) => match e { + WorkspaceError::DatabaseConnectionError(_) => { + Ok(lsp_types::HoverContents::Markup(MarkupContent { + kind: lsp_types::MarkupKind::PlainText, + value: "Cannot connect to database.".into(), + })) + } + _ => { + tracing::error!("Received an error: {:#?}", e); + Err(e.into()) + } + }, + } +} diff --git a/crates/pgt_lsp/src/server.rs b/crates/pgt_lsp/src/server.rs index 6420c511..76d9bd9a 100644 --- a/crates/pgt_lsp/src/server.rs +++ b/crates/pgt_lsp/src/server.rs @@ -265,6 +265,17 @@ impl LanguageServer for LSPServer { } } + #[tracing::instrument(level = "trace", skip_all)] + async fn hover(&self, params: HoverParams) -> LspResult> { + match handlers::hover::on_hover(&self.session, params) { + Ok(result) => LspResult::Ok(Some(Hover { + contents: result, + range: None, + })), + Err(e) => LspResult::Err(into_lsp_error(e)), + } + } + #[tracing::instrument(level = "trace", skip_all)] async fn completion(&self, params: CompletionParams) -> LspResult> { match handlers::completions::get_completions(&self.session, params) { diff --git a/crates/pgt_schema_cache/src/schema_cache.rs b/crates/pgt_schema_cache/src/schema_cache.rs index 84bcd77c..89eddca9 100644 --- a/crates/pgt_schema_cache/src/schema_cache.rs +++ b/crates/pgt_schema_cache/src/schema_cache.rs @@ -66,13 +66,13 @@ impl SchemaCache { pub fn find_table(&self, name: &str, schema: Option<&str>) -> Option<&Table> { self.tables .iter() - .find(|t| t.name == name && schema.is_none() || Some(t.schema.as_str()) == schema) + .find(|t| t.name == name && schema.is_none_or(|s| s == t.schema.as_str())) } pub fn find_type(&self, name: &str, schema: Option<&str>) -> Option<&PostgresType> { self.types .iter() - .find(|t| t.name == name && schema.is_none() || Some(t.schema.as_str()) == schema) + .find(|t| t.name == name && schema.is_none_or(|s| s == t.schema.as_str())) } pub fn find_col(&self, name: &str, table: &str, schema: Option<&str>) -> Option<&Column> { @@ -86,7 +86,7 @@ impl SchemaCache { pub fn find_types(&self, name: &str, schema: Option<&str>) -> Vec<&PostgresType> { self.types .iter() - .filter(|t| t.name == name && schema.is_none() || Some(t.schema.as_str()) == schema) + .filter(|t| t.name == name && schema.is_none_or(|s| s == t.schema.as_str())) .collect() } } diff --git a/crates/pgt_treesitter/Cargo.toml b/crates/pgt_treesitter/Cargo.toml index f2d8b46e..d6107f94 100644 --- a/crates/pgt_treesitter/Cargo.toml +++ b/crates/pgt_treesitter/Cargo.toml @@ -10,6 +10,8 @@ name = "pgt_treesitter" repository.workspace = true version = "0.0.0" +[lib] +doctest = false [dependencies] clap = { version = "4.5.23", features = ["derive"] } @@ -20,6 +22,3 @@ tree_sitter_sql.workspace = true [dev-dependencies] pgt_test_utils.workspace = true - -[lib] -doctest = false diff --git a/crates/pgt_workspace/Cargo.toml b/crates/pgt_workspace/Cargo.toml index 860b5133..397e41a4 100644 --- a/crates/pgt_workspace/Cargo.toml +++ b/crates/pgt_workspace/Cargo.toml @@ -25,6 +25,7 @@ pgt_configuration = { workspace = true } pgt_console = { workspace = true } pgt_diagnostics = { workspace = true } pgt_fs = { workspace = true, features = ["serde"] } +pgt_hover = { workspace = true } pgt_lexer = { workspace = true } pgt_plpgsql_check = { workspace = true } pgt_query = { workspace = true } diff --git a/crates/pgt_workspace/src/features/completions.rs b/crates/pgt_workspace/src/features/completions.rs index a41dd06e..5944f14c 100644 --- a/crates/pgt_workspace/src/features/completions.rs +++ b/crates/pgt_workspace/src/features/completions.rs @@ -4,7 +4,7 @@ use pgt_completions::CompletionItem; use pgt_fs::PgTPath; use pgt_text_size::{TextRange, TextSize}; -use crate::workspace::{Document, GetCompletionsFilter, GetCompletionsMapper, StatementId}; +use crate::workspace::{Document, GetCompletionsFilter, StatementId, WithCSTMapper}; #[derive(Debug, serde::Serialize, serde::Deserialize)] #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] @@ -32,7 +32,7 @@ impl IntoIterator for CompletionsResult { pub(crate) fn get_statement_for_completions( doc: &Document, position: TextSize, -) -> Option<(StatementId, TextRange, String, Arc)> { +) -> Option<(StatementId, TextRange, Arc)> { let count = doc.count(); // no arms no cookies if count == 0 { @@ -40,7 +40,7 @@ pub(crate) fn get_statement_for_completions( } let mut eligible_statements = doc.iter_with_filter( - GetCompletionsMapper, + WithCSTMapper, GetCompletionsFilter { cursor_position: position, }, @@ -49,7 +49,7 @@ pub(crate) fn get_statement_for_completions( if count == 1 { eligible_statements.next() } else { - let mut prev_stmt: Option<(StatementId, TextRange, String, Arc)> = None; + let mut prev_stmt: Option<(StatementId, TextRange, Arc)> = None; for current_stmt in eligible_statements { /* @@ -112,10 +112,10 @@ mod tests { let (doc, position) = get_doc_and_pos(sql.as_str()); - let (_, _, text, _) = + let (stmt, _, _) = get_statement_for_completions(&doc, position).expect("Expected Statement"); - assert_eq!(text, "update users set email = 'myemail@com';") + assert_eq!(stmt.content(), "update users set email = 'myemail@com';") } #[test] @@ -151,10 +151,10 @@ mod tests { let (doc, position) = get_doc_and_pos(sql.as_str()); - let (_, _, text, _) = + let (stmt, _, _) = get_statement_for_completions(&doc, position).expect("Expected Statement"); - assert_eq!(text, "select * from ;") + assert_eq!(stmt.content(), "select * from ;") } #[test] @@ -163,10 +163,10 @@ mod tests { let (doc, position) = get_doc_and_pos(sql.as_str()); - let (_, _, text, _) = + let (stmt, _, _) = get_statement_for_completions(&doc, position).expect("Expected Statement"); - assert_eq!(text, "select * from") + assert_eq!(stmt.content(), "select * from") } #[test] @@ -187,10 +187,10 @@ mod tests { let (doc, position) = get_doc_and_pos(sql); - let (_, _, text, _) = + let (stmt, _, _) = get_statement_for_completions(&doc, position).expect("Expected Statement"); - assert_eq!(text.trim(), "select from cool;") + assert_eq!(stmt.content().trim(), "select from cool;") } #[test] diff --git a/crates/pgt_workspace/src/features/mod.rs b/crates/pgt_workspace/src/features/mod.rs index 31013f36..7455f0be 100644 --- a/crates/pgt_workspace/src/features/mod.rs +++ b/crates/pgt_workspace/src/features/mod.rs @@ -1,3 +1,4 @@ pub mod code_actions; pub mod completions; pub mod diagnostics; +pub mod on_hover; diff --git a/crates/pgt_workspace/src/features/on_hover.rs b/crates/pgt_workspace/src/features/on_hover.rs new file mode 100644 index 00000000..3e3fcd49 --- /dev/null +++ b/crates/pgt_workspace/src/features/on_hover.rs @@ -0,0 +1,25 @@ +use pgt_fs::PgTPath; +use pgt_text_size::TextSize; + +#[derive(Debug, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct OnHoverParams { + pub path: PgTPath, + pub position: TextSize, +} + +#[derive(Debug, serde::Serialize, serde::Deserialize, Default)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +pub struct OnHoverResult { + /// Can contain multiple blocks of markdown + /// if the hovered-on item is ambiguous. + pub(crate) markdown_blocks: Vec, +} + +impl IntoIterator for OnHoverResult { + type Item = String; + type IntoIter = as IntoIterator>::IntoIter; + fn into_iter(self) -> Self::IntoIter { + self.markdown_blocks.into_iter() + } +} diff --git a/crates/pgt_workspace/src/workspace.rs b/crates/pgt_workspace/src/workspace.rs index 9206b39d..0747c081 100644 --- a/crates/pgt_workspace/src/workspace.rs +++ b/crates/pgt_workspace/src/workspace.rs @@ -17,6 +17,7 @@ use crate::{ }, completions::{CompletionsResult, GetCompletionsParams}, diagnostics::{PullDiagnosticsParams, PullDiagnosticsResult}, + on_hover::{OnHoverParams, OnHoverResult}, }, }; @@ -113,6 +114,8 @@ pub trait Workspace: Send + Sync + RefUnwindSafe { params: GetCompletionsParams, ) -> Result; + fn on_hover(&self, params: OnHoverParams) -> Result; + /// Register a possible workspace project folder. Returns the key of said project. Use this key when you want to switch to different projects. fn register_project_folder( &self, diff --git a/crates/pgt_workspace/src/workspace/client.rs b/crates/pgt_workspace/src/workspace/client.rs index 2bd21513..05e964f6 100644 --- a/crates/pgt_workspace/src/workspace/client.rs +++ b/crates/pgt_workspace/src/workspace/client.rs @@ -161,4 +161,11 @@ where ) -> Result { self.request("pgt/get_completions", params) } + + fn on_hover( + &self, + params: crate::features::on_hover::OnHoverParams, + ) -> Result { + self.request("pgt/on_hover", params) + } } diff --git a/crates/pgt_workspace/src/workspace/server.rs b/crates/pgt_workspace/src/workspace/server.rs index 49c306f2..ea9039ce 100644 --- a/crates/pgt_workspace/src/workspace/server.rs +++ b/crates/pgt_workspace/src/workspace/server.rs @@ -37,9 +37,10 @@ use crate::{ }, completions::{CompletionsResult, GetCompletionsParams, get_statement_for_completions}, diagnostics::{PullDiagnosticsParams, PullDiagnosticsResult}, + on_hover::{OnHoverParams, OnHoverResult}, }, settings::{WorkspaceSettings, WorkspaceSettingsHandle, WorkspaceSettingsHandleMut}, - workspace::AnalyserDiagnosticsMapper, + workspace::{AnalyserDiagnosticsMapper, WithCSTandASTMapper}, }; use super::{ @@ -681,20 +682,59 @@ impl Workspace for WorkspaceServer { tracing::debug!("No statement found."); Ok(CompletionsResult::default()) } - Some((_id, range, content, cst)) => { + Some((id, range, cst)) => { let position = params.position - range.start(); let items = pgt_completions::complete(pgt_completions::CompletionParams { position, schema: schema_cache.as_ref(), tree: &cst, - text: content, + text: id.content().to_string(), }); Ok(CompletionsResult { items }) } } } + + fn on_hover(&self, params: OnHoverParams) -> Result { + let documents = self.documents.read().unwrap(); + let doc = documents + .get(¶ms.path) + .ok_or(WorkspaceError::not_found())?; + + let pool = self.get_current_connection(); + if pool.is_none() { + tracing::debug!("No database connection available. Skipping completions."); + return Ok(OnHoverResult::default()); + } + let pool = pool.unwrap(); + + let schema_cache = self.schema_cache.load(pool)?; + + match doc + .iter_with_filter( + WithCSTandASTMapper, + CursorPositionFilter::new(params.position), + ) + .next() + { + Some((stmt_id, range, ts_tree, maybe_ast)) => { + let position_in_stmt = params.position + range.start(); + + let markdown_blocks = pgt_hover::on_hover(pgt_hover::OnHoverParams { + ts_tree: &ts_tree, + schema_cache: &schema_cache, + ast: maybe_ast.as_ref(), + position: position_in_stmt, + stmt_sql: stmt_id.content(), + }); + + Ok(OnHoverResult { markdown_blocks }) + } + None => Ok(OnHoverResult::default()), + } + } } /// Returns `true` if `path` is a directory or diff --git a/crates/pgt_workspace/src/workspace/server/document.rs b/crates/pgt_workspace/src/workspace/server/document.rs index c8cdc1f1..9a496dc1 100644 --- a/crates/pgt_workspace/src/workspace/server/document.rs +++ b/crates/pgt_workspace/src/workspace/server/document.rs @@ -263,14 +263,35 @@ impl<'a> StatementMapper<'a> for AnalyserDiagnosticsMapper { ) } } +pub struct WithCSTMapper; +impl<'a> StatementMapper<'a> for WithCSTMapper { + type Output = (StatementId, TextRange, Arc); -pub struct GetCompletionsMapper; -impl<'a> StatementMapper<'a> for GetCompletionsMapper { - type Output = (StatementId, TextRange, String, Arc); + fn map(&self, parser: &'a Document, id: StatementId, range: TextRange) -> Self::Output { + let tree = parser.cst_db.get_or_cache_tree(&id); + (id.clone(), range, tree) + } +} + +pub struct WithCSTandASTMapper; +impl<'a> StatementMapper<'a> for WithCSTandASTMapper { + type Output = ( + StatementId, + TextRange, + Arc, + Option, + ); fn map(&self, parser: &'a Document, id: StatementId, range: TextRange) -> Self::Output { let tree = parser.cst_db.get_or_cache_tree(&id); - (id.clone(), range, id.content().to_string(), tree) + let ast_result = parser.ast_db.get_or_cache_ast(&id); + + let ast_option = match &*ast_result { + Ok(node) => Some(node.clone()), + Err(_) => None, + }; + + (id.clone(), range, tree, ast_option) } } @@ -550,11 +571,11 @@ $$ LANGUAGE plpgsql;"; let input = "SELECT * FROM users;"; let d = Document::new(input.to_string(), 1); - let results = d.iter(GetCompletionsMapper).collect::>(); + let results = d.iter(WithCSTMapper).collect::>(); assert_eq!(results.len(), 1); - let (_id, _range, content, tree) = &results[0]; - assert_eq!(content, "SELECT * FROM users;"); + let (id, _, tree) = &results[0]; + assert_eq!(id.content(), "SELECT * FROM users;"); assert_eq!(tree.root_node().kind(), "program"); } diff --git a/packages/@postgrestools/backend-jsonrpc/src/workspace.ts b/packages/@postgrestools/backend-jsonrpc/src/workspace.ts index 971f07ec..60680b8a 100644 --- a/packages/@postgrestools/backend-jsonrpc/src/workspace.ts +++ b/packages/@postgrestools/backend-jsonrpc/src/workspace.ts @@ -79,6 +79,7 @@ export type Category = | "flags/invalid" | "project" | "typecheck" + | "plpgsql_check" | "internalError/panic" | "syntax" | "dummy" From 63efa148de1afc22c2923519319a83f57cc3d756 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Steinr=C3=B6tter?= Date: Wed, 20 Aug 2025 07:55:32 +0200 Subject: [PATCH 25/29] chore: pglpgsql docs (#478) --- docs/plpgsql.md | 9 +++++++++ mkdocs.yml | 1 + 2 files changed, 10 insertions(+) create mode 100644 docs/plpgsql.md diff --git a/docs/plpgsql.md b/docs/plpgsql.md new file mode 100644 index 00000000..da190b0c --- /dev/null +++ b/docs/plpgsql.md @@ -0,0 +1,9 @@ +# PL/pgSQL Support + +Postgres Language Tools partially supports PL/pgSQL. We use `libpg_query` to parse the function body and show any syntax error. For a more sophisticated integration, make sure to enable the `plpgsql_check` extension in your development database. + +```sql +CREATE EXTENSION IF NOT EXISTS plpgsql_check; +``` + +If the extension is detected, we leverage it to run more advanced checks against your PL/pgSQL functions. diff --git a/mkdocs.yml b/mkdocs.yml index 572642c6..120be04d 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -16,6 +16,7 @@ nav: - Introduction: index.md - Guides: - Linting Migrations: checking_migrations.md + - PL/pgSQL Support: plpgsql.md - Troubleshooting: troubleshooting.md - Reference: - Rules: rules.md From db0b62e3eabad3129fb32692e9ba4fa8d3d07407 Mon Sep 17 00:00:00 2001 From: psteinroe Date: Wed, 20 Aug 2025 08:15:25 +0200 Subject: [PATCH 26/29] fix: release workflow --- .github/workflows/release.yml | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 07dd6ecd..240d1452 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -67,9 +67,29 @@ jobs: sudo apt-get update sudo apt-get install -y gcc-aarch64-linux-gnu - # running containers via `services` only works on linux - # https://github.com/actions/runner/issues/1866 - - name: 🐘 Setup postgres + # For Linux, use custom Docker image with plpgsql_check + - name: Build and start PostgreSQL with plpgsql_check + if: runner.os == 'Linux' + run: | + docker build -t postgres-plpgsql-check:latest . + docker run -d --name postgres \ + -e POSTGRES_USER=postgres \ + -e POSTGRES_PASSWORD=postgres \ + -e POSTGRES_DB=postgres \ + -p 5432:5432 \ + postgres-plpgsql-check:latest + # Wait for postgres to be ready + for _ in {1..30}; do + if docker exec postgres pg_isready -U postgres; then + break + fi + sleep 1 + done + + # For Windows, use the action since PostgreSQL Docker image doesn't support Windows containers + - name: Setup postgres (Windows) + if: runner.os == 'Windows' + id: postgres uses: ikalnytskyi/action-setup-postgres@v7 - name: 🧪 Run Tests From 422fdafd05880d62a57915d6056b0a1d734e84a2 Mon Sep 17 00:00:00 2001 From: psteinroe Date: Wed, 20 Aug 2025 08:40:06 +0200 Subject: [PATCH 27/29] fix: release workflow --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 240d1452..61e77d02 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -69,7 +69,7 @@ jobs: # For Linux, use custom Docker image with plpgsql_check - name: Build and start PostgreSQL with plpgsql_check - if: runner.os == 'Linux' + if: runner.os == 'macOS' || runner.os == 'Linux' run: | docker build -t postgres-plpgsql-check:latest . docker run -d --name postgres \ From 1d7c8851805abbaf3e26c9dc81b3b3c149475877 Mon Sep 17 00:00:00 2001 From: psteinroe Date: Wed, 20 Aug 2025 08:44:18 +0200 Subject: [PATCH 28/29] fix: release workflow --- .github/workflows/release.yml | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 61e77d02..4b427889 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -67,6 +67,15 @@ jobs: sudo apt-get update sudo apt-get install -y gcc-aarch64-linux-gnu + # The Docker runtime is not available by default on macOS runners + # https://github.com/actions/runner-images/issues/2150 + # https://blog.netnerds.net/2022/11/docker-macos-github-actions/ + - name: Install Docker + if: runner.os == 'macOS' + run: | + brew install docker + colima start + # For Linux, use custom Docker image with plpgsql_check - name: Build and start PostgreSQL with plpgsql_check if: runner.os == 'macOS' || runner.os == 'Linux' From 7cca8f449575b3b06485bd2f72bfbfa626ebafe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20Steinr=C3=B6tter?= Date: Wed, 20 Aug 2025 10:37:27 +0200 Subject: [PATCH 29/29] chore: run tests on macos in ci (#479) --- .github/actions/setup-postgres/action.yml | 87 +++++++++++++++++++++++ .github/workflows/pull_request.yml | 35 ++------- .github/workflows/release.yml | 35 +-------- 3 files changed, 93 insertions(+), 64 deletions(-) create mode 100644 .github/actions/setup-postgres/action.yml diff --git a/.github/actions/setup-postgres/action.yml b/.github/actions/setup-postgres/action.yml new file mode 100644 index 00000000..a07fdfbc --- /dev/null +++ b/.github/actions/setup-postgres/action.yml @@ -0,0 +1,87 @@ +name: Setup Postgres +description: Setup Postgres across operating systems +inputs: + postgres-version: + description: Postgres Version + default: 15 + +runs: + using: composite + steps: + # For Windows and macOS, use the action since + # PostgreSQL Docker image doesn't support Windows containers and + # macOS runners do not support Docker + - name: Setup postgres (Windows) + if: runner.os == 'Windows' || runner.os == 'macOS' + id: postgres + uses: ikalnytskyi/action-setup-postgres@v7 + with: + postgres-version: ${{ inputs.postgres-version }} + username: postgres + password: postgres + database: postgres + port: 5432 + + # Install the pglpgsql_check extension on macOS (Part 1) + - name: Install and compile plpgsql_check + if: runner.os == 'macOS' + shell: bash + run: | + # First, ensure we're using the same PostgreSQL that the action installed + export PATH="$(pg_config --bindir):$PATH" + + # Verify we're targeting the right PostgreSQL installation + echo "PostgreSQL version: $(pg_config --version)" + echo "Extension directory: $(pg_config --sharedir)/extension" + echo "Library directory: $(pg_config --pkglibdir)" + + # Clone and build plpgsql_check + git clone https://github.com/okbob/plpgsql_check.git + cd plpgsql_check + + # Clean and compile + make USE_PGXS=1 clean + make USE_PGXS=1 all + + # Install (may need sudo depending on permissions) + sudo make USE_PGXS=1 install + + # Verify installation + echo "Extension control files:" + ls -la "$(pg_config --sharedir)/extension/" | grep plpgsql || echo "No plpgsql_check found" + + echo "Extension library files:" + ls -la "$(pg_config --pkglibdir)/" | grep plpgsql || echo "No plpgsql_check library found" + + # Install the pglpgsql_check extension on macOS (Part 2) + - name: Create extension in database + if: runner.os == 'macOS' + shell: bash + env: + PGSERVICE: ${{ steps.postgres.outputs.service-name }} + run: | + psql -c "CREATE EXTENSION plpgsql_check;" + + # Verify installation + psql -c "SELECT extname, extversion FROM pg_extension WHERE extname = 'plpgsql_check';" + + # For Linux, use custom Docker image with plpgsql_check + - name: Build and start PostgreSQL with plpgsql_check + if: runner.os == 'Linux' + shell: bash + run: | + docker build -t postgres-plpgsql-check:latest . + docker run -d --name postgres \ + -e POSTGRES_USER=postgres \ + -e POSTGRES_PASSWORD=postgres \ + -e POSTGRES_DB=postgres \ + -p 5432:5432 \ + postgres-plpgsql-check:latest + # Wait for postgres to be ready + for _ in {1..30}; do + if docker exec postgres pg_isready -U postgres; then + break + fi + sleep 1 + done + diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index 20218378..72474527 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -149,6 +149,7 @@ jobs: # use the same images we use for compiling - os: windows-2022 - os: ubuntu-22.04 + - os: macos-14 steps: - name: Checkout PR branch uses: actions/checkout@v4 @@ -163,37 +164,9 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # For Linux, use custom Docker image with plpgsql_check - - name: Build and start PostgreSQL with plpgsql_check - if: runner.os == 'Linux' - run: | - docker build -t postgres-plpgsql-check:latest . - docker run -d --name postgres \ - -e POSTGRES_USER=postgres \ - -e POSTGRES_PASSWORD=postgres \ - -e POSTGRES_DB=postgres \ - -p 5432:5432 \ - postgres-plpgsql-check:latest - # Wait for postgres to be ready - for _ in {1..30}; do - if docker exec postgres pg_isready -U postgres; then - break - fi - sleep 1 - done - # For Windows, use the action since PostgreSQL Docker image doesn't support Windows containers - - name: Setup postgres (Windows) - if: runner.os == 'Windows' - id: postgres - uses: ikalnytskyi/action-setup-postgres@v7 - - name: Print Roles - run: | - if [[ "$RUNNER_OS" == "Linux" ]]; then - docker exec postgres psql -U postgres -c "select rolname from pg_roles;" - else - psql ${{ steps.postgres.outputs.connection-uri }} -c "select rolname from pg_roles;" - fi - shell: bash + - name: Setup Postgres + uses: ./.github/actions/setup-postgres + - name: Run tests run: cargo test --workspace diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4b427889..c2f8eeff 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -67,39 +67,8 @@ jobs: sudo apt-get update sudo apt-get install -y gcc-aarch64-linux-gnu - # The Docker runtime is not available by default on macOS runners - # https://github.com/actions/runner-images/issues/2150 - # https://blog.netnerds.net/2022/11/docker-macos-github-actions/ - - name: Install Docker - if: runner.os == 'macOS' - run: | - brew install docker - colima start - - # For Linux, use custom Docker image with plpgsql_check - - name: Build and start PostgreSQL with plpgsql_check - if: runner.os == 'macOS' || runner.os == 'Linux' - run: | - docker build -t postgres-plpgsql-check:latest . - docker run -d --name postgres \ - -e POSTGRES_USER=postgres \ - -e POSTGRES_PASSWORD=postgres \ - -e POSTGRES_DB=postgres \ - -p 5432:5432 \ - postgres-plpgsql-check:latest - # Wait for postgres to be ready - for _ in {1..30}; do - if docker exec postgres pg_isready -U postgres; then - break - fi - sleep 1 - done - - # For Windows, use the action since PostgreSQL Docker image doesn't support Windows containers - - name: Setup postgres (Windows) - if: runner.os == 'Windows' - id: postgres - uses: ikalnytskyi/action-setup-postgres@v7 + - name: Setup Postgres + uses: ./.github/actions/setup-postgres - name: 🧪 Run Tests run: cargo test --release