diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index a16d60ca..0a7a4f91 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -8,6 +8,13 @@ "ilspycmd" ], "rollForward": false + }, + "h5-compiler": { + "version": "24.11.53871", + "commands": [ + "h5" + ], + "rollForward": false } } -} \ No newline at end of file +} diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..4ce633c2 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,5 @@ +# TLDR; + +# Brief Details + +# How Do The Tests Prove This Works? \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..56206437 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,257 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + workflow_dispatch: + +env: + DOTNET_VERSION: '9.0.x' + DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + DOTNET_CLI_TELEMETRY_OPTOUT: true + +jobs: + build: + name: Build + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: | + 8.0.x + ${{ env.DOTNET_VERSION }} + + - name: Restore .NET tools + run: | + dotnet tool restore + # Verify h5 tool is available + dotnet tool run h5 --version || echo "h5 tool check (may fail if no version flag)" + + - name: Cache NuGet packages + uses: actions/cache@v4 + with: + path: ~/.nuget/packages + key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} + restore-keys: | + ${{ runner.os }}-nuget- + + - name: Restore dependencies + run: dotnet restore + + - name: Build + run: dotnet build --no-restore -c Release + + # Tests that only need SQLite (no Docker) + unit-tests: + name: Unit Tests + runs-on: ubuntu-latest + needs: build + strategy: + fail-fast: false + matrix: + project: + - DataProvider/DataProvider.Tests + - DataProvider/DataProvider.Example.Tests + - Lql/Lql.Tests + - Lql/LqlCli.SQLite.Tests + - Migration/Migration.Tests + - Sync/Sync.Tests + - Sync/Sync.SQLite.Tests + steps: + - uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + + - name: Cache NuGet packages + uses: actions/cache@v4 + with: + path: ~/.nuget/packages + key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} + restore-keys: | + ${{ runner.os }}-nuget- + + - name: Restore + run: dotnet restore ${{ matrix.project }} + + - name: Test + run: dotnet test ${{ matrix.project }} --no-restore --verbosity normal --logger "trx;LogFileName=test-results.trx" + + - name: Upload test results + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-results-unit-${{ strategy.job-index }} + path: '**/TestResults/*.trx' + + # API integration tests (SQLite, no Docker) + api-tests: + name: API Tests + runs-on: ubuntu-latest + needs: build + strategy: + fail-fast: false + matrix: + project: + - Gatekeeper/Gatekeeper.Api.Tests + - Samples/Clinical/Clinical.Api.Tests + - Samples/Scheduling/Scheduling.Api.Tests + - Samples/Dashboard/Dashboard.Web.Tests + steps: + - uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: | + 8.0.x + ${{ env.DOTNET_VERSION }} + + - name: Restore .NET tools + run: dotnet tool restore + + - name: Cache NuGet packages + uses: actions/cache@v4 + with: + path: ~/.nuget/packages + key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} + restore-keys: | + ${{ runner.os }}-nuget- + + - name: Restore + run: dotnet restore ${{ matrix.project }} + + - name: Test + run: dotnet test ${{ matrix.project }} --no-restore --verbosity normal --logger "trx;LogFileName=test-results.trx" + + - name: Upload test results + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-results-api-${{ strategy.job-index }} + path: '**/TestResults/*.trx' + + # Tests that need Docker for Postgres (via Testcontainers) + postgres-tests: + name: Postgres Tests + runs-on: ubuntu-latest + needs: build + strategy: + fail-fast: false + matrix: + project: + - Sync/Sync.Postgres.Tests + - Sync/Sync.Integration.Tests + - Sync/Sync.Http.Tests + steps: + - uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ env.DOTNET_VERSION }} + + - name: Cache NuGet packages + uses: actions/cache@v4 + with: + path: ~/.nuget/packages + key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} + restore-keys: | + ${{ runner.os }}-nuget- + + - name: Restore + run: dotnet restore ${{ matrix.project }} + + - name: Test + run: dotnet test ${{ matrix.project }} --no-restore --verbosity normal --logger "trx;LogFileName=test-results.trx" + env: + # Testcontainers will automatically use the Docker daemon + TESTCONTAINERS_RYUK_DISABLED: false + + - name: Upload test results + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-results-postgres-${{ strategy.job-index }} + path: '**/TestResults/*.trx' + + # Dashboard E2E tests (need Playwright browser) + e2e-tests: + name: Dashboard E2E Tests + runs-on: ubuntu-latest + needs: build + steps: + - uses: actions/checkout@v4 + + - name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + dotnet-version: | + 8.0.x + ${{ env.DOTNET_VERSION }} + + - name: Restore .NET tools + run: dotnet tool restore + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Cache NuGet packages + uses: actions/cache@v4 + with: + path: ~/.nuget/packages + key: ${{ runner.os }}-nuget-${{ hashFiles('**/*.csproj') }} + restore-keys: | + ${{ runner.os }}-nuget- + + - name: Restore Dashboard.Web + run: dotnet restore Samples/Dashboard/Dashboard.Web + + - name: Build Dashboard.Web (downloads React vendor files) + run: dotnet build Samples/Dashboard/Dashboard.Web -c Release --no-restore + + - name: Build Sync projects (required for Sync E2E tests) + run: | + dotnet build Samples/Clinical/Clinical.Sync -c Release + dotnet build Samples/Scheduling/Scheduling.Sync -c Release + + - name: Build Integration Tests (includes wwwroot copy) + run: dotnet build Samples/Dashboard/Dashboard.Integration.Tests -c Release + + - name: Install Playwright browsers + run: dotnet tool install --global Microsoft.Playwright.CLI && playwright install --with-deps chromium + + - name: Verify wwwroot files + run: | + echo "=== Dashboard.Web wwwroot (source) ===" + ls -la Samples/Dashboard/Dashboard.Web/wwwroot/js/ || echo "Dashboard.Web js folder not found" + ls -la Samples/Dashboard/Dashboard.Web/wwwroot/js/vendor/ || echo "Dashboard.Web vendor folder not found" + echo "=== Integration Tests wwwroot (output) ===" + ls -la Samples/Dashboard/Dashboard.Integration.Tests/bin/Release/net9.0/wwwroot/js/ || echo "Integration Tests js folder not found" + ls -la Samples/Dashboard/Dashboard.Integration.Tests/bin/Release/net9.0/wwwroot/js/vendor/ || echo "Integration Tests vendor folder not found" + + - name: Test + run: dotnet test Samples/Dashboard/Dashboard.Integration.Tests -c Release --no-build --verbosity normal --logger "trx;LogFileName=test-results.trx" + + - name: Upload test results + uses: actions/upload-artifact@v4 + if: always() + with: + name: test-results-e2e + path: '**/TestResults/*.trx' + + - name: Upload Playwright traces + uses: actions/upload-artifact@v4 + if: failure() + with: + name: playwright-traces + path: '**/playwright-traces/**' diff --git a/.gitignore b/.gitignore index 55ca622b..6e397495 100644 --- a/.gitignore +++ b/.gitignore @@ -1,29 +1,11 @@ -# Build results +# ═══════════════════════════════════════════════════════════════════════════════ +# .NET Build Output +# ═══════════════════════════════════════════════════════════════════════════════ bin/ obj/ out/ publish/ - -# User-specific files -*.rsuser -*.suo -*.user -*.userosscache -*.sln.docstates - -# Visual Studio Code -.vscode/settings.json -.vscode/tasks.json -.vscode/extensions.json -.history/ - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Mono auto generated files -mono_crash.* - -# Build results +artifacts/ [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ @@ -39,41 +21,23 @@ bld/ [Ll]og/ [Ll]ogs/ -# Visual Studio cache/options directory -.vs/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUnit -*.VisualState.xml -TestResult.xml -nunit-*.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ - -# ASP.NET Scaffolding -ScaffoldingReadMe.txt +# ═══════════════════════════════════════════════════════════════════════════════ +# Visual Studio +# ═══════════════════════════════════════════════════════════════════════════════ +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates +*.userprefs -# StyleCop -StyleCopReport.xml +# Cache and options +.vs/ +*.[Cc]ache +!?*.[Cc]ache/ -# Files built by Visual Studio -*_i.c -*_p.c -*_h.h +# Build files *.ilk *.meta *.obj @@ -100,58 +64,60 @@ StyleCopReport.xml *.svclog *.scc -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler +# Profiler *.psess *.vsp *.vspx *.sap -# Visual Studio Trace Files +# Trace files *.e2e -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# TeamCity is a build add-in -_TeamCity* +# LightSwitch +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions -# DotCover is a Code Coverage Tool -*.dotCover +# ═══════════════════════════════════════════════════════════════════════════════ +# VS Code +# ═══════════════════════════════════════════════════════════════════════════════ +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace +.history/ -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json +# ═══════════════════════════════════════════════════════════════════════════════ +# JetBrains Rider +# ═══════════════════════════════════════════════════════════════════════════════ +.idea/ +*.sln.iml -# Coverlet is a free, cross platform Code Coverage Tool +# ═══════════════════════════════════════════════════════════════════════════════ +# Test Results & Coverage +# ═══════════════════════════════════════════════════════════════════════════════ +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* +*.VisualState.xml +TestResult.xml +nunit-*.xml +BenchmarkDotNet.Artifacts/ +coverage/ +coverage-report/ +coverage-results/ coverage*.json coverage*.xml coverage*.info - -# Visual Studio code coverage results *.coverage *.coveragexml +*.dotCover +.axoCover/* +!.axoCover/settings.json # NCrunch _NCrunch_* @@ -162,47 +128,73 @@ nCrunchTemp_* *.mm.* AutoTest.Net/ -# Web workbench (sass) -.sass-cache/ +# ═══════════════════════════════════════════════════════════════════════════════ +# .NET Core / NuGet +# ═══════════════════════════════════════════════════════════════════════════════ +project.lock.json +project.fragment.lock.json -# Installshield output folder -[Ee]xpress/ +# ═══════════════════════════════════════════════════════════════════════════════ +# Mono +# ═══════════════════════════════════════════════════════════════════════════════ +mono_crash.* -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html +# ═══════════════════════════════════════════════════════════════════════════════ +# Visual C++ +# ═══════════════════════════════════════════════════════════════════════════════ +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c +*_i.c +*_p.c +*_h.h -# Click-Once directory -publish/ +# ═══════════════════════════════════════════════════════════════════════════════ +# Visual Studio 6 +# ═══════════════════════════════════════════════════════════════════════════════ +*.plg +*.opt +*.vbw +*.vbp +*.dsw +*.dsp -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj +# ═══════════════════════════════════════════════════════════════════════════════ +# SQL Server +# ═══════════════════════════════════════════════════════════════════════════════ +*.mdf +*.ldf +*.ndf -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these files may be revealed. +# ═══════════════════════════════════════════════════════════════════════════════ +# Azure +# ═══════════════════════════════════════════════════════════════════════════════ *.azurePubxml - -# Microsoft Azure Build Output csx/ *.build.csdef - -# Microsoft Azure Emulator ecf/ rcf/ +ASALocalRun/ + +# ═══════════════════════════════════════════════════════════════════════════════ +# Publishing +# ═══════════════════════════════════════════════════════════════════════════════ +*.[Pp]ublish.xml +*.pubxml +*.publishproj -# Windows Store app package directories and files +# ═══════════════════════════════════════════════════════════════════════════════ +# Windows Store +# ═══════════════════════════════════════════════════════════════════════════════ AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml @@ -211,95 +203,52 @@ _pkginfo.txt *.appxbundle *.appxupload -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!?*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -CordovaApp.projitems +# Windows Installer +*.cab +*.msi +*.msix +*.msm +*.msp -# SQL Server files -*.mdf -*.ldf -*.ndf +# ═══════════════════════════════════════════════════════════════════════════════ +# ReSharper +# ═══════════════════════════════════════════════════════════════════════════════ +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings -*.rptproj.rsuser -*- [Bb]ackup.rdl -*- [Bb]ackup ([0-9]).rdl -*- [Bb]ackup ([0-9][0-9]).rdl +# ═══════════════════════════════════════════════════════════════════════════════ +# TeamCity +# ═══════════════════════════════════════════════════════════════════════════════ +_TeamCity* +# ═══════════════════════════════════════════════════════════════════════════════ # Microsoft Fakes +# ═══════════════════════════════════════════════════════════════════════════════ FakesAssemblies/ -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio 6 auto-generated project file (contains which files were open etc.) -*.vbp - -# Visual Studio 6 workspace and project file (working project files containing files to include in project) -*.dsw -*.dsp +# ═══════════════════════════════════════════════════════════════════════════════ +# Documentation Tools +# ═══════════════════════════════════════════════════════════════════════════════ +# DocProject +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html -# Visual Studio 6 technical files -*.ncb -*.aps +# GhostDoc +*.GhostDoc.xml -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions +# ASP.NET Scaffolding +ScaffoldingReadMe.txt +# ═══════════════════════════════════════════════════════════════════════════════ +# Other Tools +# ═══════════════════════════════════════════════════════════════════════════════ # Paket dependency manager .paket/paket.exe paket-files/ @@ -307,110 +256,136 @@ paket-files/ # FAKE - F# Make .fake/ -# CodeRush personal settings +# CodeRush .cr/personal -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - # Tabs Studio *.tss -# Telerik's JustMock configuration file +# Telerik JustMock *.jmconfig -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs +# NVidia Nsight +*.nvuser -# OpenCover UI analysis results -OpenCover/ +# MFractors (Xamarin) +.mfractor/ -# Azure Stream Analytics local run output -ASALocalRun/ +# Ionide (F# VS Code) +.ionide/ -# MSBuild Binary and Structured Log -*.binlog +# Fody +FodyWeavers.xsd -# NVidia Nsight GPU debugger configuration file -*.nvuser +# TFS 2012 Local Workspace +$tf/ -# MFractors (Xamarin productivity tool) working folder -.mfractor/ +# Guidance Automation Toolkit +*.gpState -# Local History for Visual Studio -.localhistory/ +# Chutzpah +_Chutzpah* -# Visual Studio History (VSHistory) files -.vshistory/ +# StyleCop +StyleCopReport.xml -# BeatPulse healthcheck temp database -healthchecksdb +# OpenCover +OpenCover/ -# Backup folder for Package Reference Convert tool in Visual Studio 2017 -MigrationBackup/ +# MSBuild Binary Log +*.binlog -# Ionide (cross platform F# VS Code extension) temporary files -.ionide/ +# ═══════════════════════════════════════════════════════════════════════════════ +# Web Development +# ═══════════════════════════════════════════════════════════════════════════════ +# Node.js +node_modules/ +.ntvs_analysis.dat -# Fody - auto-generated XML schema -FodyWeavers.xsd +# Sass +.sass-cache/ -# VS Code files for those working on multiple tools -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -*.code-workspace +# Blazor WebAssembly +**/wwwroot/_framework/ +**/wwwroot/css/*.min.css +**/wwwroot/js/*.min.js -# Local History for Visual Studio Code -.history/ +# ═══════════════════════════════════════════════════════════════════════════════ +# Python +# ═══════════════════════════════════════════════════════════════════════════════ +__pycache__/ +*.pyc -# Windows Installer files from build outputs -*.cab -*.msi -*.msix -*.msm -*.msp +# ═══════════════════════════════════════════════════════════════════════════════ +# Business Intelligence +# ═══════════════════════════════════════════════════════════════════════════════ +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl -# JetBrains Rider -.idea/ -*.sln.iml +# BizTalk +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs +# ═══════════════════════════════════════════════════════════════════════════════ # macOS +# ═══════════════════════════════════════════════════════════════════════════════ .DS_Store .AppleDouble .LSOverride -# Project-specific -invoices.db -*.generated.sql - -# Blazor WebAssembly publish folder -**/wwwroot/_framework/ -**/wwwroot/css/*.min.css -**/wwwroot/js/*.min.js - -# Temporary files +# ═══════════════════════════════════════════════════════════════════════════════ +# Temporary & Backup Files +# ═══════════════════════════════════════════════════════════════════════════════ *.tmp *.bak *.swp *~ +~$* +ClientBin/ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs +healthchecksdb +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +CordovaApp.projitems +MigrationBackup/ +.localhistory/ +.vshistory/ +Generated_Code/ +[Ee]xpress/ -coverage/ -coverage-report/ -coverage-results/ +# ═══════════════════════════════════════════════════════════════════════════════ +# PROJECT-SPECIFIC: DataProvider +# ═══════════════════════════════════════════════════════════════════════════════ +# SQLite databases (created at build time and by tests) +*.db +# Generated SQL files from DataProvider +*.generated.sql +# Generated C# code from DataProvider source generator +*.g.cs +# Build timestamps +.timestamp + +# Dashboard vendor JS (downloaded at build time) Samples/Dashboard/Dashboard.Web/wwwroot/js/vendor/ -*.db +# Dashboard compiled JS (H5/Bridge output) +Samples/Dashboard/Dashboard.Web/wwwroot/js/Dashboard.js +Samples/Dashboard/Dashboard.Web/wwwroot/js/Dashboard.min.js +Samples/Dashboard/Dashboard.Web/wwwroot/js/index.html \ No newline at end of file diff --git a/Agents.md b/Agents.md index 7d73c46f..7fa52990 100644 --- a/Agents.md +++ b/Agents.md @@ -1,77 +1,56 @@ # CLAUDE.md -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -## Multi-Agent Coordination (Too Many Cooks) -- Keep your key! It's critical. Do not lose it! -- Check messages regularly, lock files before editing, unlock after -- Don't edit locked files; signal intent via plans and messages -- Coordinator: keep delegating via messages. Worker: keep asking for tasks via messages -- Clean up expired locks routinely -- Do not use Git unless asked by user - -Medical: All medical data MUST conform to the [FHIR spec](https://build.fhir.org/resourcelist.html). - -## Build Commands -```bash -dotnet build DataProvider.sln # Build entire solution -dotnet test # Run all tests -dotnet test --filter "FullyQualifiedName~ClassName" # Run specific test class -dotnet test --filter "FullyQualifiedName~MethodName" # Run single test -dotnet csharpier . # Format all code (run periodically) +## Multi-Agent (Too Many Cooks) +- Keep your key! Don't lose it! +- Check messages, lock files before editing, unlock after +- Don't edit locked files; telegraph intent via plans/messages +- Coordinator: delegate. Worker: ask for tasks. Update plans constantly. + +## Coding Rules + +- **NEVER THROW** - Return `Result`. Wrap failures in try/catch +- **No casting/!** - Pattern match on type only +- **NO GIT** - Source control is illegal +- **No suppressing warnings** - Illegal +- **No raw SQL inserts/updates** - Use generated extensions +- **Use DataProvider Migrations to spin up DBs** - ⛔️ SQL for creating db schema = ILLEGAL (schema.sql = ILLEGAL). Use the Migration.CLI with YAML. This is the ONLY valid tool to migrate dbs unless the app itself spins up the migrations in code. +- **NO CLASSES** - Records + static methods (FP style) +- **Copious ILogger** - Especially sync projects +- **NO INTERFACES** - Use `Action`/`Func` +- **Expressions over assignments** +- **Named parameters** - No ordinal calls +- **Close type hierarchies** - Private constructors: +```csharp +public abstract partial record Result { private Result() { } } ``` - -DO NOT USE GIT!!! <-- ⛔️ Source control is illegal for you 🙅🏼 - -## Architecture Overview - -This repository contains two complementary projects: - -**DataProvider** - Source generator creating compile-time safe extension methods from SQL files -- Core library in `DataProvider/DataProvider/` - base types, config records, code generation -- Database-specific implementations: `DataProvider.SQLite/`, `DataProvider.SqlServer/` -- Uses ANTLR grammars for SQL parsing (`Parsing/*.g4` files) -- Generates extension methods on `IDbConnection` and `IDbTransaction` -- Routinely format all C# code with `dotnet csharpier .` - -**LQL (Lambda Query Language)** - Functional DSL that transpiles to SQL -- Core transpiler in `Lql/Lql/` - ANTLR grammar, pipeline steps, AST -- Database dialects: `Lql.SQLite/`, `Lql.SqlServer/`, `Lql.Postgres/` -- CLI tool: `LqlCli.SQLite/` -- Browser playground: `Lql.Browser/` - -**Shared Libraries** in `Other/`: -- `Results/` - `Result` type for functional error handling -- `Selecta/` - SQL parsing and AST utilities - -## Coding Rules (CRITICAL) - -- **NEVER THROW EXCEPTIONS** - Always return `Result` for fallible operations. Wrap anything that can fail in try/catch -- **NO CLASSES** - Use records and static methods. FP style with pure static methods -- **Copious logging with ILogger** - Especially in the sync projects -- **NO INTERFACES** - Use `Action` or `Func` for abstractions -- **AVOID ASSIGNMENTS** - Use expressions where possible -- **Static extension methods on IDbConnection and IDbTransaction only** - No classes for data access -- **Test at the highest level** - Avoid mocks. Only full integration testing -- **Always use type aliases (using) for result types** - Don't write like this: `new Result.Ok` -- **No singletons** - Inject `Func` into static methods -- **Immutable types!** - Use records. Don't use `List`. Use `ImmutableList` `FrozenSet` or `ImmutableArray` -- **NO REGEX** - Parse SQL with ANTLR .g4 grammars or SqlParserCS library -- **All public members require XMLDOC** - Except in test projects -- **Keep files under 450 LOC** -- **One type per file** (except small records) -- **No commented-out code** - Delete it -- **No consecutive Console.WriteLine** - Use single string interpolation -- **No placeholders** - If incomplete, leave LOUD compilation error with TODO -- **Never use Fluent Assertions** - -## Project Configuration - -- .NET 9.0, C# latest with nullable enabled -- All warnings as errors (TreatWarningsAsErrors=true) -- Central config in `Directory.Build.props` - don't duplicate in .csproj files -- xUnit for testing with Moq - -## Code Generation Note - -This is a code generation project. Don't generate code manually that is the responsibility of the generator. Check for existing types/methods before creating new ones. +- **Extension methods on IDbConnection/IDbTransaction only** +- **Pattern match, don't if** - Switch expressions on type +- **No skipping tests** - Failing = OK, Skip = illegal +- **E2E tests only** - No mocks, integration testing +- **Type aliases for Results** - `using XResult = Result` +- **Immutable** - Records, `ImmutableList`, `FrozenSet`, `ImmutableArray` +- **NO REGEX** - ANTLR or SqlParserCS +- **XMLDOC on public members** - Except tests +- **< 450 LOC per file** +- **No commented code** - Delete it +- **No placeholders** - Leave compile errors with TODO + +## Testing +- E2E with zero mocking +- 100% coverage, Stryker score 70%+ +- Medical data: [FHIR spec](https://build.fhir.org/resourcelist.html) + +## Architecture + +| Component | Path | Purpose | +|-----------|------|---------| +| DataProvider | `DataProvider/` | Source gen for SQL -> extension methods | +| LQL | `Lql/` | Lambda Query Language -> SQL transpiler | +| Sync | `Sync/` | Offline-first bidirectional sync | +| Gatekeeper | `Gatekeeper/` | WebAuthn + RBAC auth | +| Samples | `Samples/` | Clinical, Scheduling, Dashboard | + +## Config +- .NET 9.0, C# latest, nullable, warnings as errors +- Central config in `Directory.Build.props` +- Format: `dotnet csharpier .` diff --git a/CLAUDE.md b/CLAUDE.md index 90e25294..71596ef1 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,112 +1,57 @@ # CLAUDE.md -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. - -# Rules - -## Multi-Agent Coordination (Too Many Cooks) -- Keep your key! It's critical. Do not lose it! -- Check messages regularly, lock files before editing, unlock after -- Don't edit locked files; signal intent via plans and messages -- Coordinator: keep delegating via messages. Worker: keep asking for tasks via messages -- Telegraph EVERYTHING with messages and plan updates -- Clean up expired locks routinely +## Multi-Agent (Too Many Cooks) +- Keep your key! Don't lose it! +- Check messages, lock files before editing, unlock after +- Don't edit locked files; telegraph intent via plans/messages +- Coordinator: delegate. Worker: ask for tasks. Update plans constantly. ## Coding Rules -- **NEVER THROW EXCEPTIONS** - Always return `Result` for fallible operations. Wrap anything that can fail in try/catch -- **No casting or using ! for nulls** - Only pattern matching on type -- **DO NOT USE GIT** <-- ⛔️ Source control is illegal for you 🙅🏼 -- **Do not supress analyzer warnings/errors** <-- Illegal -- **No direct CREATE TABLE or other SQL to create schema** - Use the DataProvider Migrations! -- **No direct SQL inserts/Updates!** - Generate extensions for inserts/updates with DataProvider -- **NO CLASSES** - Use records and static methods. FP style with pure static methods -- **Copious logging with ILogger** - Especially in the sync projects -- **NO INTERFACES** - Use `Action` or `Func` for abstractions -- **AVOID ASSIGNMENTS** - Use expressions where possible -- **You MUST close type hierarchies** - Make the constructor restricted so it is not possible to create new implementations from the base type. Eg: +- **NEVER THROW** - Return `Result`. Wrap failures in try/catch +- **No casting/!** - Pattern match on type only +- **NO GIT** - Source control is illegal +- **No suppressing warnings** - Illegal +- **No raw SQL inserts/updates** - Use generated extensions +- **Use DataProvider Migrations to spin up DBs** - ⛔️ SQL for creating db schema = ILLEGAL (schema.sql = ILLEGAL). Use the Migration.CLI with YAML. This is the ONLY valid tool to migrate dbs unless the app itself spins up the migrations in code. +- **NO CLASSES** - Records + static methods (FP style) +- **Copious ILogger** - Especially sync projects +- **NO INTERFACES** - Use `Action`/`Func` +- **Expressions over assignments** +- **Routinely format with csharpier** - `dotnet csharpier .` <- In root folder +- **Named parameters** - No ordinal calls +- **Close type hierarchies** - Private constructors: ```csharp -public abstract partial record Result -{ - // [...] - /// - private Result() { } - // [...] -} -`` -- **Static extension methods on IDbConnection and IDbTransaction only** - No classes for data access -- **Don't use statements like if** - use pattern matching switch expressions on type -⛔️ wrong -```csharp -if (triggerResult is Result.Error triggerErr) +public abstract partial record Result { private Result() { } } ``` -- **Skipping tests = ⛔️ ILLEGAL** - Failing tests = OK. Aggressively unskip tests -- **Test at the highest level** - Avoid mocks. Only full integration testing -- **Always use type aliases (using) for result types** - Don't write like this: `new Result.Ok` -- **No singletons** - Inject `Func` into static methods -- **Immutable types!** - Use records. Don't use `List`. Use `ImmutableList` `FrozenSet` or `ImmutableArray` -- **NO REGEX** - Parse SQL with ANTLR .g4 grammars or SqlParserCS library -- **All public members require XMLDOC** - Except in test projects -- **Keep files under 450 LOC** -- **One type per file** (except small records) -- **No commented-out code** - Delete it -- **No consecutive Console.WriteLine** - Use single string interpolation -- **No placeholders** - If incomplete, leave LOUD compilation error with TODO -- **Never use Fluent Assertions** +- **Extension methods on IDbConnection/IDbTransaction only** +- **Pattern match, don't if** - Switch expressions on type +- **No skipping tests** - Failing = OK, Skip = illegal +- **E2E tests only** - No mocks, integration testing +- **Type aliases for Results** - `using XResult = Result` +- **Immutable** - Records, `ImmutableList`, `FrozenSet`, `ImmutableArray` +- **NO REGEX** - ANTLR or SqlParserCS +- **XMLDOC on public members** - Except tests +- **< 450 LOC per file** +- **No commented code** - Delete it +- **No placeholders** - Leave compile errors with TODO ## Testing -- Use e2e tests with zero mocking where possible -- Fall back on unit testing only when absolutely necessary -- Create MEANINGFUL tests that test REAL WORLD use cases -- All projects must have 100% test coverage and a Stryker Mutator testing score of 70% or above. Use [Stryker Mutator](https://stryker-mutator.io/docs/stryker-net/getting-started/) as the ultimate arbiter of test quality - -## Architecture Overview - -This repository contains four major components: - -**DataProvider** - Source generator creating compile-time safe extension methods from SQL files -- Core library in `DataProvider/DataProvider/` - base types, config records, code generation -- Database-specific implementations: `DataProvider.SQLite/`, `DataProvider.SqlServer/` -- Uses ANTLR grammars for SQL parsing (`Parsing/*.g4` files) -- Generates extension methods on `IDbConnection` and `IDbTransaction` -- Routinely format all C# code with `dotnet csharpier .` - -**LQL (Lambda Query Language)** - Functional DSL that transpiles to SQL -- Core transpiler in `Lql/Lql/` - ANTLR grammar, pipeline steps, AST -- Database dialects: `Lql.SQLite/`, `Lql.SqlServer/`, `Lql.Postgres/` -- CLI tool: `LqlCli.SQLite/` -- Browser playground: `Lql.Browser/` - -**Sync Framework** - Offline-first bidirectional synchronization -- Core engine in `Sync/Sync/` - SyncCoordinator, ConflictResolver, BatchManager -- Database implementations: `Sync.SQLite/`, `Sync.Postgres/` -- HTTP layer: `Sync.Http/` - REST endpoints with SSE subscriptions -- Key components: TriggerGenerator, ChangeApplier, MappingEngine -- Comprehensive tests: `Sync.Tests/`, `Sync.SQLite.Tests/`, `Sync.Postgres.Tests/` - -**Gatekeeper** - Authentication and authorization microservice -- API in `Gatekeeper/Gatekeeper.Api/` - WebAuthn passkey auth, RBAC, record-level permissions -- Schema in `Gatekeeper/Gatekeeper.Migration/` - Uses DataProvider migrations -- Key files: `TokenService.cs`, `AuthorizationService.cs`, `Program.cs` -- Tests: `Gatekeeper/Gatekeeper.Api.Tests/` - -**Shared Libraries** in `Other/`: -- `Results/` - `Result` type for functional error handling -- `Selecta/` - SQL parsing and AST utilities - -**Samples** - Healthcare microservices demonstrating the suite -- `Samples/Clinical/` - FHIR-compliant clinical API (Patient, Encounter, Condition) -- `Samples/Scheduling/` - FHIR-compliant scheduling API (Practitioner, Appointment) -- `Samples/Dashboard/` - React/H5 dashboard -- Medical: All medical data MUST conform to the [FHIR spec](https://build.fhir.org/resourcelist.html). - -## Project Configuration - -- .NET 9.0, C# latest with nullable enabled -- All warnings as errors (TreatWarningsAsErrors=true) -- Central config in `Directory.Build.props` - don't duplicate in .csproj files -- xUnit for testing with Moq - -## Code Generation Note - -This is a code generation project. Don't generate code manually that is the responsibility of the generator. Check for existing types/methods before creating new ones. +- E2E with zero mocking +- 100% coverage, Stryker score 70%+ +- Medical data: [FHIR spec](https://build.fhir.org/resourcelist.html) + +## Architecture + +| Component | Path | Purpose | +|-----------|------|---------| +| DataProvider | `DataProvider/` | Source gen for SQL -> extension methods | +| LQL | `Lql/` | Lambda Query Language -> SQL transpiler | +| Sync | `Sync/` | Offline-first bidirectional sync | +| Gatekeeper | `Gatekeeper/` | WebAuthn + RBAC auth | +| Samples | `Samples/` | Clinical, Scheduling, Dashboard | + +## Config +- .NET 9.0, C# latest, nullable, warnings as errors +- Central config in `Directory.Build.props` +- Format: `dotnet csharpier .` diff --git a/CodeAnalysis.ruleset b/CodeAnalysis.ruleset index 426b589f..df992b21 100644 --- a/CodeAnalysis.ruleset +++ b/CodeAnalysis.ruleset @@ -12,8 +12,15 @@ + + + + + + + @@ -69,8 +76,6 @@ - - @@ -207,8 +212,6 @@ - - diff --git a/DataProvider.sln b/DataProvider.sln index 63727602..3a9342c3 100644 --- a/DataProvider.sln +++ b/DataProvider.sln @@ -13,8 +13,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lql.SqlServer", "Lql\Lql.Sq EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lql.Tests", "Lql\Lql.Tests\Lql.Tests.csproj", "{707C273D-CCC9-4CF3-B234-F54B2AB3D178}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LqlCli", "Lql\LqlCli.SQLite\LqlCli.csproj", "{980E4F1D-520B-441C-B6E4-9249E49BD247}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LqlCli.SQLite.Tests", "Lql\LqlCli.SQLite.Tests\LqlCli.SQLite.Tests.csproj", "{DC406D52-3A4B-4632-AD67-462875C067D3}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Lql.Postgres", "Lql\Lql.Postgres\Lql.Postgres.csproj", "{9DF737C9-6EE5-4255-85C9-65337350DFDD}" @@ -73,18 +71,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Scheduling.Api", "Samples\S EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Scheduling.Sync", "Samples\Scheduling\Scheduling.Sync\Scheduling.Sync.csproj", "{7782890E-712E-4658-8BF2-0DC5794A87AC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.Web", "Samples\Dashboard\Dashboard.Web\Dashboard.Web.csproj", "{B4D83D1B-D454-499D-9775-F1FA1F50A4C5}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.Web.Tests", "Samples\Dashboard\Dashboard.Web.Tests\Dashboard.Web.Tests.csproj", "{FFD87B38-0A7B-47AA-AD31-F76CB9ED15DB}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Clinical.Api.Tests", "Samples\Clinical\Clinical.Api.Tests\Clinical.Api.Tests.csproj", "{8131E980-CA39-4BAD-9ADE-34E6597BD00F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Scheduling.Api.Tests", "Samples\Scheduling\Scheduling.Api.Tests\Scheduling.Api.Tests.csproj", "{C23F467D-B5F1-400D-9EEA-96E3F467BAB7}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Dashboard", "Dashboard", "{B03CA193-C175-FB88-B41C-CBBC0E037C7E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.Web.Tests.Runner", "Samples\Dashboard\Dashboard.Web.Tests.Runner\Dashboard.Web.Tests.Runner.csproj", "{9FD2FBE2-7210-4977-94F6-4B30A5A2BEB7}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Misc", "Misc", "{C841F5C2-8F30-5BE9-ECA6-260644CF6F9F}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Selecta", "Other\Selecta\Selecta.csproj", "{BE9AC443-C15D-4962-A8D2-0CCD328E6B68}" @@ -97,13 +89,23 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Gatekeeper", "Gatekeeper", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gatekeeper.Api", "Gatekeeper\Gatekeeper.Api\Gatekeeper.Api.csproj", "{4EB6CC28-7D1B-4E39-80F2-84CA4494AF23}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gatekeeper.Migration", "Gatekeeper\Gatekeeper.Migration\Gatekeeper.Migration.csproj", "{D4FA15C4-B541-4060-8993-DD64667C95E9}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gatekeeper.Api.Tests", "Gatekeeper\Gatekeeper.Api.Tests\Gatekeeper.Api.Tests.csproj", "{2FD305AC-927E-4D24-9FA6-923C30E4E4A8}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Clinical", "Clinical", "{A6BB2AFE-065B-0F4D-CD68-D95721F016DB}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Migration.Cli", "Migration\Migration.Cli\Migration.Cli.csproj", "{57572A45-33CD-4928-9C30-13480AEDB313}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataProvider.Postgres.Cli", "DataProvider\DataProvider.Postgres.Cli\DataProvider.Postgres.Cli.csproj", "{A8A70E6D-1D43-437F-9971-44A4FA1BDD74}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Schema.Export.Cli", "Migration\Schema.Export.Cli\Schema.Export.Cli.csproj", "{0858FE19-C59B-4A77-B76E-7053E8AFCC8D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Authorization", "Samples\Shared\Authorization\Authorization.csproj", "{CA395494-F072-4A5B-9DD4-950530A69E0E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LqlCli.SQLite", "Lql\LqlCli.SQLite\LqlCli.SQLite.csproj", "{1AE87774-E914-40BC-95BA-56FB45D78C0D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LqlWebsite", "Lql\LqlWebsite\LqlWebsite.csproj", "{6AB2EA96-4A75-49DB-AC65-B247BBFAE9A3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.Web", "Samples\Dashboard\Dashboard.Web\Dashboard.Web.csproj", "{A82453CD-8E3C-44B7-A78F-97F392016385}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scheduling", "Scheduling", "{123EF49C-90D7-6BDF-2ECE-985BBA3B8036}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Dashboard.Web.Tests", "Samples\Dashboard\Dashboard.Web.Tests\Dashboard.Web.Tests.csproj", "{25C125F3-B766-4DCD-8032-DB89818FFBC3}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -163,18 +165,6 @@ Global {707C273D-CCC9-4CF3-B234-F54B2AB3D178}.Release|x64.Build.0 = Release|Any CPU {707C273D-CCC9-4CF3-B234-F54B2AB3D178}.Release|x86.ActiveCfg = Release|Any CPU {707C273D-CCC9-4CF3-B234-F54B2AB3D178}.Release|x86.Build.0 = Release|Any CPU - {980E4F1D-520B-441C-B6E4-9249E49BD247}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {980E4F1D-520B-441C-B6E4-9249E49BD247}.Debug|Any CPU.Build.0 = Debug|Any CPU - {980E4F1D-520B-441C-B6E4-9249E49BD247}.Debug|x64.ActiveCfg = Debug|Any CPU - {980E4F1D-520B-441C-B6E4-9249E49BD247}.Debug|x64.Build.0 = Debug|Any CPU - {980E4F1D-520B-441C-B6E4-9249E49BD247}.Debug|x86.ActiveCfg = Debug|Any CPU - {980E4F1D-520B-441C-B6E4-9249E49BD247}.Debug|x86.Build.0 = Debug|Any CPU - {980E4F1D-520B-441C-B6E4-9249E49BD247}.Release|Any CPU.ActiveCfg = Release|Any CPU - {980E4F1D-520B-441C-B6E4-9249E49BD247}.Release|Any CPU.Build.0 = Release|Any CPU - {980E4F1D-520B-441C-B6E4-9249E49BD247}.Release|x64.ActiveCfg = Release|Any CPU - {980E4F1D-520B-441C-B6E4-9249E49BD247}.Release|x64.Build.0 = Release|Any CPU - {980E4F1D-520B-441C-B6E4-9249E49BD247}.Release|x86.ActiveCfg = Release|Any CPU - {980E4F1D-520B-441C-B6E4-9249E49BD247}.Release|x86.Build.0 = Release|Any CPU {DC406D52-3A4B-4632-AD67-462875C067D3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DC406D52-3A4B-4632-AD67-462875C067D3}.Debug|Any CPU.Build.0 = Debug|Any CPU {DC406D52-3A4B-4632-AD67-462875C067D3}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -475,30 +465,6 @@ Global {7782890E-712E-4658-8BF2-0DC5794A87AC}.Release|x64.Build.0 = Release|Any CPU {7782890E-712E-4658-8BF2-0DC5794A87AC}.Release|x86.ActiveCfg = Release|Any CPU {7782890E-712E-4658-8BF2-0DC5794A87AC}.Release|x86.Build.0 = Release|Any CPU - {B4D83D1B-D454-499D-9775-F1FA1F50A4C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B4D83D1B-D454-499D-9775-F1FA1F50A4C5}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B4D83D1B-D454-499D-9775-F1FA1F50A4C5}.Debug|x64.ActiveCfg = Debug|Any CPU - {B4D83D1B-D454-499D-9775-F1FA1F50A4C5}.Debug|x64.Build.0 = Debug|Any CPU - {B4D83D1B-D454-499D-9775-F1FA1F50A4C5}.Debug|x86.ActiveCfg = Debug|Any CPU - {B4D83D1B-D454-499D-9775-F1FA1F50A4C5}.Debug|x86.Build.0 = Debug|Any CPU - {B4D83D1B-D454-499D-9775-F1FA1F50A4C5}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B4D83D1B-D454-499D-9775-F1FA1F50A4C5}.Release|Any CPU.Build.0 = Release|Any CPU - {B4D83D1B-D454-499D-9775-F1FA1F50A4C5}.Release|x64.ActiveCfg = Release|Any CPU - {B4D83D1B-D454-499D-9775-F1FA1F50A4C5}.Release|x64.Build.0 = Release|Any CPU - {B4D83D1B-D454-499D-9775-F1FA1F50A4C5}.Release|x86.ActiveCfg = Release|Any CPU - {B4D83D1B-D454-499D-9775-F1FA1F50A4C5}.Release|x86.Build.0 = Release|Any CPU - {FFD87B38-0A7B-47AA-AD31-F76CB9ED15DB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FFD87B38-0A7B-47AA-AD31-F76CB9ED15DB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FFD87B38-0A7B-47AA-AD31-F76CB9ED15DB}.Debug|x64.ActiveCfg = Debug|Any CPU - {FFD87B38-0A7B-47AA-AD31-F76CB9ED15DB}.Debug|x64.Build.0 = Debug|Any CPU - {FFD87B38-0A7B-47AA-AD31-F76CB9ED15DB}.Debug|x86.ActiveCfg = Debug|Any CPU - {FFD87B38-0A7B-47AA-AD31-F76CB9ED15DB}.Debug|x86.Build.0 = Debug|Any CPU - {FFD87B38-0A7B-47AA-AD31-F76CB9ED15DB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FFD87B38-0A7B-47AA-AD31-F76CB9ED15DB}.Release|Any CPU.Build.0 = Release|Any CPU - {FFD87B38-0A7B-47AA-AD31-F76CB9ED15DB}.Release|x64.ActiveCfg = Release|Any CPU - {FFD87B38-0A7B-47AA-AD31-F76CB9ED15DB}.Release|x64.Build.0 = Release|Any CPU - {FFD87B38-0A7B-47AA-AD31-F76CB9ED15DB}.Release|x86.ActiveCfg = Release|Any CPU - {FFD87B38-0A7B-47AA-AD31-F76CB9ED15DB}.Release|x86.Build.0 = Release|Any CPU {8131E980-CA39-4BAD-9ADE-34E6597BD00F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8131E980-CA39-4BAD-9ADE-34E6597BD00F}.Debug|Any CPU.Build.0 = Debug|Any CPU {8131E980-CA39-4BAD-9ADE-34E6597BD00F}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -523,18 +489,6 @@ Global {C23F467D-B5F1-400D-9EEA-96E3F467BAB7}.Release|x64.Build.0 = Release|Any CPU {C23F467D-B5F1-400D-9EEA-96E3F467BAB7}.Release|x86.ActiveCfg = Release|Any CPU {C23F467D-B5F1-400D-9EEA-96E3F467BAB7}.Release|x86.Build.0 = Release|Any CPU - {9FD2FBE2-7210-4977-94F6-4B30A5A2BEB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9FD2FBE2-7210-4977-94F6-4B30A5A2BEB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9FD2FBE2-7210-4977-94F6-4B30A5A2BEB7}.Debug|x64.ActiveCfg = Debug|Any CPU - {9FD2FBE2-7210-4977-94F6-4B30A5A2BEB7}.Debug|x64.Build.0 = Debug|Any CPU - {9FD2FBE2-7210-4977-94F6-4B30A5A2BEB7}.Debug|x86.ActiveCfg = Debug|Any CPU - {9FD2FBE2-7210-4977-94F6-4B30A5A2BEB7}.Debug|x86.Build.0 = Debug|Any CPU - {9FD2FBE2-7210-4977-94F6-4B30A5A2BEB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9FD2FBE2-7210-4977-94F6-4B30A5A2BEB7}.Release|Any CPU.Build.0 = Release|Any CPU - {9FD2FBE2-7210-4977-94F6-4B30A5A2BEB7}.Release|x64.ActiveCfg = Release|Any CPU - {9FD2FBE2-7210-4977-94F6-4B30A5A2BEB7}.Release|x64.Build.0 = Release|Any CPU - {9FD2FBE2-7210-4977-94F6-4B30A5A2BEB7}.Release|x86.ActiveCfg = Release|Any CPU - {9FD2FBE2-7210-4977-94F6-4B30A5A2BEB7}.Release|x86.Build.0 = Release|Any CPU {BE9AC443-C15D-4962-A8D2-0CCD328E6B68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BE9AC443-C15D-4962-A8D2-0CCD328E6B68}.Debug|Any CPU.Build.0 = Debug|Any CPU {BE9AC443-C15D-4962-A8D2-0CCD328E6B68}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -583,18 +537,6 @@ Global {4EB6CC28-7D1B-4E39-80F2-84CA4494AF23}.Release|x64.Build.0 = Release|Any CPU {4EB6CC28-7D1B-4E39-80F2-84CA4494AF23}.Release|x86.ActiveCfg = Release|Any CPU {4EB6CC28-7D1B-4E39-80F2-84CA4494AF23}.Release|x86.Build.0 = Release|Any CPU - {D4FA15C4-B541-4060-8993-DD64667C95E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D4FA15C4-B541-4060-8993-DD64667C95E9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D4FA15C4-B541-4060-8993-DD64667C95E9}.Debug|x64.ActiveCfg = Debug|Any CPU - {D4FA15C4-B541-4060-8993-DD64667C95E9}.Debug|x64.Build.0 = Debug|Any CPU - {D4FA15C4-B541-4060-8993-DD64667C95E9}.Debug|x86.ActiveCfg = Debug|Any CPU - {D4FA15C4-B541-4060-8993-DD64667C95E9}.Debug|x86.Build.0 = Debug|Any CPU - {D4FA15C4-B541-4060-8993-DD64667C95E9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D4FA15C4-B541-4060-8993-DD64667C95E9}.Release|Any CPU.Build.0 = Release|Any CPU - {D4FA15C4-B541-4060-8993-DD64667C95E9}.Release|x64.ActiveCfg = Release|Any CPU - {D4FA15C4-B541-4060-8993-DD64667C95E9}.Release|x64.Build.0 = Release|Any CPU - {D4FA15C4-B541-4060-8993-DD64667C95E9}.Release|x86.ActiveCfg = Release|Any CPU - {D4FA15C4-B541-4060-8993-DD64667C95E9}.Release|x86.Build.0 = Release|Any CPU {2FD305AC-927E-4D24-9FA6-923C30E4E4A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2FD305AC-927E-4D24-9FA6-923C30E4E4A8}.Debug|Any CPU.Build.0 = Debug|Any CPU {2FD305AC-927E-4D24-9FA6-923C30E4E4A8}.Debug|x64.ActiveCfg = Debug|Any CPU @@ -607,6 +549,102 @@ Global {2FD305AC-927E-4D24-9FA6-923C30E4E4A8}.Release|x64.Build.0 = Release|Any CPU {2FD305AC-927E-4D24-9FA6-923C30E4E4A8}.Release|x86.ActiveCfg = Release|Any CPU {2FD305AC-927E-4D24-9FA6-923C30E4E4A8}.Release|x86.Build.0 = Release|Any CPU + {57572A45-33CD-4928-9C30-13480AEDB313}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {57572A45-33CD-4928-9C30-13480AEDB313}.Debug|Any CPU.Build.0 = Debug|Any CPU + {57572A45-33CD-4928-9C30-13480AEDB313}.Debug|x64.ActiveCfg = Debug|Any CPU + {57572A45-33CD-4928-9C30-13480AEDB313}.Debug|x64.Build.0 = Debug|Any CPU + {57572A45-33CD-4928-9C30-13480AEDB313}.Debug|x86.ActiveCfg = Debug|Any CPU + {57572A45-33CD-4928-9C30-13480AEDB313}.Debug|x86.Build.0 = Debug|Any CPU + {57572A45-33CD-4928-9C30-13480AEDB313}.Release|Any CPU.ActiveCfg = Release|Any CPU + {57572A45-33CD-4928-9C30-13480AEDB313}.Release|Any CPU.Build.0 = Release|Any CPU + {57572A45-33CD-4928-9C30-13480AEDB313}.Release|x64.ActiveCfg = Release|Any CPU + {57572A45-33CD-4928-9C30-13480AEDB313}.Release|x64.Build.0 = Release|Any CPU + {57572A45-33CD-4928-9C30-13480AEDB313}.Release|x86.ActiveCfg = Release|Any CPU + {57572A45-33CD-4928-9C30-13480AEDB313}.Release|x86.Build.0 = Release|Any CPU + {A8A70E6D-1D43-437F-9971-44A4FA1BDD74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A8A70E6D-1D43-437F-9971-44A4FA1BDD74}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8A70E6D-1D43-437F-9971-44A4FA1BDD74}.Debug|x64.ActiveCfg = Debug|Any CPU + {A8A70E6D-1D43-437F-9971-44A4FA1BDD74}.Debug|x64.Build.0 = Debug|Any CPU + {A8A70E6D-1D43-437F-9971-44A4FA1BDD74}.Debug|x86.ActiveCfg = Debug|Any CPU + {A8A70E6D-1D43-437F-9971-44A4FA1BDD74}.Debug|x86.Build.0 = Debug|Any CPU + {A8A70E6D-1D43-437F-9971-44A4FA1BDD74}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A8A70E6D-1D43-437F-9971-44A4FA1BDD74}.Release|Any CPU.Build.0 = Release|Any CPU + {A8A70E6D-1D43-437F-9971-44A4FA1BDD74}.Release|x64.ActiveCfg = Release|Any CPU + {A8A70E6D-1D43-437F-9971-44A4FA1BDD74}.Release|x64.Build.0 = Release|Any CPU + {A8A70E6D-1D43-437F-9971-44A4FA1BDD74}.Release|x86.ActiveCfg = Release|Any CPU + {A8A70E6D-1D43-437F-9971-44A4FA1BDD74}.Release|x86.Build.0 = Release|Any CPU + {0858FE19-C59B-4A77-B76E-7053E8AFCC8D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0858FE19-C59B-4A77-B76E-7053E8AFCC8D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0858FE19-C59B-4A77-B76E-7053E8AFCC8D}.Debug|x64.ActiveCfg = Debug|Any CPU + {0858FE19-C59B-4A77-B76E-7053E8AFCC8D}.Debug|x64.Build.0 = Debug|Any CPU + {0858FE19-C59B-4A77-B76E-7053E8AFCC8D}.Debug|x86.ActiveCfg = Debug|Any CPU + {0858FE19-C59B-4A77-B76E-7053E8AFCC8D}.Debug|x86.Build.0 = Debug|Any CPU + {0858FE19-C59B-4A77-B76E-7053E8AFCC8D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0858FE19-C59B-4A77-B76E-7053E8AFCC8D}.Release|Any CPU.Build.0 = Release|Any CPU + {0858FE19-C59B-4A77-B76E-7053E8AFCC8D}.Release|x64.ActiveCfg = Release|Any CPU + {0858FE19-C59B-4A77-B76E-7053E8AFCC8D}.Release|x64.Build.0 = Release|Any CPU + {0858FE19-C59B-4A77-B76E-7053E8AFCC8D}.Release|x86.ActiveCfg = Release|Any CPU + {0858FE19-C59B-4A77-B76E-7053E8AFCC8D}.Release|x86.Build.0 = Release|Any CPU + {CA395494-F072-4A5B-9DD4-950530A69E0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CA395494-F072-4A5B-9DD4-950530A69E0E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CA395494-F072-4A5B-9DD4-950530A69E0E}.Debug|x64.ActiveCfg = Debug|Any CPU + {CA395494-F072-4A5B-9DD4-950530A69E0E}.Debug|x64.Build.0 = Debug|Any CPU + {CA395494-F072-4A5B-9DD4-950530A69E0E}.Debug|x86.ActiveCfg = Debug|Any CPU + {CA395494-F072-4A5B-9DD4-950530A69E0E}.Debug|x86.Build.0 = Debug|Any CPU + {CA395494-F072-4A5B-9DD4-950530A69E0E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CA395494-F072-4A5B-9DD4-950530A69E0E}.Release|Any CPU.Build.0 = Release|Any CPU + {CA395494-F072-4A5B-9DD4-950530A69E0E}.Release|x64.ActiveCfg = Release|Any CPU + {CA395494-F072-4A5B-9DD4-950530A69E0E}.Release|x64.Build.0 = Release|Any CPU + {CA395494-F072-4A5B-9DD4-950530A69E0E}.Release|x86.ActiveCfg = Release|Any CPU + {CA395494-F072-4A5B-9DD4-950530A69E0E}.Release|x86.Build.0 = Release|Any CPU + {1AE87774-E914-40BC-95BA-56FB45D78C0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1AE87774-E914-40BC-95BA-56FB45D78C0D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1AE87774-E914-40BC-95BA-56FB45D78C0D}.Debug|x64.ActiveCfg = Debug|Any CPU + {1AE87774-E914-40BC-95BA-56FB45D78C0D}.Debug|x64.Build.0 = Debug|Any CPU + {1AE87774-E914-40BC-95BA-56FB45D78C0D}.Debug|x86.ActiveCfg = Debug|Any CPU + {1AE87774-E914-40BC-95BA-56FB45D78C0D}.Debug|x86.Build.0 = Debug|Any CPU + {1AE87774-E914-40BC-95BA-56FB45D78C0D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1AE87774-E914-40BC-95BA-56FB45D78C0D}.Release|Any CPU.Build.0 = Release|Any CPU + {1AE87774-E914-40BC-95BA-56FB45D78C0D}.Release|x64.ActiveCfg = Release|Any CPU + {1AE87774-E914-40BC-95BA-56FB45D78C0D}.Release|x64.Build.0 = Release|Any CPU + {1AE87774-E914-40BC-95BA-56FB45D78C0D}.Release|x86.ActiveCfg = Release|Any CPU + {1AE87774-E914-40BC-95BA-56FB45D78C0D}.Release|x86.Build.0 = Release|Any CPU + {6AB2EA96-4A75-49DB-AC65-B247BBFAE9A3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6AB2EA96-4A75-49DB-AC65-B247BBFAE9A3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6AB2EA96-4A75-49DB-AC65-B247BBFAE9A3}.Debug|x64.ActiveCfg = Debug|Any CPU + {6AB2EA96-4A75-49DB-AC65-B247BBFAE9A3}.Debug|x64.Build.0 = Debug|Any CPU + {6AB2EA96-4A75-49DB-AC65-B247BBFAE9A3}.Debug|x86.ActiveCfg = Debug|Any CPU + {6AB2EA96-4A75-49DB-AC65-B247BBFAE9A3}.Debug|x86.Build.0 = Debug|Any CPU + {6AB2EA96-4A75-49DB-AC65-B247BBFAE9A3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6AB2EA96-4A75-49DB-AC65-B247BBFAE9A3}.Release|Any CPU.Build.0 = Release|Any CPU + {6AB2EA96-4A75-49DB-AC65-B247BBFAE9A3}.Release|x64.ActiveCfg = Release|Any CPU + {6AB2EA96-4A75-49DB-AC65-B247BBFAE9A3}.Release|x64.Build.0 = Release|Any CPU + {6AB2EA96-4A75-49DB-AC65-B247BBFAE9A3}.Release|x86.ActiveCfg = Release|Any CPU + {6AB2EA96-4A75-49DB-AC65-B247BBFAE9A3}.Release|x86.Build.0 = Release|Any CPU + {A82453CD-8E3C-44B7-A78F-97F392016385}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A82453CD-8E3C-44B7-A78F-97F392016385}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A82453CD-8E3C-44B7-A78F-97F392016385}.Debug|x64.ActiveCfg = Debug|Any CPU + {A82453CD-8E3C-44B7-A78F-97F392016385}.Debug|x64.Build.0 = Debug|Any CPU + {A82453CD-8E3C-44B7-A78F-97F392016385}.Debug|x86.ActiveCfg = Debug|Any CPU + {A82453CD-8E3C-44B7-A78F-97F392016385}.Debug|x86.Build.0 = Debug|Any CPU + {A82453CD-8E3C-44B7-A78F-97F392016385}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A82453CD-8E3C-44B7-A78F-97F392016385}.Release|Any CPU.Build.0 = Release|Any CPU + {A82453CD-8E3C-44B7-A78F-97F392016385}.Release|x64.ActiveCfg = Release|Any CPU + {A82453CD-8E3C-44B7-A78F-97F392016385}.Release|x64.Build.0 = Release|Any CPU + {A82453CD-8E3C-44B7-A78F-97F392016385}.Release|x86.ActiveCfg = Release|Any CPU + {A82453CD-8E3C-44B7-A78F-97F392016385}.Release|x86.Build.0 = Release|Any CPU + {25C125F3-B766-4DCD-8032-DB89818FFBC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {25C125F3-B766-4DCD-8032-DB89818FFBC3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {25C125F3-B766-4DCD-8032-DB89818FFBC3}.Debug|x64.ActiveCfg = Debug|Any CPU + {25C125F3-B766-4DCD-8032-DB89818FFBC3}.Debug|x64.Build.0 = Debug|Any CPU + {25C125F3-B766-4DCD-8032-DB89818FFBC3}.Debug|x86.ActiveCfg = Debug|Any CPU + {25C125F3-B766-4DCD-8032-DB89818FFBC3}.Debug|x86.Build.0 = Debug|Any CPU + {25C125F3-B766-4DCD-8032-DB89818FFBC3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {25C125F3-B766-4DCD-8032-DB89818FFBC3}.Release|Any CPU.Build.0 = Release|Any CPU + {25C125F3-B766-4DCD-8032-DB89818FFBC3}.Release|x64.ActiveCfg = Release|Any CPU + {25C125F3-B766-4DCD-8032-DB89818FFBC3}.Release|x64.Build.0 = Release|Any CPU + {25C125F3-B766-4DCD-8032-DB89818FFBC3}.Release|x86.ActiveCfg = Release|Any CPU + {25C125F3-B766-4DCD-8032-DB89818FFBC3}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -616,7 +654,6 @@ Global {903D712A-27E0-4615-AB93-16083E5B3E3A} = {54B846BA-A27D-B76F-8730-402A5742FF43} {AD717205-C676-4DF9-8095-4583BA342515} = {54B846BA-A27D-B76F-8730-402A5742FF43} {707C273D-CCC9-4CF3-B234-F54B2AB3D178} = {54B846BA-A27D-B76F-8730-402A5742FF43} - {980E4F1D-520B-441C-B6E4-9249E49BD247} = {54B846BA-A27D-B76F-8730-402A5742FF43} {DC406D52-3A4B-4632-AD67-462875C067D3} = {54B846BA-A27D-B76F-8730-402A5742FF43} {9DF737C9-6EE5-4255-85C9-65337350DFDD} = {54B846BA-A27D-B76F-8730-402A5742FF43} {7D4F4EC0-C221-4BC9-8F8C-77BD4A3D39AA} = {43BAF0A3-C050-BE83-B489-7FC6F9FDE235} @@ -642,20 +679,17 @@ Global {4189D963-E5AA-4782-AD78-72FBA9536B59} = {5D20AA90-6969-D8BD-9DCD-8634F4692FDA} {0F990389-7C88-4C7A-99F8-60E5243216FF} = {5D20AA90-6969-D8BD-9DCD-8634F4692FDA} {7782890E-712E-4658-8BF2-0DC5794A87AC} = {5D20AA90-6969-D8BD-9DCD-8634F4692FDA} - {B4D83D1B-D454-499D-9775-F1FA1F50A4C5} = {5D20AA90-6969-D8BD-9DCD-8634F4692FDA} - {FFD87B38-0A7B-47AA-AD31-F76CB9ED15DB} = {5D20AA90-6969-D8BD-9DCD-8634F4692FDA} {8131E980-CA39-4BAD-9ADE-34E6597BD00F} = {5D20AA90-6969-D8BD-9DCD-8634F4692FDA} {C23F467D-B5F1-400D-9EEA-96E3F467BAB7} = {5D20AA90-6969-D8BD-9DCD-8634F4692FDA} {B03CA193-C175-FB88-B41C-CBBC0E037C7E} = {5D20AA90-6969-D8BD-9DCD-8634F4692FDA} - {9FD2FBE2-7210-4977-94F6-4B30A5A2BEB7} = {B03CA193-C175-FB88-B41C-CBBC0E037C7E} {BE9AC443-C15D-4962-A8D2-0CCD328E6B68} = {C841F5C2-8F30-5BE9-ECA6-260644CF6F9F} {392C12C2-ECBA-4728-9D8D-54BD2E10F7ED} = {5E63119C-E70B-5D45-ECC9-8CBACC584223} {83E43658-7186-4E8B-AFD0-BDE5DB7BFB58} = {B03CA193-C175-FB88-B41C-CBBC0E037C7E} {4EB6CC28-7D1B-4E39-80F2-84CA4494AF23} = {048F5F03-6DDC-C04F-70D5-B8139DC8E373} - {D4FA15C4-B541-4060-8993-DD64667C95E9} = {048F5F03-6DDC-C04F-70D5-B8139DC8E373} {2FD305AC-927E-4D24-9FA6-923C30E4E4A8} = {048F5F03-6DDC-C04F-70D5-B8139DC8E373} - {A6BB2AFE-065B-0F4D-CD68-D95721F016DB} = {5D20AA90-6969-D8BD-9DCD-8634F4692FDA} - {123EF49C-90D7-6BDF-2ECE-985BBA3B8036} = {5D20AA90-6969-D8BD-9DCD-8634F4692FDA} + {57572A45-33CD-4928-9C30-13480AEDB313} = {C7F49633-8D5E-7E19-1580-A6459B2EAE66} + {A82453CD-8E3C-44B7-A78F-97F392016385} = {B03CA193-C175-FB88-B41C-CBBC0E037C7E} + {25C125F3-B766-4DCD-8032-DB89818FFBC3} = {B03CA193-C175-FB88-B41C-CBBC0E037C7E} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {53128A75-E7B6-4B83-B079-A309FCC2AD9C} diff --git a/DataProvider/DataProvider.Example.Tests/DataProviderIntegrationTests.cs b/DataProvider/DataProvider.Example.Tests/DataProviderIntegrationTests.cs index e4388ee0..70b88cb3 100644 --- a/DataProvider/DataProvider.Example.Tests/DataProviderIntegrationTests.cs +++ b/DataProvider/DataProvider.Example.Tests/DataProviderIntegrationTests.cs @@ -1,4 +1,3 @@ -using Generated; using Lql.SQLite; using Microsoft.Data.Sqlite; using Selecta; @@ -49,7 +48,7 @@ public async Task GetInvoicesAsync_WithValidData_ReturnsCorrectTypes() // Verify Invoice type and properties Assert.IsType(invoice); - Assert.IsType(invoice.Id); + Assert.IsType(invoice.Id); Assert.IsType(invoice.InvoiceNumber); Assert.IsType(invoice.InvoiceDate); Assert.IsType(invoice.CustomerName); @@ -58,8 +57,8 @@ public async Task GetInvoicesAsync_WithValidData_ReturnsCorrectTypes() // Verify InvoiceLine type and properties Assert.IsType(line); - Assert.IsType(line.LineId); - Assert.IsType(line.InvoiceId); + Assert.IsType(line.LineId); + Assert.IsType(line.InvoiceId); Assert.IsType(line.Description); Assert.IsType(line.Quantity); Assert.IsType(line.UnitPrice); @@ -98,7 +97,7 @@ public async Task GetCustomersLqlAsync_WithValidData_ReturnsCorrectTypes() // Verify Customer type and properties Assert.IsType(customer); - Assert.IsType(customer.Id); + Assert.IsType(customer.Id); Assert.IsType(customer.CustomerName); Assert.IsType(customer.Email); // Phone property not available in generated Customer type @@ -107,8 +106,8 @@ public async Task GetCustomersLqlAsync_WithValidData_ReturnsCorrectTypes() // Verify Address type and properties Assert.IsType
(address); - Assert.IsType(address.AddressId); - Assert.IsType(address.CustomerId); + Assert.IsType(address.AddressId); + Assert.IsType(address.CustomerId); Assert.IsType(address.Street); Assert.IsType(address.City); Assert.IsType(address.State); @@ -123,7 +122,12 @@ public async Task GetOrdersAsync_WithValidData_ReturnsCorrectTypes() await SetupTestDatabase(); // Act - var result = await _connection.GetOrdersAsync(1, "Completed", "2024-01-01", "2024-12-31"); + var result = await _connection.GetOrdersAsync( + "cust-1", + "Completed", + "2024-01-01", + "2024-12-31" + ); // Assert Assert.True(result is OrderListOk, $"Expected Success but got {result.GetType()}"); @@ -137,18 +141,18 @@ public async Task GetOrdersAsync_WithValidData_ReturnsCorrectTypes() // Verify Order type and properties Assert.IsType(order); - Assert.IsType(order.Id); + Assert.IsType(order.Id); Assert.IsType(order.OrderNumber); // OrderDate property not available in generated Order type - Assert.IsType(order.CustomerId); + Assert.IsType(order.CustomerId); Assert.IsType(order.TotalAmount); Assert.IsType(order.Status); Assert.IsAssignableFrom>(order.OrderItems); // Verify OrderItem type and properties Assert.IsType(item); - Assert.IsType(item.ItemId); - Assert.IsType(item.OrderId); + Assert.IsType(item.ItemId); + Assert.IsType(item.OrderId); Assert.IsType(item.ProductName); Assert.IsType(item.Quantity); Assert.IsType(item.Price); @@ -164,7 +168,7 @@ public async Task AllQueries_VerifyCorrectTableNamesGenerated() // Act & Assert - Verify extension methods exist with correct names var invoiceResult = await _connection.GetInvoicesAsync("Acme Corp", null!, null!); var customerResult = await _connection.GetCustomersLqlAsync(null); - var orderResult = await _connection.GetOrdersAsync(1, null!, null!, null!); + var orderResult = await _connection.GetOrdersAsync("cust-1", null!, null!, null!); // All should succeed (this proves the extension methods were generated) Assert.True( @@ -319,7 +323,12 @@ public async Task GetOrdersAsync_WithEmptyDatabase_ReturnsEmpty() await SetupEmptyDatabase(); // Act - var result = await _connection.GetOrdersAsync(1, "Completed", "2024-01-01", "2024-12-31"); + var result = await _connection.GetOrdersAsync( + "cust-1", + "Completed", + "2024-01-01", + "2024-12-31" + ); // Assert Assert.True(result is OrderListOk, $"Expected Success but got {result.GetType()}"); @@ -465,10 +474,13 @@ private async Task SetupTestDatabase() await pragmaCommand.ExecuteNonQueryAsync().ConfigureAwait(false); } + // I don't know why this is here. We're supposed to use Migrations to create the schema and + // inserts/updates are supposed to be extension methods. + // Create all tables var createTablesScript = """ CREATE TABLE IF NOT EXISTS Invoice ( - Id INTEGER PRIMARY KEY, + Id TEXT PRIMARY KEY, InvoiceNumber TEXT NOT NULL, InvoiceDate TEXT NOT NULL, CustomerName TEXT NOT NULL, @@ -479,8 +491,8 @@ Notes TEXT NULL ); CREATE TABLE IF NOT EXISTS InvoiceLine ( - Id INTEGER PRIMARY KEY, - InvoiceId INT NOT NULL, + Id TEXT PRIMARY KEY, + InvoiceId TEXT NOT NULL, Description TEXT NOT NULL, Quantity REAL NOT NULL, UnitPrice REAL NOT NULL, @@ -491,7 +503,7 @@ FOREIGN KEY (InvoiceId) REFERENCES Invoice (Id) ); CREATE TABLE IF NOT EXISTS Customer ( - Id INTEGER PRIMARY KEY, + Id TEXT PRIMARY KEY, CustomerName TEXT NOT NULL, Email TEXT NULL, Phone TEXT NULL, @@ -499,8 +511,8 @@ CreatedDate TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS Address ( - Id INTEGER PRIMARY KEY, - CustomerId INT NOT NULL, + Id TEXT PRIMARY KEY, + CustomerId TEXT NOT NULL, Street TEXT NOT NULL, City TEXT NOT NULL, State TEXT NOT NULL, @@ -510,18 +522,18 @@ FOREIGN KEY (CustomerId) REFERENCES Customer (Id) ); CREATE TABLE IF NOT EXISTS Orders ( - Id INTEGER PRIMARY KEY, + Id TEXT PRIMARY KEY, OrderNumber TEXT NOT NULL, OrderDate TEXT NOT NULL, - CustomerId INT NOT NULL, + CustomerId TEXT NOT NULL, TotalAmount REAL NOT NULL, Status TEXT NOT NULL, FOREIGN KEY (CustomerId) REFERENCES Customer (Id) ); CREATE TABLE IF NOT EXISTS OrderItem ( - Id INTEGER PRIMARY KEY, - OrderId INT NOT NULL, + Id TEXT PRIMARY KEY, + OrderId TEXT NOT NULL, ProductName TEXT NOT NULL, Quantity REAL NOT NULL, Price REAL NOT NULL, @@ -535,36 +547,36 @@ FOREIGN KEY (OrderId) REFERENCES Orders (Id) // Insert comprehensive test data var insertScript = """ - INSERT INTO Invoice (InvoiceNumber, InvoiceDate, CustomerName, CustomerEmail, TotalAmount, DiscountAmount, Notes) VALUES - ('INV-001', '2024-01-15', 'Acme Corp', 'accounting@acme.com', 1250.00, NULL, 'Test invoice'), - ('INV-002', '2024-01-16', 'Acme Corp', 'accounting@acme.com', 850.75, 25.00, NULL), - ('INV-003', '2024-01-17', 'Acme Corp', 'accounting@acme.com', 2100.25, 100.00, 'Large order discount'); - - INSERT INTO InvoiceLine (InvoiceId, Description, Quantity, UnitPrice, Amount, DiscountPercentage, Notes) VALUES - (1, 'Software License', 1.0, 1000.00, 1000.00, NULL, NULL), - (1, 'Support Package', 1.0, 250.00, 250.00, 10.0, 'First year support'), - (2, 'Consulting Hours', 5.0, 150.00, 750.00, NULL, NULL), - (2, 'Travel Expenses', 1.0, 100.75, 100.75, NULL, 'Reimbursement'), - (3, 'Hardware Components', 10.0, 125.50, 1255.00, 5.0, 'Bulk discount'), - (3, 'Installation Service', 3.0, 281.75, 845.25, NULL, NULL); - - INSERT INTO Customer (CustomerName, Email, Phone, CreatedDate) VALUES - ('Acme Corp', 'contact@acme.com', '555-0100', '2024-01-01'), - ('Tech Solutions', 'info@techsolutions.com', '555-0200', '2024-01-02'); - - INSERT INTO Address (CustomerId, Street, City, State, ZipCode, Country) VALUES - (1, '123 Business Ave', 'New York', 'NY', '10001', 'USA'), - (1, '456 Main St', 'Albany', 'NY', '12201', 'USA'), - (2, '789 Tech Blvd', 'San Francisco', 'CA', '94105', 'USA'); - - INSERT INTO Orders (OrderNumber, OrderDate, CustomerId, TotalAmount, Status) VALUES - ('ORD-001', '2024-01-10', 1, 500.00, 'Completed'), - ('ORD-002', '2024-01-11', 2, 750.00, 'Processing'); - - INSERT INTO OrderItem (OrderId, ProductName, Quantity, Price, Subtotal) VALUES - (1, 'Widget A', 2.0, 100.00, 200.00), - (1, 'Widget B', 3.0, 100.00, 300.00), - (2, 'Service Package', 1.0, 750.00, 750.00); + INSERT INTO Invoice (Id, InvoiceNumber, InvoiceDate, CustomerName, CustomerEmail, TotalAmount, DiscountAmount, Notes) VALUES + ('inv-1', 'INV-001', '2024-01-15', 'Acme Corp', 'accounting@acme.com', 1250.00, NULL, 'Test invoice'), + ('inv-2', 'INV-002', '2024-01-16', 'Acme Corp', 'accounting@acme.com', 850.75, 25.00, NULL), + ('inv-3', 'INV-003', '2024-01-17', 'Acme Corp', 'accounting@acme.com', 2100.25, 100.00, 'Large order discount'); + + INSERT INTO InvoiceLine (Id, InvoiceId, Description, Quantity, UnitPrice, Amount, DiscountPercentage, Notes) VALUES + ('line-1', 'inv-1', 'Software License', 1.0, 1000.00, 1000.00, NULL, NULL), + ('line-2', 'inv-1', 'Support Package', 1.0, 250.00, 250.00, 10.0, 'First year support'), + ('line-3', 'inv-2', 'Consulting Hours', 5.0, 150.00, 750.00, NULL, NULL), + ('line-4', 'inv-2', 'Travel Expenses', 1.0, 100.75, 100.75, NULL, 'Reimbursement'), + ('line-5', 'inv-3', 'Hardware Components', 10.0, 125.50, 1255.00, 5.0, 'Bulk discount'), + ('line-6', 'inv-3', 'Installation Service', 3.0, 281.75, 845.25, NULL, NULL); + + INSERT INTO Customer (Id, CustomerName, Email, Phone, CreatedDate) VALUES + ('cust-1', 'Acme Corp', 'contact@acme.com', '555-0100', '2024-01-01'), + ('cust-2', 'Tech Solutions', 'info@techsolutions.com', '555-0200', '2024-01-02'); + + INSERT INTO Address (Id, CustomerId, Street, City, State, ZipCode, Country) VALUES + ('addr-1', 'cust-1', '123 Business Ave', 'New York', 'NY', '10001', 'USA'), + ('addr-2', 'cust-1', '456 Main St', 'Albany', 'NY', '12201', 'USA'), + ('addr-3', 'cust-2', '789 Tech Blvd', 'San Francisco', 'CA', '94105', 'USA'); + + INSERT INTO Orders (Id, OrderNumber, OrderDate, CustomerId, TotalAmount, Status) VALUES + ('ord-1', 'ORD-001', '2024-01-10', 'cust-1', 500.00, 'Completed'), + ('ord-2', 'ORD-002', '2024-01-11', 'cust-2', 750.00, 'Processing'); + + INSERT INTO OrderItem (Id, OrderId, ProductName, Quantity, Price, Subtotal) VALUES + ('item-1', 'ord-1', 'Widget A', 2.0, 100.00, 200.00), + ('item-2', 'ord-1', 'Widget B', 3.0, 100.00, 300.00), + ('item-3', 'ord-2', 'Service Package', 1.0, 750.00, 750.00); """; using var insertCommand = new SqliteCommand(insertScript, _connection); @@ -578,7 +590,7 @@ private async Task SetupEmptyDatabase() // Create tables but don't insert any data - same script as above but without inserts var createTablesScript = """ CREATE TABLE IF NOT EXISTS Invoice ( - Id INTEGER PRIMARY KEY, + Id TEXT PRIMARY KEY, InvoiceNumber TEXT NOT NULL, InvoiceDate TEXT NOT NULL, CustomerName TEXT NOT NULL, @@ -589,8 +601,8 @@ Notes TEXT NULL ); CREATE TABLE IF NOT EXISTS InvoiceLine ( - Id INTEGER PRIMARY KEY, - InvoiceId INT NOT NULL, + Id TEXT PRIMARY KEY, + InvoiceId TEXT NOT NULL, Description TEXT NOT NULL, Quantity REAL NOT NULL, UnitPrice REAL NOT NULL, @@ -601,7 +613,7 @@ FOREIGN KEY (InvoiceId) REFERENCES Invoice (Id) ); CREATE TABLE IF NOT EXISTS Customer ( - Id INTEGER PRIMARY KEY, + Id TEXT PRIMARY KEY, CustomerName TEXT NOT NULL, Email TEXT NULL, Phone TEXT NULL, @@ -609,8 +621,8 @@ CreatedDate TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS Address ( - Id INTEGER PRIMARY KEY, - CustomerId INT NOT NULL, + Id TEXT PRIMARY KEY, + CustomerId TEXT NOT NULL, Street TEXT NOT NULL, City TEXT NOT NULL, State TEXT NOT NULL, @@ -620,18 +632,18 @@ FOREIGN KEY (CustomerId) REFERENCES Customer (Id) ); CREATE TABLE IF NOT EXISTS Orders ( - Id INTEGER PRIMARY KEY, + Id TEXT PRIMARY KEY, OrderNumber TEXT NOT NULL, OrderDate TEXT NOT NULL, - CustomerId INT NOT NULL, + CustomerId TEXT NOT NULL, TotalAmount REAL NOT NULL, Status TEXT NOT NULL, FOREIGN KEY (CustomerId) REFERENCES Customer (Id) ); CREATE TABLE IF NOT EXISTS OrderItem ( - Id INTEGER PRIMARY KEY, - OrderId INT NOT NULL, + Id TEXT PRIMARY KEY, + OrderId TEXT NOT NULL, ProductName TEXT NOT NULL, Quantity REAL NOT NULL, Price REAL NOT NULL, @@ -697,9 +709,12 @@ public async Task PredicateBuilder_Or_E2E_CombinesPredicatesWithOrLogic() // Arrange await SetupTestDatabase(); var predicate = PredicateBuilder.False(); - predicate = predicate.Or(c => c.Id == 1); - predicate = predicate.Or(c => c.Id == 2); - var query = SelectStatement.From("Customer").Where(predicate).OrderBy(c => c.Id); + predicate = predicate.Or(c => c.CustomerName == "Acme Corp"); + predicate = predicate.Or(c => c.CustomerName == "Tech Solutions"); + var query = SelectStatement + .From("Customer") + .Where(predicate) + .OrderBy(c => c.CustomerName); // Act var statement = query.ToSqlStatement(); @@ -709,8 +724,8 @@ public async Task PredicateBuilder_Or_E2E_CombinesPredicatesWithOrLogic() Assert.True(result is CustomerReadOnlyListOk); var customers = ((CustomerReadOnlyListOk)result).Value; Assert.Equal(2, customers.Count); - Assert.Equal(1, customers[0].Id); - Assert.Equal(2, customers[1].Id); + Assert.Equal("Acme Corp", customers[0].CustomerName); + Assert.Equal("Tech Solutions", customers[1].CustomerName); } /// @@ -722,8 +737,8 @@ public async Task PredicateBuilder_And_E2E_CombinesPredicatesWithAndLogic() // Arrange await SetupTestDatabase(); var predicate = PredicateBuilder.True(); - predicate = predicate.And(c => c.Id >= 1); - predicate = predicate.And(c => c.Id <= 1); + predicate = predicate.And(c => c.CustomerName == "Acme Corp"); + predicate = predicate.And(c => c.Email != null); var query = SelectStatement.From("Customer").Where(predicate); // Act @@ -734,7 +749,7 @@ public async Task PredicateBuilder_And_E2E_CombinesPredicatesWithAndLogic() Assert.True(result is CustomerReadOnlyListOk); var customers = ((CustomerReadOnlyListOk)result).Value; Assert.Single(customers); - Assert.Equal(1, customers[0].Id); + Assert.Equal("Acme Corp", customers[0].CustomerName); } /// @@ -746,7 +761,7 @@ public async Task PredicateBuilder_Not_E2E_NegatesPredicateLogic() // Arrange await SetupTestDatabase(); var predicate = PredicateBuilder.True(); - predicate = predicate.And(c => c.Id == 1); + predicate = predicate.And(c => c.CustomerName == "Acme Corp"); predicate = predicate.Not(); var query = SelectStatement.From("Customer").Where(predicate); @@ -758,7 +773,7 @@ public async Task PredicateBuilder_Not_E2E_NegatesPredicateLogic() Assert.True(result is CustomerReadOnlyListOk); var customers = ((CustomerReadOnlyListOk)result).Value; Assert.Single(customers); - Assert.Equal(2, customers[0].Id); + Assert.Equal("Tech Solutions", customers[0].CustomerName); } /// @@ -769,14 +784,14 @@ public async Task PredicateBuilder_DynamicOrConditions_E2E_BuildsSearchFilters() { // Arrange await SetupTestDatabase(); - var searchIds = new[] { 1, 3, 5 }; // Only ID 1 exists in test data + var searchNames = new[] { "Acme Corp", "Unknown Corp", "Missing Inc" }; // Only "Acme Corp" exists in test data var predicate = PredicateBuilder.False(); // Act - simulate building dynamic OR conditions - foreach (var id in searchIds) + foreach (var name in searchNames) { - var tempId = id; // Capture for closure - predicate = predicate.Or(c => c.Id == tempId); + var tempName = name; // Capture for closure + predicate = predicate.Or(c => c.CustomerName == tempName); } var query = SelectStatement.From("Customer").Where(predicate); @@ -786,8 +801,8 @@ public async Task PredicateBuilder_DynamicOrConditions_E2E_BuildsSearchFilters() // Assert Assert.True(result is CustomerReadOnlyListOk); var customers = ((CustomerReadOnlyListOk)result).Value; - Assert.Single(customers); // Only customer with ID 1 exists - Assert.Equal(1, customers[0].Id); + Assert.Single(customers); // Only customer "Acme Corp" exists + Assert.Equal("Acme Corp", customers[0].CustomerName); } /// @@ -801,11 +816,14 @@ public async Task PredicateBuilder_DynamicAndConditions_E2E_BuildsFilterChains() var predicate = PredicateBuilder.True(); // Act - simulate building dynamic AND conditions for filtering - predicate = predicate.And(c => c.Id >= 1); - predicate = predicate.And(c => c.Id <= 2); + predicate = predicate.And(c => c.Id != null); predicate = predicate.And(c => c.Email != null); + predicate = predicate.And(c => c.CustomerName != null); - var query = SelectStatement.From("Customer").Where(predicate).OrderBy(c => c.Id); + var query = SelectStatement + .From("Customer") + .Where(predicate) + .OrderBy(c => c.CustomerName); var statement = query.ToSqlStatement(); var result = _connection.GetRecords(statement, s => s.ToSQLite(), MapCustomer); diff --git a/DataProvider/DataProvider.Example.Tests/GlobalUsings.cs b/DataProvider/DataProvider.Example.Tests/GlobalUsings.cs index 334fba72..7920ea67 100644 --- a/DataProvider/DataProvider.Example.Tests/GlobalUsings.cs +++ b/DataProvider/DataProvider.Example.Tests/GlobalUsings.cs @@ -1,3 +1,4 @@ +global using Generated; // Type aliases for Result types to reduce verbosity in DataProvider.Example.Tests global using CustomerListError = Outcome.Result< System.Collections.Immutable.ImmutableList, diff --git a/DataProvider/DataProvider.Example/DataProvider.Example.csproj b/DataProvider/DataProvider.Example/DataProvider.Example.csproj index 58444216..963dc042 100644 --- a/DataProvider/DataProvider.Example/DataProvider.Example.csproj +++ b/DataProvider/DataProvider.Example/DataProvider.Example.csproj @@ -1,17 +1,24 @@ Exe - EPC12 + EPC12;CA1303;CA1515 true $(MSBuildThisFileDirectory)Example.ruleset CA1303 - CA1303 true + + + + + + + + @@ -25,24 +32,35 @@ + + + + + PreserveNewest + + + + + + + + - + - - + + - - - - + + + + - - - + diff --git a/DataProvider/DataProvider.Example/DataProvider.json b/DataProvider/DataProvider.Example/DataProvider.json index 212e31d2..a436f824 100644 --- a/DataProvider/DataProvider.Example/DataProvider.json +++ b/DataProvider/DataProvider.Example/DataProvider.json @@ -23,7 +23,6 @@ "generateInsert": true, "generateUpdate": true, "generateDelete": true, - "excludeColumns": ["Id"], "primaryKeyColumns": ["Id"] }, { @@ -32,7 +31,6 @@ "generateInsert": true, "generateUpdate": true, "generateDelete": true, - "excludeColumns": ["Id"], "primaryKeyColumns": ["Id"] }, { @@ -41,7 +39,6 @@ "generateInsert": true, "generateUpdate": true, "generateDelete": true, - "excludeColumns": ["Id"], "primaryKeyColumns": ["Id"] }, { @@ -50,7 +47,6 @@ "generateInsert": true, "generateUpdate": true, "generateDelete": true, - "excludeColumns": ["Id"], "primaryKeyColumns": ["Id"] }, { @@ -59,7 +55,6 @@ "generateInsert": true, "generateUpdate": true, "generateDelete": true, - "excludeColumns": ["Id"], "primaryKeyColumns": ["Id"] }, { @@ -68,7 +63,6 @@ "generateInsert": true, "generateUpdate": true, "generateDelete": true, - "excludeColumns": ["Id"], "primaryKeyColumns": ["Id"] } ], diff --git a/DataProvider/DataProvider.Example/DatabaseManager.cs b/DataProvider/DataProvider.Example/DatabaseManager.cs index d4673497..004528ef 100644 --- a/DataProvider/DataProvider.Example/DatabaseManager.cs +++ b/DataProvider/DataProvider.Example/DatabaseManager.cs @@ -64,7 +64,7 @@ private static async Task CreateSchemaAsync(SqliteConnection connection) using var command = new SqliteCommand( """ CREATE TABLE IF NOT EXISTS Invoice ( - Id INTEGER PRIMARY KEY, + Id TEXT PRIMARY KEY, InvoiceNumber TEXT NOT NULL, InvoiceDate TEXT NOT NULL, CustomerName TEXT NOT NULL, @@ -75,8 +75,8 @@ Notes TEXT NULL ); CREATE TABLE IF NOT EXISTS InvoiceLine ( - Id INTEGER PRIMARY KEY, - InvoiceId SMALLINT NOT NULL, + Id TEXT PRIMARY KEY, + InvoiceId TEXT NOT NULL, Description TEXT NOT NULL, Quantity REAL NOT NULL, UnitPrice REAL NOT NULL, @@ -87,7 +87,7 @@ FOREIGN KEY (InvoiceId) REFERENCES Invoice (Id) ); CREATE TABLE IF NOT EXISTS Customer ( - Id INTEGER PRIMARY KEY, + Id TEXT PRIMARY KEY, CustomerName TEXT NOT NULL, Email TEXT NULL, Phone TEXT NULL, @@ -95,8 +95,8 @@ CreatedDate TEXT NOT NULL ); CREATE TABLE IF NOT EXISTS Address ( - Id INTEGER PRIMARY KEY, - CustomerId SMALLINT NOT NULL, + Id TEXT PRIMARY KEY, + CustomerId TEXT NOT NULL, Street TEXT NOT NULL, City TEXT NOT NULL, State TEXT NOT NULL, @@ -106,18 +106,18 @@ FOREIGN KEY (CustomerId) REFERENCES Customer (Id) ); CREATE TABLE IF NOT EXISTS Orders ( - Id INTEGER PRIMARY KEY, + Id TEXT PRIMARY KEY, OrderNumber TEXT NOT NULL, OrderDate TEXT NOT NULL, - CustomerId SMALLINT NOT NULL, + CustomerId TEXT NOT NULL, TotalAmount REAL NOT NULL, Status TEXT NOT NULL, FOREIGN KEY (CustomerId) REFERENCES Customer (Id) ); CREATE TABLE IF NOT EXISTS OrderItem ( - Id INTEGER PRIMARY KEY, - OrderId SMALLINT NOT NULL, + Id TEXT PRIMARY KEY, + OrderId TEXT NOT NULL, ProductName TEXT NOT NULL, Quantity REAL NOT NULL, Price REAL NOT NULL, diff --git a/DataProvider/DataProvider.Example/GlobalUsings.cs b/DataProvider/DataProvider.Example/GlobalUsings.cs index 884bdd6c..a2a45c36 100644 --- a/DataProvider/DataProvider.Example/GlobalUsings.cs +++ b/DataProvider/DataProvider.Example/GlobalUsings.cs @@ -1,5 +1,4 @@ global using Generated; -global using Outcome; global using Selecta; // Type aliases for Result types to reduce verbosity in DataProvider.Example global using BasicOrderListError = Outcome.Result< @@ -32,6 +31,8 @@ System.Collections.Generic.IReadOnlyList, Selecta.SqlError >.Ok, Selecta.SqlError>; +global using IntSqlError = Outcome.Result.Error; +global using IntSqlOk = Outcome.Result.Ok; global using InvoiceListError = Outcome.Result< System.Collections.Immutable.ImmutableList, Selecta.SqlError @@ -40,8 +41,6 @@ System.Collections.Immutable.ImmutableList, Selecta.SqlError >.Ok, Selecta.SqlError>; -global using LongSqlError = Outcome.Result.Error; -global using LongSqlOk = Outcome.Result.Ok; global using OrderListError = Outcome.Result< System.Collections.Immutable.ImmutableList, Selecta.SqlError diff --git a/DataProvider/DataProvider.Example/MapFunctions.cs b/DataProvider/DataProvider.Example/MapFunctions.cs index 03b2137e..0110f808 100644 --- a/DataProvider/DataProvider.Example/MapFunctions.cs +++ b/DataProvider/DataProvider.Example/MapFunctions.cs @@ -16,13 +16,13 @@ public static class MapFunctions /// Order instance public static Generated.Order MapOrder(IDataReader reader) => new( - reader.GetInt64(reader.GetOrdinal("Id")), - reader.GetString(reader.GetOrdinal("OrderNumber")), - reader.GetString(reader.GetOrdinal("OrderDate")), - reader.GetInt64(reader.GetOrdinal("CustomerId")), - reader.GetDouble(reader.GetOrdinal("TotalAmount")), - reader.GetString(reader.GetOrdinal("Status")), - [] + Id: reader.GetString(reader.GetOrdinal("Id")), + OrderNumber: reader.GetString(reader.GetOrdinal("OrderNumber")), + OrderDate: reader.GetString(reader.GetOrdinal("OrderDate")), + CustomerId: reader.GetString(reader.GetOrdinal("CustomerId")), + TotalAmount: reader.GetDouble(reader.GetOrdinal("TotalAmount")), + Status: reader.GetString(reader.GetOrdinal("Status")), + OrderItems: [] ); /// @@ -32,16 +32,16 @@ public static Generated.Order MapOrder(IDataReader reader) => /// Customer instance public static Generated.Customer MapCustomer(IDataReader reader) => new( - reader.GetInt64(reader.GetOrdinal("Id")), - reader.GetString(reader.GetOrdinal("CustomerName")), - reader.IsDBNull(reader.GetOrdinal("Email")) + Id: reader.GetString(reader.GetOrdinal("Id")), + CustomerName: reader.GetString(reader.GetOrdinal("CustomerName")), + Email: reader.IsDBNull(reader.GetOrdinal("Email")) ? null : reader.GetString(reader.GetOrdinal("Email")), - reader.IsDBNull(reader.GetOrdinal("Phone")) + Phone: reader.IsDBNull(reader.GetOrdinal("Phone")) ? null : reader.GetString(reader.GetOrdinal("Phone")), - reader.GetString(reader.GetOrdinal("CreatedDate")), - [] + CreatedDate: reader.GetString(reader.GetOrdinal("CreatedDate")), + Addresss: [] ); /// diff --git a/DataProvider/DataProvider.Example/Program.cs b/DataProvider/DataProvider.Example/Program.cs index e36506c8..b0023572 100644 --- a/DataProvider/DataProvider.Example/Program.cs +++ b/DataProvider/DataProvider.Example/Program.cs @@ -355,7 +355,7 @@ private static void DemonstratePredicateBuilder(SqliteConnection connection) var dynamicResult = connection.GetRecords( SelectStatement .From() - .Where(c => c.Id >= 1) // Simple predicate that works + .Where(c => c.Id != null) // Simple predicate that works .OrderBy(c => c.CustomerName) .ToSqlStatement(), stmt => stmt.ToSQLite(), diff --git a/DataProvider/DataProvider.Example/SampleDataSeeder.cs b/DataProvider/DataProvider.Example/SampleDataSeeder.cs index f5a3f327..91cd90e8 100644 --- a/DataProvider/DataProvider.Example/SampleDataSeeder.cs +++ b/DataProvider/DataProvider.Example/SampleDataSeeder.cs @@ -17,32 +17,43 @@ IDbTransaction transaction ) { // Insert Customers + var customer1Id = Guid.NewGuid().ToString(); var customer1Result = await transaction - .InsertCustomerAsync("Acme Corp", "contact@acme.com", "555-0100", "2024-01-01") + .InsertCustomerAsync( + customer1Id, + "Acme Corp", + "contact@acme.com", + "555-0100", + "2024-01-01" + ) .ConfigureAwait(false); - if (customer1Result is not LongSqlOk customer1Success) + if (customer1Result is not IntSqlOk) return ( flowControl: false, - value: new StringSqlError((customer1Result as LongSqlError)!.Value) + value: new StringSqlError((customer1Result as IntSqlError)!.Value) ); + var customer2Id = Guid.NewGuid().ToString(); var customer2Result = await transaction .InsertCustomerAsync( + customer2Id, "Tech Solutions", "info@techsolutions.com", "555-0200", "2024-01-02" ) .ConfigureAwait(false); - if (customer2Result is not LongSqlOk customer2Success) + if (customer2Result is not IntSqlOk) return ( flowControl: false, - value: new StringSqlError((customer2Result as LongSqlError)!.Value) + value: new StringSqlError((customer2Result as IntSqlError)!.Value) ); // Insert Invoice + var invoiceId = Guid.NewGuid().ToString(); var invoiceResult = await transaction .InsertInvoiceAsync( + invoiceId, "INV-001", "2024-01-15", "Acme Corp", @@ -52,51 +63,54 @@ IDbTransaction transaction "Sample invoice" ) .ConfigureAwait(false); - if (invoiceResult is not LongSqlOk invoiceSuccess) + if (invoiceResult is not IntSqlOk) return ( flowControl: false, - value: new StringSqlError((invoiceResult as LongSqlError)!.Value) + value: new StringSqlError((invoiceResult as IntSqlError)!.Value) ); // Insert InvoiceLines var invoiceLine1Result = await transaction .InsertInvoiceLineAsync( - invoiceSuccess.Value, + Guid.NewGuid().ToString(), + invoiceId, "Software License", - 1.0, + 1, 1000.00, 1000.00, null, null ) .ConfigureAwait(false); - if (invoiceLine1Result is not LongSqlOk) + if (invoiceLine1Result is not IntSqlOk) return ( flowControl: false, - value: new StringSqlError((invoiceLine1Result as LongSqlError)!.Value) + value: new StringSqlError((invoiceLine1Result as IntSqlError)!.Value) ); var invoiceLine2Result = await transaction .InsertInvoiceLineAsync( - invoiceSuccess.Value, + Guid.NewGuid().ToString(), + invoiceId, "Support Package", - 1.0, + 1, 250.00, 250.00, null, "First year" ) .ConfigureAwait(false); - if (invoiceLine2Result is not LongSqlOk) + if (invoiceLine2Result is not IntSqlOk) return ( flowControl: false, - value: new StringSqlError((invoiceLine2Result as LongSqlError)!.Value) + value: new StringSqlError((invoiceLine2Result as IntSqlError)!.Value) ); // Insert Addresses var address1Result = await transaction .InsertAddressAsync( - customer1Success.Value, + Guid.NewGuid().ToString(), + customer1Id, "123 Business Ave", "New York", "NY", @@ -104,15 +118,16 @@ IDbTransaction transaction "USA" ) .ConfigureAwait(false); - if (address1Result is not LongSqlOk) + if (address1Result is not IntSqlOk) return ( flowControl: false, - value: new StringSqlError((address1Result as LongSqlError)!.Value) + value: new StringSqlError((address1Result as IntSqlError)!.Value) ); var address2Result = await transaction .InsertAddressAsync( - customer1Success.Value, + Guid.NewGuid().ToString(), + customer1Id, "456 Main St", "Albany", "NY", @@ -120,15 +135,16 @@ IDbTransaction transaction "USA" ) .ConfigureAwait(false); - if (address2Result is not LongSqlOk) + if (address2Result is not IntSqlOk) return ( flowControl: false, - value: new StringSqlError((address2Result as LongSqlError)!.Value) + value: new StringSqlError((address2Result as IntSqlError)!.Value) ); var address3Result = await transaction .InsertAddressAsync( - customer2Success.Value, + Guid.NewGuid().ToString(), + customer2Id, "789 Tech Blvd", "San Francisco", "CA", @@ -136,63 +152,80 @@ IDbTransaction transaction "USA" ) .ConfigureAwait(false); - if (address3Result is not LongSqlOk) + if (address3Result is not IntSqlOk) return ( flowControl: false, - value: new StringSqlError((address3Result as LongSqlError)!.Value) + value: new StringSqlError((address3Result as IntSqlError)!.Value) ); // Insert Orders + var order1Id = Guid.NewGuid().ToString(); var order1Result = await transaction - .InsertOrdersAsync("ORD-001", "2024-01-10", customer1Success.Value, 500.00, "Completed") + .InsertOrdersAsync(order1Id, "ORD-001", "2024-01-10", customer1Id, 500.00, "Completed") .ConfigureAwait(false); - if (order1Result is not LongSqlOk order1Success) + if (order1Result is not IntSqlOk) return ( flowControl: false, - value: new StringSqlError((order1Result as LongSqlError)!.Value) + value: new StringSqlError((order1Result as IntSqlError)!.Value) ); + var order2Id = Guid.NewGuid().ToString(); var order2Result = await transaction - .InsertOrdersAsync( - "ORD-002", - "2024-01-11", - customer2Success.Value, - 750.00, - "Processing" - ) + .InsertOrdersAsync(order2Id, "ORD-002", "2024-01-11", customer2Id, 750.00, "Processing") .ConfigureAwait(false); - if (order2Result is not LongSqlOk order2Success) + if (order2Result is not IntSqlOk) return ( flowControl: false, - value: new StringSqlError((order2Result as LongSqlError)!.Value) + value: new StringSqlError((order2Result as IntSqlError)!.Value) ); // Insert OrderItems var orderItem1Result = await transaction - .InsertOrderItemAsync(order1Success.Value, "Widget A", 2.0, 100.00, 200.00) + .InsertOrderItemAsync( + Guid.NewGuid().ToString(), + order1Id, + "Widget A", + 2, + 100.00, + 200.00 + ) .ConfigureAwait(false); - if (orderItem1Result is not LongSqlOk) + if (orderItem1Result is not IntSqlOk) return ( flowControl: false, - value: new StringSqlError((orderItem1Result as LongSqlError)!.Value) + value: new StringSqlError((orderItem1Result as IntSqlError)!.Value) ); var orderItem2Result = await transaction - .InsertOrderItemAsync(order1Success.Value, "Widget B", 3.0, 100.00, 300.00) + .InsertOrderItemAsync( + Guid.NewGuid().ToString(), + order1Id, + "Widget B", + 3, + 100.00, + 300.00 + ) .ConfigureAwait(false); - if (orderItem2Result is not LongSqlOk) + if (orderItem2Result is not IntSqlOk) return ( flowControl: false, - value: new StringSqlError((orderItem2Result as LongSqlError)!.Value) + value: new StringSqlError((orderItem2Result as IntSqlError)!.Value) ); var orderItem3Result = await transaction - .InsertOrderItemAsync(order2Success.Value, "Service Package", 1.0, 750.00, 750.00) + .InsertOrderItemAsync( + Guid.NewGuid().ToString(), + order2Id, + "Service Package", + 1, + 750.00, + 750.00 + ) .ConfigureAwait(false); - if (orderItem3Result is not LongSqlOk) + if (orderItem3Result is not IntSqlOk) return ( flowControl: false, - value: new StringSqlError((orderItem3Result as LongSqlError)!.Value) + value: new StringSqlError((orderItem3Result as IntSqlError)!.Value) ); return (flowControl: true, value: new StringSqlOk("Sample data seeded successfully")); diff --git a/DataProvider/DataProvider.Example/example-schema.yaml b/DataProvider/DataProvider.Example/example-schema.yaml new file mode 100644 index 00000000..37d7e343 --- /dev/null +++ b/DataProvider/DataProvider.Example/example-schema.yaml @@ -0,0 +1,146 @@ +name: example +tables: +- name: Invoice + columns: + - name: Id + type: Text + - name: InvoiceNumber + type: Text + - name: InvoiceDate + type: Text + - name: CustomerName + type: Text + - name: CustomerEmail + type: Text + - name: TotalAmount + type: Double + - name: DiscountAmount + type: Double + - name: Notes + type: Text + primaryKey: + name: PK_Invoice + columns: + - Id +- name: InvoiceLine + columns: + - name: Id + type: Text + - name: InvoiceId + type: Text + - name: Description + type: Text + - name: Quantity + type: Double + - name: UnitPrice + type: Double + - name: Amount + type: Double + - name: DiscountPercentage + type: Double + - name: Notes + type: Text + foreignKeys: + - name: FK_InvoiceLine_InvoiceId + columns: + - InvoiceId + referencedTable: Invoice + referencedColumns: + - Id + primaryKey: + name: PK_InvoiceLine + columns: + - Id +- name: Customer + columns: + - name: Id + type: Text + - name: CustomerName + type: Text + - name: Email + type: Text + - name: Phone + type: Text + - name: CreatedDate + type: Text + primaryKey: + name: PK_Customer + columns: + - Id +- name: Address + columns: + - name: Id + type: Text + - name: CustomerId + type: Text + - name: Street + type: Text + - name: City + type: Text + - name: State + type: Text + - name: ZipCode + type: Text + - name: Country + type: Text + foreignKeys: + - name: FK_Address_CustomerId + columns: + - CustomerId + referencedTable: Customer + referencedColumns: + - Id + primaryKey: + name: PK_Address + columns: + - Id +- name: Orders + columns: + - name: Id + type: Text + - name: OrderNumber + type: Text + - name: OrderDate + type: Text + - name: CustomerId + type: Text + - name: TotalAmount + type: Double + - name: Status + type: Text + foreignKeys: + - name: FK_Orders_CustomerId + columns: + - CustomerId + referencedTable: Customer + referencedColumns: + - Id + primaryKey: + name: PK_Orders + columns: + - Id +- name: OrderItem + columns: + - name: Id + type: Text + - name: OrderId + type: Text + - name: ProductName + type: Text + - name: Quantity + type: Double + - name: Price + type: Double + - name: Subtotal + type: Double + foreignKeys: + - name: FK_OrderItem_OrderId + columns: + - OrderId + referencedTable: Orders + referencedColumns: + - Id + primaryKey: + name: PK_OrderItem + columns: + - Id diff --git a/DataProvider/DataProvider.Postgres.Cli/DataProvider.Postgres.Cli.csproj b/DataProvider/DataProvider.Postgres.Cli/DataProvider.Postgres.Cli.csproj new file mode 100644 index 00000000..4a6740cf --- /dev/null +++ b/DataProvider/DataProvider.Postgres.Cli/DataProvider.Postgres.Cli.csproj @@ -0,0 +1,23 @@ + + + Exe + net9.0 + enable + enable + false + false + false + false + false + EPC12;CA2100 + + + + + + + + + + + diff --git a/DataProvider/DataProvider.Postgres.Cli/Program.cs b/DataProvider/DataProvider.Postgres.Cli/Program.cs new file mode 100644 index 00000000..95085a08 --- /dev/null +++ b/DataProvider/DataProvider.Postgres.Cli/Program.cs @@ -0,0 +1,1331 @@ +using System.CommandLine; +using System.Text; +using System.Text.Json; +using DataProvider.CodeGeneration; +using Npgsql; +using Outcome; +using Selecta; + +#pragma warning disable CA1849 // Call async methods when in an async method +#pragma warning disable CA2100 // Review SQL queries for security vulnerabilities + +namespace DataProvider.Postgres.Cli; + +/// +/// PostgreSQL code generation CLI for DataProvider. +/// +internal static class Program +{ + private static readonly JsonSerializerOptions JsonOptions = new() + { + PropertyNameCaseInsensitive = true, + }; + + /// + /// Entry point. + /// + public static async Task Main(string[] args) + { + var projectDir = new Option( + "--project-dir", + description: "Project directory containing sql files and DataProvider.json" + ) + { + IsRequired = true, + }; + var config = new Option("--config", description: "Path to DataProvider.json") + { + IsRequired = true, + }; + var outDir = new Option( + "--out", + description: "Output directory for generated .g.cs files" + ) + { + IsRequired = true, + }; + var root = new RootCommand("DataProvider.Postgres codegen CLI") + { + projectDir, + config, + outDir, + }; + root.SetHandler( + async (DirectoryInfo proj, FileInfo cfg, DirectoryInfo output) => + { + var exit = await RunAsync(proj, cfg, output).ConfigureAwait(false); + Environment.Exit(exit); + }, + projectDir, + config, + outDir + ); + + return await root.InvokeAsync(args).ConfigureAwait(false); + } + + private static async Task RunAsync( + DirectoryInfo projectDir, + FileInfo configFile, + DirectoryInfo outDir + ) + { + try + { + if (!configFile.Exists) + { + Console.WriteLine($"❌ Config not found: {configFile.FullName}"); + return 1; + } + + if (!outDir.Exists) + outDir.Create(); + + var cfgText = await File.ReadAllTextAsync(configFile.FullName).ConfigureAwait(false); + var cfg = JsonSerializer.Deserialize(cfgText, JsonOptions); + if (cfg is null || string.IsNullOrWhiteSpace(cfg.ConnectionString)) + { + Console.WriteLine("❌ DataProvider.json ConnectionString is required"); + return 1; + } + + // Verify DB connection + try + { + await using var conn = new NpgsqlConnection(cfg.ConnectionString); + await conn.OpenAsync().ConfigureAwait(false); + } + catch (Exception ex) + { + Console.WriteLine($"❌ Failed to connect to database: {ex.Message}"); + return 1; + } + + // Gather SQL files + var sqlFiles = Directory.GetFiles( + projectDir.FullName, + "*.sql", + SearchOption.AllDirectories + ); + + var hadErrors = false; + + foreach (var sqlPath in sqlFiles) + { + try + { + var sql = await File.ReadAllTextAsync(sqlPath).ConfigureAwait(false); + if (string.IsNullOrWhiteSpace(sql)) + continue; + + var baseName = Path.GetFileNameWithoutExtension(sqlPath); + if (baseName.EndsWith(".generated", StringComparison.OrdinalIgnoreCase)) + { + baseName = baseName[..^".generated".Length]; + } + if ( + string.Equals(baseName, "schema", StringComparison.OrdinalIgnoreCase) + || baseName.EndsWith("_schema", StringComparison.OrdinalIgnoreCase) + ) + { + continue; + } + + // Parse SQL to extract parameters + var parameters = ExtractParameters(sql); + + // Get column metadata from PostgreSQL + var colsResult = await GetColumnMetadataAsync( + cfg.ConnectionString, + sql, + parameters + ) + .ConfigureAwait(false); + if ( + colsResult + is Result, SqlError>.Error< + IReadOnlyList, + SqlError + > colsError + ) + { + Console.WriteLine($"❌ {colsError.Value.Message}"); + Console.Error.WriteLine( + $"{sqlPath}(1,1): error DP0001: {colsError.Value.Message}" + ); + var errorFile = Path.Combine(outDir.FullName, baseName + ".g.cs"); + var content = + $"// Auto-generated due to SQL error in {sqlPath}\n#error {EscapeForPreprocessor(colsError.Value.Message)}\n"; + await File.WriteAllTextAsync(errorFile, content).ConfigureAwait(false); + hadErrors = true; + continue; + } + + var cols = ( + (Result, SqlError>.Ok< + IReadOnlyList, + SqlError + >)colsResult + ).Value; + + // Generate code + var genResult = GenerateCode(baseName, sql, cols, parameters); + if (genResult is Result.Ok success) + { + var target = Path.Combine(outDir.FullName, baseName + ".g.cs"); + await File.WriteAllTextAsync(target, success.Value).ConfigureAwait(false); + Console.WriteLine($"✅ Generated {target}"); + } + else if (genResult is Result.Error failure) + { + Console.WriteLine($"❌ {failure.Value.Message}"); + Console.Error.WriteLine( + $"{sqlPath}(1,1): error DP0002: {failure.Value.Message}" + ); + var errorFile = Path.Combine(outDir.FullName, baseName + ".g.cs"); + var content = + $"// Auto-generated due to generation error in {sqlPath}\n#error {EscapeForPreprocessor(failure.Value.Message)}\n"; + await File.WriteAllTextAsync(errorFile, content).ConfigureAwait(false); + hadErrors = true; + } + } + catch (Exception ex) + { + Console.WriteLine($"❌ Error processing {sqlPath}: {ex.Message}"); + var baseName = Path.GetFileNameWithoutExtension(sqlPath); + if (baseName.EndsWith(".generated", StringComparison.OrdinalIgnoreCase)) + { + baseName = baseName[..^".generated".Length]; + } + var errorFile = Path.Combine(outDir.FullName, baseName + ".g.cs"); + var content = + $"// Auto-generated due to unexpected error in {sqlPath}\n#error {EscapeForPreprocessor(ex.Message)}\n"; + Console.Error.WriteLine($"{sqlPath}(1,1): error DP0003: {ex.Message}"); + await File.WriteAllTextAsync(errorFile, content).ConfigureAwait(false); + hadErrors = true; + } + } + + // Process table configurations for INSERT/UPDATE generation + if (cfg.Tables is { Count: > 0 }) + { + foreach (var table in cfg.Tables) + { + try + { + var tableCode = await GenerateTableOperationsAsync( + cfg.ConnectionString, + table, + outDir.FullName + ) + .ConfigureAwait(false); + + if (tableCode is Result.Error err) + { + Console.WriteLine($"❌ Table {table.Name}: {err.Value.Message}"); + hadErrors = true; + } + } + catch (Exception ex) + { + Console.WriteLine($"❌ Error processing table {table.Name}: {ex.Message}"); + hadErrors = true; + } + } + } + + return hadErrors ? 1 : 0; + } + catch (Exception ex) + { + Console.WriteLine($"❌ Unexpected error: {ex}"); + return 1; + } + } + + private static async Task> GenerateTableOperationsAsync( + string connectionString, + TableConfigItem table, + string outDir + ) + { + await using var conn = new NpgsqlConnection(connectionString); + await conn.OpenAsync().ConfigureAwait(false); + + // Get column metadata from information_schema + var columns = new List(); + await using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = """ + SELECT c.column_name, c.data_type, c.is_nullable, c.column_default, + COALESCE(c.is_identity, 'NO') as is_identity, + CASE WHEN pk.column_name IS NOT NULL THEN 'YES' ELSE 'NO' END as is_pk + FROM information_schema.columns c + LEFT JOIN ( + SELECT kcu.column_name + FROM information_schema.table_constraints tc + JOIN information_schema.key_column_usage kcu + ON tc.constraint_name = kcu.constraint_name + WHERE tc.table_schema = @schema + AND tc.table_name = @table + AND tc.constraint_type = 'PRIMARY KEY' + ) pk ON pk.column_name = c.column_name + WHERE c.table_schema = @schema AND c.table_name = @table + ORDER BY c.ordinal_position + """; + cmd.Parameters.AddWithValue("schema", table.Schema); + cmd.Parameters.AddWithValue("table", table.Name); + + await using var reader = await cmd.ExecuteReaderAsync().ConfigureAwait(false); + while (await reader.ReadAsync().ConfigureAwait(false)) + { + var colName = reader.GetString(0); + var dataType = reader.GetString(1); + var isNullable = reader.GetString(2) == "YES"; + var colDefault = reader.IsDBNull(3) ? null : reader.GetString(3); + var isIdentity = reader.GetString(4) == "YES"; + var isPk = reader.GetString(5) == "YES"; + + // Skip excluded columns + if (table.ExcludeColumns.Contains(colName, StringComparer.OrdinalIgnoreCase)) + continue; + + columns.Add( + new DatabaseColumn + { + Name = colName, + SqlType = dataType, + CSharpType = MapPostgresTypeToCSharp(dataType, isNullable), + IsNullable = isNullable, + IsPrimaryKey = isPk, + IsIdentity = isIdentity, + IsComputed = + colDefault?.StartsWith("nextval", StringComparison.OrdinalIgnoreCase) + == true, + } + ); + } + } + + if (columns.Count == 0) + { + return new Result.Error( + new SqlError($"No columns found for table {table.Schema}.{table.Name}") + ); + } + + var sb = new StringBuilder(); + var pascalName = ToPascalCase(table.Name); + + // Header + sb.AppendLine("// "); + sb.AppendLine("#nullable enable"); + sb.AppendLine(); + sb.AppendLine("using Npgsql;"); + sb.AppendLine("using Outcome;"); + sb.AppendLine("using Selecta;"); + sb.AppendLine(); + + // Extension class + sb.AppendLine($"/// "); + sb.AppendLine($"/// Generated CRUD operations for {table.Name} table."); + sb.AppendLine($"/// "); + sb.AppendLine($"public static class {pascalName}Extensions"); + sb.AppendLine("{"); + + // Generate INSERT method + if (table.GenerateInsert) + { + GenerateInsertMethod(sb, table, columns, pascalName); + } + + // Generate UPDATE method + if (table.GenerateUpdate) + { + GenerateUpdateMethod(sb, table, columns, pascalName); + } + + // Generate DELETE method + if (table.GenerateDelete) + { + GenerateDeleteMethod(sb, table, columns, pascalName); + } + + // Generate bulk INSERT method + if (table.GenerateBulkInsert) + { + GenerateBulkInsertMethod(sb, table, columns, pascalName); + } + + // Generate bulk UPSERT method + if (table.GenerateBulkUpsert) + { + GenerateBulkUpsertMethod(sb, table, columns, pascalName); + } + + sb.AppendLine("}"); + + var target = Path.Combine(outDir, $"{pascalName}Operations.g.cs"); + await File.WriteAllTextAsync(target, sb.ToString()).ConfigureAwait(false); + Console.WriteLine($"✅ Generated {target}"); + + return new Result.Ok(sb.ToString()); + } + + private static void GenerateInsertMethod( + StringBuilder sb, + TableConfigItem table, + List columns, + string pascalName + ) + { + // Get insertable columns (exclude auto-generated ones) + var insertable = columns.Where(c => !c.IsIdentity && !c.IsComputed).ToList(); + var parameters = string.Join( + ", ", + insertable.Select(c => $"{c.CSharpType} {ToCamelCase(c.Name)}") + ); + + sb.AppendLine(); + sb.AppendLine($" /// "); + sb.AppendLine( + $" /// Inserts a row into {table.Name}. Returns inserted id or null on conflict." + ); + sb.AppendLine($" /// "); + sb.AppendLine( + $" public static async Task> Insert{pascalName}Async(" + ); + sb.AppendLine($" this NpgsqlConnection conn,"); + sb.AppendLine($" {parameters})"); + sb.AppendLine(" {"); + + var colNames = string.Join(", ", insertable.Select(c => c.Name)); + var paramNames = string.Join(", ", insertable.Select(c => $"@{ToCamelCase(c.Name)}")); + + sb.AppendLine($" const string sql = @\""); + sb.AppendLine($" INSERT INTO {table.Schema}.{table.Name} ({colNames})"); + sb.AppendLine($" VALUES ({paramNames})"); + sb.AppendLine($" ON CONFLICT DO NOTHING"); + sb.AppendLine($" RETURNING id\";"); + sb.AppendLine(); + sb.AppendLine(" try"); + sb.AppendLine(" {"); + sb.AppendLine(" await using var cmd = new NpgsqlCommand(sql, conn);"); + + foreach (var col in insertable) + { + var paramName = ToCamelCase(col.Name); + if (col.IsNullable) + { + sb.AppendLine( + $" cmd.Parameters.AddWithValue(\"{paramName}\", {paramName} ?? (object)DBNull.Value);" + ); + } + else + { + sb.AppendLine( + $" cmd.Parameters.AddWithValue(\"{paramName}\", {paramName});" + ); + } + } + + sb.AppendLine(); + sb.AppendLine( + " var result = await cmd.ExecuteScalarAsync().ConfigureAwait(false);" + ); + sb.AppendLine( + " return new Result.Ok(result is Guid g ? g : null);" + ); + sb.AppendLine(" }"); + sb.AppendLine(" catch (Exception ex)"); + sb.AppendLine(" {"); + sb.AppendLine( + " return new Result.Error(SqlError.FromException(ex));" + ); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + } + + private static void GenerateUpdateMethod( + StringBuilder sb, + TableConfigItem table, + List columns, + string pascalName + ) + { + var pkCols = columns.Where(c => c.IsPrimaryKey).ToList(); + var updateable = columns + .Where(c => !c.IsPrimaryKey && !c.IsIdentity && !c.IsComputed) + .ToList(); + + if (pkCols.Count == 0 || updateable.Count == 0) + return; + + var allParams = pkCols.Concat(updateable).ToList(); + var parameters = string.Join( + ", ", + allParams.Select(c => $"{c.CSharpType} {ToCamelCase(c.Name)}") + ); + + sb.AppendLine(); + sb.AppendLine($" /// "); + sb.AppendLine($" /// Updates a row in {table.Name} by primary key."); + sb.AppendLine($" /// "); + sb.AppendLine( + $" public static async Task> Update{pascalName}Async(" + ); + sb.AppendLine($" this NpgsqlConnection conn,"); + sb.AppendLine($" {parameters})"); + sb.AppendLine(" {"); + + var setClauses = string.Join( + ", ", + updateable.Select(c => $"{c.Name} = @{ToCamelCase(c.Name)}") + ); + var whereClauses = string.Join( + " AND ", + pkCols.Select(c => $"{c.Name} = @{ToCamelCase(c.Name)}") + ); + + sb.AppendLine($" const string sql = @\""); + sb.AppendLine($" UPDATE {table.Schema}.{table.Name}"); + sb.AppendLine($" SET {setClauses}"); + sb.AppendLine($" WHERE {whereClauses}\";"); + sb.AppendLine(); + sb.AppendLine(" try"); + sb.AppendLine(" {"); + sb.AppendLine(" await using var cmd = new NpgsqlCommand(sql, conn);"); + + foreach (var col in allParams) + { + var paramName = ToCamelCase(col.Name); + if (col.IsNullable) + { + sb.AppendLine( + $" cmd.Parameters.AddWithValue(\"{paramName}\", {paramName} ?? (object)DBNull.Value);" + ); + } + else + { + sb.AppendLine( + $" cmd.Parameters.AddWithValue(\"{paramName}\", {paramName});" + ); + } + } + + sb.AppendLine(); + sb.AppendLine( + " var rows = await cmd.ExecuteNonQueryAsync().ConfigureAwait(false);" + ); + sb.AppendLine(" return new Result.Ok(rows);"); + sb.AppendLine(" }"); + sb.AppendLine(" catch (Exception ex)"); + sb.AppendLine(" {"); + sb.AppendLine( + " return new Result.Error(SqlError.FromException(ex));" + ); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + } + + private static void GenerateDeleteMethod( + StringBuilder sb, + TableConfigItem table, + List columns, + string pascalName + ) + { + var pkCols = columns.Where(c => c.IsPrimaryKey).ToList(); + if (pkCols.Count == 0) + return; + + var parameters = string.Join( + ", ", + pkCols.Select(c => $"{c.CSharpType} {ToCamelCase(c.Name)}") + ); + + sb.AppendLine(); + sb.AppendLine($" /// "); + sb.AppendLine($" /// Deletes a row from {table.Name} by primary key."); + sb.AppendLine($" /// "); + sb.AppendLine( + $" public static async Task> Delete{pascalName}Async(" + ); + sb.AppendLine($" this NpgsqlConnection conn,"); + sb.AppendLine($" {parameters})"); + sb.AppendLine(" {"); + + var whereClauses = string.Join( + " AND ", + pkCols.Select(c => $"{c.Name} = @{ToCamelCase(c.Name)}") + ); + + sb.AppendLine( + $" const string sql = @\"DELETE FROM {table.Schema}.{table.Name} WHERE {whereClauses}\";" + ); + sb.AppendLine(); + sb.AppendLine(" try"); + sb.AppendLine(" {"); + sb.AppendLine(" await using var cmd = new NpgsqlCommand(sql, conn);"); + + foreach (var col in pkCols) + { + sb.AppendLine( + $" cmd.Parameters.AddWithValue(\"{ToCamelCase(col.Name)}\", {ToCamelCase(col.Name)});" + ); + } + + sb.AppendLine(); + sb.AppendLine( + " var rows = await cmd.ExecuteNonQueryAsync().ConfigureAwait(false);" + ); + sb.AppendLine(" return new Result.Ok(rows);"); + sb.AppendLine(" }"); + sb.AppendLine(" catch (Exception ex)"); + sb.AppendLine(" {"); + sb.AppendLine( + " return new Result.Error(SqlError.FromException(ex));" + ); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + } + + private static void GenerateBulkInsertMethod( + StringBuilder sb, + TableConfigItem table, + List columns, + string pascalName + ) + { + // Get insertable columns (exclude auto-generated ones) + var insertable = columns.Where(c => !c.IsIdentity && !c.IsComputed).ToList(); + if (insertable.Count == 0) + return; + + // Build tuple type for the IEnumerable parameter + var tupleType = string.Join( + ", ", + insertable.Select(c => $"{c.CSharpType} {ToPascalCase(c.Name)}") + ); + + sb.AppendLine(); + sb.AppendLine($" /// "); + sb.AppendLine( + $" /// Bulk inserts rows into {table.Name} using batched multi-row VALUES." + ); + sb.AppendLine($" /// Uses ON CONFLICT DO NOTHING to skip duplicates."); + sb.AppendLine($" /// "); + sb.AppendLine($" /// Open database connection."); + sb.AppendLine($" /// Records to insert as tuples."); + sb.AppendLine( + $" /// Max rows per batch (default 1000)." + ); + sb.AppendLine($" /// Total rows inserted."); + sb.AppendLine( + $" public static async Task> BulkInsert{pascalName}Async(" + ); + sb.AppendLine($" this NpgsqlConnection conn,"); + sb.AppendLine($" IEnumerable<({tupleType})> records,"); + sb.AppendLine($" int batchSize = 1000)"); + sb.AppendLine(" {"); + sb.AppendLine(" var totalInserted = 0;"); + sb.AppendLine($" var batch = new List<({tupleType})>(batchSize);"); + sb.AppendLine(); + sb.AppendLine(" try"); + sb.AppendLine(" {"); + sb.AppendLine(" foreach (var record in records)"); + sb.AppendLine(" {"); + sb.AppendLine(" batch.Add(record);"); + sb.AppendLine(" if (batch.Count >= batchSize)"); + sb.AppendLine(" {"); + sb.AppendLine( + $" var result = await ExecuteBulkInsert{pascalName}BatchAsync(conn, batch).ConfigureAwait(false);" + ); + sb.AppendLine( + " if (result is Result.Error err)" + ); + sb.AppendLine(" return err;"); + sb.AppendLine( + " totalInserted += ((Result.Ok)result).Value;" + ); + sb.AppendLine(" batch.Clear();"); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine(" if (batch.Count > 0)"); + sb.AppendLine(" {"); + sb.AppendLine( + $" var finalResult = await ExecuteBulkInsert{pascalName}BatchAsync(conn, batch).ConfigureAwait(false);" + ); + sb.AppendLine( + " if (finalResult is Result.Error finalErr)" + ); + sb.AppendLine(" return finalErr;"); + sb.AppendLine( + " totalInserted += ((Result.Ok)finalResult).Value;" + ); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine( + " return new Result.Ok(totalInserted);" + ); + sb.AppendLine(" }"); + sb.AppendLine(" catch (Exception ex)"); + sb.AppendLine(" {"); + sb.AppendLine( + " return new Result.Error(SqlError.FromException(ex));" + ); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + sb.AppendLine(); + + // Generate batch execution helper + sb.AppendLine( + $" private static async Task> ExecuteBulkInsert{pascalName}BatchAsync(" + ); + sb.AppendLine($" NpgsqlConnection conn,"); + sb.AppendLine($" List<({tupleType})> batch)"); + sb.AppendLine(" {"); + sb.AppendLine(" if (batch.Count == 0)"); + sb.AppendLine(" return new Result.Ok(0);"); + sb.AppendLine(); + + var colNames = string.Join(", ", insertable.Select(c => c.Name)); + sb.AppendLine( + $" var sql = new System.Text.StringBuilder(\"INSERT INTO {table.Schema}.{table.Name} ({colNames}) VALUES \");" + ); + sb.AppendLine(); + sb.AppendLine(" for (int i = 0; i < batch.Count; i++)"); + sb.AppendLine(" {"); + sb.AppendLine(" if (i > 0) sql.Append(\", \");"); + + // Build VALUES placeholders + var placeholders = string.Join( + ", ", + insertable.Select((c, idx) => $"@p\" + (i * {insertable.Count} + {idx}) + \"") + ); + sb.AppendLine($" sql.Append(\"({placeholders})\");"); + sb.AppendLine(" }"); + sb.AppendLine(" sql.Append(\" ON CONFLICT DO NOTHING\");"); + sb.AppendLine(); + sb.AppendLine(" await using var cmd = new NpgsqlCommand(sql.ToString(), conn);"); + sb.AppendLine(); + sb.AppendLine(" for (int i = 0; i < batch.Count; i++)"); + sb.AppendLine(" {"); + sb.AppendLine(" var rec = batch[i];"); + + for (int i = 0; i < insertable.Count; i++) + { + var col = insertable[i]; + var propName = ToPascalCase(col.Name); + if (col.IsNullable) + { + sb.AppendLine( + $" cmd.Parameters.AddWithValue(\"p\" + (i * {insertable.Count} + {i}), rec.{propName} ?? (object)DBNull.Value);" + ); + } + else + { + sb.AppendLine( + $" cmd.Parameters.AddWithValue(\"p\" + (i * {insertable.Count} + {i}), rec.{propName});" + ); + } + } + + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine(" var rows = await cmd.ExecuteNonQueryAsync().ConfigureAwait(false);"); + sb.AppendLine(" return new Result.Ok(rows);"); + sb.AppendLine(" }"); + } + + private static void GenerateBulkUpsertMethod( + StringBuilder sb, + TableConfigItem table, + List columns, + string pascalName + ) + { + // Get insertable columns (exclude auto-generated ones) + var insertable = columns.Where(c => !c.IsIdentity && !c.IsComputed).ToList(); + var pkCols = columns.Where(c => c.IsPrimaryKey).ToList(); + + if (insertable.Count == 0 || pkCols.Count == 0) + return; + + // Build tuple type for the IEnumerable parameter + var tupleType = string.Join( + ", ", + insertable.Select(c => $"{c.CSharpType} {ToPascalCase(c.Name)}") + ); + + sb.AppendLine(); + sb.AppendLine($" /// "); + sb.AppendLine( + $" /// Bulk upserts rows into {table.Name} using batched multi-row VALUES." + ); + sb.AppendLine($" /// Uses ON CONFLICT DO UPDATE to insert or update existing rows."); + sb.AppendLine($" /// "); + sb.AppendLine($" /// Open database connection."); + sb.AppendLine($" /// Records to upsert as tuples."); + sb.AppendLine( + $" /// Max rows per batch (default 1000)." + ); + sb.AppendLine($" /// Total rows affected."); + sb.AppendLine( + $" public static async Task> BulkUpsert{pascalName}Async(" + ); + sb.AppendLine($" this NpgsqlConnection conn,"); + sb.AppendLine($" IEnumerable<({tupleType})> records,"); + sb.AppendLine($" int batchSize = 1000)"); + sb.AppendLine(" {"); + sb.AppendLine(" var totalAffected = 0;"); + sb.AppendLine($" var batch = new List<({tupleType})>(batchSize);"); + sb.AppendLine(); + sb.AppendLine(" try"); + sb.AppendLine(" {"); + sb.AppendLine(" foreach (var record in records)"); + sb.AppendLine(" {"); + sb.AppendLine(" batch.Add(record);"); + sb.AppendLine(" if (batch.Count >= batchSize)"); + sb.AppendLine(" {"); + sb.AppendLine( + $" var result = await ExecuteBulkUpsert{pascalName}BatchAsync(conn, batch).ConfigureAwait(false);" + ); + sb.AppendLine( + " if (result is Result.Error err)" + ); + sb.AppendLine(" return err;"); + sb.AppendLine( + " totalAffected += ((Result.Ok)result).Value;" + ); + sb.AppendLine(" batch.Clear();"); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine(" if (batch.Count > 0)"); + sb.AppendLine(" {"); + sb.AppendLine( + $" var finalResult = await ExecuteBulkUpsert{pascalName}BatchAsync(conn, batch).ConfigureAwait(false);" + ); + sb.AppendLine( + " if (finalResult is Result.Error finalErr)" + ); + sb.AppendLine(" return finalErr;"); + sb.AppendLine( + " totalAffected += ((Result.Ok)finalResult).Value;" + ); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine( + " return new Result.Ok(totalAffected);" + ); + sb.AppendLine(" }"); + sb.AppendLine(" catch (Exception ex)"); + sb.AppendLine(" {"); + sb.AppendLine( + " return new Result.Error(SqlError.FromException(ex));" + ); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + sb.AppendLine(); + + // Generate batch execution helper + sb.AppendLine( + $" private static async Task> ExecuteBulkUpsert{pascalName}BatchAsync(" + ); + sb.AppendLine($" NpgsqlConnection conn,"); + sb.AppendLine($" List<({tupleType})> batch)"); + sb.AppendLine(" {"); + sb.AppendLine(" if (batch.Count == 0)"); + sb.AppendLine(" return new Result.Ok(0);"); + sb.AppendLine(); + + var colNames = string.Join(", ", insertable.Select(c => c.Name)); + var pkColNames = string.Join(", ", pkCols.Select(c => c.Name)); + var updateCols = insertable.Where(c => !c.IsPrimaryKey).ToList(); + var updateSet = string.Join(", ", updateCols.Select(c => $"{c.Name} = EXCLUDED.{c.Name}")); + + sb.AppendLine( + $" var sql = new System.Text.StringBuilder(\"INSERT INTO {table.Schema}.{table.Name} ({colNames}) VALUES \");" + ); + sb.AppendLine(); + sb.AppendLine(" for (int i = 0; i < batch.Count; i++)"); + sb.AppendLine(" {"); + sb.AppendLine(" if (i > 0) sql.Append(\", \");"); + + // Build VALUES placeholders + var placeholders = string.Join( + ", ", + insertable.Select((c, idx) => $"@p\" + (i * {insertable.Count} + {idx}) + \"") + ); + sb.AppendLine($" sql.Append(\"({placeholders})\");"); + sb.AppendLine(" }"); + + // Add ON CONFLICT DO UPDATE clause + if (updateCols.Count > 0) + { + sb.AppendLine( + $" sql.Append(\" ON CONFLICT ({pkColNames}) DO UPDATE SET {updateSet}\");" + ); + } + else + { + // If all columns are PKs, just do nothing on conflict + sb.AppendLine($" sql.Append(\" ON CONFLICT ({pkColNames}) DO NOTHING\");"); + } + + sb.AppendLine(); + sb.AppendLine(" await using var cmd = new NpgsqlCommand(sql.ToString(), conn);"); + sb.AppendLine(); + sb.AppendLine(" for (int i = 0; i < batch.Count; i++)"); + sb.AppendLine(" {"); + sb.AppendLine(" var rec = batch[i];"); + + for (int i = 0; i < insertable.Count; i++) + { + var col = insertable[i]; + var propName = ToPascalCase(col.Name); + if (col.IsNullable) + { + sb.AppendLine( + $" cmd.Parameters.AddWithValue(\"p\" + (i * {insertable.Count} + {i}), rec.{propName} ?? (object)DBNull.Value);" + ); + } + else + { + sb.AppendLine( + $" cmd.Parameters.AddWithValue(\"p\" + (i * {insertable.Count} + {i}), rec.{propName});" + ); + } + } + + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine(" var rows = await cmd.ExecuteNonQueryAsync().ConfigureAwait(false);"); + sb.AppendLine(" return new Result.Ok(rows);"); + sb.AppendLine(" }"); + } + + private static List ExtractParameters(string sql) + { + var parameters = new List(); + var i = 0; + while (i < sql.Length) + { + if (sql[i] == '@') + { + var start = i + 1; + while ( + start < sql.Length && (char.IsLetterOrDigit(sql[start]) || sql[start] == '_') + ) + { + start++; + } + if (start > i + 1) + { + var paramName = sql[(i + 1)..start]; + if (!parameters.Contains(paramName, StringComparer.OrdinalIgnoreCase)) + { + parameters.Add(paramName); + } + } + i = start; + } + else + { + i++; + } + } + return parameters; + } + + private static async Task< + Result, SqlError> + > GetColumnMetadataAsync(string connectionString, string sql, List parameters) + { + try + { + await using var conn = new NpgsqlConnection(connectionString); + await conn.OpenAsync().ConfigureAwait(false); + + // Replace @params with NULL for metadata query + var metaSql = sql; + foreach (var param in parameters) + { + metaSql = metaSql.Replace($"@{param}", "NULL", StringComparison.OrdinalIgnoreCase); + } + + // Wrap in a CTE to get metadata without executing + var wrappedSql = $"SELECT * FROM ({metaSql}) AS _meta WHERE 1=0"; + + await using var cmd = new NpgsqlCommand(wrappedSql, conn); + await using var reader = await cmd.ExecuteReaderAsync( + System.Data.CommandBehavior.SchemaOnly + ) + .ConfigureAwait(false); + + var schema = reader.GetColumnSchema(); + var columns = new List(); + + foreach (var col in schema) + { + var dbType = col.DataTypeName ?? "text"; + var csharpType = MapPostgresTypeToCSharp(dbType, col.AllowDBNull ?? true); + + columns.Add( + new DatabaseColumn + { + Name = col.ColumnName, + SqlType = dbType, + CSharpType = csharpType, + IsNullable = col.AllowDBNull ?? true, + IsPrimaryKey = col.IsKey ?? false, + IsIdentity = col.IsIdentity ?? false, + IsComputed = col.IsReadOnly ?? false, + } + ); + } + + return new Result, SqlError>.Ok< + IReadOnlyList, + SqlError + >(columns.AsReadOnly()); + } + catch (Exception ex) + { + return new Result, SqlError>.Error< + IReadOnlyList, + SqlError + >(SqlError.FromException(ex)); + } + } + + private static string MapPostgresTypeToCSharp(string pgType, bool isNullable) + { + var baseType = pgType.ToLowerInvariant() switch + { + "uuid" => "Guid", + "boolean" or "bool" => "bool", + "smallint" or "int2" => "short", + "integer" or "int4" or "int" => "int", + "bigint" or "int8" => "long", + "real" or "float4" => "float", + "double precision" or "float8" => "double", + "numeric" or "decimal" or "money" => "decimal", + "date" => "DateOnly", + "time" or "time without time zone" => "TimeOnly", + "time with time zone" or "timetz" => "TimeOnly", + "timestamp" or "timestamp without time zone" => "DateTime", + "timestamp with time zone" or "timestamptz" => "DateTimeOffset", + "interval" => "TimeSpan", + "bytea" => "byte[]", + "text" or "varchar" or "character varying" or "char" or "character" or "name" => + "string", + "json" or "jsonb" => "string", + var t when t.EndsWith("[]", StringComparison.Ordinal) => "string[]", + _ => "string", + }; + + // Add nullable suffix for nullable types (including strings but not arrays) + if (isNullable && !baseType.EndsWith("[]", StringComparison.Ordinal)) + { + return baseType + "?"; + } + + return baseType; + } + + private static Result GenerateCode( + string fileName, + string sql, + IReadOnlyList columns, + List parameters + ) + { + var sb = new StringBuilder(); + var recordName = fileName; + + // Header with all using statements (including type aliases) at the top + sb.AppendLine("// "); + sb.AppendLine("#nullable enable"); + sb.AppendLine(); + sb.AppendLine("using System.Collections.Immutable;"); + sb.AppendLine("using Npgsql;"); + sb.AppendLine("using Outcome;"); + sb.AppendLine("using Selecta;"); + sb.AppendLine(); + // Result type aliases must come after standard usings but before any type definitions + // Use fully qualified names since type aliases don't use namespace context + sb.AppendLine( + $"using {fileName}Result = Outcome.Result, Selecta.SqlError>;" + ); + sb.AppendLine( + $"using {fileName}Ok = Outcome.Result, Selecta.SqlError>.Ok, Selecta.SqlError>;" + ); + sb.AppendLine( + $"using {fileName}Error = Outcome.Result, Selecta.SqlError>.Error, Selecta.SqlError>;" + ); + sb.AppendLine(); + + // Generate record type + sb.AppendLine($"/// "); + sb.AppendLine($"/// Generated record for {fileName} query."); + sb.AppendLine($"/// "); + sb.Append($"public sealed record {recordName}("); + + var first = true; + foreach (var col in columns) + { + if (!first) + sb.Append(", "); + first = false; + + var propName = ToPascalCase(col.Name); + sb.Append($"{col.CSharpType} {propName}"); + } + sb.AppendLine(");"); + sb.AppendLine(); + + // Generate extension method + sb.AppendLine($"/// "); + sb.AppendLine($"/// Extension methods for {fileName} query."); + sb.AppendLine($"/// "); + sb.AppendLine($"public static class {fileName}Extensions"); + sb.AppendLine("{"); + + // SQL constant + sb.AppendLine($" private const string Sql = @\""); + sb.AppendLine(sql.Replace("\"", "\"\"")); + sb.AppendLine("\";"); + sb.AppendLine(); + + // Async method + sb.AppendLine($" /// "); + sb.AppendLine($" /// Executes the {fileName} query."); + sb.AppendLine($" /// "); + sb.Append( + $" public static async Task<{fileName}Result> {fileName}Async(this NpgsqlConnection conn" + ); + + foreach (var param in parameters) + { + var paramType = InferParameterType(param); + sb.Append($", {paramType} {ToCamelCase(param)}"); + } + sb.AppendLine(")"); + sb.AppendLine(" {"); + sb.AppendLine(" try"); + sb.AppendLine(" {"); + sb.AppendLine($" var results = ImmutableList.CreateBuilder<{recordName}>();"); + sb.AppendLine(" await using var cmd = new NpgsqlCommand(Sql, conn);"); + + foreach (var param in parameters) + { + sb.AppendLine( + $" cmd.Parameters.AddWithValue(\"{param}\", {ToCamelCase(param)});" + ); + } + + sb.AppendLine(); + sb.AppendLine( + " await using var reader = await cmd.ExecuteReaderAsync().ConfigureAwait(false);" + ); + sb.AppendLine(" while (await reader.ReadAsync().ConfigureAwait(false))"); + sb.AppendLine(" {"); + sb.AppendLine($" results.Add(Read{recordName}(reader));"); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine($" return new {fileName}Ok(results.ToImmutable());"); + sb.AppendLine(" }"); + sb.AppendLine(" catch (Exception ex)"); + sb.AppendLine(" {"); + sb.AppendLine($" return new {fileName}Error(SqlError.FromException(ex));"); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + sb.AppendLine(); + + // Reader method + sb.AppendLine( + $" private static {recordName} Read{recordName}(NpgsqlDataReader reader) =>" + ); + sb.Append($" new("); + + first = true; + var ordinal = 0; + foreach (var col in columns) + { + if (!first) + sb.Append(", "); + first = false; + + var propName = ToPascalCase(col.Name); + var readExpr = GetReaderExpression(col, ordinal); + sb.Append($"{propName}: {readExpr}"); + ordinal++; + } + sb.AppendLine(");"); + + sb.AppendLine("}"); + + return new Result.Ok(sb.ToString()); + } + + private static string GetReaderExpression(DatabaseColumn col, int ordinal) + { + var nullCheck = col.IsNullable ? $"reader.IsDBNull({ordinal}) ? null : " : ""; + + return col.CSharpType.TrimEnd('?') switch + { + "Guid" => $"{nullCheck}reader.GetGuid({ordinal})", + "bool" => $"{nullCheck}reader.GetBoolean({ordinal})", + "short" => $"{nullCheck}reader.GetInt16({ordinal})", + "int" => $"{nullCheck}reader.GetInt32({ordinal})", + "long" => $"{nullCheck}reader.GetInt64({ordinal})", + "float" => $"{nullCheck}reader.GetFloat({ordinal})", + "double" => $"{nullCheck}reader.GetDouble({ordinal})", + "decimal" => $"{nullCheck}reader.GetDecimal({ordinal})", + "DateOnly" => $"{nullCheck}DateOnly.FromDateTime(reader.GetDateTime({ordinal}))", + "TimeOnly" => $"{nullCheck}TimeOnly.FromTimeSpan(reader.GetTimeSpan({ordinal}))", + "DateTime" => $"{nullCheck}reader.GetDateTime({ordinal})", + "DateTimeOffset" => $"{nullCheck}reader.GetFieldValue({ordinal})", + "TimeSpan" => $"{nullCheck}reader.GetTimeSpan({ordinal})", + "byte[]" => $"{nullCheck}reader.GetFieldValue({ordinal})", + "string[]" => $"reader.GetFieldValue({ordinal})", + _ => $"{nullCheck}reader.GetString({ordinal})", + }; + } + + private static string InferParameterType(string paramName) + { + var lower = paramName.ToLowerInvariant(); + if (lower.EndsWith("id", StringComparison.Ordinal)) + return "Guid"; + if (lower.Contains("limit") || lower.Contains("offset") || lower.Contains("count")) + return "int"; + return "object"; + } + + private static string ToPascalCase(string name) + { + if (string.IsNullOrEmpty(name)) + return name; + + var parts = name.Split('_'); + var sb = new StringBuilder(); + foreach (var part in parts) + { + if (part.Length > 0) + { + sb.Append(char.ToUpperInvariant(part[0])); + if (part.Length > 1) + sb.Append(part[1..].ToLowerInvariant()); + } + } + return sb.ToString(); + } + + private static string ToCamelCase(string name) + { + var pascal = ToPascalCase(name); + if (string.IsNullOrEmpty(pascal)) + return pascal; + return char.ToLowerInvariant(pascal[0]) + pascal[1..]; + } + + private static string EscapeForPreprocessor(string message) + { + if (string.IsNullOrEmpty(message)) + return string.Empty; + var oneLine = message.Replace('\r', ' ').Replace('\n', ' '); + return oneLine.Replace('"', '\''); + } +} + +/// +/// Configuration for PostgreSQL DataProvider code generation. +/// +internal sealed record PostgresDataProviderConfig +{ + /// + /// The connection string to the PostgreSQL database. + /// + public string ConnectionString { get; init; } = string.Empty; + + /// + /// List of query configurations. + /// + public List? Queries { get; init; } + + /// + /// List of table configurations for CRUD generation. + /// + public List? Tables { get; init; } +} + +/// +/// Query configuration. +/// +internal sealed record QueryConfig +{ + /// + /// Query name. + /// + public string Name { get; init; } = string.Empty; + + /// + /// Path to SQL file. + /// + public string SqlFile { get; init; } = string.Empty; +} + +/// +/// Table configuration for CRUD generation. +/// +internal sealed record TableConfigItem +{ + /// + /// Schema name. + /// + public string Schema { get; init; } = "public"; + + /// + /// Table name. + /// + public string Name { get; init; } = string.Empty; + + /// + /// Generate INSERT method. + /// + public bool GenerateInsert { get; init; } + + /// + /// Generate UPDATE method. + /// + public bool GenerateUpdate { get; init; } + + /// + /// Generate DELETE method. + /// + public bool GenerateDelete { get; init; } + + /// + /// Generate bulk INSERT method for batch operations. + /// + public bool GenerateBulkInsert { get; init; } + + /// + /// Generate bulk UPSERT method for batch insert-or-update operations. + /// + public bool GenerateBulkUpsert { get; init; } + + /// + /// Columns to exclude from generation. + /// + public IReadOnlyList ExcludeColumns { get; init; } = Array.Empty(); + + /// + /// Primary key columns. + /// + public IReadOnlyList PrimaryKeyColumns { get; init; } = Array.Empty(); +} diff --git a/DataProvider/DataProvider.SQLite.Cli/Program.cs b/DataProvider/DataProvider.SQLite.Cli/Program.cs index 44001e63..f036f130 100644 --- a/DataProvider/DataProvider.SQLite.Cli/Program.cs +++ b/DataProvider/DataProvider.SQLite.Cli/Program.cs @@ -1,4 +1,5 @@ using System.CommandLine; +using System.Globalization; using System.Text.Json; using DataProvider.CodeGeneration; using DataProvider.SQLite.Parsing; @@ -84,11 +85,42 @@ DirectoryInfo outDir return 1; } - // Verify DB exists and is accessible + // Verify DB exists and is accessible; if empty, run schema file try { using var conn = new Microsoft.Data.Sqlite.SqliteConnection(cfg.ConnectionString); await conn.OpenAsync().ConfigureAwait(false); + + // Check if any tables exist + using var checkCmd = conn.CreateCommand(); + checkCmd.CommandText = + "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"; + var tableCount = Convert.ToInt64( + await checkCmd.ExecuteScalarAsync().ConfigureAwait(false), + CultureInfo.InvariantCulture + ); + + if (tableCount == 0) + { + // Look for build-schema.sql in project directory + var schemaFile = Path.Combine(projectDir.FullName, "build-schema.sql"); + if (File.Exists(schemaFile)) + { + Console.WriteLine($"🔧 Creating database schema from {schemaFile}"); + var schemaSql = await File.ReadAllTextAsync(schemaFile) + .ConfigureAwait(false); + using var schemaCmd = conn.CreateCommand(); + schemaCmd.CommandText = schemaSql; + await schemaCmd.ExecuteNonQueryAsync().ConfigureAwait(false); + Console.WriteLine("✅ Database schema created"); + } + else + { + Console.WriteLine( + $"⚠️ No tables in database and no build-schema.sql found" + ); + } + } } catch (Exception ex) { @@ -138,6 +170,8 @@ DirectoryInfo outDir if ( string.Equals(baseName, "schema", StringComparison.OrdinalIgnoreCase) || baseName.EndsWith("_schema", StringComparison.OrdinalIgnoreCase) + || baseName.EndsWith("-schema", StringComparison.OrdinalIgnoreCase) + || baseName.StartsWith("build-", StringComparison.OrdinalIgnoreCase) ) { // Skip schema files; they're only for DB initialization diff --git a/DataProvider/DataProvider.SQLite/DataProvider.SQLite.csproj b/DataProvider/DataProvider.SQLite/DataProvider.SQLite.csproj index 8d3a6a2f..54192897 100644 --- a/DataProvider/DataProvider.SQLite/DataProvider.SQLite.csproj +++ b/DataProvider/DataProvider.SQLite/DataProvider.SQLite.csproj @@ -8,21 +8,13 @@ SQLite source generator for DataProvider - CA1849;CA2100;EPC13;CA1305;CA1307; + CA1849;CA2100;EPC13;CA1307;CS3021;CS0108 true true - + - - - - MSBuild:Compile - DataProvider.SQLite.Parsing - true - true - diff --git a/DataProvider/DataProvider.SQLite/Parsing/SQLiteLexer.cs b/DataProvider/DataProvider.SQLite/Parsing/SQLiteLexer.cs new file mode 100644 index 00000000..a2949918 --- /dev/null +++ b/DataProvider/DataProvider.SQLite/Parsing/SQLiteLexer.cs @@ -0,0 +1,790 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// ANTLR Version: 4.13.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// Generated from SQLiteLexer.g4 by ANTLR 4.13.1 + +// Unreachable code detected +#pragma warning disable 0162 +// The variable '...' is assigned but its value is never used +#pragma warning disable 0219 +// Missing XML comment for publicly visible type or member '...' +#pragma warning disable 1591 +// Ambiguous reference in cref attribute +#pragma warning disable 419 + +namespace DataProvider.SQLite.Parsing { +using System; +using System.IO; +using System.Text; +using Antlr4.Runtime; +using Antlr4.Runtime.Atn; +using Antlr4.Runtime.Misc; +using DFA = Antlr4.Runtime.Dfa.DFA; + +[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.13.1")] +[System.CLSCompliant(false)] +public partial class SQLiteLexer : Lexer { + protected static DFA[] decisionToDFA; + protected static PredictionContextCache sharedContextCache = new PredictionContextCache(); + public const int + SCOL=1, DOT=2, OPEN_PAR=3, CLOSE_PAR=4, COMMA=5, ASSIGN=6, STAR=7, PLUS=8, + MINUS=9, TILDE=10, PIPE2=11, DIV=12, MOD=13, LT2=14, GT2=15, AMP=16, PIPE=17, + LT=18, LT_EQ=19, GT=20, GT_EQ=21, EQ=22, NOT_EQ1=23, NOT_EQ2=24, ABORT_=25, + ACTION_=26, ADD_=27, AFTER_=28, ALL_=29, ALTER_=30, ANALYZE_=31, AND_=32, + AS_=33, ASC_=34, ATTACH_=35, AUTOINCREMENT_=36, BEFORE_=37, BEGIN_=38, + BETWEEN_=39, BY_=40, CASCADE_=41, CASE_=42, CAST_=43, CHECK_=44, COLLATE_=45, + COLUMN_=46, COMMIT_=47, CONFLICT_=48, CONSTRAINT_=49, CREATE_=50, CROSS_=51, + CURRENT_DATE_=52, CURRENT_TIME_=53, CURRENT_TIMESTAMP_=54, DATABASE_=55, + DEFAULT_=56, DEFERRABLE_=57, DEFERRED_=58, DELETE_=59, DESC_=60, DETACH_=61, + DISTINCT_=62, DROP_=63, EACH_=64, ELSE_=65, END_=66, ESCAPE_=67, EXCEPT_=68, + EXCLUSIVE_=69, EXISTS_=70, EXPLAIN_=71, FAIL_=72, FOR_=73, FOREIGN_=74, + FROM_=75, FULL_=76, GLOB_=77, GROUP_=78, HAVING_=79, IF_=80, IGNORE_=81, + IMMEDIATE_=82, IN_=83, INDEX_=84, INDEXED_=85, INITIALLY_=86, INNER_=87, + INSERT_=88, INSTEAD_=89, INTERSECT_=90, INTO_=91, IS_=92, ISNULL_=93, + JOIN_=94, KEY_=95, LEFT_=96, LIKE_=97, LIMIT_=98, MATCH_=99, NATURAL_=100, + NO_=101, NOT_=102, NOTNULL_=103, NULL_=104, OF_=105, OFFSET_=106, ON_=107, + OR_=108, ORDER_=109, OUTER_=110, PLAN_=111, PRAGMA_=112, PRIMARY_=113, + QUERY_=114, RAISE_=115, RECURSIVE_=116, REFERENCES_=117, REGEXP_=118, + REINDEX_=119, RELEASE_=120, RENAME_=121, REPLACE_=122, RESTRICT_=123, + RETURNING_=124, RIGHT_=125, ROLLBACK_=126, ROW_=127, ROWS_=128, SAVEPOINT_=129, + SELECT_=130, SET_=131, TABLE_=132, TEMP_=133, TEMPORARY_=134, THEN_=135, + TO_=136, TRANSACTION_=137, TRIGGER_=138, UNION_=139, UNIQUE_=140, UPDATE_=141, + USING_=142, VACUUM_=143, VALUES_=144, VIEW_=145, VIRTUAL_=146, WHEN_=147, + WHERE_=148, WITH_=149, WITHOUT_=150, FIRST_VALUE_=151, OVER_=152, PARTITION_=153, + RANGE_=154, PRECEDING_=155, UNBOUNDED_=156, CURRENT_=157, FOLLOWING_=158, + CUME_DIST_=159, DENSE_RANK_=160, LAG_=161, LAST_VALUE_=162, LEAD_=163, + NTH_VALUE_=164, NTILE_=165, PERCENT_RANK_=166, RANK_=167, ROW_NUMBER_=168, + GENERATED_=169, ALWAYS_=170, STORED_=171, TRUE_=172, FALSE_=173, WINDOW_=174, + NULLS_=175, FIRST_=176, LAST_=177, FILTER_=178, GROUPS_=179, EXCLUDE_=180, + TIES_=181, OTHERS_=182, DO_=183, NOTHING_=184, IDENTIFIER=185, NUMERIC_LITERAL=186, + BIND_PARAMETER=187, STRING_LITERAL=188, BLOB_LITERAL=189, SINGLE_LINE_COMMENT=190, + MULTILINE_COMMENT=191, SPACES=192, UNEXPECTED_CHAR=193; + public static string[] channelNames = { + "DEFAULT_TOKEN_CHANNEL", "HIDDEN" + }; + + public static string[] modeNames = { + "DEFAULT_MODE" + }; + + public static readonly string[] ruleNames = { + "SCOL", "DOT", "OPEN_PAR", "CLOSE_PAR", "COMMA", "ASSIGN", "STAR", "PLUS", + "MINUS", "TILDE", "PIPE2", "DIV", "MOD", "LT2", "GT2", "AMP", "PIPE", + "LT", "LT_EQ", "GT", "GT_EQ", "EQ", "NOT_EQ1", "NOT_EQ2", "ABORT_", "ACTION_", + "ADD_", "AFTER_", "ALL_", "ALTER_", "ANALYZE_", "AND_", "AS_", "ASC_", + "ATTACH_", "AUTOINCREMENT_", "BEFORE_", "BEGIN_", "BETWEEN_", "BY_", "CASCADE_", + "CASE_", "CAST_", "CHECK_", "COLLATE_", "COLUMN_", "COMMIT_", "CONFLICT_", + "CONSTRAINT_", "CREATE_", "CROSS_", "CURRENT_DATE_", "CURRENT_TIME_", + "CURRENT_TIMESTAMP_", "DATABASE_", "DEFAULT_", "DEFERRABLE_", "DEFERRED_", + "DELETE_", "DESC_", "DETACH_", "DISTINCT_", "DROP_", "EACH_", "ELSE_", + "END_", "ESCAPE_", "EXCEPT_", "EXCLUSIVE_", "EXISTS_", "EXPLAIN_", "FAIL_", + "FOR_", "FOREIGN_", "FROM_", "FULL_", "GLOB_", "GROUP_", "HAVING_", "IF_", + "IGNORE_", "IMMEDIATE_", "IN_", "INDEX_", "INDEXED_", "INITIALLY_", "INNER_", + "INSERT_", "INSTEAD_", "INTERSECT_", "INTO_", "IS_", "ISNULL_", "JOIN_", + "KEY_", "LEFT_", "LIKE_", "LIMIT_", "MATCH_", "NATURAL_", "NO_", "NOT_", + "NOTNULL_", "NULL_", "OF_", "OFFSET_", "ON_", "OR_", "ORDER_", "OUTER_", + "PLAN_", "PRAGMA_", "PRIMARY_", "QUERY_", "RAISE_", "RECURSIVE_", "REFERENCES_", + "REGEXP_", "REINDEX_", "RELEASE_", "RENAME_", "REPLACE_", "RESTRICT_", + "RETURNING_", "RIGHT_", "ROLLBACK_", "ROW_", "ROWS_", "SAVEPOINT_", "SELECT_", + "SET_", "TABLE_", "TEMP_", "TEMPORARY_", "THEN_", "TO_", "TRANSACTION_", + "TRIGGER_", "UNION_", "UNIQUE_", "UPDATE_", "USING_", "VACUUM_", "VALUES_", + "VIEW_", "VIRTUAL_", "WHEN_", "WHERE_", "WITH_", "WITHOUT_", "FIRST_VALUE_", + "OVER_", "PARTITION_", "RANGE_", "PRECEDING_", "UNBOUNDED_", "CURRENT_", + "FOLLOWING_", "CUME_DIST_", "DENSE_RANK_", "LAG_", "LAST_VALUE_", "LEAD_", + "NTH_VALUE_", "NTILE_", "PERCENT_RANK_", "RANK_", "ROW_NUMBER_", "GENERATED_", + "ALWAYS_", "STORED_", "TRUE_", "FALSE_", "WINDOW_", "NULLS_", "FIRST_", + "LAST_", "FILTER_", "GROUPS_", "EXCLUDE_", "TIES_", "OTHERS_", "DO_", + "NOTHING_", "IDENTIFIER", "NUMERIC_LITERAL", "BIND_PARAMETER", "STRING_LITERAL", + "BLOB_LITERAL", "SINGLE_LINE_COMMENT", "MULTILINE_COMMENT", "SPACES", + "UNEXPECTED_CHAR", "HEX_DIGIT", "DIGIT" + }; + + + public SQLiteLexer(ICharStream input) + : this(input, Console.Out, Console.Error) { } + + public SQLiteLexer(ICharStream input, TextWriter output, TextWriter errorOutput) + : base(input, output, errorOutput) + { + Interpreter = new LexerATNSimulator(this, _ATN, decisionToDFA, sharedContextCache); + } + + private static readonly string[] _LiteralNames = { + null, "';'", "'.'", "'('", "')'", "','", "'='", "'*'", "'+'", "'-'", "'~'", + "'||'", "'/'", "'%'", "'<<'", "'>>'", "'&'", "'|'", "'<'", "'<='", "'>'", + "'>='", "'=='", "'!='", "'<>'", "'ABORT'", "'ACTION'", "'ADD'", "'AFTER'", + "'ALL'", "'ALTER'", "'ANALYZE'", "'AND'", "'AS'", "'ASC'", "'ATTACH'", + "'AUTOINCREMENT'", "'BEFORE'", "'BEGIN'", "'BETWEEN'", "'BY'", "'CASCADE'", + "'CASE'", "'CAST'", "'CHECK'", "'COLLATE'", "'COLUMN'", "'COMMIT'", "'CONFLICT'", + "'CONSTRAINT'", "'CREATE'", "'CROSS'", "'CURRENT_DATE'", "'CURRENT_TIME'", + "'CURRENT_TIMESTAMP'", "'DATABASE'", "'DEFAULT'", "'DEFERRABLE'", "'DEFERRED'", + "'DELETE'", "'DESC'", "'DETACH'", "'DISTINCT'", "'DROP'", "'EACH'", "'ELSE'", + "'END'", "'ESCAPE'", "'EXCEPT'", "'EXCLUSIVE'", "'EXISTS'", "'EXPLAIN'", + "'FAIL'", "'FOR'", "'FOREIGN'", "'FROM'", "'FULL'", "'GLOB'", "'GROUP'", + "'HAVING'", "'IF'", "'IGNORE'", "'IMMEDIATE'", "'IN'", "'INDEX'", "'INDEXED'", + "'INITIALLY'", "'INNER'", "'INSERT'", "'INSTEAD'", "'INTERSECT'", "'INTO'", + "'IS'", "'ISNULL'", "'JOIN'", "'KEY'", "'LEFT'", "'LIKE'", "'LIMIT'", + "'MATCH'", "'NATURAL'", "'NO'", "'NOT'", "'NOTNULL'", "'NULL'", "'OF'", + "'OFFSET'", "'ON'", "'OR'", "'ORDER'", "'OUTER'", "'PLAN'", "'PRAGMA'", + "'PRIMARY'", "'QUERY'", "'RAISE'", "'RECURSIVE'", "'REFERENCES'", "'REGEXP'", + "'REINDEX'", "'RELEASE'", "'RENAME'", "'REPLACE'", "'RESTRICT'", "'RETURNING'", + "'RIGHT'", "'ROLLBACK'", "'ROW'", "'ROWS'", "'SAVEPOINT'", "'SELECT'", + "'SET'", "'TABLE'", "'TEMP'", "'TEMPORARY'", "'THEN'", "'TO'", "'TRANSACTION'", + "'TRIGGER'", "'UNION'", "'UNIQUE'", "'UPDATE'", "'USING'", "'VACUUM'", + "'VALUES'", "'VIEW'", "'VIRTUAL'", "'WHEN'", "'WHERE'", "'WITH'", "'WITHOUT'", + "'FIRST_VALUE'", "'OVER'", "'PARTITION'", "'RANGE'", "'PRECEDING'", "'UNBOUNDED'", + "'CURRENT'", "'FOLLOWING'", "'CUME_DIST'", "'DENSE_RANK'", "'LAG'", "'LAST_VALUE'", + "'LEAD'", "'NTH_VALUE'", "'NTILE'", "'PERCENT_RANK'", "'RANK'", "'ROW_NUMBER'", + "'GENERATED'", "'ALWAYS'", "'STORED'", "'TRUE'", "'FALSE'", "'WINDOW'", + "'NULLS'", "'FIRST'", "'LAST'", "'FILTER'", "'GROUPS'", "'EXCLUDE'", "'TIES'", + "'OTHERS'", "'DO'", "'NOTHING'" + }; + private static readonly string[] _SymbolicNames = { + null, "SCOL", "DOT", "OPEN_PAR", "CLOSE_PAR", "COMMA", "ASSIGN", "STAR", + "PLUS", "MINUS", "TILDE", "PIPE2", "DIV", "MOD", "LT2", "GT2", "AMP", + "PIPE", "LT", "LT_EQ", "GT", "GT_EQ", "EQ", "NOT_EQ1", "NOT_EQ2", "ABORT_", + "ACTION_", "ADD_", "AFTER_", "ALL_", "ALTER_", "ANALYZE_", "AND_", "AS_", + "ASC_", "ATTACH_", "AUTOINCREMENT_", "BEFORE_", "BEGIN_", "BETWEEN_", + "BY_", "CASCADE_", "CASE_", "CAST_", "CHECK_", "COLLATE_", "COLUMN_", + "COMMIT_", "CONFLICT_", "CONSTRAINT_", "CREATE_", "CROSS_", "CURRENT_DATE_", + "CURRENT_TIME_", "CURRENT_TIMESTAMP_", "DATABASE_", "DEFAULT_", "DEFERRABLE_", + "DEFERRED_", "DELETE_", "DESC_", "DETACH_", "DISTINCT_", "DROP_", "EACH_", + "ELSE_", "END_", "ESCAPE_", "EXCEPT_", "EXCLUSIVE_", "EXISTS_", "EXPLAIN_", + "FAIL_", "FOR_", "FOREIGN_", "FROM_", "FULL_", "GLOB_", "GROUP_", "HAVING_", + "IF_", "IGNORE_", "IMMEDIATE_", "IN_", "INDEX_", "INDEXED_", "INITIALLY_", + "INNER_", "INSERT_", "INSTEAD_", "INTERSECT_", "INTO_", "IS_", "ISNULL_", + "JOIN_", "KEY_", "LEFT_", "LIKE_", "LIMIT_", "MATCH_", "NATURAL_", "NO_", + "NOT_", "NOTNULL_", "NULL_", "OF_", "OFFSET_", "ON_", "OR_", "ORDER_", + "OUTER_", "PLAN_", "PRAGMA_", "PRIMARY_", "QUERY_", "RAISE_", "RECURSIVE_", + "REFERENCES_", "REGEXP_", "REINDEX_", "RELEASE_", "RENAME_", "REPLACE_", + "RESTRICT_", "RETURNING_", "RIGHT_", "ROLLBACK_", "ROW_", "ROWS_", "SAVEPOINT_", + "SELECT_", "SET_", "TABLE_", "TEMP_", "TEMPORARY_", "THEN_", "TO_", "TRANSACTION_", + "TRIGGER_", "UNION_", "UNIQUE_", "UPDATE_", "USING_", "VACUUM_", "VALUES_", + "VIEW_", "VIRTUAL_", "WHEN_", "WHERE_", "WITH_", "WITHOUT_", "FIRST_VALUE_", + "OVER_", "PARTITION_", "RANGE_", "PRECEDING_", "UNBOUNDED_", "CURRENT_", + "FOLLOWING_", "CUME_DIST_", "DENSE_RANK_", "LAG_", "LAST_VALUE_", "LEAD_", + "NTH_VALUE_", "NTILE_", "PERCENT_RANK_", "RANK_", "ROW_NUMBER_", "GENERATED_", + "ALWAYS_", "STORED_", "TRUE_", "FALSE_", "WINDOW_", "NULLS_", "FIRST_", + "LAST_", "FILTER_", "GROUPS_", "EXCLUDE_", "TIES_", "OTHERS_", "DO_", + "NOTHING_", "IDENTIFIER", "NUMERIC_LITERAL", "BIND_PARAMETER", "STRING_LITERAL", + "BLOB_LITERAL", "SINGLE_LINE_COMMENT", "MULTILINE_COMMENT", "SPACES", + "UNEXPECTED_CHAR" + }; + public static readonly IVocabulary DefaultVocabulary = new Vocabulary(_LiteralNames, _SymbolicNames); + + [NotNull] + public override IVocabulary Vocabulary + { + get + { + return DefaultVocabulary; + } + } + + public override string GrammarFileName { get { return "SQLiteLexer.g4"; } } + + public override string[] RuleNames { get { return ruleNames; } } + + public override string[] ChannelNames { get { return channelNames; } } + + public override string[] ModeNames { get { return modeNames; } } + + public override int[] SerializedAtn { get { return _serializedATN; } } + + static SQLiteLexer() { + decisionToDFA = new DFA[_ATN.NumberOfDecisions]; + for (int i = 0; i < _ATN.NumberOfDecisions; i++) { + decisionToDFA[i] = new DFA(_ATN.GetDecisionState(i), i); + } + } + private static int[] _serializedATN = { + 4,0,193,1704,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6, + 7,6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2, + 14,7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7,19,2,20,7,20,2, + 21,7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,2,26,7,26,2,27,7,27,2, + 28,7,28,2,29,7,29,2,30,7,30,2,31,7,31,2,32,7,32,2,33,7,33,2,34,7,34,2, + 35,7,35,2,36,7,36,2,37,7,37,2,38,7,38,2,39,7,39,2,40,7,40,2,41,7,41,2, + 42,7,42,2,43,7,43,2,44,7,44,2,45,7,45,2,46,7,46,2,47,7,47,2,48,7,48,2, + 49,7,49,2,50,7,50,2,51,7,51,2,52,7,52,2,53,7,53,2,54,7,54,2,55,7,55,2, + 56,7,56,2,57,7,57,2,58,7,58,2,59,7,59,2,60,7,60,2,61,7,61,2,62,7,62,2, + 63,7,63,2,64,7,64,2,65,7,65,2,66,7,66,2,67,7,67,2,68,7,68,2,69,7,69,2, + 70,7,70,2,71,7,71,2,72,7,72,2,73,7,73,2,74,7,74,2,75,7,75,2,76,7,76,2, + 77,7,77,2,78,7,78,2,79,7,79,2,80,7,80,2,81,7,81,2,82,7,82,2,83,7,83,2, + 84,7,84,2,85,7,85,2,86,7,86,2,87,7,87,2,88,7,88,2,89,7,89,2,90,7,90,2, + 91,7,91,2,92,7,92,2,93,7,93,2,94,7,94,2,95,7,95,2,96,7,96,2,97,7,97,2, + 98,7,98,2,99,7,99,2,100,7,100,2,101,7,101,2,102,7,102,2,103,7,103,2,104, + 7,104,2,105,7,105,2,106,7,106,2,107,7,107,2,108,7,108,2,109,7,109,2,110, + 7,110,2,111,7,111,2,112,7,112,2,113,7,113,2,114,7,114,2,115,7,115,2,116, + 7,116,2,117,7,117,2,118,7,118,2,119,7,119,2,120,7,120,2,121,7,121,2,122, + 7,122,2,123,7,123,2,124,7,124,2,125,7,125,2,126,7,126,2,127,7,127,2,128, + 7,128,2,129,7,129,2,130,7,130,2,131,7,131,2,132,7,132,2,133,7,133,2,134, + 7,134,2,135,7,135,2,136,7,136,2,137,7,137,2,138,7,138,2,139,7,139,2,140, + 7,140,2,141,7,141,2,142,7,142,2,143,7,143,2,144,7,144,2,145,7,145,2,146, + 7,146,2,147,7,147,2,148,7,148,2,149,7,149,2,150,7,150,2,151,7,151,2,152, + 7,152,2,153,7,153,2,154,7,154,2,155,7,155,2,156,7,156,2,157,7,157,2,158, + 7,158,2,159,7,159,2,160,7,160,2,161,7,161,2,162,7,162,2,163,7,163,2,164, + 7,164,2,165,7,165,2,166,7,166,2,167,7,167,2,168,7,168,2,169,7,169,2,170, + 7,170,2,171,7,171,2,172,7,172,2,173,7,173,2,174,7,174,2,175,7,175,2,176, + 7,176,2,177,7,177,2,178,7,178,2,179,7,179,2,180,7,180,2,181,7,181,2,182, + 7,182,2,183,7,183,2,184,7,184,2,185,7,185,2,186,7,186,2,187,7,187,2,188, + 7,188,2,189,7,189,2,190,7,190,2,191,7,191,2,192,7,192,2,193,7,193,2,194, + 7,194,1,0,1,0,1,1,1,1,1,2,1,2,1,3,1,3,1,4,1,4,1,5,1,5,1,6,1,6,1,7,1,7, + 1,8,1,8,1,9,1,9,1,10,1,10,1,10,1,11,1,11,1,12,1,12,1,13,1,13,1,13,1,14, + 1,14,1,14,1,15,1,15,1,16,1,16,1,17,1,17,1,18,1,18,1,18,1,19,1,19,1,20, + 1,20,1,20,1,21,1,21,1,21,1,22,1,22,1,22,1,23,1,23,1,23,1,24,1,24,1,24, + 1,24,1,24,1,24,1,25,1,25,1,25,1,25,1,25,1,25,1,25,1,26,1,26,1,26,1,26, + 1,27,1,27,1,27,1,27,1,27,1,27,1,28,1,28,1,28,1,28,1,29,1,29,1,29,1,29, + 1,29,1,29,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,30,1,31,1,31,1,31,1,31, + 1,32,1,32,1,32,1,33,1,33,1,33,1,33,1,34,1,34,1,34,1,34,1,34,1,34,1,34, + 1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35,1,35, + 1,36,1,36,1,36,1,36,1,36,1,36,1,36,1,37,1,37,1,37,1,37,1,37,1,37,1,38, + 1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,39,1,39,1,39,1,40,1,40,1,40,1,40, + 1,40,1,40,1,40,1,40,1,41,1,41,1,41,1,41,1,41,1,42,1,42,1,42,1,42,1,42, + 1,43,1,43,1,43,1,43,1,43,1,43,1,44,1,44,1,44,1,44,1,44,1,44,1,44,1,44, + 1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,46,1,46,1,46,1,46,1,46,1,46,1,46, + 1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,47,1,48,1,48,1,48,1,48,1,48, + 1,48,1,48,1,48,1,48,1,48,1,48,1,49,1,49,1,49,1,49,1,49,1,49,1,49,1,50, + 1,50,1,50,1,50,1,50,1,50,1,51,1,51,1,51,1,51,1,51,1,51,1,51,1,51,1,51, + 1,51,1,51,1,51,1,51,1,52,1,52,1,52,1,52,1,52,1,52,1,52,1,52,1,52,1,52, + 1,52,1,52,1,52,1,53,1,53,1,53,1,53,1,53,1,53,1,53,1,53,1,53,1,53,1,53, + 1,53,1,53,1,53,1,53,1,53,1,53,1,53,1,54,1,54,1,54,1,54,1,54,1,54,1,54, + 1,54,1,54,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,55,1,56,1,56,1,56,1,56, + 1,56,1,56,1,56,1,56,1,56,1,56,1,56,1,57,1,57,1,57,1,57,1,57,1,57,1,57, + 1,57,1,57,1,58,1,58,1,58,1,58,1,58,1,58,1,58,1,59,1,59,1,59,1,59,1,59, + 1,60,1,60,1,60,1,60,1,60,1,60,1,60,1,61,1,61,1,61,1,61,1,61,1,61,1,61, + 1,61,1,61,1,62,1,62,1,62,1,62,1,62,1,63,1,63,1,63,1,63,1,63,1,64,1,64, + 1,64,1,64,1,64,1,65,1,65,1,65,1,65,1,66,1,66,1,66,1,66,1,66,1,66,1,66, + 1,67,1,67,1,67,1,67,1,67,1,67,1,67,1,68,1,68,1,68,1,68,1,68,1,68,1,68, + 1,68,1,68,1,68,1,69,1,69,1,69,1,69,1,69,1,69,1,69,1,70,1,70,1,70,1,70, + 1,70,1,70,1,70,1,70,1,71,1,71,1,71,1,71,1,71,1,72,1,72,1,72,1,72,1,73, + 1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,74,1,74,1,74,1,74,1,74,1,75,1,75, + 1,75,1,75,1,75,1,76,1,76,1,76,1,76,1,76,1,77,1,77,1,77,1,77,1,77,1,77, + 1,78,1,78,1,78,1,78,1,78,1,78,1,78,1,79,1,79,1,79,1,80,1,80,1,80,1,80, + 1,80,1,80,1,80,1,81,1,81,1,81,1,81,1,81,1,81,1,81,1,81,1,81,1,81,1,82, + 1,82,1,82,1,83,1,83,1,83,1,83,1,83,1,83,1,84,1,84,1,84,1,84,1,84,1,84, + 1,84,1,84,1,85,1,85,1,85,1,85,1,85,1,85,1,85,1,85,1,85,1,85,1,86,1,86, + 1,86,1,86,1,86,1,86,1,87,1,87,1,87,1,87,1,87,1,87,1,87,1,88,1,88,1,88, + 1,88,1,88,1,88,1,88,1,88,1,89,1,89,1,89,1,89,1,89,1,89,1,89,1,89,1,89, + 1,89,1,90,1,90,1,90,1,90,1,90,1,91,1,91,1,91,1,92,1,92,1,92,1,92,1,92, + 1,92,1,92,1,93,1,93,1,93,1,93,1,93,1,94,1,94,1,94,1,94,1,95,1,95,1,95, + 1,95,1,95,1,96,1,96,1,96,1,96,1,96,1,97,1,97,1,97,1,97,1,97,1,97,1,98, + 1,98,1,98,1,98,1,98,1,98,1,99,1,99,1,99,1,99,1,99,1,99,1,99,1,99,1,100, + 1,100,1,100,1,101,1,101,1,101,1,101,1,102,1,102,1,102,1,102,1,102,1,102, + 1,102,1,102,1,103,1,103,1,103,1,103,1,103,1,104,1,104,1,104,1,105,1,105, + 1,105,1,105,1,105,1,105,1,105,1,106,1,106,1,106,1,107,1,107,1,107,1,108, + 1,108,1,108,1,108,1,108,1,108,1,109,1,109,1,109,1,109,1,109,1,109,1,110, + 1,110,1,110,1,110,1,110,1,111,1,111,1,111,1,111,1,111,1,111,1,111,1,112, + 1,112,1,112,1,112,1,112,1,112,1,112,1,112,1,113,1,113,1,113,1,113,1,113, + 1,113,1,114,1,114,1,114,1,114,1,114,1,114,1,115,1,115,1,115,1,115,1,115, + 1,115,1,115,1,115,1,115,1,115,1,116,1,116,1,116,1,116,1,116,1,116,1,116, + 1,116,1,116,1,116,1,116,1,117,1,117,1,117,1,117,1,117,1,117,1,117,1,118, + 1,118,1,118,1,118,1,118,1,118,1,118,1,118,1,119,1,119,1,119,1,119,1,119, + 1,119,1,119,1,119,1,120,1,120,1,120,1,120,1,120,1,120,1,120,1,121,1,121, + 1,121,1,121,1,121,1,121,1,121,1,121,1,122,1,122,1,122,1,122,1,122,1,122, + 1,122,1,122,1,122,1,123,1,123,1,123,1,123,1,123,1,123,1,123,1,123,1,123, + 1,123,1,124,1,124,1,124,1,124,1,124,1,124,1,125,1,125,1,125,1,125,1,125, + 1,125,1,125,1,125,1,125,1,126,1,126,1,126,1,126,1,127,1,127,1,127,1,127, + 1,127,1,128,1,128,1,128,1,128,1,128,1,128,1,128,1,128,1,128,1,128,1,129, + 1,129,1,129,1,129,1,129,1,129,1,129,1,130,1,130,1,130,1,130,1,131,1,131, + 1,131,1,131,1,131,1,131,1,132,1,132,1,132,1,132,1,132,1,133,1,133,1,133, + 1,133,1,133,1,133,1,133,1,133,1,133,1,133,1,134,1,134,1,134,1,134,1,134, + 1,135,1,135,1,135,1,136,1,136,1,136,1,136,1,136,1,136,1,136,1,136,1,136, + 1,136,1,136,1,136,1,137,1,137,1,137,1,137,1,137,1,137,1,137,1,137,1,138, + 1,138,1,138,1,138,1,138,1,138,1,139,1,139,1,139,1,139,1,139,1,139,1,139, + 1,140,1,140,1,140,1,140,1,140,1,140,1,140,1,141,1,141,1,141,1,141,1,141, + 1,141,1,142,1,142,1,142,1,142,1,142,1,142,1,142,1,143,1,143,1,143,1,143, + 1,143,1,143,1,143,1,144,1,144,1,144,1,144,1,144,1,145,1,145,1,145,1,145, + 1,145,1,145,1,145,1,145,1,146,1,146,1,146,1,146,1,146,1,147,1,147,1,147, + 1,147,1,147,1,147,1,148,1,148,1,148,1,148,1,148,1,149,1,149,1,149,1,149, + 1,149,1,149,1,149,1,149,1,150,1,150,1,150,1,150,1,150,1,150,1,150,1,150, + 1,150,1,150,1,150,1,150,1,151,1,151,1,151,1,151,1,151,1,152,1,152,1,152, + 1,152,1,152,1,152,1,152,1,152,1,152,1,152,1,153,1,153,1,153,1,153,1,153, + 1,153,1,154,1,154,1,154,1,154,1,154,1,154,1,154,1,154,1,154,1,154,1,155, + 1,155,1,155,1,155,1,155,1,155,1,155,1,155,1,155,1,155,1,156,1,156,1,156, + 1,156,1,156,1,156,1,156,1,156,1,157,1,157,1,157,1,157,1,157,1,157,1,157, + 1,157,1,157,1,157,1,158,1,158,1,158,1,158,1,158,1,158,1,158,1,158,1,158, + 1,158,1,159,1,159,1,159,1,159,1,159,1,159,1,159,1,159,1,159,1,159,1,159, + 1,160,1,160,1,160,1,160,1,161,1,161,1,161,1,161,1,161,1,161,1,161,1,161, + 1,161,1,161,1,161,1,162,1,162,1,162,1,162,1,162,1,163,1,163,1,163,1,163, + 1,163,1,163,1,163,1,163,1,163,1,163,1,164,1,164,1,164,1,164,1,164,1,164, + 1,165,1,165,1,165,1,165,1,165,1,165,1,165,1,165,1,165,1,165,1,165,1,165, + 1,165,1,166,1,166,1,166,1,166,1,166,1,167,1,167,1,167,1,167,1,167,1,167, + 1,167,1,167,1,167,1,167,1,167,1,168,1,168,1,168,1,168,1,168,1,168,1,168, + 1,168,1,168,1,168,1,169,1,169,1,169,1,169,1,169,1,169,1,169,1,170,1,170, + 1,170,1,170,1,170,1,170,1,170,1,171,1,171,1,171,1,171,1,171,1,172,1,172, + 1,172,1,172,1,172,1,172,1,173,1,173,1,173,1,173,1,173,1,173,1,173,1,174, + 1,174,1,174,1,174,1,174,1,174,1,175,1,175,1,175,1,175,1,175,1,175,1,176, + 1,176,1,176,1,176,1,176,1,177,1,177,1,177,1,177,1,177,1,177,1,177,1,178, + 1,178,1,178,1,178,1,178,1,178,1,178,1,179,1,179,1,179,1,179,1,179,1,179, + 1,179,1,179,1,180,1,180,1,180,1,180,1,180,1,181,1,181,1,181,1,181,1,181, + 1,181,1,181,1,182,1,182,1,182,1,183,1,183,1,183,1,183,1,183,1,183,1,183, + 1,183,1,184,1,184,1,184,1,184,5,184,1562,8,184,10,184,12,184,1565,9,184, + 1,184,1,184,1,184,1,184,1,184,5,184,1572,8,184,10,184,12,184,1575,9,184, + 1,184,1,184,1,184,5,184,1580,8,184,10,184,12,184,1583,9,184,1,184,1,184, + 1,184,5,184,1588,8,184,10,184,12,184,1591,9,184,3,184,1593,8,184,1,185, + 4,185,1596,8,185,11,185,12,185,1597,1,185,1,185,5,185,1602,8,185,10,185, + 12,185,1605,9,185,3,185,1607,8,185,1,185,1,185,4,185,1611,8,185,11,185, + 12,185,1612,3,185,1615,8,185,1,185,1,185,3,185,1619,8,185,1,185,4,185, + 1622,8,185,11,185,12,185,1623,3,185,1626,8,185,1,185,1,185,1,185,1,185, + 4,185,1632,8,185,11,185,12,185,1633,3,185,1636,8,185,1,186,1,186,5,186, + 1640,8,186,10,186,12,186,1643,9,186,1,186,1,186,3,186,1647,8,186,1,187, + 1,187,1,187,1,187,5,187,1653,8,187,10,187,12,187,1656,9,187,1,187,1,187, + 1,188,1,188,1,188,1,189,1,189,1,189,1,189,5,189,1667,8,189,10,189,12,189, + 1670,9,189,1,189,3,189,1673,8,189,1,189,1,189,3,189,1677,8,189,1,189,1, + 189,1,190,1,190,1,190,1,190,5,190,1685,8,190,10,190,12,190,1688,9,190, + 1,190,1,190,1,190,1,190,1,190,1,191,1,191,1,191,1,191,1,192,1,192,1,193, + 1,193,1,194,1,194,1,1686,0,195,1,1,3,2,5,3,7,4,9,5,11,6,13,7,15,8,17,9, + 19,10,21,11,23,12,25,13,27,14,29,15,31,16,33,17,35,18,37,19,39,20,41,21, + 43,22,45,23,47,24,49,25,51,26,53,27,55,28,57,29,59,30,61,31,63,32,65,33, + 67,34,69,35,71,36,73,37,75,38,77,39,79,40,81,41,83,42,85,43,87,44,89,45, + 91,46,93,47,95,48,97,49,99,50,101,51,103,52,105,53,107,54,109,55,111,56, + 113,57,115,58,117,59,119,60,121,61,123,62,125,63,127,64,129,65,131,66, + 133,67,135,68,137,69,139,70,141,71,143,72,145,73,147,74,149,75,151,76, + 153,77,155,78,157,79,159,80,161,81,163,82,165,83,167,84,169,85,171,86, + 173,87,175,88,177,89,179,90,181,91,183,92,185,93,187,94,189,95,191,96, + 193,97,195,98,197,99,199,100,201,101,203,102,205,103,207,104,209,105,211, + 106,213,107,215,108,217,109,219,110,221,111,223,112,225,113,227,114,229, + 115,231,116,233,117,235,118,237,119,239,120,241,121,243,122,245,123,247, + 124,249,125,251,126,253,127,255,128,257,129,259,130,261,131,263,132,265, + 133,267,134,269,135,271,136,273,137,275,138,277,139,279,140,281,141,283, + 142,285,143,287,144,289,145,291,146,293,147,295,148,297,149,299,150,301, + 151,303,152,305,153,307,154,309,155,311,156,313,157,315,158,317,159,319, + 160,321,161,323,162,325,163,327,164,329,165,331,166,333,167,335,168,337, + 169,339,170,341,171,343,172,345,173,347,174,349,175,351,176,353,177,355, + 178,357,179,359,180,361,181,363,182,365,183,367,184,369,185,371,186,373, + 187,375,188,377,189,379,190,381,191,383,192,385,193,387,0,389,0,1,0,38, + 2,0,65,65,97,97,2,0,66,66,98,98,2,0,79,79,111,111,2,0,82,82,114,114,2, + 0,84,84,116,116,2,0,67,67,99,99,2,0,73,73,105,105,2,0,78,78,110,110,2, + 0,68,68,100,100,2,0,70,70,102,102,2,0,69,69,101,101,2,0,76,76,108,108, + 2,0,89,89,121,121,2,0,90,90,122,122,2,0,83,83,115,115,2,0,72,72,104,104, + 2,0,85,85,117,117,2,0,77,77,109,109,2,0,71,71,103,103,2,0,87,87,119,119, + 2,0,75,75,107,107,2,0,80,80,112,112,2,0,88,88,120,120,2,0,86,86,118,118, + 2,0,74,74,106,106,2,0,81,81,113,113,1,0,34,34,1,0,96,96,1,0,93,93,4,0, + 65,90,95,95,97,122,127,65535,5,0,48,57,65,90,95,95,97,122,127,65535,2, + 0,43,43,45,45,3,0,36,36,58,58,64,64,1,0,39,39,2,0,10,10,13,13,3,0,9,11, + 13,13,32,32,3,0,48,57,65,70,97,102,1,0,48,57,1728,0,1,1,0,0,0,0,3,1,0, + 0,0,0,5,1,0,0,0,0,7,1,0,0,0,0,9,1,0,0,0,0,11,1,0,0,0,0,13,1,0,0,0,0,15, + 1,0,0,0,0,17,1,0,0,0,0,19,1,0,0,0,0,21,1,0,0,0,0,23,1,0,0,0,0,25,1,0,0, + 0,0,27,1,0,0,0,0,29,1,0,0,0,0,31,1,0,0,0,0,33,1,0,0,0,0,35,1,0,0,0,0,37, + 1,0,0,0,0,39,1,0,0,0,0,41,1,0,0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,47,1,0,0, + 0,0,49,1,0,0,0,0,51,1,0,0,0,0,53,1,0,0,0,0,55,1,0,0,0,0,57,1,0,0,0,0,59, + 1,0,0,0,0,61,1,0,0,0,0,63,1,0,0,0,0,65,1,0,0,0,0,67,1,0,0,0,0,69,1,0,0, + 0,0,71,1,0,0,0,0,73,1,0,0,0,0,75,1,0,0,0,0,77,1,0,0,0,0,79,1,0,0,0,0,81, + 1,0,0,0,0,83,1,0,0,0,0,85,1,0,0,0,0,87,1,0,0,0,0,89,1,0,0,0,0,91,1,0,0, + 0,0,93,1,0,0,0,0,95,1,0,0,0,0,97,1,0,0,0,0,99,1,0,0,0,0,101,1,0,0,0,0, + 103,1,0,0,0,0,105,1,0,0,0,0,107,1,0,0,0,0,109,1,0,0,0,0,111,1,0,0,0,0, + 113,1,0,0,0,0,115,1,0,0,0,0,117,1,0,0,0,0,119,1,0,0,0,0,121,1,0,0,0,0, + 123,1,0,0,0,0,125,1,0,0,0,0,127,1,0,0,0,0,129,1,0,0,0,0,131,1,0,0,0,0, + 133,1,0,0,0,0,135,1,0,0,0,0,137,1,0,0,0,0,139,1,0,0,0,0,141,1,0,0,0,0, + 143,1,0,0,0,0,145,1,0,0,0,0,147,1,0,0,0,0,149,1,0,0,0,0,151,1,0,0,0,0, + 153,1,0,0,0,0,155,1,0,0,0,0,157,1,0,0,0,0,159,1,0,0,0,0,161,1,0,0,0,0, + 163,1,0,0,0,0,165,1,0,0,0,0,167,1,0,0,0,0,169,1,0,0,0,0,171,1,0,0,0,0, + 173,1,0,0,0,0,175,1,0,0,0,0,177,1,0,0,0,0,179,1,0,0,0,0,181,1,0,0,0,0, + 183,1,0,0,0,0,185,1,0,0,0,0,187,1,0,0,0,0,189,1,0,0,0,0,191,1,0,0,0,0, + 193,1,0,0,0,0,195,1,0,0,0,0,197,1,0,0,0,0,199,1,0,0,0,0,201,1,0,0,0,0, + 203,1,0,0,0,0,205,1,0,0,0,0,207,1,0,0,0,0,209,1,0,0,0,0,211,1,0,0,0,0, + 213,1,0,0,0,0,215,1,0,0,0,0,217,1,0,0,0,0,219,1,0,0,0,0,221,1,0,0,0,0, + 223,1,0,0,0,0,225,1,0,0,0,0,227,1,0,0,0,0,229,1,0,0,0,0,231,1,0,0,0,0, + 233,1,0,0,0,0,235,1,0,0,0,0,237,1,0,0,0,0,239,1,0,0,0,0,241,1,0,0,0,0, + 243,1,0,0,0,0,245,1,0,0,0,0,247,1,0,0,0,0,249,1,0,0,0,0,251,1,0,0,0,0, + 253,1,0,0,0,0,255,1,0,0,0,0,257,1,0,0,0,0,259,1,0,0,0,0,261,1,0,0,0,0, + 263,1,0,0,0,0,265,1,0,0,0,0,267,1,0,0,0,0,269,1,0,0,0,0,271,1,0,0,0,0, + 273,1,0,0,0,0,275,1,0,0,0,0,277,1,0,0,0,0,279,1,0,0,0,0,281,1,0,0,0,0, + 283,1,0,0,0,0,285,1,0,0,0,0,287,1,0,0,0,0,289,1,0,0,0,0,291,1,0,0,0,0, + 293,1,0,0,0,0,295,1,0,0,0,0,297,1,0,0,0,0,299,1,0,0,0,0,301,1,0,0,0,0, + 303,1,0,0,0,0,305,1,0,0,0,0,307,1,0,0,0,0,309,1,0,0,0,0,311,1,0,0,0,0, + 313,1,0,0,0,0,315,1,0,0,0,0,317,1,0,0,0,0,319,1,0,0,0,0,321,1,0,0,0,0, + 323,1,0,0,0,0,325,1,0,0,0,0,327,1,0,0,0,0,329,1,0,0,0,0,331,1,0,0,0,0, + 333,1,0,0,0,0,335,1,0,0,0,0,337,1,0,0,0,0,339,1,0,0,0,0,341,1,0,0,0,0, + 343,1,0,0,0,0,345,1,0,0,0,0,347,1,0,0,0,0,349,1,0,0,0,0,351,1,0,0,0,0, + 353,1,0,0,0,0,355,1,0,0,0,0,357,1,0,0,0,0,359,1,0,0,0,0,361,1,0,0,0,0, + 363,1,0,0,0,0,365,1,0,0,0,0,367,1,0,0,0,0,369,1,0,0,0,0,371,1,0,0,0,0, + 373,1,0,0,0,0,375,1,0,0,0,0,377,1,0,0,0,0,379,1,0,0,0,0,381,1,0,0,0,0, + 383,1,0,0,0,0,385,1,0,0,0,1,391,1,0,0,0,3,393,1,0,0,0,5,395,1,0,0,0,7, + 397,1,0,0,0,9,399,1,0,0,0,11,401,1,0,0,0,13,403,1,0,0,0,15,405,1,0,0,0, + 17,407,1,0,0,0,19,409,1,0,0,0,21,411,1,0,0,0,23,414,1,0,0,0,25,416,1,0, + 0,0,27,418,1,0,0,0,29,421,1,0,0,0,31,424,1,0,0,0,33,426,1,0,0,0,35,428, + 1,0,0,0,37,430,1,0,0,0,39,433,1,0,0,0,41,435,1,0,0,0,43,438,1,0,0,0,45, + 441,1,0,0,0,47,444,1,0,0,0,49,447,1,0,0,0,51,453,1,0,0,0,53,460,1,0,0, + 0,55,464,1,0,0,0,57,470,1,0,0,0,59,474,1,0,0,0,61,480,1,0,0,0,63,488,1, + 0,0,0,65,492,1,0,0,0,67,495,1,0,0,0,69,499,1,0,0,0,71,506,1,0,0,0,73,520, + 1,0,0,0,75,527,1,0,0,0,77,533,1,0,0,0,79,541,1,0,0,0,81,544,1,0,0,0,83, + 552,1,0,0,0,85,557,1,0,0,0,87,562,1,0,0,0,89,568,1,0,0,0,91,576,1,0,0, + 0,93,583,1,0,0,0,95,590,1,0,0,0,97,599,1,0,0,0,99,610,1,0,0,0,101,617, + 1,0,0,0,103,623,1,0,0,0,105,636,1,0,0,0,107,649,1,0,0,0,109,667,1,0,0, + 0,111,676,1,0,0,0,113,684,1,0,0,0,115,695,1,0,0,0,117,704,1,0,0,0,119, + 711,1,0,0,0,121,716,1,0,0,0,123,723,1,0,0,0,125,732,1,0,0,0,127,737,1, + 0,0,0,129,742,1,0,0,0,131,747,1,0,0,0,133,751,1,0,0,0,135,758,1,0,0,0, + 137,765,1,0,0,0,139,775,1,0,0,0,141,782,1,0,0,0,143,790,1,0,0,0,145,795, + 1,0,0,0,147,799,1,0,0,0,149,807,1,0,0,0,151,812,1,0,0,0,153,817,1,0,0, + 0,155,822,1,0,0,0,157,828,1,0,0,0,159,835,1,0,0,0,161,838,1,0,0,0,163, + 845,1,0,0,0,165,855,1,0,0,0,167,858,1,0,0,0,169,864,1,0,0,0,171,872,1, + 0,0,0,173,882,1,0,0,0,175,888,1,0,0,0,177,895,1,0,0,0,179,903,1,0,0,0, + 181,913,1,0,0,0,183,918,1,0,0,0,185,921,1,0,0,0,187,928,1,0,0,0,189,933, + 1,0,0,0,191,937,1,0,0,0,193,942,1,0,0,0,195,947,1,0,0,0,197,953,1,0,0, + 0,199,959,1,0,0,0,201,967,1,0,0,0,203,970,1,0,0,0,205,974,1,0,0,0,207, + 982,1,0,0,0,209,987,1,0,0,0,211,990,1,0,0,0,213,997,1,0,0,0,215,1000,1, + 0,0,0,217,1003,1,0,0,0,219,1009,1,0,0,0,221,1015,1,0,0,0,223,1020,1,0, + 0,0,225,1027,1,0,0,0,227,1035,1,0,0,0,229,1041,1,0,0,0,231,1047,1,0,0, + 0,233,1057,1,0,0,0,235,1068,1,0,0,0,237,1075,1,0,0,0,239,1083,1,0,0,0, + 241,1091,1,0,0,0,243,1098,1,0,0,0,245,1106,1,0,0,0,247,1115,1,0,0,0,249, + 1125,1,0,0,0,251,1131,1,0,0,0,253,1140,1,0,0,0,255,1144,1,0,0,0,257,1149, + 1,0,0,0,259,1159,1,0,0,0,261,1166,1,0,0,0,263,1170,1,0,0,0,265,1176,1, + 0,0,0,267,1181,1,0,0,0,269,1191,1,0,0,0,271,1196,1,0,0,0,273,1199,1,0, + 0,0,275,1211,1,0,0,0,277,1219,1,0,0,0,279,1225,1,0,0,0,281,1232,1,0,0, + 0,283,1239,1,0,0,0,285,1245,1,0,0,0,287,1252,1,0,0,0,289,1259,1,0,0,0, + 291,1264,1,0,0,0,293,1272,1,0,0,0,295,1277,1,0,0,0,297,1283,1,0,0,0,299, + 1288,1,0,0,0,301,1296,1,0,0,0,303,1308,1,0,0,0,305,1313,1,0,0,0,307,1323, + 1,0,0,0,309,1329,1,0,0,0,311,1339,1,0,0,0,313,1349,1,0,0,0,315,1357,1, + 0,0,0,317,1367,1,0,0,0,319,1377,1,0,0,0,321,1388,1,0,0,0,323,1392,1,0, + 0,0,325,1403,1,0,0,0,327,1408,1,0,0,0,329,1418,1,0,0,0,331,1424,1,0,0, + 0,333,1437,1,0,0,0,335,1442,1,0,0,0,337,1453,1,0,0,0,339,1463,1,0,0,0, + 341,1470,1,0,0,0,343,1477,1,0,0,0,345,1482,1,0,0,0,347,1488,1,0,0,0,349, + 1495,1,0,0,0,351,1501,1,0,0,0,353,1507,1,0,0,0,355,1512,1,0,0,0,357,1519, + 1,0,0,0,359,1526,1,0,0,0,361,1534,1,0,0,0,363,1539,1,0,0,0,365,1546,1, + 0,0,0,367,1549,1,0,0,0,369,1592,1,0,0,0,371,1635,1,0,0,0,373,1646,1,0, + 0,0,375,1648,1,0,0,0,377,1659,1,0,0,0,379,1662,1,0,0,0,381,1680,1,0,0, + 0,383,1694,1,0,0,0,385,1698,1,0,0,0,387,1700,1,0,0,0,389,1702,1,0,0,0, + 391,392,5,59,0,0,392,2,1,0,0,0,393,394,5,46,0,0,394,4,1,0,0,0,395,396, + 5,40,0,0,396,6,1,0,0,0,397,398,5,41,0,0,398,8,1,0,0,0,399,400,5,44,0,0, + 400,10,1,0,0,0,401,402,5,61,0,0,402,12,1,0,0,0,403,404,5,42,0,0,404,14, + 1,0,0,0,405,406,5,43,0,0,406,16,1,0,0,0,407,408,5,45,0,0,408,18,1,0,0, + 0,409,410,5,126,0,0,410,20,1,0,0,0,411,412,5,124,0,0,412,413,5,124,0,0, + 413,22,1,0,0,0,414,415,5,47,0,0,415,24,1,0,0,0,416,417,5,37,0,0,417,26, + 1,0,0,0,418,419,5,60,0,0,419,420,5,60,0,0,420,28,1,0,0,0,421,422,5,62, + 0,0,422,423,5,62,0,0,423,30,1,0,0,0,424,425,5,38,0,0,425,32,1,0,0,0,426, + 427,5,124,0,0,427,34,1,0,0,0,428,429,5,60,0,0,429,36,1,0,0,0,430,431,5, + 60,0,0,431,432,5,61,0,0,432,38,1,0,0,0,433,434,5,62,0,0,434,40,1,0,0,0, + 435,436,5,62,0,0,436,437,5,61,0,0,437,42,1,0,0,0,438,439,5,61,0,0,439, + 440,5,61,0,0,440,44,1,0,0,0,441,442,5,33,0,0,442,443,5,61,0,0,443,46,1, + 0,0,0,444,445,5,60,0,0,445,446,5,62,0,0,446,48,1,0,0,0,447,448,7,0,0,0, + 448,449,7,1,0,0,449,450,7,2,0,0,450,451,7,3,0,0,451,452,7,4,0,0,452,50, + 1,0,0,0,453,454,7,0,0,0,454,455,7,5,0,0,455,456,7,4,0,0,456,457,7,6,0, + 0,457,458,7,2,0,0,458,459,7,7,0,0,459,52,1,0,0,0,460,461,7,0,0,0,461,462, + 7,8,0,0,462,463,7,8,0,0,463,54,1,0,0,0,464,465,7,0,0,0,465,466,7,9,0,0, + 466,467,7,4,0,0,467,468,7,10,0,0,468,469,7,3,0,0,469,56,1,0,0,0,470,471, + 7,0,0,0,471,472,7,11,0,0,472,473,7,11,0,0,473,58,1,0,0,0,474,475,7,0,0, + 0,475,476,7,11,0,0,476,477,7,4,0,0,477,478,7,10,0,0,478,479,7,3,0,0,479, + 60,1,0,0,0,480,481,7,0,0,0,481,482,7,7,0,0,482,483,7,0,0,0,483,484,7,11, + 0,0,484,485,7,12,0,0,485,486,7,13,0,0,486,487,7,10,0,0,487,62,1,0,0,0, + 488,489,7,0,0,0,489,490,7,7,0,0,490,491,7,8,0,0,491,64,1,0,0,0,492,493, + 7,0,0,0,493,494,7,14,0,0,494,66,1,0,0,0,495,496,7,0,0,0,496,497,7,14,0, + 0,497,498,7,5,0,0,498,68,1,0,0,0,499,500,7,0,0,0,500,501,7,4,0,0,501,502, + 7,4,0,0,502,503,7,0,0,0,503,504,7,5,0,0,504,505,7,15,0,0,505,70,1,0,0, + 0,506,507,7,0,0,0,507,508,7,16,0,0,508,509,7,4,0,0,509,510,7,2,0,0,510, + 511,7,6,0,0,511,512,7,7,0,0,512,513,7,5,0,0,513,514,7,3,0,0,514,515,7, + 10,0,0,515,516,7,17,0,0,516,517,7,10,0,0,517,518,7,7,0,0,518,519,7,4,0, + 0,519,72,1,0,0,0,520,521,7,1,0,0,521,522,7,10,0,0,522,523,7,9,0,0,523, + 524,7,2,0,0,524,525,7,3,0,0,525,526,7,10,0,0,526,74,1,0,0,0,527,528,7, + 1,0,0,528,529,7,10,0,0,529,530,7,18,0,0,530,531,7,6,0,0,531,532,7,7,0, + 0,532,76,1,0,0,0,533,534,7,1,0,0,534,535,7,10,0,0,535,536,7,4,0,0,536, + 537,7,19,0,0,537,538,7,10,0,0,538,539,7,10,0,0,539,540,7,7,0,0,540,78, + 1,0,0,0,541,542,7,1,0,0,542,543,7,12,0,0,543,80,1,0,0,0,544,545,7,5,0, + 0,545,546,7,0,0,0,546,547,7,14,0,0,547,548,7,5,0,0,548,549,7,0,0,0,549, + 550,7,8,0,0,550,551,7,10,0,0,551,82,1,0,0,0,552,553,7,5,0,0,553,554,7, + 0,0,0,554,555,7,14,0,0,555,556,7,10,0,0,556,84,1,0,0,0,557,558,7,5,0,0, + 558,559,7,0,0,0,559,560,7,14,0,0,560,561,7,4,0,0,561,86,1,0,0,0,562,563, + 7,5,0,0,563,564,7,15,0,0,564,565,7,10,0,0,565,566,7,5,0,0,566,567,7,20, + 0,0,567,88,1,0,0,0,568,569,7,5,0,0,569,570,7,2,0,0,570,571,7,11,0,0,571, + 572,7,11,0,0,572,573,7,0,0,0,573,574,7,4,0,0,574,575,7,10,0,0,575,90,1, + 0,0,0,576,577,7,5,0,0,577,578,7,2,0,0,578,579,7,11,0,0,579,580,7,16,0, + 0,580,581,7,17,0,0,581,582,7,7,0,0,582,92,1,0,0,0,583,584,7,5,0,0,584, + 585,7,2,0,0,585,586,7,17,0,0,586,587,7,17,0,0,587,588,7,6,0,0,588,589, + 7,4,0,0,589,94,1,0,0,0,590,591,7,5,0,0,591,592,7,2,0,0,592,593,7,7,0,0, + 593,594,7,9,0,0,594,595,7,11,0,0,595,596,7,6,0,0,596,597,7,5,0,0,597,598, + 7,4,0,0,598,96,1,0,0,0,599,600,7,5,0,0,600,601,7,2,0,0,601,602,7,7,0,0, + 602,603,7,14,0,0,603,604,7,4,0,0,604,605,7,3,0,0,605,606,7,0,0,0,606,607, + 7,6,0,0,607,608,7,7,0,0,608,609,7,4,0,0,609,98,1,0,0,0,610,611,7,5,0,0, + 611,612,7,3,0,0,612,613,7,10,0,0,613,614,7,0,0,0,614,615,7,4,0,0,615,616, + 7,10,0,0,616,100,1,0,0,0,617,618,7,5,0,0,618,619,7,3,0,0,619,620,7,2,0, + 0,620,621,7,14,0,0,621,622,7,14,0,0,622,102,1,0,0,0,623,624,7,5,0,0,624, + 625,7,16,0,0,625,626,7,3,0,0,626,627,7,3,0,0,627,628,7,10,0,0,628,629, + 7,7,0,0,629,630,7,4,0,0,630,631,5,95,0,0,631,632,7,8,0,0,632,633,7,0,0, + 0,633,634,7,4,0,0,634,635,7,10,0,0,635,104,1,0,0,0,636,637,7,5,0,0,637, + 638,7,16,0,0,638,639,7,3,0,0,639,640,7,3,0,0,640,641,7,10,0,0,641,642, + 7,7,0,0,642,643,7,4,0,0,643,644,5,95,0,0,644,645,7,4,0,0,645,646,7,6,0, + 0,646,647,7,17,0,0,647,648,7,10,0,0,648,106,1,0,0,0,649,650,7,5,0,0,650, + 651,7,16,0,0,651,652,7,3,0,0,652,653,7,3,0,0,653,654,7,10,0,0,654,655, + 7,7,0,0,655,656,7,4,0,0,656,657,5,95,0,0,657,658,7,4,0,0,658,659,7,6,0, + 0,659,660,7,17,0,0,660,661,7,10,0,0,661,662,7,14,0,0,662,663,7,4,0,0,663, + 664,7,0,0,0,664,665,7,17,0,0,665,666,7,21,0,0,666,108,1,0,0,0,667,668, + 7,8,0,0,668,669,7,0,0,0,669,670,7,4,0,0,670,671,7,0,0,0,671,672,7,1,0, + 0,672,673,7,0,0,0,673,674,7,14,0,0,674,675,7,10,0,0,675,110,1,0,0,0,676, + 677,7,8,0,0,677,678,7,10,0,0,678,679,7,9,0,0,679,680,7,0,0,0,680,681,7, + 16,0,0,681,682,7,11,0,0,682,683,7,4,0,0,683,112,1,0,0,0,684,685,7,8,0, + 0,685,686,7,10,0,0,686,687,7,9,0,0,687,688,7,10,0,0,688,689,7,3,0,0,689, + 690,7,3,0,0,690,691,7,0,0,0,691,692,7,1,0,0,692,693,7,11,0,0,693,694,7, + 10,0,0,694,114,1,0,0,0,695,696,7,8,0,0,696,697,7,10,0,0,697,698,7,9,0, + 0,698,699,7,10,0,0,699,700,7,3,0,0,700,701,7,3,0,0,701,702,7,10,0,0,702, + 703,7,8,0,0,703,116,1,0,0,0,704,705,7,8,0,0,705,706,7,10,0,0,706,707,7, + 11,0,0,707,708,7,10,0,0,708,709,7,4,0,0,709,710,7,10,0,0,710,118,1,0,0, + 0,711,712,7,8,0,0,712,713,7,10,0,0,713,714,7,14,0,0,714,715,7,5,0,0,715, + 120,1,0,0,0,716,717,7,8,0,0,717,718,7,10,0,0,718,719,7,4,0,0,719,720,7, + 0,0,0,720,721,7,5,0,0,721,722,7,15,0,0,722,122,1,0,0,0,723,724,7,8,0,0, + 724,725,7,6,0,0,725,726,7,14,0,0,726,727,7,4,0,0,727,728,7,6,0,0,728,729, + 7,7,0,0,729,730,7,5,0,0,730,731,7,4,0,0,731,124,1,0,0,0,732,733,7,8,0, + 0,733,734,7,3,0,0,734,735,7,2,0,0,735,736,7,21,0,0,736,126,1,0,0,0,737, + 738,7,10,0,0,738,739,7,0,0,0,739,740,7,5,0,0,740,741,7,15,0,0,741,128, + 1,0,0,0,742,743,7,10,0,0,743,744,7,11,0,0,744,745,7,14,0,0,745,746,7,10, + 0,0,746,130,1,0,0,0,747,748,7,10,0,0,748,749,7,7,0,0,749,750,7,8,0,0,750, + 132,1,0,0,0,751,752,7,10,0,0,752,753,7,14,0,0,753,754,7,5,0,0,754,755, + 7,0,0,0,755,756,7,21,0,0,756,757,7,10,0,0,757,134,1,0,0,0,758,759,7,10, + 0,0,759,760,7,22,0,0,760,761,7,5,0,0,761,762,7,10,0,0,762,763,7,21,0,0, + 763,764,7,4,0,0,764,136,1,0,0,0,765,766,7,10,0,0,766,767,7,22,0,0,767, + 768,7,5,0,0,768,769,7,11,0,0,769,770,7,16,0,0,770,771,7,14,0,0,771,772, + 7,6,0,0,772,773,7,23,0,0,773,774,7,10,0,0,774,138,1,0,0,0,775,776,7,10, + 0,0,776,777,7,22,0,0,777,778,7,6,0,0,778,779,7,14,0,0,779,780,7,4,0,0, + 780,781,7,14,0,0,781,140,1,0,0,0,782,783,7,10,0,0,783,784,7,22,0,0,784, + 785,7,21,0,0,785,786,7,11,0,0,786,787,7,0,0,0,787,788,7,6,0,0,788,789, + 7,7,0,0,789,142,1,0,0,0,790,791,7,9,0,0,791,792,7,0,0,0,792,793,7,6,0, + 0,793,794,7,11,0,0,794,144,1,0,0,0,795,796,7,9,0,0,796,797,7,2,0,0,797, + 798,7,3,0,0,798,146,1,0,0,0,799,800,7,9,0,0,800,801,7,2,0,0,801,802,7, + 3,0,0,802,803,7,10,0,0,803,804,7,6,0,0,804,805,7,18,0,0,805,806,7,7,0, + 0,806,148,1,0,0,0,807,808,7,9,0,0,808,809,7,3,0,0,809,810,7,2,0,0,810, + 811,7,17,0,0,811,150,1,0,0,0,812,813,7,9,0,0,813,814,7,16,0,0,814,815, + 7,11,0,0,815,816,7,11,0,0,816,152,1,0,0,0,817,818,7,18,0,0,818,819,7,11, + 0,0,819,820,7,2,0,0,820,821,7,1,0,0,821,154,1,0,0,0,822,823,7,18,0,0,823, + 824,7,3,0,0,824,825,7,2,0,0,825,826,7,16,0,0,826,827,7,21,0,0,827,156, + 1,0,0,0,828,829,7,15,0,0,829,830,7,0,0,0,830,831,7,23,0,0,831,832,7,6, + 0,0,832,833,7,7,0,0,833,834,7,18,0,0,834,158,1,0,0,0,835,836,7,6,0,0,836, + 837,7,9,0,0,837,160,1,0,0,0,838,839,7,6,0,0,839,840,7,18,0,0,840,841,7, + 7,0,0,841,842,7,2,0,0,842,843,7,3,0,0,843,844,7,10,0,0,844,162,1,0,0,0, + 845,846,7,6,0,0,846,847,7,17,0,0,847,848,7,17,0,0,848,849,7,10,0,0,849, + 850,7,8,0,0,850,851,7,6,0,0,851,852,7,0,0,0,852,853,7,4,0,0,853,854,7, + 10,0,0,854,164,1,0,0,0,855,856,7,6,0,0,856,857,7,7,0,0,857,166,1,0,0,0, + 858,859,7,6,0,0,859,860,7,7,0,0,860,861,7,8,0,0,861,862,7,10,0,0,862,863, + 7,22,0,0,863,168,1,0,0,0,864,865,7,6,0,0,865,866,7,7,0,0,866,867,7,8,0, + 0,867,868,7,10,0,0,868,869,7,22,0,0,869,870,7,10,0,0,870,871,7,8,0,0,871, + 170,1,0,0,0,872,873,7,6,0,0,873,874,7,7,0,0,874,875,7,6,0,0,875,876,7, + 4,0,0,876,877,7,6,0,0,877,878,7,0,0,0,878,879,7,11,0,0,879,880,7,11,0, + 0,880,881,7,12,0,0,881,172,1,0,0,0,882,883,7,6,0,0,883,884,7,7,0,0,884, + 885,7,7,0,0,885,886,7,10,0,0,886,887,7,3,0,0,887,174,1,0,0,0,888,889,7, + 6,0,0,889,890,7,7,0,0,890,891,7,14,0,0,891,892,7,10,0,0,892,893,7,3,0, + 0,893,894,7,4,0,0,894,176,1,0,0,0,895,896,7,6,0,0,896,897,7,7,0,0,897, + 898,7,14,0,0,898,899,7,4,0,0,899,900,7,10,0,0,900,901,7,0,0,0,901,902, + 7,8,0,0,902,178,1,0,0,0,903,904,7,6,0,0,904,905,7,7,0,0,905,906,7,4,0, + 0,906,907,7,10,0,0,907,908,7,3,0,0,908,909,7,14,0,0,909,910,7,10,0,0,910, + 911,7,5,0,0,911,912,7,4,0,0,912,180,1,0,0,0,913,914,7,6,0,0,914,915,7, + 7,0,0,915,916,7,4,0,0,916,917,7,2,0,0,917,182,1,0,0,0,918,919,7,6,0,0, + 919,920,7,14,0,0,920,184,1,0,0,0,921,922,7,6,0,0,922,923,7,14,0,0,923, + 924,7,7,0,0,924,925,7,16,0,0,925,926,7,11,0,0,926,927,7,11,0,0,927,186, + 1,0,0,0,928,929,7,24,0,0,929,930,7,2,0,0,930,931,7,6,0,0,931,932,7,7,0, + 0,932,188,1,0,0,0,933,934,7,20,0,0,934,935,7,10,0,0,935,936,7,12,0,0,936, + 190,1,0,0,0,937,938,7,11,0,0,938,939,7,10,0,0,939,940,7,9,0,0,940,941, + 7,4,0,0,941,192,1,0,0,0,942,943,7,11,0,0,943,944,7,6,0,0,944,945,7,20, + 0,0,945,946,7,10,0,0,946,194,1,0,0,0,947,948,7,11,0,0,948,949,7,6,0,0, + 949,950,7,17,0,0,950,951,7,6,0,0,951,952,7,4,0,0,952,196,1,0,0,0,953,954, + 7,17,0,0,954,955,7,0,0,0,955,956,7,4,0,0,956,957,7,5,0,0,957,958,7,15, + 0,0,958,198,1,0,0,0,959,960,7,7,0,0,960,961,7,0,0,0,961,962,7,4,0,0,962, + 963,7,16,0,0,963,964,7,3,0,0,964,965,7,0,0,0,965,966,7,11,0,0,966,200, + 1,0,0,0,967,968,7,7,0,0,968,969,7,2,0,0,969,202,1,0,0,0,970,971,7,7,0, + 0,971,972,7,2,0,0,972,973,7,4,0,0,973,204,1,0,0,0,974,975,7,7,0,0,975, + 976,7,2,0,0,976,977,7,4,0,0,977,978,7,7,0,0,978,979,7,16,0,0,979,980,7, + 11,0,0,980,981,7,11,0,0,981,206,1,0,0,0,982,983,7,7,0,0,983,984,7,16,0, + 0,984,985,7,11,0,0,985,986,7,11,0,0,986,208,1,0,0,0,987,988,7,2,0,0,988, + 989,7,9,0,0,989,210,1,0,0,0,990,991,7,2,0,0,991,992,7,9,0,0,992,993,7, + 9,0,0,993,994,7,14,0,0,994,995,7,10,0,0,995,996,7,4,0,0,996,212,1,0,0, + 0,997,998,7,2,0,0,998,999,7,7,0,0,999,214,1,0,0,0,1000,1001,7,2,0,0,1001, + 1002,7,3,0,0,1002,216,1,0,0,0,1003,1004,7,2,0,0,1004,1005,7,3,0,0,1005, + 1006,7,8,0,0,1006,1007,7,10,0,0,1007,1008,7,3,0,0,1008,218,1,0,0,0,1009, + 1010,7,2,0,0,1010,1011,7,16,0,0,1011,1012,7,4,0,0,1012,1013,7,10,0,0,1013, + 1014,7,3,0,0,1014,220,1,0,0,0,1015,1016,7,21,0,0,1016,1017,7,11,0,0,1017, + 1018,7,0,0,0,1018,1019,7,7,0,0,1019,222,1,0,0,0,1020,1021,7,21,0,0,1021, + 1022,7,3,0,0,1022,1023,7,0,0,0,1023,1024,7,18,0,0,1024,1025,7,17,0,0,1025, + 1026,7,0,0,0,1026,224,1,0,0,0,1027,1028,7,21,0,0,1028,1029,7,3,0,0,1029, + 1030,7,6,0,0,1030,1031,7,17,0,0,1031,1032,7,0,0,0,1032,1033,7,3,0,0,1033, + 1034,7,12,0,0,1034,226,1,0,0,0,1035,1036,7,25,0,0,1036,1037,7,16,0,0,1037, + 1038,7,10,0,0,1038,1039,7,3,0,0,1039,1040,7,12,0,0,1040,228,1,0,0,0,1041, + 1042,7,3,0,0,1042,1043,7,0,0,0,1043,1044,7,6,0,0,1044,1045,7,14,0,0,1045, + 1046,7,10,0,0,1046,230,1,0,0,0,1047,1048,7,3,0,0,1048,1049,7,10,0,0,1049, + 1050,7,5,0,0,1050,1051,7,16,0,0,1051,1052,7,3,0,0,1052,1053,7,14,0,0,1053, + 1054,7,6,0,0,1054,1055,7,23,0,0,1055,1056,7,10,0,0,1056,232,1,0,0,0,1057, + 1058,7,3,0,0,1058,1059,7,10,0,0,1059,1060,7,9,0,0,1060,1061,7,10,0,0,1061, + 1062,7,3,0,0,1062,1063,7,10,0,0,1063,1064,7,7,0,0,1064,1065,7,5,0,0,1065, + 1066,7,10,0,0,1066,1067,7,14,0,0,1067,234,1,0,0,0,1068,1069,7,3,0,0,1069, + 1070,7,10,0,0,1070,1071,7,18,0,0,1071,1072,7,10,0,0,1072,1073,7,22,0,0, + 1073,1074,7,21,0,0,1074,236,1,0,0,0,1075,1076,7,3,0,0,1076,1077,7,10,0, + 0,1077,1078,7,6,0,0,1078,1079,7,7,0,0,1079,1080,7,8,0,0,1080,1081,7,10, + 0,0,1081,1082,7,22,0,0,1082,238,1,0,0,0,1083,1084,7,3,0,0,1084,1085,7, + 10,0,0,1085,1086,7,11,0,0,1086,1087,7,10,0,0,1087,1088,7,0,0,0,1088,1089, + 7,14,0,0,1089,1090,7,10,0,0,1090,240,1,0,0,0,1091,1092,7,3,0,0,1092,1093, + 7,10,0,0,1093,1094,7,7,0,0,1094,1095,7,0,0,0,1095,1096,7,17,0,0,1096,1097, + 7,10,0,0,1097,242,1,0,0,0,1098,1099,7,3,0,0,1099,1100,7,10,0,0,1100,1101, + 7,21,0,0,1101,1102,7,11,0,0,1102,1103,7,0,0,0,1103,1104,7,5,0,0,1104,1105, + 7,10,0,0,1105,244,1,0,0,0,1106,1107,7,3,0,0,1107,1108,7,10,0,0,1108,1109, + 7,14,0,0,1109,1110,7,4,0,0,1110,1111,7,3,0,0,1111,1112,7,6,0,0,1112,1113, + 7,5,0,0,1113,1114,7,4,0,0,1114,246,1,0,0,0,1115,1116,7,3,0,0,1116,1117, + 7,10,0,0,1117,1118,7,4,0,0,1118,1119,7,16,0,0,1119,1120,7,3,0,0,1120,1121, + 7,7,0,0,1121,1122,7,6,0,0,1122,1123,7,7,0,0,1123,1124,7,18,0,0,1124,248, + 1,0,0,0,1125,1126,7,3,0,0,1126,1127,7,6,0,0,1127,1128,7,18,0,0,1128,1129, + 7,15,0,0,1129,1130,7,4,0,0,1130,250,1,0,0,0,1131,1132,7,3,0,0,1132,1133, + 7,2,0,0,1133,1134,7,11,0,0,1134,1135,7,11,0,0,1135,1136,7,1,0,0,1136,1137, + 7,0,0,0,1137,1138,7,5,0,0,1138,1139,7,20,0,0,1139,252,1,0,0,0,1140,1141, + 7,3,0,0,1141,1142,7,2,0,0,1142,1143,7,19,0,0,1143,254,1,0,0,0,1144,1145, + 7,3,0,0,1145,1146,7,2,0,0,1146,1147,7,19,0,0,1147,1148,7,14,0,0,1148,256, + 1,0,0,0,1149,1150,7,14,0,0,1150,1151,7,0,0,0,1151,1152,7,23,0,0,1152,1153, + 7,10,0,0,1153,1154,7,21,0,0,1154,1155,7,2,0,0,1155,1156,7,6,0,0,1156,1157, + 7,7,0,0,1157,1158,7,4,0,0,1158,258,1,0,0,0,1159,1160,7,14,0,0,1160,1161, + 7,10,0,0,1161,1162,7,11,0,0,1162,1163,7,10,0,0,1163,1164,7,5,0,0,1164, + 1165,7,4,0,0,1165,260,1,0,0,0,1166,1167,7,14,0,0,1167,1168,7,10,0,0,1168, + 1169,7,4,0,0,1169,262,1,0,0,0,1170,1171,7,4,0,0,1171,1172,7,0,0,0,1172, + 1173,7,1,0,0,1173,1174,7,11,0,0,1174,1175,7,10,0,0,1175,264,1,0,0,0,1176, + 1177,7,4,0,0,1177,1178,7,10,0,0,1178,1179,7,17,0,0,1179,1180,7,21,0,0, + 1180,266,1,0,0,0,1181,1182,7,4,0,0,1182,1183,7,10,0,0,1183,1184,7,17,0, + 0,1184,1185,7,21,0,0,1185,1186,7,2,0,0,1186,1187,7,3,0,0,1187,1188,7,0, + 0,0,1188,1189,7,3,0,0,1189,1190,7,12,0,0,1190,268,1,0,0,0,1191,1192,7, + 4,0,0,1192,1193,7,15,0,0,1193,1194,7,10,0,0,1194,1195,7,7,0,0,1195,270, + 1,0,0,0,1196,1197,7,4,0,0,1197,1198,7,2,0,0,1198,272,1,0,0,0,1199,1200, + 7,4,0,0,1200,1201,7,3,0,0,1201,1202,7,0,0,0,1202,1203,7,7,0,0,1203,1204, + 7,14,0,0,1204,1205,7,0,0,0,1205,1206,7,5,0,0,1206,1207,7,4,0,0,1207,1208, + 7,6,0,0,1208,1209,7,2,0,0,1209,1210,7,7,0,0,1210,274,1,0,0,0,1211,1212, + 7,4,0,0,1212,1213,7,3,0,0,1213,1214,7,6,0,0,1214,1215,7,18,0,0,1215,1216, + 7,18,0,0,1216,1217,7,10,0,0,1217,1218,7,3,0,0,1218,276,1,0,0,0,1219,1220, + 7,16,0,0,1220,1221,7,7,0,0,1221,1222,7,6,0,0,1222,1223,7,2,0,0,1223,1224, + 7,7,0,0,1224,278,1,0,0,0,1225,1226,7,16,0,0,1226,1227,7,7,0,0,1227,1228, + 7,6,0,0,1228,1229,7,25,0,0,1229,1230,7,16,0,0,1230,1231,7,10,0,0,1231, + 280,1,0,0,0,1232,1233,7,16,0,0,1233,1234,7,21,0,0,1234,1235,7,8,0,0,1235, + 1236,7,0,0,0,1236,1237,7,4,0,0,1237,1238,7,10,0,0,1238,282,1,0,0,0,1239, + 1240,7,16,0,0,1240,1241,7,14,0,0,1241,1242,7,6,0,0,1242,1243,7,7,0,0,1243, + 1244,7,18,0,0,1244,284,1,0,0,0,1245,1246,7,23,0,0,1246,1247,7,0,0,0,1247, + 1248,7,5,0,0,1248,1249,7,16,0,0,1249,1250,7,16,0,0,1250,1251,7,17,0,0, + 1251,286,1,0,0,0,1252,1253,7,23,0,0,1253,1254,7,0,0,0,1254,1255,7,11,0, + 0,1255,1256,7,16,0,0,1256,1257,7,10,0,0,1257,1258,7,14,0,0,1258,288,1, + 0,0,0,1259,1260,7,23,0,0,1260,1261,7,6,0,0,1261,1262,7,10,0,0,1262,1263, + 7,19,0,0,1263,290,1,0,0,0,1264,1265,7,23,0,0,1265,1266,7,6,0,0,1266,1267, + 7,3,0,0,1267,1268,7,4,0,0,1268,1269,7,16,0,0,1269,1270,7,0,0,0,1270,1271, + 7,11,0,0,1271,292,1,0,0,0,1272,1273,7,19,0,0,1273,1274,7,15,0,0,1274,1275, + 7,10,0,0,1275,1276,7,7,0,0,1276,294,1,0,0,0,1277,1278,7,19,0,0,1278,1279, + 7,15,0,0,1279,1280,7,10,0,0,1280,1281,7,3,0,0,1281,1282,7,10,0,0,1282, + 296,1,0,0,0,1283,1284,7,19,0,0,1284,1285,7,6,0,0,1285,1286,7,4,0,0,1286, + 1287,7,15,0,0,1287,298,1,0,0,0,1288,1289,7,19,0,0,1289,1290,7,6,0,0,1290, + 1291,7,4,0,0,1291,1292,7,15,0,0,1292,1293,7,2,0,0,1293,1294,7,16,0,0,1294, + 1295,7,4,0,0,1295,300,1,0,0,0,1296,1297,7,9,0,0,1297,1298,7,6,0,0,1298, + 1299,7,3,0,0,1299,1300,7,14,0,0,1300,1301,7,4,0,0,1301,1302,5,95,0,0,1302, + 1303,7,23,0,0,1303,1304,7,0,0,0,1304,1305,7,11,0,0,1305,1306,7,16,0,0, + 1306,1307,7,10,0,0,1307,302,1,0,0,0,1308,1309,7,2,0,0,1309,1310,7,23,0, + 0,1310,1311,7,10,0,0,1311,1312,7,3,0,0,1312,304,1,0,0,0,1313,1314,7,21, + 0,0,1314,1315,7,0,0,0,1315,1316,7,3,0,0,1316,1317,7,4,0,0,1317,1318,7, + 6,0,0,1318,1319,7,4,0,0,1319,1320,7,6,0,0,1320,1321,7,2,0,0,1321,1322, + 7,7,0,0,1322,306,1,0,0,0,1323,1324,7,3,0,0,1324,1325,7,0,0,0,1325,1326, + 7,7,0,0,1326,1327,7,18,0,0,1327,1328,7,10,0,0,1328,308,1,0,0,0,1329,1330, + 7,21,0,0,1330,1331,7,3,0,0,1331,1332,7,10,0,0,1332,1333,7,5,0,0,1333,1334, + 7,10,0,0,1334,1335,7,8,0,0,1335,1336,7,6,0,0,1336,1337,7,7,0,0,1337,1338, + 7,18,0,0,1338,310,1,0,0,0,1339,1340,7,16,0,0,1340,1341,7,7,0,0,1341,1342, + 7,1,0,0,1342,1343,7,2,0,0,1343,1344,7,16,0,0,1344,1345,7,7,0,0,1345,1346, + 7,8,0,0,1346,1347,7,10,0,0,1347,1348,7,8,0,0,1348,312,1,0,0,0,1349,1350, + 7,5,0,0,1350,1351,7,16,0,0,1351,1352,7,3,0,0,1352,1353,7,3,0,0,1353,1354, + 7,10,0,0,1354,1355,7,7,0,0,1355,1356,7,4,0,0,1356,314,1,0,0,0,1357,1358, + 7,9,0,0,1358,1359,7,2,0,0,1359,1360,7,11,0,0,1360,1361,7,11,0,0,1361,1362, + 7,2,0,0,1362,1363,7,19,0,0,1363,1364,7,6,0,0,1364,1365,7,7,0,0,1365,1366, + 7,18,0,0,1366,316,1,0,0,0,1367,1368,7,5,0,0,1368,1369,7,16,0,0,1369,1370, + 7,17,0,0,1370,1371,7,10,0,0,1371,1372,5,95,0,0,1372,1373,7,8,0,0,1373, + 1374,7,6,0,0,1374,1375,7,14,0,0,1375,1376,7,4,0,0,1376,318,1,0,0,0,1377, + 1378,7,8,0,0,1378,1379,7,10,0,0,1379,1380,7,7,0,0,1380,1381,7,14,0,0,1381, + 1382,7,10,0,0,1382,1383,5,95,0,0,1383,1384,7,3,0,0,1384,1385,7,0,0,0,1385, + 1386,7,7,0,0,1386,1387,7,20,0,0,1387,320,1,0,0,0,1388,1389,7,11,0,0,1389, + 1390,7,0,0,0,1390,1391,7,18,0,0,1391,322,1,0,0,0,1392,1393,7,11,0,0,1393, + 1394,7,0,0,0,1394,1395,7,14,0,0,1395,1396,7,4,0,0,1396,1397,5,95,0,0,1397, + 1398,7,23,0,0,1398,1399,7,0,0,0,1399,1400,7,11,0,0,1400,1401,7,16,0,0, + 1401,1402,7,10,0,0,1402,324,1,0,0,0,1403,1404,7,11,0,0,1404,1405,7,10, + 0,0,1405,1406,7,0,0,0,1406,1407,7,8,0,0,1407,326,1,0,0,0,1408,1409,7,7, + 0,0,1409,1410,7,4,0,0,1410,1411,7,15,0,0,1411,1412,5,95,0,0,1412,1413, + 7,23,0,0,1413,1414,7,0,0,0,1414,1415,7,11,0,0,1415,1416,7,16,0,0,1416, + 1417,7,10,0,0,1417,328,1,0,0,0,1418,1419,7,7,0,0,1419,1420,7,4,0,0,1420, + 1421,7,6,0,0,1421,1422,7,11,0,0,1422,1423,7,10,0,0,1423,330,1,0,0,0,1424, + 1425,7,21,0,0,1425,1426,7,10,0,0,1426,1427,7,3,0,0,1427,1428,7,5,0,0,1428, + 1429,7,10,0,0,1429,1430,7,7,0,0,1430,1431,7,4,0,0,1431,1432,5,95,0,0,1432, + 1433,7,3,0,0,1433,1434,7,0,0,0,1434,1435,7,7,0,0,1435,1436,7,20,0,0,1436, + 332,1,0,0,0,1437,1438,7,3,0,0,1438,1439,7,0,0,0,1439,1440,7,7,0,0,1440, + 1441,7,20,0,0,1441,334,1,0,0,0,1442,1443,7,3,0,0,1443,1444,7,2,0,0,1444, + 1445,7,19,0,0,1445,1446,5,95,0,0,1446,1447,7,7,0,0,1447,1448,7,16,0,0, + 1448,1449,7,17,0,0,1449,1450,7,1,0,0,1450,1451,7,10,0,0,1451,1452,7,3, + 0,0,1452,336,1,0,0,0,1453,1454,7,18,0,0,1454,1455,7,10,0,0,1455,1456,7, + 7,0,0,1456,1457,7,10,0,0,1457,1458,7,3,0,0,1458,1459,7,0,0,0,1459,1460, + 7,4,0,0,1460,1461,7,10,0,0,1461,1462,7,8,0,0,1462,338,1,0,0,0,1463,1464, + 7,0,0,0,1464,1465,7,11,0,0,1465,1466,7,19,0,0,1466,1467,7,0,0,0,1467,1468, + 7,12,0,0,1468,1469,7,14,0,0,1469,340,1,0,0,0,1470,1471,7,14,0,0,1471,1472, + 7,4,0,0,1472,1473,7,2,0,0,1473,1474,7,3,0,0,1474,1475,7,10,0,0,1475,1476, + 7,8,0,0,1476,342,1,0,0,0,1477,1478,7,4,0,0,1478,1479,7,3,0,0,1479,1480, + 7,16,0,0,1480,1481,7,10,0,0,1481,344,1,0,0,0,1482,1483,7,9,0,0,1483,1484, + 7,0,0,0,1484,1485,7,11,0,0,1485,1486,7,14,0,0,1486,1487,7,10,0,0,1487, + 346,1,0,0,0,1488,1489,7,19,0,0,1489,1490,7,6,0,0,1490,1491,7,7,0,0,1491, + 1492,7,8,0,0,1492,1493,7,2,0,0,1493,1494,7,19,0,0,1494,348,1,0,0,0,1495, + 1496,7,7,0,0,1496,1497,7,16,0,0,1497,1498,7,11,0,0,1498,1499,7,11,0,0, + 1499,1500,7,14,0,0,1500,350,1,0,0,0,1501,1502,7,9,0,0,1502,1503,7,6,0, + 0,1503,1504,7,3,0,0,1504,1505,7,14,0,0,1505,1506,7,4,0,0,1506,352,1,0, + 0,0,1507,1508,7,11,0,0,1508,1509,7,0,0,0,1509,1510,7,14,0,0,1510,1511, + 7,4,0,0,1511,354,1,0,0,0,1512,1513,7,9,0,0,1513,1514,7,6,0,0,1514,1515, + 7,11,0,0,1515,1516,7,4,0,0,1516,1517,7,10,0,0,1517,1518,7,3,0,0,1518,356, + 1,0,0,0,1519,1520,7,18,0,0,1520,1521,7,3,0,0,1521,1522,7,2,0,0,1522,1523, + 7,16,0,0,1523,1524,7,21,0,0,1524,1525,7,14,0,0,1525,358,1,0,0,0,1526,1527, + 7,10,0,0,1527,1528,7,22,0,0,1528,1529,7,5,0,0,1529,1530,7,11,0,0,1530, + 1531,7,16,0,0,1531,1532,7,8,0,0,1532,1533,7,10,0,0,1533,360,1,0,0,0,1534, + 1535,7,4,0,0,1535,1536,7,6,0,0,1536,1537,7,10,0,0,1537,1538,7,14,0,0,1538, + 362,1,0,0,0,1539,1540,7,2,0,0,1540,1541,7,4,0,0,1541,1542,7,15,0,0,1542, + 1543,7,10,0,0,1543,1544,7,3,0,0,1544,1545,7,14,0,0,1545,364,1,0,0,0,1546, + 1547,7,8,0,0,1547,1548,7,2,0,0,1548,366,1,0,0,0,1549,1550,7,7,0,0,1550, + 1551,7,2,0,0,1551,1552,7,4,0,0,1552,1553,7,15,0,0,1553,1554,7,6,0,0,1554, + 1555,7,7,0,0,1555,1556,7,18,0,0,1556,368,1,0,0,0,1557,1563,5,34,0,0,1558, + 1562,8,26,0,0,1559,1560,5,34,0,0,1560,1562,5,34,0,0,1561,1558,1,0,0,0, + 1561,1559,1,0,0,0,1562,1565,1,0,0,0,1563,1561,1,0,0,0,1563,1564,1,0,0, + 0,1564,1566,1,0,0,0,1565,1563,1,0,0,0,1566,1593,5,34,0,0,1567,1573,5,96, + 0,0,1568,1572,8,27,0,0,1569,1570,5,96,0,0,1570,1572,5,96,0,0,1571,1568, + 1,0,0,0,1571,1569,1,0,0,0,1572,1575,1,0,0,0,1573,1571,1,0,0,0,1573,1574, + 1,0,0,0,1574,1576,1,0,0,0,1575,1573,1,0,0,0,1576,1593,5,96,0,0,1577,1581, + 5,91,0,0,1578,1580,8,28,0,0,1579,1578,1,0,0,0,1580,1583,1,0,0,0,1581,1579, + 1,0,0,0,1581,1582,1,0,0,0,1582,1584,1,0,0,0,1583,1581,1,0,0,0,1584,1593, + 5,93,0,0,1585,1589,7,29,0,0,1586,1588,7,30,0,0,1587,1586,1,0,0,0,1588, + 1591,1,0,0,0,1589,1587,1,0,0,0,1589,1590,1,0,0,0,1590,1593,1,0,0,0,1591, + 1589,1,0,0,0,1592,1557,1,0,0,0,1592,1567,1,0,0,0,1592,1577,1,0,0,0,1592, + 1585,1,0,0,0,1593,370,1,0,0,0,1594,1596,3,389,194,0,1595,1594,1,0,0,0, + 1596,1597,1,0,0,0,1597,1595,1,0,0,0,1597,1598,1,0,0,0,1598,1606,1,0,0, + 0,1599,1603,5,46,0,0,1600,1602,3,389,194,0,1601,1600,1,0,0,0,1602,1605, + 1,0,0,0,1603,1601,1,0,0,0,1603,1604,1,0,0,0,1604,1607,1,0,0,0,1605,1603, + 1,0,0,0,1606,1599,1,0,0,0,1606,1607,1,0,0,0,1607,1615,1,0,0,0,1608,1610, + 5,46,0,0,1609,1611,3,389,194,0,1610,1609,1,0,0,0,1611,1612,1,0,0,0,1612, + 1610,1,0,0,0,1612,1613,1,0,0,0,1613,1615,1,0,0,0,1614,1595,1,0,0,0,1614, + 1608,1,0,0,0,1615,1625,1,0,0,0,1616,1618,7,10,0,0,1617,1619,7,31,0,0,1618, + 1617,1,0,0,0,1618,1619,1,0,0,0,1619,1621,1,0,0,0,1620,1622,3,389,194,0, + 1621,1620,1,0,0,0,1622,1623,1,0,0,0,1623,1621,1,0,0,0,1623,1624,1,0,0, + 0,1624,1626,1,0,0,0,1625,1616,1,0,0,0,1625,1626,1,0,0,0,1626,1636,1,0, + 0,0,1627,1628,5,48,0,0,1628,1629,7,22,0,0,1629,1631,1,0,0,0,1630,1632, + 3,387,193,0,1631,1630,1,0,0,0,1632,1633,1,0,0,0,1633,1631,1,0,0,0,1633, + 1634,1,0,0,0,1634,1636,1,0,0,0,1635,1614,1,0,0,0,1635,1627,1,0,0,0,1636, + 372,1,0,0,0,1637,1641,5,63,0,0,1638,1640,3,389,194,0,1639,1638,1,0,0,0, + 1640,1643,1,0,0,0,1641,1639,1,0,0,0,1641,1642,1,0,0,0,1642,1647,1,0,0, + 0,1643,1641,1,0,0,0,1644,1645,7,32,0,0,1645,1647,3,369,184,0,1646,1637, + 1,0,0,0,1646,1644,1,0,0,0,1647,374,1,0,0,0,1648,1654,5,39,0,0,1649,1653, + 8,33,0,0,1650,1651,5,39,0,0,1651,1653,5,39,0,0,1652,1649,1,0,0,0,1652, + 1650,1,0,0,0,1653,1656,1,0,0,0,1654,1652,1,0,0,0,1654,1655,1,0,0,0,1655, + 1657,1,0,0,0,1656,1654,1,0,0,0,1657,1658,5,39,0,0,1658,376,1,0,0,0,1659, + 1660,7,22,0,0,1660,1661,3,375,187,0,1661,378,1,0,0,0,1662,1663,5,45,0, + 0,1663,1664,5,45,0,0,1664,1668,1,0,0,0,1665,1667,8,34,0,0,1666,1665,1, + 0,0,0,1667,1670,1,0,0,0,1668,1666,1,0,0,0,1668,1669,1,0,0,0,1669,1676, + 1,0,0,0,1670,1668,1,0,0,0,1671,1673,5,13,0,0,1672,1671,1,0,0,0,1672,1673, + 1,0,0,0,1673,1674,1,0,0,0,1674,1677,5,10,0,0,1675,1677,5,0,0,1,1676,1672, + 1,0,0,0,1676,1675,1,0,0,0,1677,1678,1,0,0,0,1678,1679,6,189,0,0,1679,380, + 1,0,0,0,1680,1681,5,47,0,0,1681,1682,5,42,0,0,1682,1686,1,0,0,0,1683,1685, + 9,0,0,0,1684,1683,1,0,0,0,1685,1688,1,0,0,0,1686,1687,1,0,0,0,1686,1684, + 1,0,0,0,1687,1689,1,0,0,0,1688,1686,1,0,0,0,1689,1690,5,42,0,0,1690,1691, + 5,47,0,0,1691,1692,1,0,0,0,1692,1693,6,190,0,0,1693,382,1,0,0,0,1694,1695, + 7,35,0,0,1695,1696,1,0,0,0,1696,1697,6,191,0,0,1697,384,1,0,0,0,1698,1699, + 9,0,0,0,1699,386,1,0,0,0,1700,1701,7,36,0,0,1701,388,1,0,0,0,1702,1703, + 7,37,0,0,1703,390,1,0,0,0,26,0,1561,1563,1571,1573,1581,1589,1592,1597, + 1603,1606,1612,1614,1618,1623,1625,1633,1635,1641,1646,1652,1654,1668, + 1672,1676,1686,1,0,1,0 + }; + + public static readonly ATN _ATN = + new ATNDeserializer().Deserialize(_serializedATN); + + +} +} // namespace DataProvider.SQLite.Parsing diff --git a/DataProvider/DataProvider.SQLite/Parsing/SQLiteParser.cs b/DataProvider/DataProvider.SQLite/Parsing/SQLiteParser.cs new file mode 100644 index 00000000..615e1fb6 --- /dev/null +++ b/DataProvider/DataProvider.SQLite/Parsing/SQLiteParser.cs @@ -0,0 +1,14500 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// ANTLR Version: 4.13.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// Generated from SQLiteParser.g4 by ANTLR 4.13.1 + +// Unreachable code detected +#pragma warning disable 0162 +// The variable '...' is assigned but its value is never used +#pragma warning disable 0219 +// Missing XML comment for publicly visible type or member '...' +#pragma warning disable 1591 +// Ambiguous reference in cref attribute +#pragma warning disable 419 + +namespace DataProvider.SQLite.Parsing { +using System; +using System.IO; +using System.Text; +using System.Diagnostics; +using System.Collections.Generic; +using Antlr4.Runtime; +using Antlr4.Runtime.Atn; +using Antlr4.Runtime.Misc; +using Antlr4.Runtime.Tree; +using DFA = Antlr4.Runtime.Dfa.DFA; + +[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.13.1")] +[System.CLSCompliant(false)] +public partial class SQLiteParser : Parser { + protected static DFA[] decisionToDFA; + protected static PredictionContextCache sharedContextCache = new PredictionContextCache(); + public const int + SCOL=1, DOT=2, OPEN_PAR=3, CLOSE_PAR=4, COMMA=5, ASSIGN=6, STAR=7, PLUS=8, + MINUS=9, TILDE=10, PIPE2=11, DIV=12, MOD=13, LT2=14, GT2=15, AMP=16, PIPE=17, + LT=18, LT_EQ=19, GT=20, GT_EQ=21, EQ=22, NOT_EQ1=23, NOT_EQ2=24, ABORT_=25, + ACTION_=26, ADD_=27, AFTER_=28, ALL_=29, ALTER_=30, ANALYZE_=31, AND_=32, + AS_=33, ASC_=34, ATTACH_=35, AUTOINCREMENT_=36, BEFORE_=37, BEGIN_=38, + BETWEEN_=39, BY_=40, CASCADE_=41, CASE_=42, CAST_=43, CHECK_=44, COLLATE_=45, + COLUMN_=46, COMMIT_=47, CONFLICT_=48, CONSTRAINT_=49, CREATE_=50, CROSS_=51, + CURRENT_DATE_=52, CURRENT_TIME_=53, CURRENT_TIMESTAMP_=54, DATABASE_=55, + DEFAULT_=56, DEFERRABLE_=57, DEFERRED_=58, DELETE_=59, DESC_=60, DETACH_=61, + DISTINCT_=62, DROP_=63, EACH_=64, ELSE_=65, END_=66, ESCAPE_=67, EXCEPT_=68, + EXCLUSIVE_=69, EXISTS_=70, EXPLAIN_=71, FAIL_=72, FOR_=73, FOREIGN_=74, + FROM_=75, FULL_=76, GLOB_=77, GROUP_=78, HAVING_=79, IF_=80, IGNORE_=81, + IMMEDIATE_=82, IN_=83, INDEX_=84, INDEXED_=85, INITIALLY_=86, INNER_=87, + INSERT_=88, INSTEAD_=89, INTERSECT_=90, INTO_=91, IS_=92, ISNULL_=93, + JOIN_=94, KEY_=95, LEFT_=96, LIKE_=97, LIMIT_=98, MATCH_=99, NATURAL_=100, + NO_=101, NOT_=102, NOTNULL_=103, NULL_=104, OF_=105, OFFSET_=106, ON_=107, + OR_=108, ORDER_=109, OUTER_=110, PLAN_=111, PRAGMA_=112, PRIMARY_=113, + QUERY_=114, RAISE_=115, RECURSIVE_=116, REFERENCES_=117, REGEXP_=118, + REINDEX_=119, RELEASE_=120, RENAME_=121, REPLACE_=122, RESTRICT_=123, + RETURNING_=124, RIGHT_=125, ROLLBACK_=126, ROW_=127, ROWS_=128, SAVEPOINT_=129, + SELECT_=130, SET_=131, TABLE_=132, TEMP_=133, TEMPORARY_=134, THEN_=135, + TO_=136, TRANSACTION_=137, TRIGGER_=138, UNION_=139, UNIQUE_=140, UPDATE_=141, + USING_=142, VACUUM_=143, VALUES_=144, VIEW_=145, VIRTUAL_=146, WHEN_=147, + WHERE_=148, WITH_=149, WITHOUT_=150, FIRST_VALUE_=151, OVER_=152, PARTITION_=153, + RANGE_=154, PRECEDING_=155, UNBOUNDED_=156, CURRENT_=157, FOLLOWING_=158, + CUME_DIST_=159, DENSE_RANK_=160, LAG_=161, LAST_VALUE_=162, LEAD_=163, + NTH_VALUE_=164, NTILE_=165, PERCENT_RANK_=166, RANK_=167, ROW_NUMBER_=168, + GENERATED_=169, ALWAYS_=170, STORED_=171, TRUE_=172, FALSE_=173, WINDOW_=174, + NULLS_=175, FIRST_=176, LAST_=177, FILTER_=178, GROUPS_=179, EXCLUDE_=180, + TIES_=181, OTHERS_=182, DO_=183, NOTHING_=184, IDENTIFIER=185, NUMERIC_LITERAL=186, + BIND_PARAMETER=187, STRING_LITERAL=188, BLOB_LITERAL=189, SINGLE_LINE_COMMENT=190, + MULTILINE_COMMENT=191, SPACES=192, UNEXPECTED_CHAR=193; + public const int + RULE_parse = 0, RULE_sql_stmt_list = 1, RULE_sql_stmt = 2, RULE_alter_table_stmt = 3, + RULE_analyze_stmt = 4, RULE_attach_stmt = 5, RULE_begin_stmt = 6, RULE_commit_stmt = 7, + RULE_rollback_stmt = 8, RULE_savepoint_stmt = 9, RULE_release_stmt = 10, + RULE_create_index_stmt = 11, RULE_indexed_column = 12, RULE_create_table_stmt = 13, + RULE_column_def = 14, RULE_type_name = 15, RULE_column_constraint = 16, + RULE_signed_number = 17, RULE_table_constraint = 18, RULE_foreign_key_clause = 19, + RULE_conflict_clause = 20, RULE_create_trigger_stmt = 21, RULE_create_view_stmt = 22, + RULE_create_virtual_table_stmt = 23, RULE_with_clause = 24, RULE_cte_table_name = 25, + RULE_recursive_cte = 26, RULE_common_table_expression = 27, RULE_delete_stmt = 28, + RULE_delete_stmt_limited = 29, RULE_detach_stmt = 30, RULE_drop_stmt = 31, + RULE_expr = 32, RULE_raise_function = 33, RULE_literal_value = 34, RULE_value_row = 35, + RULE_values_clause = 36, RULE_insert_stmt = 37, RULE_returning_clause = 38, + RULE_upsert_clause = 39, RULE_pragma_stmt = 40, RULE_pragma_value = 41, + RULE_reindex_stmt = 42, RULE_select_stmt = 43, RULE_join_clause = 44, + RULE_select_core = 45, RULE_factored_select_stmt = 46, RULE_simple_select_stmt = 47, + RULE_compound_select_stmt = 48, RULE_table_or_subquery = 49, RULE_result_column = 50, + RULE_join_operator = 51, RULE_join_constraint = 52, RULE_compound_operator = 53, + RULE_update_stmt = 54, RULE_column_name_list = 55, RULE_update_stmt_limited = 56, + RULE_qualified_table_name = 57, RULE_vacuum_stmt = 58, RULE_filter_clause = 59, + RULE_window_defn = 60, RULE_over_clause = 61, RULE_frame_spec = 62, RULE_frame_clause = 63, + RULE_simple_function_invocation = 64, RULE_aggregate_function_invocation = 65, + RULE_window_function_invocation = 66, RULE_common_table_stmt = 67, RULE_order_by_stmt = 68, + RULE_limit_stmt = 69, RULE_ordering_term = 70, RULE_asc_desc = 71, RULE_frame_left = 72, + RULE_frame_right = 73, RULE_frame_single = 74, RULE_window_function = 75, + RULE_offset = 76, RULE_default_value = 77, RULE_partition_by = 78, RULE_order_by_expr = 79, + RULE_order_by_expr_asc_desc = 80, RULE_expr_asc_desc = 81, RULE_initial_select = 82, + RULE_recursive_select = 83, RULE_unary_operator = 84, RULE_error_message = 85, + RULE_module_argument = 86, RULE_column_alias = 87, RULE_keyword = 88, + RULE_name = 89, RULE_function_name = 90, RULE_schema_name = 91, RULE_table_name = 92, + RULE_table_or_index_name = 93, RULE_column_name = 94, RULE_collation_name = 95, + RULE_foreign_table = 96, RULE_index_name = 97, RULE_trigger_name = 98, + RULE_view_name = 99, RULE_module_name = 100, RULE_pragma_name = 101, RULE_savepoint_name = 102, + RULE_table_alias = 103, RULE_transaction_name = 104, RULE_window_name = 105, + RULE_alias = 106, RULE_filename = 107, RULE_base_window_name = 108, RULE_simple_func = 109, + RULE_aggregate_func = 110, RULE_table_function_name = 111, RULE_any_name = 112; + public static readonly string[] ruleNames = { + "parse", "sql_stmt_list", "sql_stmt", "alter_table_stmt", "analyze_stmt", + "attach_stmt", "begin_stmt", "commit_stmt", "rollback_stmt", "savepoint_stmt", + "release_stmt", "create_index_stmt", "indexed_column", "create_table_stmt", + "column_def", "type_name", "column_constraint", "signed_number", "table_constraint", + "foreign_key_clause", "conflict_clause", "create_trigger_stmt", "create_view_stmt", + "create_virtual_table_stmt", "with_clause", "cte_table_name", "recursive_cte", + "common_table_expression", "delete_stmt", "delete_stmt_limited", "detach_stmt", + "drop_stmt", "expr", "raise_function", "literal_value", "value_row", "values_clause", + "insert_stmt", "returning_clause", "upsert_clause", "pragma_stmt", "pragma_value", + "reindex_stmt", "select_stmt", "join_clause", "select_core", "factored_select_stmt", + "simple_select_stmt", "compound_select_stmt", "table_or_subquery", "result_column", + "join_operator", "join_constraint", "compound_operator", "update_stmt", + "column_name_list", "update_stmt_limited", "qualified_table_name", "vacuum_stmt", + "filter_clause", "window_defn", "over_clause", "frame_spec", "frame_clause", + "simple_function_invocation", "aggregate_function_invocation", "window_function_invocation", + "common_table_stmt", "order_by_stmt", "limit_stmt", "ordering_term", "asc_desc", + "frame_left", "frame_right", "frame_single", "window_function", "offset", + "default_value", "partition_by", "order_by_expr", "order_by_expr_asc_desc", + "expr_asc_desc", "initial_select", "recursive_select", "unary_operator", + "error_message", "module_argument", "column_alias", "keyword", "name", + "function_name", "schema_name", "table_name", "table_or_index_name", "column_name", + "collation_name", "foreign_table", "index_name", "trigger_name", "view_name", + "module_name", "pragma_name", "savepoint_name", "table_alias", "transaction_name", + "window_name", "alias", "filename", "base_window_name", "simple_func", + "aggregate_func", "table_function_name", "any_name" + }; + + private static readonly string[] _LiteralNames = { + null, "';'", "'.'", "'('", "')'", "','", "'='", "'*'", "'+'", "'-'", "'~'", + "'||'", "'/'", "'%'", "'<<'", "'>>'", "'&'", "'|'", "'<'", "'<='", "'>'", + "'>='", "'=='", "'!='", "'<>'", "'ABORT'", "'ACTION'", "'ADD'", "'AFTER'", + "'ALL'", "'ALTER'", "'ANALYZE'", "'AND'", "'AS'", "'ASC'", "'ATTACH'", + "'AUTOINCREMENT'", "'BEFORE'", "'BEGIN'", "'BETWEEN'", "'BY'", "'CASCADE'", + "'CASE'", "'CAST'", "'CHECK'", "'COLLATE'", "'COLUMN'", "'COMMIT'", "'CONFLICT'", + "'CONSTRAINT'", "'CREATE'", "'CROSS'", "'CURRENT_DATE'", "'CURRENT_TIME'", + "'CURRENT_TIMESTAMP'", "'DATABASE'", "'DEFAULT'", "'DEFERRABLE'", "'DEFERRED'", + "'DELETE'", "'DESC'", "'DETACH'", "'DISTINCT'", "'DROP'", "'EACH'", "'ELSE'", + "'END'", "'ESCAPE'", "'EXCEPT'", "'EXCLUSIVE'", "'EXISTS'", "'EXPLAIN'", + "'FAIL'", "'FOR'", "'FOREIGN'", "'FROM'", "'FULL'", "'GLOB'", "'GROUP'", + "'HAVING'", "'IF'", "'IGNORE'", "'IMMEDIATE'", "'IN'", "'INDEX'", "'INDEXED'", + "'INITIALLY'", "'INNER'", "'INSERT'", "'INSTEAD'", "'INTERSECT'", "'INTO'", + "'IS'", "'ISNULL'", "'JOIN'", "'KEY'", "'LEFT'", "'LIKE'", "'LIMIT'", + "'MATCH'", "'NATURAL'", "'NO'", "'NOT'", "'NOTNULL'", "'NULL'", "'OF'", + "'OFFSET'", "'ON'", "'OR'", "'ORDER'", "'OUTER'", "'PLAN'", "'PRAGMA'", + "'PRIMARY'", "'QUERY'", "'RAISE'", "'RECURSIVE'", "'REFERENCES'", "'REGEXP'", + "'REINDEX'", "'RELEASE'", "'RENAME'", "'REPLACE'", "'RESTRICT'", "'RETURNING'", + "'RIGHT'", "'ROLLBACK'", "'ROW'", "'ROWS'", "'SAVEPOINT'", "'SELECT'", + "'SET'", "'TABLE'", "'TEMP'", "'TEMPORARY'", "'THEN'", "'TO'", "'TRANSACTION'", + "'TRIGGER'", "'UNION'", "'UNIQUE'", "'UPDATE'", "'USING'", "'VACUUM'", + "'VALUES'", "'VIEW'", "'VIRTUAL'", "'WHEN'", "'WHERE'", "'WITH'", "'WITHOUT'", + "'FIRST_VALUE'", "'OVER'", "'PARTITION'", "'RANGE'", "'PRECEDING'", "'UNBOUNDED'", + "'CURRENT'", "'FOLLOWING'", "'CUME_DIST'", "'DENSE_RANK'", "'LAG'", "'LAST_VALUE'", + "'LEAD'", "'NTH_VALUE'", "'NTILE'", "'PERCENT_RANK'", "'RANK'", "'ROW_NUMBER'", + "'GENERATED'", "'ALWAYS'", "'STORED'", "'TRUE'", "'FALSE'", "'WINDOW'", + "'NULLS'", "'FIRST'", "'LAST'", "'FILTER'", "'GROUPS'", "'EXCLUDE'", "'TIES'", + "'OTHERS'", "'DO'", "'NOTHING'" + }; + private static readonly string[] _SymbolicNames = { + null, "SCOL", "DOT", "OPEN_PAR", "CLOSE_PAR", "COMMA", "ASSIGN", "STAR", + "PLUS", "MINUS", "TILDE", "PIPE2", "DIV", "MOD", "LT2", "GT2", "AMP", + "PIPE", "LT", "LT_EQ", "GT", "GT_EQ", "EQ", "NOT_EQ1", "NOT_EQ2", "ABORT_", + "ACTION_", "ADD_", "AFTER_", "ALL_", "ALTER_", "ANALYZE_", "AND_", "AS_", + "ASC_", "ATTACH_", "AUTOINCREMENT_", "BEFORE_", "BEGIN_", "BETWEEN_", + "BY_", "CASCADE_", "CASE_", "CAST_", "CHECK_", "COLLATE_", "COLUMN_", + "COMMIT_", "CONFLICT_", "CONSTRAINT_", "CREATE_", "CROSS_", "CURRENT_DATE_", + "CURRENT_TIME_", "CURRENT_TIMESTAMP_", "DATABASE_", "DEFAULT_", "DEFERRABLE_", + "DEFERRED_", "DELETE_", "DESC_", "DETACH_", "DISTINCT_", "DROP_", "EACH_", + "ELSE_", "END_", "ESCAPE_", "EXCEPT_", "EXCLUSIVE_", "EXISTS_", "EXPLAIN_", + "FAIL_", "FOR_", "FOREIGN_", "FROM_", "FULL_", "GLOB_", "GROUP_", "HAVING_", + "IF_", "IGNORE_", "IMMEDIATE_", "IN_", "INDEX_", "INDEXED_", "INITIALLY_", + "INNER_", "INSERT_", "INSTEAD_", "INTERSECT_", "INTO_", "IS_", "ISNULL_", + "JOIN_", "KEY_", "LEFT_", "LIKE_", "LIMIT_", "MATCH_", "NATURAL_", "NO_", + "NOT_", "NOTNULL_", "NULL_", "OF_", "OFFSET_", "ON_", "OR_", "ORDER_", + "OUTER_", "PLAN_", "PRAGMA_", "PRIMARY_", "QUERY_", "RAISE_", "RECURSIVE_", + "REFERENCES_", "REGEXP_", "REINDEX_", "RELEASE_", "RENAME_", "REPLACE_", + "RESTRICT_", "RETURNING_", "RIGHT_", "ROLLBACK_", "ROW_", "ROWS_", "SAVEPOINT_", + "SELECT_", "SET_", "TABLE_", "TEMP_", "TEMPORARY_", "THEN_", "TO_", "TRANSACTION_", + "TRIGGER_", "UNION_", "UNIQUE_", "UPDATE_", "USING_", "VACUUM_", "VALUES_", + "VIEW_", "VIRTUAL_", "WHEN_", "WHERE_", "WITH_", "WITHOUT_", "FIRST_VALUE_", + "OVER_", "PARTITION_", "RANGE_", "PRECEDING_", "UNBOUNDED_", "CURRENT_", + "FOLLOWING_", "CUME_DIST_", "DENSE_RANK_", "LAG_", "LAST_VALUE_", "LEAD_", + "NTH_VALUE_", "NTILE_", "PERCENT_RANK_", "RANK_", "ROW_NUMBER_", "GENERATED_", + "ALWAYS_", "STORED_", "TRUE_", "FALSE_", "WINDOW_", "NULLS_", "FIRST_", + "LAST_", "FILTER_", "GROUPS_", "EXCLUDE_", "TIES_", "OTHERS_", "DO_", + "NOTHING_", "IDENTIFIER", "NUMERIC_LITERAL", "BIND_PARAMETER", "STRING_LITERAL", + "BLOB_LITERAL", "SINGLE_LINE_COMMENT", "MULTILINE_COMMENT", "SPACES", + "UNEXPECTED_CHAR" + }; + public static readonly IVocabulary DefaultVocabulary = new Vocabulary(_LiteralNames, _SymbolicNames); + + [NotNull] + public override IVocabulary Vocabulary + { + get + { + return DefaultVocabulary; + } + } + + public override string GrammarFileName { get { return "SQLiteParser.g4"; } } + + public override string[] RuleNames { get { return ruleNames; } } + + public override int[] SerializedAtn { get { return _serializedATN; } } + + static SQLiteParser() { + decisionToDFA = new DFA[_ATN.NumberOfDecisions]; + for (int i = 0; i < _ATN.NumberOfDecisions; i++) { + decisionToDFA[i] = new DFA(_ATN.GetDecisionState(i), i); + } + } + + public SQLiteParser(ITokenStream input) : this(input, Console.Out, Console.Error) { } + + public SQLiteParser(ITokenStream input, TextWriter output, TextWriter errorOutput) + : base(input, output, errorOutput) + { + Interpreter = new ParserATNSimulator(this, _ATN, decisionToDFA, sharedContextCache); + } + + public partial class ParseContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode Eof() { return GetToken(SQLiteParser.Eof, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Sql_stmt_listContext[] sql_stmt_list() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Sql_stmt_listContext sql_stmt_list(int i) { + return GetRuleContext(i); + } + public ParseContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_parse; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterParse(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitParse(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitParse(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ParseContext parse() { + ParseContext _localctx = new ParseContext(Context, State); + EnterRule(_localctx, 0, RULE_parse); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 229; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while ((((_la) & ~0x3f) == 0 && ((1L << _la) & -6339801325483589630L) != 0) || ((((_la - 66)) & ~0x3f) == 0 && ((1L << (_la - 66)) & -7971300971697405919L) != 0) || ((((_la - 130)) & ~0x3f) == 0 && ((1L << (_la - 130)) & 550913L) != 0)) { + { + { + State = 226; + sql_stmt_list(); + } + } + State = 231; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 232; + Match(Eof); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Sql_stmt_listContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Sql_stmtContext[] sql_stmt() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Sql_stmtContext sql_stmt(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] SCOL() { return GetTokens(SQLiteParser.SCOL); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode SCOL(int i) { + return GetToken(SQLiteParser.SCOL, i); + } + public Sql_stmt_listContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_sql_stmt_list; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterSql_stmt_list(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitSql_stmt_list(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitSql_stmt_list(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Sql_stmt_listContext sql_stmt_list() { + Sql_stmt_listContext _localctx = new Sql_stmt_listContext(Context, State); + EnterRule(_localctx, 2, RULE_sql_stmt_list); + int _la; + try { + int _alt; + EnterOuterAlt(_localctx, 1); + { + State = 237; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==SCOL) { + { + { + State = 234; + Match(SCOL); + } + } + State = 239; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 240; + sql_stmt(); + State = 249; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,3,Context); + while ( _alt!=2 && _alt!=global::Antlr4.Runtime.Atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + State = 242; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + do { + { + { + State = 241; + Match(SCOL); + } + } + State = 244; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } while ( _la==SCOL ); + State = 246; + sql_stmt(); + } + } + } + State = 251; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,3,Context); + } + State = 255; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,4,Context); + while ( _alt!=2 && _alt!=global::Antlr4.Runtime.Atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + State = 252; + Match(SCOL); + } + } + } + State = 257; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,4,Context); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Sql_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Alter_table_stmtContext alter_table_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Analyze_stmtContext analyze_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Attach_stmtContext attach_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Begin_stmtContext begin_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Commit_stmtContext commit_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Create_index_stmtContext create_index_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Create_table_stmtContext create_table_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Create_trigger_stmtContext create_trigger_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Create_view_stmtContext create_view_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Create_virtual_table_stmtContext create_virtual_table_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Delete_stmtContext delete_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Delete_stmt_limitedContext delete_stmt_limited() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Detach_stmtContext detach_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Drop_stmtContext drop_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Insert_stmtContext insert_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Pragma_stmtContext pragma_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Reindex_stmtContext reindex_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Release_stmtContext release_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Rollback_stmtContext rollback_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Savepoint_stmtContext savepoint_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Select_stmtContext select_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Update_stmtContext update_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Update_stmt_limitedContext update_stmt_limited() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Vacuum_stmtContext vacuum_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode EXPLAIN_() { return GetToken(SQLiteParser.EXPLAIN_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode QUERY_() { return GetToken(SQLiteParser.QUERY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PLAN_() { return GetToken(SQLiteParser.PLAN_, 0); } + public Sql_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_sql_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterSql_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitSql_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitSql_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Sql_stmtContext sql_stmt() { + Sql_stmtContext _localctx = new Sql_stmtContext(Context, State); + EnterRule(_localctx, 4, RULE_sql_stmt); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 263; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==EXPLAIN_) { + { + State = 258; + Match(EXPLAIN_); + State = 261; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==QUERY_) { + { + State = 259; + Match(QUERY_); + State = 260; + Match(PLAN_); + } + } + + } + } + + State = 289; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,7,Context) ) { + case 1: + { + State = 265; + alter_table_stmt(); + } + break; + case 2: + { + State = 266; + analyze_stmt(); + } + break; + case 3: + { + State = 267; + attach_stmt(); + } + break; + case 4: + { + State = 268; + begin_stmt(); + } + break; + case 5: + { + State = 269; + commit_stmt(); + } + break; + case 6: + { + State = 270; + create_index_stmt(); + } + break; + case 7: + { + State = 271; + create_table_stmt(); + } + break; + case 8: + { + State = 272; + create_trigger_stmt(); + } + break; + case 9: + { + State = 273; + create_view_stmt(); + } + break; + case 10: + { + State = 274; + create_virtual_table_stmt(); + } + break; + case 11: + { + State = 275; + delete_stmt(); + } + break; + case 12: + { + State = 276; + delete_stmt_limited(); + } + break; + case 13: + { + State = 277; + detach_stmt(); + } + break; + case 14: + { + State = 278; + drop_stmt(); + } + break; + case 15: + { + State = 279; + insert_stmt(); + } + break; + case 16: + { + State = 280; + pragma_stmt(); + } + break; + case 17: + { + State = 281; + reindex_stmt(); + } + break; + case 18: + { + State = 282; + release_stmt(); + } + break; + case 19: + { + State = 283; + rollback_stmt(); + } + break; + case 20: + { + State = 284; + savepoint_stmt(); + } + break; + case 21: + { + State = 285; + select_stmt(); + } + break; + case 22: + { + State = 286; + update_stmt(); + } + break; + case 23: + { + State = 287; + update_stmt_limited(); + } + break; + case 24: + { + State = 288; + vacuum_stmt(); + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Alter_table_stmtContext : ParserRuleContext { + public Table_nameContext new_table_name; + public Column_nameContext old_column_name; + public Column_nameContext new_column_name; + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ALTER_() { return GetToken(SQLiteParser.ALTER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TABLE_() { return GetToken(SQLiteParser.TABLE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Table_nameContext[] table_name() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Table_nameContext table_name(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode RENAME_() { return GetToken(SQLiteParser.RENAME_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ADD_() { return GetToken(SQLiteParser.ADD_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Column_defContext column_def() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DROP_() { return GetToken(SQLiteParser.DROP_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext[] column_name() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext column_name(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Schema_nameContext schema_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DOT() { return GetToken(SQLiteParser.DOT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TO_() { return GetToken(SQLiteParser.TO_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COLUMN_() { return GetToken(SQLiteParser.COLUMN_, 0); } + public Alter_table_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_alter_table_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterAlter_table_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitAlter_table_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitAlter_table_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Alter_table_stmtContext alter_table_stmt() { + Alter_table_stmtContext _localctx = new Alter_table_stmtContext(Context, State); + EnterRule(_localctx, 6, RULE_alter_table_stmt); + try { + EnterOuterAlt(_localctx, 1); + { + State = 291; + Match(ALTER_); + State = 292; + Match(TABLE_); + State = 296; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,8,Context) ) { + case 1: + { + State = 293; + schema_name(); + State = 294; + Match(DOT); + } + break; + } + State = 298; + table_name(); + State = 321; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case RENAME_: + { + State = 299; + Match(RENAME_); + State = 309; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,10,Context) ) { + case 1: + { + State = 300; + Match(TO_); + State = 301; + _localctx.new_table_name = table_name(); + } + break; + case 2: + { + State = 303; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,9,Context) ) { + case 1: + { + State = 302; + Match(COLUMN_); + } + break; + } + State = 305; + _localctx.old_column_name = column_name(); + State = 306; + Match(TO_); + State = 307; + _localctx.new_column_name = column_name(); + } + break; + } + } + break; + case ADD_: + { + State = 311; + Match(ADD_); + State = 313; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,11,Context) ) { + case 1: + { + State = 312; + Match(COLUMN_); + } + break; + } + State = 315; + column_def(); + } + break; + case DROP_: + { + State = 316; + Match(DROP_); + State = 318; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,12,Context) ) { + case 1: + { + State = 317; + Match(COLUMN_); + } + break; + } + State = 320; + column_name(); + } + break; + default: + throw new NoViableAltException(this); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Analyze_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ANALYZE_() { return GetToken(SQLiteParser.ANALYZE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Schema_nameContext schema_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Table_or_index_nameContext table_or_index_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DOT() { return GetToken(SQLiteParser.DOT, 0); } + public Analyze_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_analyze_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterAnalyze_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitAnalyze_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitAnalyze_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Analyze_stmtContext analyze_stmt() { + Analyze_stmtContext _localctx = new Analyze_stmtContext(Context, State); + EnterRule(_localctx, 8, RULE_analyze_stmt); + try { + EnterOuterAlt(_localctx, 1); + { + State = 323; + Match(ANALYZE_); + State = 331; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,15,Context) ) { + case 1: + { + State = 324; + schema_name(); + } + break; + case 2: + { + State = 328; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,14,Context) ) { + case 1: + { + State = 325; + schema_name(); + State = 326; + Match(DOT); + } + break; + } + State = 330; + table_or_index_name(); + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Attach_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ATTACH_() { return GetToken(SQLiteParser.ATTACH_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AS_() { return GetToken(SQLiteParser.AS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Schema_nameContext schema_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DATABASE_() { return GetToken(SQLiteParser.DATABASE_, 0); } + public Attach_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_attach_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterAttach_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitAttach_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitAttach_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Attach_stmtContext attach_stmt() { + Attach_stmtContext _localctx = new Attach_stmtContext(Context, State); + EnterRule(_localctx, 10, RULE_attach_stmt); + try { + EnterOuterAlt(_localctx, 1); + { + State = 333; + Match(ATTACH_); + State = 335; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,16,Context) ) { + case 1: + { + State = 334; + Match(DATABASE_); + } + break; + } + State = 337; + expr(0); + State = 338; + Match(AS_); + State = 339; + schema_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Begin_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BEGIN_() { return GetToken(SQLiteParser.BEGIN_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TRANSACTION_() { return GetToken(SQLiteParser.TRANSACTION_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DEFERRED_() { return GetToken(SQLiteParser.DEFERRED_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IMMEDIATE_() { return GetToken(SQLiteParser.IMMEDIATE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode EXCLUSIVE_() { return GetToken(SQLiteParser.EXCLUSIVE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Transaction_nameContext transaction_name() { + return GetRuleContext(0); + } + public Begin_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_begin_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterBegin_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitBegin_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitBegin_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Begin_stmtContext begin_stmt() { + Begin_stmtContext _localctx = new Begin_stmtContext(Context, State); + EnterRule(_localctx, 12, RULE_begin_stmt); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 341; + Match(BEGIN_); + State = 343; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 58)) & ~0x3f) == 0 && ((1L << (_la - 58)) & 16779265L) != 0)) { + { + State = 342; + _la = TokenStream.LA(1); + if ( !(((((_la - 58)) & ~0x3f) == 0 && ((1L << (_la - 58)) & 16779265L) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + + State = 349; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==TRANSACTION_) { + { + State = 345; + Match(TRANSACTION_); + State = 347; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,18,Context) ) { + case 1: + { + State = 346; + transaction_name(); + } + break; + } + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Commit_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMIT_() { return GetToken(SQLiteParser.COMMIT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode END_() { return GetToken(SQLiteParser.END_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TRANSACTION_() { return GetToken(SQLiteParser.TRANSACTION_, 0); } + public Commit_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_commit_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterCommit_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitCommit_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitCommit_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Commit_stmtContext commit_stmt() { + Commit_stmtContext _localctx = new Commit_stmtContext(Context, State); + EnterRule(_localctx, 14, RULE_commit_stmt); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 351; + _la = TokenStream.LA(1); + if ( !(_la==COMMIT_ || _la==END_) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 353; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==TRANSACTION_) { + { + State = 352; + Match(TRANSACTION_); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Rollback_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ROLLBACK_() { return GetToken(SQLiteParser.ROLLBACK_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TRANSACTION_() { return GetToken(SQLiteParser.TRANSACTION_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TO_() { return GetToken(SQLiteParser.TO_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Savepoint_nameContext savepoint_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode SAVEPOINT_() { return GetToken(SQLiteParser.SAVEPOINT_, 0); } + public Rollback_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_rollback_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterRollback_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitRollback_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitRollback_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Rollback_stmtContext rollback_stmt() { + Rollback_stmtContext _localctx = new Rollback_stmtContext(Context, State); + EnterRule(_localctx, 16, RULE_rollback_stmt); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 355; + Match(ROLLBACK_); + State = 357; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==TRANSACTION_) { + { + State = 356; + Match(TRANSACTION_); + } + } + + State = 364; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==TO_) { + { + State = 359; + Match(TO_); + State = 361; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,22,Context) ) { + case 1: + { + State = 360; + Match(SAVEPOINT_); + } + break; + } + State = 363; + savepoint_name(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Savepoint_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode SAVEPOINT_() { return GetToken(SQLiteParser.SAVEPOINT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Savepoint_nameContext savepoint_name() { + return GetRuleContext(0); + } + public Savepoint_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_savepoint_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterSavepoint_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitSavepoint_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitSavepoint_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Savepoint_stmtContext savepoint_stmt() { + Savepoint_stmtContext _localctx = new Savepoint_stmtContext(Context, State); + EnterRule(_localctx, 18, RULE_savepoint_stmt); + try { + EnterOuterAlt(_localctx, 1); + { + State = 366; + Match(SAVEPOINT_); + State = 367; + savepoint_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Release_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode RELEASE_() { return GetToken(SQLiteParser.RELEASE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Savepoint_nameContext savepoint_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode SAVEPOINT_() { return GetToken(SQLiteParser.SAVEPOINT_, 0); } + public Release_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_release_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterRelease_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitRelease_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitRelease_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Release_stmtContext release_stmt() { + Release_stmtContext _localctx = new Release_stmtContext(Context, State); + EnterRule(_localctx, 20, RULE_release_stmt); + try { + EnterOuterAlt(_localctx, 1); + { + State = 369; + Match(RELEASE_); + State = 371; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,24,Context) ) { + case 1: + { + State = 370; + Match(SAVEPOINT_); + } + break; + } + State = 373; + savepoint_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Create_index_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CREATE_() { return GetToken(SQLiteParser.CREATE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INDEX_() { return GetToken(SQLiteParser.INDEX_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Index_nameContext index_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ON_() { return GetToken(SQLiteParser.ON_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Table_nameContext table_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Indexed_columnContext[] indexed_column() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Indexed_columnContext indexed_column(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode UNIQUE_() { return GetToken(SQLiteParser.UNIQUE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IF_() { return GetToken(SQLiteParser.IF_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NOT_() { return GetToken(SQLiteParser.NOT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode EXISTS_() { return GetToken(SQLiteParser.EXISTS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Schema_nameContext schema_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DOT() { return GetToken(SQLiteParser.DOT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode WHERE_() { return GetToken(SQLiteParser.WHERE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr() { + return GetRuleContext(0); + } + public Create_index_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_create_index_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterCreate_index_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitCreate_index_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreate_index_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Create_index_stmtContext create_index_stmt() { + Create_index_stmtContext _localctx = new Create_index_stmtContext(Context, State); + EnterRule(_localctx, 22, RULE_create_index_stmt); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 375; + Match(CREATE_); + State = 377; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==UNIQUE_) { + { + State = 376; + Match(UNIQUE_); + } + } + + State = 379; + Match(INDEX_); + State = 383; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,26,Context) ) { + case 1: + { + State = 380; + Match(IF_); + State = 381; + Match(NOT_); + State = 382; + Match(EXISTS_); + } + break; + } + State = 388; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,27,Context) ) { + case 1: + { + State = 385; + schema_name(); + State = 386; + Match(DOT); + } + break; + } + State = 390; + index_name(); + State = 391; + Match(ON_); + State = 392; + table_name(); + State = 393; + Match(OPEN_PAR); + State = 394; + indexed_column(); + State = 399; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 395; + Match(COMMA); + State = 396; + indexed_column(); + } + } + State = 401; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 402; + Match(CLOSE_PAR); + State = 405; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE_) { + { + State = 403; + Match(WHERE_); + State = 404; + expr(0); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Indexed_columnContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext column_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COLLATE_() { return GetToken(SQLiteParser.COLLATE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Collation_nameContext collation_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Asc_descContext asc_desc() { + return GetRuleContext(0); + } + public Indexed_columnContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_indexed_column; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterIndexed_column(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitIndexed_column(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitIndexed_column(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Indexed_columnContext indexed_column() { + Indexed_columnContext _localctx = new Indexed_columnContext(Context, State); + EnterRule(_localctx, 24, RULE_indexed_column); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 409; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,30,Context) ) { + case 1: + { + State = 407; + column_name(); + } + break; + case 2: + { + State = 408; + expr(0); + } + break; + } + State = 413; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==COLLATE_) { + { + State = 411; + Match(COLLATE_); + State = 412; + collation_name(); + } + } + + State = 416; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ASC_ || _la==DESC_) { + { + State = 415; + asc_desc(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Create_table_stmtContext : ParserRuleContext { + public IToken row_ROW_ID; + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CREATE_() { return GetToken(SQLiteParser.CREATE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TABLE_() { return GetToken(SQLiteParser.TABLE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Table_nameContext table_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Column_defContext[] column_def() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_defContext column_def(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AS_() { return GetToken(SQLiteParser.AS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Select_stmtContext select_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IF_() { return GetToken(SQLiteParser.IF_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NOT_() { return GetToken(SQLiteParser.NOT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode EXISTS_() { return GetToken(SQLiteParser.EXISTS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Schema_nameContext schema_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DOT() { return GetToken(SQLiteParser.DOT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TEMP_() { return GetToken(SQLiteParser.TEMP_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TEMPORARY_() { return GetToken(SQLiteParser.TEMPORARY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + [System.Diagnostics.DebuggerNonUserCode] public Table_constraintContext[] table_constraint() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Table_constraintContext table_constraint(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode WITHOUT_() { return GetToken(SQLiteParser.WITHOUT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IDENTIFIER() { return GetToken(SQLiteParser.IDENTIFIER, 0); } + public Create_table_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_create_table_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterCreate_table_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitCreate_table_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreate_table_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Create_table_stmtContext create_table_stmt() { + Create_table_stmtContext _localctx = new Create_table_stmtContext(Context, State); + EnterRule(_localctx, 26, RULE_create_table_stmt); + int _la; + try { + int _alt; + EnterOuterAlt(_localctx, 1); + { + State = 418; + Match(CREATE_); + State = 420; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==TEMP_ || _la==TEMPORARY_) { + { + State = 419; + _la = TokenStream.LA(1); + if ( !(_la==TEMP_ || _la==TEMPORARY_) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + + State = 422; + Match(TABLE_); + State = 426; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,34,Context) ) { + case 1: + { + State = 423; + Match(IF_); + State = 424; + Match(NOT_); + State = 425; + Match(EXISTS_); + } + break; + } + State = 431; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,35,Context) ) { + case 1: + { + State = 428; + schema_name(); + State = 429; + Match(DOT); + } + break; + } + State = 433; + table_name(); + State = 457; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case OPEN_PAR: + { + State = 434; + Match(OPEN_PAR); + State = 435; + column_def(); + State = 440; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,36,Context); + while ( _alt!=1 && _alt!=global::Antlr4.Runtime.Atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1+1 ) { + { + { + State = 436; + Match(COMMA); + State = 437; + column_def(); + } + } + } + State = 442; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,36,Context); + } + State = 447; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 443; + Match(COMMA); + State = 444; + table_constraint(); + } + } + State = 449; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 450; + Match(CLOSE_PAR); + State = 453; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WITHOUT_) { + { + State = 451; + Match(WITHOUT_); + State = 452; + _localctx.row_ROW_ID = Match(IDENTIFIER); + } + } + + } + break; + case AS_: + { + State = 455; + Match(AS_); + State = 456; + select_stmt(); + } + break; + default: + throw new NoViableAltException(this); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Column_defContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext column_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Type_nameContext type_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_constraintContext[] column_constraint() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_constraintContext column_constraint(int i) { + return GetRuleContext(i); + } + public Column_defContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_column_def; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterColumn_def(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitColumn_def(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitColumn_def(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Column_defContext column_def() { + Column_defContext _localctx = new Column_defContext(Context, State); + EnterRule(_localctx, 28, RULE_column_def); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 459; + column_name(); + State = 461; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,40,Context) ) { + case 1: + { + State = 460; + type_name(); + } + break; + } + State = 466; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while ((((_la) & ~0x3f) == 0 && ((1L << _la) & 72673329139417088L) != 0) || ((((_la - 102)) & ~0x3f) == 0 && ((1L << (_la - 102)) & 274877941765L) != 0) || _la==GENERATED_) { + { + { + State = 463; + column_constraint(); + } + } + State = 468; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Type_nameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public NameContext[] name() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public NameContext name(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Signed_numberContext[] signed_number() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Signed_numberContext signed_number(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA() { return GetToken(SQLiteParser.COMMA, 0); } + public Type_nameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_type_name; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterType_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitType_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitType_name(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Type_nameContext type_name() { + Type_nameContext _localctx = new Type_nameContext(Context, State); + EnterRule(_localctx, 30, RULE_type_name); + try { + int _alt; + EnterOuterAlt(_localctx, 1); + { + State = 470; + ErrorHandler.Sync(this); + _alt = 1+1; + do { + switch (_alt) { + case 1+1: + { + { + State = 469; + name(); + } + } + break; + default: + throw new NoViableAltException(this); + } + State = 472; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,42,Context); + } while ( _alt!=1 && _alt!=global::Antlr4.Runtime.Atn.ATN.INVALID_ALT_NUMBER ); + State = 484; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,43,Context) ) { + case 1: + { + State = 474; + Match(OPEN_PAR); + State = 475; + signed_number(); + State = 476; + Match(CLOSE_PAR); + } + break; + case 2: + { + State = 478; + Match(OPEN_PAR); + State = 479; + signed_number(); + State = 480; + Match(COMMA); + State = 481; + signed_number(); + State = 482; + Match(CLOSE_PAR); + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Column_constraintContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CHECK_() { return GetToken(SQLiteParser.CHECK_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DEFAULT_() { return GetToken(SQLiteParser.DEFAULT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COLLATE_() { return GetToken(SQLiteParser.COLLATE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Collation_nameContext collation_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Foreign_key_clauseContext foreign_key_clause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AS_() { return GetToken(SQLiteParser.AS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CONSTRAINT_() { return GetToken(SQLiteParser.CONSTRAINT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public NameContext name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PRIMARY_() { return GetToken(SQLiteParser.PRIMARY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode KEY_() { return GetToken(SQLiteParser.KEY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NULL_() { return GetToken(SQLiteParser.NULL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode UNIQUE_() { return GetToken(SQLiteParser.UNIQUE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Signed_numberContext signed_number() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Literal_valueContext literal_value() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Conflict_clauseContext conflict_clause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode GENERATED_() { return GetToken(SQLiteParser.GENERATED_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ALWAYS_() { return GetToken(SQLiteParser.ALWAYS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode STORED_() { return GetToken(SQLiteParser.STORED_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode VIRTUAL_() { return GetToken(SQLiteParser.VIRTUAL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Asc_descContext asc_desc() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AUTOINCREMENT_() { return GetToken(SQLiteParser.AUTOINCREMENT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NOT_() { return GetToken(SQLiteParser.NOT_, 0); } + public Column_constraintContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_column_constraint; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterColumn_constraint(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitColumn_constraint(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitColumn_constraint(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Column_constraintContext column_constraint() { + Column_constraintContext _localctx = new Column_constraintContext(Context, State); + EnterRule(_localctx, 32, RULE_column_constraint); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 488; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==CONSTRAINT_) { + { + State = 486; + Match(CONSTRAINT_); + State = 487; + name(); + } + } + + State = 539; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case PRIMARY_: + { + { + State = 490; + Match(PRIMARY_); + State = 491; + Match(KEY_); + State = 493; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ASC_ || _la==DESC_) { + { + State = 492; + asc_desc(); + } + } + + State = 496; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ON_) { + { + State = 495; + conflict_clause(); + } + } + + State = 499; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AUTOINCREMENT_) { + { + State = 498; + Match(AUTOINCREMENT_); + } + } + + } + } + break; + case NOT_: + case NULL_: + case UNIQUE_: + { + State = 506; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case NOT_: + case NULL_: + { + State = 502; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==NOT_) { + { + State = 501; + Match(NOT_); + } + } + + State = 504; + Match(NULL_); + } + break; + case UNIQUE_: + { + State = 505; + Match(UNIQUE_); + } + break; + default: + throw new NoViableAltException(this); + } + State = 509; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ON_) { + { + State = 508; + conflict_clause(); + } + } + + } + break; + case CHECK_: + { + State = 511; + Match(CHECK_); + State = 512; + Match(OPEN_PAR); + State = 513; + expr(0); + State = 514; + Match(CLOSE_PAR); + } + break; + case DEFAULT_: + { + State = 516; + Match(DEFAULT_); + State = 523; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,51,Context) ) { + case 1: + { + State = 517; + signed_number(); + } + break; + case 2: + { + State = 518; + literal_value(); + } + break; + case 3: + { + State = 519; + Match(OPEN_PAR); + State = 520; + expr(0); + State = 521; + Match(CLOSE_PAR); + } + break; + } + } + break; + case COLLATE_: + { + State = 525; + Match(COLLATE_); + State = 526; + collation_name(); + } + break; + case REFERENCES_: + { + State = 527; + foreign_key_clause(); + } + break; + case AS_: + case GENERATED_: + { + State = 530; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==GENERATED_) { + { + State = 528; + Match(GENERATED_); + State = 529; + Match(ALWAYS_); + } + } + + State = 532; + Match(AS_); + State = 533; + Match(OPEN_PAR); + State = 534; + expr(0); + State = 535; + Match(CLOSE_PAR); + State = 537; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==VIRTUAL_ || _la==STORED_) { + { + State = 536; + _la = TokenStream.LA(1); + if ( !(_la==VIRTUAL_ || _la==STORED_) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + + } + break; + default: + throw new NoViableAltException(this); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Signed_numberContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NUMERIC_LITERAL() { return GetToken(SQLiteParser.NUMERIC_LITERAL, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PLUS() { return GetToken(SQLiteParser.PLUS, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode MINUS() { return GetToken(SQLiteParser.MINUS, 0); } + public Signed_numberContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_signed_number; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterSigned_number(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitSigned_number(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitSigned_number(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Signed_numberContext signed_number() { + Signed_numberContext _localctx = new Signed_numberContext(Context, State); + EnterRule(_localctx, 34, RULE_signed_number); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 542; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==PLUS || _la==MINUS) { + { + State = 541; + _la = TokenStream.LA(1); + if ( !(_la==PLUS || _la==MINUS) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + + State = 544; + Match(NUMERIC_LITERAL); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Table_constraintContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Indexed_columnContext[] indexed_column() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Indexed_columnContext indexed_column(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CHECK_() { return GetToken(SQLiteParser.CHECK_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FOREIGN_() { return GetToken(SQLiteParser.FOREIGN_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode KEY_() { return GetToken(SQLiteParser.KEY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext[] column_name() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext column_name(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Foreign_key_clauseContext foreign_key_clause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CONSTRAINT_() { return GetToken(SQLiteParser.CONSTRAINT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public NameContext name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PRIMARY_() { return GetToken(SQLiteParser.PRIMARY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode UNIQUE_() { return GetToken(SQLiteParser.UNIQUE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + [System.Diagnostics.DebuggerNonUserCode] public Conflict_clauseContext conflict_clause() { + return GetRuleContext(0); + } + public Table_constraintContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_table_constraint; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterTable_constraint(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitTable_constraint(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitTable_constraint(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Table_constraintContext table_constraint() { + Table_constraintContext _localctx = new Table_constraintContext(Context, State); + EnterRule(_localctx, 36, RULE_table_constraint); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 548; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==CONSTRAINT_) { + { + State = 546; + Match(CONSTRAINT_); + State = 547; + name(); + } + } + + State = 587; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case PRIMARY_: + case UNIQUE_: + { + State = 553; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case PRIMARY_: + { + State = 550; + Match(PRIMARY_); + State = 551; + Match(KEY_); + } + break; + case UNIQUE_: + { + State = 552; + Match(UNIQUE_); + } + break; + default: + throw new NoViableAltException(this); + } + State = 555; + Match(OPEN_PAR); + State = 556; + indexed_column(); + State = 561; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 557; + Match(COMMA); + State = 558; + indexed_column(); + } + } + State = 563; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 564; + Match(CLOSE_PAR); + State = 566; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ON_) { + { + State = 565; + conflict_clause(); + } + } + + } + break; + case CHECK_: + { + State = 568; + Match(CHECK_); + State = 569; + Match(OPEN_PAR); + State = 570; + expr(0); + State = 571; + Match(CLOSE_PAR); + } + break; + case FOREIGN_: + { + State = 573; + Match(FOREIGN_); + State = 574; + Match(KEY_); + State = 575; + Match(OPEN_PAR); + State = 576; + column_name(); + State = 581; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 577; + Match(COMMA); + State = 578; + column_name(); + } + } + State = 583; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 584; + Match(CLOSE_PAR); + State = 585; + foreign_key_clause(); + } + break; + default: + throw new NoViableAltException(this); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Foreign_key_clauseContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode REFERENCES_() { return GetToken(SQLiteParser.REFERENCES_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Foreign_tableContext foreign_table() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext[] column_name() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext column_name(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] ON_() { return GetTokens(SQLiteParser.ON_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ON_(int i) { + return GetToken(SQLiteParser.ON_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] MATCH_() { return GetTokens(SQLiteParser.MATCH_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode MATCH_(int i) { + return GetToken(SQLiteParser.MATCH_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public NameContext[] name() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public NameContext name(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DEFERRABLE_() { return GetToken(SQLiteParser.DEFERRABLE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] DELETE_() { return GetTokens(SQLiteParser.DELETE_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DELETE_(int i) { + return GetToken(SQLiteParser.DELETE_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] UPDATE_() { return GetTokens(SQLiteParser.UPDATE_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode UPDATE_(int i) { + return GetToken(SQLiteParser.UPDATE_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] SET_() { return GetTokens(SQLiteParser.SET_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode SET_(int i) { + return GetToken(SQLiteParser.SET_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] CASCADE_() { return GetTokens(SQLiteParser.CASCADE_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CASCADE_(int i) { + return GetToken(SQLiteParser.CASCADE_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] RESTRICT_() { return GetTokens(SQLiteParser.RESTRICT_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode RESTRICT_(int i) { + return GetToken(SQLiteParser.RESTRICT_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] NO_() { return GetTokens(SQLiteParser.NO_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NO_(int i) { + return GetToken(SQLiteParser.NO_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] ACTION_() { return GetTokens(SQLiteParser.ACTION_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ACTION_(int i) { + return GetToken(SQLiteParser.ACTION_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] NULL_() { return GetTokens(SQLiteParser.NULL_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NULL_(int i) { + return GetToken(SQLiteParser.NULL_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] DEFAULT_() { return GetTokens(SQLiteParser.DEFAULT_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DEFAULT_(int i) { + return GetToken(SQLiteParser.DEFAULT_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NOT_() { return GetToken(SQLiteParser.NOT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INITIALLY_() { return GetToken(SQLiteParser.INITIALLY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DEFERRED_() { return GetToken(SQLiteParser.DEFERRED_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IMMEDIATE_() { return GetToken(SQLiteParser.IMMEDIATE_, 0); } + public Foreign_key_clauseContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_foreign_key_clause; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterForeign_key_clause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitForeign_key_clause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitForeign_key_clause(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Foreign_key_clauseContext foreign_key_clause() { + Foreign_key_clauseContext _localctx = new Foreign_key_clauseContext(Context, State); + EnterRule(_localctx, 38, RULE_foreign_key_clause); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 589; + Match(REFERENCES_); + State = 590; + foreign_table(); + State = 602; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==OPEN_PAR) { + { + State = 591; + Match(OPEN_PAR); + State = 592; + column_name(); + State = 597; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 593; + Match(COMMA); + State = 594; + column_name(); + } + } + State = 599; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 600; + Match(CLOSE_PAR); + } + } + + State = 618; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==MATCH_ || _la==ON_) { + { + State = 616; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case ON_: + { + State = 604; + Match(ON_); + State = 605; + _la = TokenStream.LA(1); + if ( !(_la==DELETE_ || _la==UPDATE_) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 612; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case SET_: + { + State = 606; + Match(SET_); + State = 607; + _la = TokenStream.LA(1); + if ( !(_la==DEFAULT_ || _la==NULL_) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + break; + case CASCADE_: + { + State = 608; + Match(CASCADE_); + } + break; + case RESTRICT_: + { + State = 609; + Match(RESTRICT_); + } + break; + case NO_: + { + State = 610; + Match(NO_); + State = 611; + Match(ACTION_); + } + break; + default: + throw new NoViableAltException(this); + } + } + break; + case MATCH_: + { + State = 614; + Match(MATCH_); + State = 615; + name(); + } + break; + default: + throw new NoViableAltException(this); + } + } + State = 620; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 629; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,69,Context) ) { + case 1: + { + State = 622; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==NOT_) { + { + State = 621; + Match(NOT_); + } + } + + State = 624; + Match(DEFERRABLE_); + State = 627; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==INITIALLY_) { + { + State = 625; + Match(INITIALLY_); + State = 626; + _la = TokenStream.LA(1); + if ( !(_la==DEFERRED_ || _la==IMMEDIATE_) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Conflict_clauseContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ON_() { return GetToken(SQLiteParser.ON_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CONFLICT_() { return GetToken(SQLiteParser.CONFLICT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ROLLBACK_() { return GetToken(SQLiteParser.ROLLBACK_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ABORT_() { return GetToken(SQLiteParser.ABORT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FAIL_() { return GetToken(SQLiteParser.FAIL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IGNORE_() { return GetToken(SQLiteParser.IGNORE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode REPLACE_() { return GetToken(SQLiteParser.REPLACE_, 0); } + public Conflict_clauseContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_conflict_clause; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterConflict_clause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitConflict_clause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitConflict_clause(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Conflict_clauseContext conflict_clause() { + Conflict_clauseContext _localctx = new Conflict_clauseContext(Context, State); + EnterRule(_localctx, 40, RULE_conflict_clause); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 631; + Match(ON_); + State = 632; + Match(CONFLICT_); + State = 633; + _la = TokenStream.LA(1); + if ( !(_la==ABORT_ || ((((_la - 72)) & ~0x3f) == 0 && ((1L << (_la - 72)) & 19140298416325121L) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Create_trigger_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CREATE_() { return GetToken(SQLiteParser.CREATE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TRIGGER_() { return GetToken(SQLiteParser.TRIGGER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Trigger_nameContext trigger_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ON_() { return GetToken(SQLiteParser.ON_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Table_nameContext table_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BEGIN_() { return GetToken(SQLiteParser.BEGIN_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode END_() { return GetToken(SQLiteParser.END_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DELETE_() { return GetToken(SQLiteParser.DELETE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INSERT_() { return GetToken(SQLiteParser.INSERT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode UPDATE_() { return GetToken(SQLiteParser.UPDATE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IF_() { return GetToken(SQLiteParser.IF_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NOT_() { return GetToken(SQLiteParser.NOT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode EXISTS_() { return GetToken(SQLiteParser.EXISTS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Schema_nameContext schema_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DOT() { return GetToken(SQLiteParser.DOT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BEFORE_() { return GetToken(SQLiteParser.BEFORE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AFTER_() { return GetToken(SQLiteParser.AFTER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INSTEAD_() { return GetToken(SQLiteParser.INSTEAD_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] OF_() { return GetTokens(SQLiteParser.OF_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OF_(int i) { + return GetToken(SQLiteParser.OF_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FOR_() { return GetToken(SQLiteParser.FOR_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode EACH_() { return GetToken(SQLiteParser.EACH_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ROW_() { return GetToken(SQLiteParser.ROW_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode WHEN_() { return GetToken(SQLiteParser.WHEN_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] SCOL() { return GetTokens(SQLiteParser.SCOL); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode SCOL(int i) { + return GetToken(SQLiteParser.SCOL, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TEMP_() { return GetToken(SQLiteParser.TEMP_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TEMPORARY_() { return GetToken(SQLiteParser.TEMPORARY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext[] column_name() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext column_name(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Update_stmtContext[] update_stmt() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Update_stmtContext update_stmt(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Insert_stmtContext[] insert_stmt() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Insert_stmtContext insert_stmt(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Delete_stmtContext[] delete_stmt() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Delete_stmtContext delete_stmt(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Select_stmtContext[] select_stmt() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Select_stmtContext select_stmt(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + public Create_trigger_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_create_trigger_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterCreate_trigger_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitCreate_trigger_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreate_trigger_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Create_trigger_stmtContext create_trigger_stmt() { + Create_trigger_stmtContext _localctx = new Create_trigger_stmtContext(Context, State); + EnterRule(_localctx, 42, RULE_create_trigger_stmt); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 635; + Match(CREATE_); + State = 637; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==TEMP_ || _la==TEMPORARY_) { + { + State = 636; + _la = TokenStream.LA(1); + if ( !(_la==TEMP_ || _la==TEMPORARY_) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + + State = 639; + Match(TRIGGER_); + State = 643; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,71,Context) ) { + case 1: + { + State = 640; + Match(IF_); + State = 641; + Match(NOT_); + State = 642; + Match(EXISTS_); + } + break; + } + State = 648; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,72,Context) ) { + case 1: + { + State = 645; + schema_name(); + State = 646; + Match(DOT); + } + break; + } + State = 650; + trigger_name(); + State = 655; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case BEFORE_: + { + State = 651; + Match(BEFORE_); + } + break; + case AFTER_: + { + State = 652; + Match(AFTER_); + } + break; + case INSTEAD_: + { + State = 653; + Match(INSTEAD_); + State = 654; + Match(OF_); + } + break; + case DELETE_: + case INSERT_: + case UPDATE_: + break; + default: + break; + } + State = 671; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case DELETE_: + { + State = 657; + Match(DELETE_); + } + break; + case INSERT_: + { + State = 658; + Match(INSERT_); + } + break; + case UPDATE_: + { + State = 659; + Match(UPDATE_); + State = 669; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==OF_) { + { + State = 660; + Match(OF_); + State = 661; + column_name(); + State = 666; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 662; + Match(COMMA); + State = 663; + column_name(); + } + } + State = 668; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + + } + break; + default: + throw new NoViableAltException(this); + } + State = 673; + Match(ON_); + State = 674; + table_name(); + State = 678; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==FOR_) { + { + State = 675; + Match(FOR_); + State = 676; + Match(EACH_); + State = 677; + Match(ROW_); + } + } + + State = 682; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHEN_) { + { + State = 680; + Match(WHEN_); + State = 681; + expr(0); + } + } + + State = 684; + Match(BEGIN_); + State = 693; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + do { + { + { + State = 689; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,79,Context) ) { + case 1: + { + State = 685; + update_stmt(); + } + break; + case 2: + { + State = 686; + insert_stmt(); + } + break; + case 3: + { + State = 687; + delete_stmt(); + } + break; + case 4: + { + State = 688; + select_stmt(); + } + break; + } + State = 691; + Match(SCOL); + } + } + State = 695; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } while ( _la==DELETE_ || ((((_la - 88)) & ~0x3f) == 0 && ((1L << (_la - 88)) & 2386912217732743169L) != 0) ); + State = 697; + Match(END_); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Create_view_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CREATE_() { return GetToken(SQLiteParser.CREATE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode VIEW_() { return GetToken(SQLiteParser.VIEW_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public View_nameContext view_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AS_() { return GetToken(SQLiteParser.AS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Select_stmtContext select_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IF_() { return GetToken(SQLiteParser.IF_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NOT_() { return GetToken(SQLiteParser.NOT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode EXISTS_() { return GetToken(SQLiteParser.EXISTS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Schema_nameContext schema_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DOT() { return GetToken(SQLiteParser.DOT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext[] column_name() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext column_name(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TEMP_() { return GetToken(SQLiteParser.TEMP_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TEMPORARY_() { return GetToken(SQLiteParser.TEMPORARY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + public Create_view_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_create_view_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterCreate_view_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitCreate_view_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreate_view_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Create_view_stmtContext create_view_stmt() { + Create_view_stmtContext _localctx = new Create_view_stmtContext(Context, State); + EnterRule(_localctx, 44, RULE_create_view_stmt); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 699; + Match(CREATE_); + State = 701; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==TEMP_ || _la==TEMPORARY_) { + { + State = 700; + _la = TokenStream.LA(1); + if ( !(_la==TEMP_ || _la==TEMPORARY_) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + + State = 703; + Match(VIEW_); + State = 707; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,82,Context) ) { + case 1: + { + State = 704; + Match(IF_); + State = 705; + Match(NOT_); + State = 706; + Match(EXISTS_); + } + break; + } + State = 712; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,83,Context) ) { + case 1: + { + State = 709; + schema_name(); + State = 710; + Match(DOT); + } + break; + } + State = 714; + view_name(); + State = 726; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==OPEN_PAR) { + { + State = 715; + Match(OPEN_PAR); + State = 716; + column_name(); + State = 721; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 717; + Match(COMMA); + State = 718; + column_name(); + } + } + State = 723; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 724; + Match(CLOSE_PAR); + } + } + + State = 728; + Match(AS_); + State = 729; + select_stmt(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Create_virtual_table_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CREATE_() { return GetToken(SQLiteParser.CREATE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode VIRTUAL_() { return GetToken(SQLiteParser.VIRTUAL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TABLE_() { return GetToken(SQLiteParser.TABLE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Table_nameContext table_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode USING_() { return GetToken(SQLiteParser.USING_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Module_nameContext module_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IF_() { return GetToken(SQLiteParser.IF_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NOT_() { return GetToken(SQLiteParser.NOT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode EXISTS_() { return GetToken(SQLiteParser.EXISTS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Schema_nameContext schema_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DOT() { return GetToken(SQLiteParser.DOT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Module_argumentContext[] module_argument() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Module_argumentContext module_argument(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + public Create_virtual_table_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_create_virtual_table_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterCreate_virtual_table_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitCreate_virtual_table_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitCreate_virtual_table_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Create_virtual_table_stmtContext create_virtual_table_stmt() { + Create_virtual_table_stmtContext _localctx = new Create_virtual_table_stmtContext(Context, State); + EnterRule(_localctx, 46, RULE_create_virtual_table_stmt); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 731; + Match(CREATE_); + State = 732; + Match(VIRTUAL_); + State = 733; + Match(TABLE_); + State = 737; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,86,Context) ) { + case 1: + { + State = 734; + Match(IF_); + State = 735; + Match(NOT_); + State = 736; + Match(EXISTS_); + } + break; + } + State = 742; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,87,Context) ) { + case 1: + { + State = 739; + schema_name(); + State = 740; + Match(DOT); + } + break; + } + State = 744; + table_name(); + State = 745; + Match(USING_); + State = 746; + module_name(); + State = 758; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==OPEN_PAR) { + { + State = 747; + Match(OPEN_PAR); + State = 748; + module_argument(); + State = 753; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 749; + Match(COMMA); + State = 750; + module_argument(); + } + } + State = 755; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 756; + Match(CLOSE_PAR); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class With_clauseContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode WITH_() { return GetToken(SQLiteParser.WITH_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Cte_table_nameContext[] cte_table_name() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Cte_table_nameContext cte_table_name(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] AS_() { return GetTokens(SQLiteParser.AS_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AS_(int i) { + return GetToken(SQLiteParser.AS_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] OPEN_PAR() { return GetTokens(SQLiteParser.OPEN_PAR); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR(int i) { + return GetToken(SQLiteParser.OPEN_PAR, i); + } + [System.Diagnostics.DebuggerNonUserCode] public Select_stmtContext[] select_stmt() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Select_stmtContext select_stmt(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] CLOSE_PAR() { return GetTokens(SQLiteParser.CLOSE_PAR); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR(int i) { + return GetToken(SQLiteParser.CLOSE_PAR, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode RECURSIVE_() { return GetToken(SQLiteParser.RECURSIVE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + public With_clauseContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_with_clause; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterWith_clause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitWith_clause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitWith_clause(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public With_clauseContext with_clause() { + With_clauseContext _localctx = new With_clauseContext(Context, State); + EnterRule(_localctx, 48, RULE_with_clause); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 760; + Match(WITH_); + State = 762; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,90,Context) ) { + case 1: + { + State = 761; + Match(RECURSIVE_); + } + break; + } + State = 764; + cte_table_name(); + State = 765; + Match(AS_); + State = 766; + Match(OPEN_PAR); + State = 767; + select_stmt(); + State = 768; + Match(CLOSE_PAR); + State = 778; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 769; + Match(COMMA); + State = 770; + cte_table_name(); + State = 771; + Match(AS_); + State = 772; + Match(OPEN_PAR); + State = 773; + select_stmt(); + State = 774; + Match(CLOSE_PAR); + } + } + State = 780; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Cte_table_nameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Table_nameContext table_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext[] column_name() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext column_name(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + public Cte_table_nameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_cte_table_name; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterCte_table_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitCte_table_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitCte_table_name(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Cte_table_nameContext cte_table_name() { + Cte_table_nameContext _localctx = new Cte_table_nameContext(Context, State); + EnterRule(_localctx, 50, RULE_cte_table_name); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 781; + table_name(); + State = 793; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==OPEN_PAR) { + { + State = 782; + Match(OPEN_PAR); + State = 783; + column_name(); + State = 788; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 784; + Match(COMMA); + State = 785; + column_name(); + } + } + State = 790; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 791; + Match(CLOSE_PAR); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Recursive_cteContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Cte_table_nameContext cte_table_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AS_() { return GetToken(SQLiteParser.AS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Initial_selectContext initial_select() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode UNION_() { return GetToken(SQLiteParser.UNION_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Recursive_selectContext recursive_select() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ALL_() { return GetToken(SQLiteParser.ALL_, 0); } + public Recursive_cteContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_recursive_cte; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterRecursive_cte(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitRecursive_cte(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitRecursive_cte(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Recursive_cteContext recursive_cte() { + Recursive_cteContext _localctx = new Recursive_cteContext(Context, State); + EnterRule(_localctx, 52, RULE_recursive_cte); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 795; + cte_table_name(); + State = 796; + Match(AS_); + State = 797; + Match(OPEN_PAR); + State = 798; + initial_select(); + State = 799; + Match(UNION_); + State = 801; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ALL_) { + { + State = 800; + Match(ALL_); + } + } + + State = 803; + recursive_select(); + State = 804; + Match(CLOSE_PAR); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Common_table_expressionContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Table_nameContext table_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AS_() { return GetToken(SQLiteParser.AS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] OPEN_PAR() { return GetTokens(SQLiteParser.OPEN_PAR); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR(int i) { + return GetToken(SQLiteParser.OPEN_PAR, i); + } + [System.Diagnostics.DebuggerNonUserCode] public Select_stmtContext select_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] CLOSE_PAR() { return GetTokens(SQLiteParser.CLOSE_PAR); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR(int i) { + return GetToken(SQLiteParser.CLOSE_PAR, i); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext[] column_name() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext column_name(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + public Common_table_expressionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_common_table_expression; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterCommon_table_expression(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitCommon_table_expression(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitCommon_table_expression(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Common_table_expressionContext common_table_expression() { + Common_table_expressionContext _localctx = new Common_table_expressionContext(Context, State); + EnterRule(_localctx, 54, RULE_common_table_expression); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 806; + table_name(); + State = 818; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==OPEN_PAR) { + { + State = 807; + Match(OPEN_PAR); + State = 808; + column_name(); + State = 813; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 809; + Match(COMMA); + State = 810; + column_name(); + } + } + State = 815; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 816; + Match(CLOSE_PAR); + } + } + + State = 820; + Match(AS_); + State = 821; + Match(OPEN_PAR); + State = 822; + select_stmt(); + State = 823; + Match(CLOSE_PAR); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Delete_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DELETE_() { return GetToken(SQLiteParser.DELETE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FROM_() { return GetToken(SQLiteParser.FROM_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Qualified_table_nameContext qualified_table_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public With_clauseContext with_clause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode WHERE_() { return GetToken(SQLiteParser.WHERE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Returning_clauseContext returning_clause() { + return GetRuleContext(0); + } + public Delete_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_delete_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterDelete_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitDelete_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitDelete_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Delete_stmtContext delete_stmt() { + Delete_stmtContext _localctx = new Delete_stmtContext(Context, State); + EnterRule(_localctx, 56, RULE_delete_stmt); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 826; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WITH_) { + { + State = 825; + with_clause(); + } + } + + State = 828; + Match(DELETE_); + State = 829; + Match(FROM_); + State = 830; + qualified_table_name(); + State = 833; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE_) { + { + State = 831; + Match(WHERE_); + State = 832; + expr(0); + } + } + + State = 836; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==RETURNING_) { + { + State = 835; + returning_clause(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Delete_stmt_limitedContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DELETE_() { return GetToken(SQLiteParser.DELETE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FROM_() { return GetToken(SQLiteParser.FROM_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Qualified_table_nameContext qualified_table_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public With_clauseContext with_clause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode WHERE_() { return GetToken(SQLiteParser.WHERE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Returning_clauseContext returning_clause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Limit_stmtContext limit_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Order_by_stmtContext order_by_stmt() { + return GetRuleContext(0); + } + public Delete_stmt_limitedContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_delete_stmt_limited; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterDelete_stmt_limited(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitDelete_stmt_limited(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitDelete_stmt_limited(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Delete_stmt_limitedContext delete_stmt_limited() { + Delete_stmt_limitedContext _localctx = new Delete_stmt_limitedContext(Context, State); + EnterRule(_localctx, 58, RULE_delete_stmt_limited); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 839; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WITH_) { + { + State = 838; + with_clause(); + } + } + + State = 841; + Match(DELETE_); + State = 842; + Match(FROM_); + State = 843; + qualified_table_name(); + State = 846; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE_) { + { + State = 844; + Match(WHERE_); + State = 845; + expr(0); + } + } + + State = 849; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==RETURNING_) { + { + State = 848; + returning_clause(); + } + } + + State = 855; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LIMIT_ || _la==ORDER_) { + { + State = 852; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ORDER_) { + { + State = 851; + order_by_stmt(); + } + } + + State = 854; + limit_stmt(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Detach_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DETACH_() { return GetToken(SQLiteParser.DETACH_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Schema_nameContext schema_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DATABASE_() { return GetToken(SQLiteParser.DATABASE_, 0); } + public Detach_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_detach_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterDetach_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitDetach_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitDetach_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Detach_stmtContext detach_stmt() { + Detach_stmtContext _localctx = new Detach_stmtContext(Context, State); + EnterRule(_localctx, 60, RULE_detach_stmt); + try { + EnterOuterAlt(_localctx, 1); + { + State = 857; + Match(DETACH_); + State = 859; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,105,Context) ) { + case 1: + { + State = 858; + Match(DATABASE_); + } + break; + } + State = 861; + schema_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Drop_stmtContext : ParserRuleContext { + public IToken @object; + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DROP_() { return GetToken(SQLiteParser.DROP_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INDEX_() { return GetToken(SQLiteParser.INDEX_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TABLE_() { return GetToken(SQLiteParser.TABLE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TRIGGER_() { return GetToken(SQLiteParser.TRIGGER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode VIEW_() { return GetToken(SQLiteParser.VIEW_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IF_() { return GetToken(SQLiteParser.IF_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode EXISTS_() { return GetToken(SQLiteParser.EXISTS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Schema_nameContext schema_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DOT() { return GetToken(SQLiteParser.DOT, 0); } + public Drop_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_drop_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterDrop_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitDrop_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitDrop_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Drop_stmtContext drop_stmt() { + Drop_stmtContext _localctx = new Drop_stmtContext(Context, State); + EnterRule(_localctx, 62, RULE_drop_stmt); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 863; + Match(DROP_); + State = 864; + _localctx.@object = TokenStream.LT(1); + _la = TokenStream.LA(1); + if ( !(((((_la - 84)) & ~0x3f) == 0 && ((1L << (_la - 84)) & 2324138882699886593L) != 0)) ) { + _localctx.@object = ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 867; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,106,Context) ) { + case 1: + { + State = 865; + Match(IF_); + State = 866; + Match(EXISTS_); + } + break; + } + State = 872; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,107,Context) ) { + case 1: + { + State = 869; + schema_name(); + State = 870; + Match(DOT); + } + break; + } + State = 874; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ExprContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Literal_valueContext literal_value() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BIND_PARAMETER() { return GetToken(SQLiteParser.BIND_PARAMETER, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext column_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Table_nameContext table_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] DOT() { return GetTokens(SQLiteParser.DOT); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DOT(int i) { + return GetToken(SQLiteParser.DOT, i); + } + [System.Diagnostics.DebuggerNonUserCode] public Schema_nameContext schema_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Unary_operatorContext unary_operator() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext[] expr() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Function_nameContext function_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode STAR() { return GetToken(SQLiteParser.STAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Filter_clauseContext filter_clause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Over_clauseContext over_clause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DISTINCT_() { return GetToken(SQLiteParser.DISTINCT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CAST_() { return GetToken(SQLiteParser.CAST_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AS_() { return GetToken(SQLiteParser.AS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Type_nameContext type_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Select_stmtContext select_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode EXISTS_() { return GetToken(SQLiteParser.EXISTS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NOT_() { return GetToken(SQLiteParser.NOT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CASE_() { return GetToken(SQLiteParser.CASE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode END_() { return GetToken(SQLiteParser.END_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] WHEN_() { return GetTokens(SQLiteParser.WHEN_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode WHEN_(int i) { + return GetToken(SQLiteParser.WHEN_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] THEN_() { return GetTokens(SQLiteParser.THEN_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode THEN_(int i) { + return GetToken(SQLiteParser.THEN_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ELSE_() { return GetToken(SQLiteParser.ELSE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Raise_functionContext raise_function() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PIPE2() { return GetToken(SQLiteParser.PIPE2, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DIV() { return GetToken(SQLiteParser.DIV, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode MOD() { return GetToken(SQLiteParser.MOD, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PLUS() { return GetToken(SQLiteParser.PLUS, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode MINUS() { return GetToken(SQLiteParser.MINUS, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode LT2() { return GetToken(SQLiteParser.LT2, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode GT2() { return GetToken(SQLiteParser.GT2, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AMP() { return GetToken(SQLiteParser.AMP, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PIPE() { return GetToken(SQLiteParser.PIPE, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode LT() { return GetToken(SQLiteParser.LT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode LT_EQ() { return GetToken(SQLiteParser.LT_EQ, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode GT() { return GetToken(SQLiteParser.GT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode GT_EQ() { return GetToken(SQLiteParser.GT_EQ, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ASSIGN() { return GetToken(SQLiteParser.ASSIGN, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode EQ() { return GetToken(SQLiteParser.EQ, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NOT_EQ1() { return GetToken(SQLiteParser.NOT_EQ1, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NOT_EQ2() { return GetToken(SQLiteParser.NOT_EQ2, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IS_() { return GetToken(SQLiteParser.IS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FROM_() { return GetToken(SQLiteParser.FROM_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IN_() { return GetToken(SQLiteParser.IN_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode LIKE_() { return GetToken(SQLiteParser.LIKE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode GLOB_() { return GetToken(SQLiteParser.GLOB_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode MATCH_() { return GetToken(SQLiteParser.MATCH_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode REGEXP_() { return GetToken(SQLiteParser.REGEXP_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AND_() { return GetToken(SQLiteParser.AND_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OR_() { return GetToken(SQLiteParser.OR_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BETWEEN_() { return GetToken(SQLiteParser.BETWEEN_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COLLATE_() { return GetToken(SQLiteParser.COLLATE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Collation_nameContext collation_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ESCAPE_() { return GetToken(SQLiteParser.ESCAPE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ISNULL_() { return GetToken(SQLiteParser.ISNULL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NOTNULL_() { return GetToken(SQLiteParser.NOTNULL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NULL_() { return GetToken(SQLiteParser.NULL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Table_function_nameContext table_function_name() { + return GetRuleContext(0); + } + public ExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_expr; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ExprContext expr() { + return expr(0); + } + + private ExprContext expr(int _p) { + ParserRuleContext _parentctx = Context; + int _parentState = State; + ExprContext _localctx = new ExprContext(Context, _parentState); + ExprContext _prevctx = _localctx; + int _startState = 64; + EnterRecursionRule(_localctx, 64, RULE_expr, _p); + int _la; + try { + int _alt; + EnterOuterAlt(_localctx, 1); + { + State = 964; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,121,Context) ) { + case 1: + { + State = 877; + literal_value(); + } + break; + case 2: + { + State = 878; + Match(BIND_PARAMETER); + } + break; + case 3: + { + State = 887; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,109,Context) ) { + case 1: + { + State = 882; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,108,Context) ) { + case 1: + { + State = 879; + schema_name(); + State = 880; + Match(DOT); + } + break; + } + State = 884; + table_name(); + State = 885; + Match(DOT); + } + break; + } + State = 889; + column_name(); + } + break; + case 4: + { + State = 890; + unary_operator(); + State = 891; + expr(21); + } + break; + case 5: + { + State = 893; + function_name(); + State = 894; + Match(OPEN_PAR); + State = 907; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case OPEN_PAR: + case PLUS: + case MINUS: + case TILDE: + case ABORT_: + case ACTION_: + case ADD_: + case AFTER_: + case ALL_: + case ALTER_: + case ANALYZE_: + case AND_: + case AS_: + case ASC_: + case ATTACH_: + case AUTOINCREMENT_: + case BEFORE_: + case BEGIN_: + case BETWEEN_: + case BY_: + case CASCADE_: + case CASE_: + case CAST_: + case CHECK_: + case COLLATE_: + case COLUMN_: + case COMMIT_: + case CONFLICT_: + case CONSTRAINT_: + case CREATE_: + case CROSS_: + case CURRENT_DATE_: + case CURRENT_TIME_: + case CURRENT_TIMESTAMP_: + case DATABASE_: + case DEFAULT_: + case DEFERRABLE_: + case DEFERRED_: + case DELETE_: + case DESC_: + case DETACH_: + case DISTINCT_: + case DROP_: + case EACH_: + case ELSE_: + case END_: + case ESCAPE_: + case EXCEPT_: + case EXCLUSIVE_: + case EXISTS_: + case EXPLAIN_: + case FAIL_: + case FOR_: + case FOREIGN_: + case FROM_: + case FULL_: + case GLOB_: + case GROUP_: + case HAVING_: + case IF_: + case IGNORE_: + case IMMEDIATE_: + case IN_: + case INDEX_: + case INDEXED_: + case INITIALLY_: + case INNER_: + case INSERT_: + case INSTEAD_: + case INTERSECT_: + case INTO_: + case IS_: + case ISNULL_: + case JOIN_: + case KEY_: + case LEFT_: + case LIKE_: + case LIMIT_: + case MATCH_: + case NATURAL_: + case NO_: + case NOT_: + case NOTNULL_: + case NULL_: + case OF_: + case OFFSET_: + case ON_: + case OR_: + case ORDER_: + case OUTER_: + case PLAN_: + case PRAGMA_: + case PRIMARY_: + case QUERY_: + case RAISE_: + case RECURSIVE_: + case REFERENCES_: + case REGEXP_: + case REINDEX_: + case RELEASE_: + case RENAME_: + case REPLACE_: + case RESTRICT_: + case RIGHT_: + case ROLLBACK_: + case ROW_: + case ROWS_: + case SAVEPOINT_: + case SELECT_: + case SET_: + case TABLE_: + case TEMP_: + case TEMPORARY_: + case THEN_: + case TO_: + case TRANSACTION_: + case TRIGGER_: + case UNION_: + case UNIQUE_: + case UPDATE_: + case USING_: + case VACUUM_: + case VALUES_: + case VIEW_: + case VIRTUAL_: + case WHEN_: + case WHERE_: + case WITH_: + case WITHOUT_: + case FIRST_VALUE_: + case OVER_: + case PARTITION_: + case RANGE_: + case PRECEDING_: + case UNBOUNDED_: + case CURRENT_: + case FOLLOWING_: + case CUME_DIST_: + case DENSE_RANK_: + case LAG_: + case LAST_VALUE_: + case LEAD_: + case NTH_VALUE_: + case NTILE_: + case PERCENT_RANK_: + case RANK_: + case ROW_NUMBER_: + case GENERATED_: + case ALWAYS_: + case STORED_: + case TRUE_: + case FALSE_: + case WINDOW_: + case NULLS_: + case FIRST_: + case LAST_: + case FILTER_: + case GROUPS_: + case EXCLUDE_: + case IDENTIFIER: + case NUMERIC_LITERAL: + case BIND_PARAMETER: + case STRING_LITERAL: + case BLOB_LITERAL: + { + { + State = 896; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,110,Context) ) { + case 1: + { + State = 895; + Match(DISTINCT_); + } + break; + } + State = 898; + expr(0); + State = 903; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 899; + Match(COMMA); + State = 900; + expr(0); + } + } + State = 905; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + break; + case STAR: + { + State = 906; + Match(STAR); + } + break; + case CLOSE_PAR: + break; + default: + break; + } + State = 909; + Match(CLOSE_PAR); + State = 911; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,113,Context) ) { + case 1: + { + State = 910; + filter_clause(); + } + break; + } + State = 914; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,114,Context) ) { + case 1: + { + State = 913; + over_clause(); + } + break; + } + } + break; + case 6: + { + State = 916; + Match(OPEN_PAR); + State = 917; + expr(0); + State = 922; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 918; + Match(COMMA); + State = 919; + expr(0); + } + } + State = 924; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 925; + Match(CLOSE_PAR); + } + break; + case 7: + { + State = 927; + Match(CAST_); + State = 928; + Match(OPEN_PAR); + State = 929; + expr(0); + State = 930; + Match(AS_); + State = 931; + type_name(); + State = 932; + Match(CLOSE_PAR); + } + break; + case 8: + { + State = 938; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==EXISTS_ || _la==NOT_) { + { + State = 935; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==NOT_) { + { + State = 934; + Match(NOT_); + } + } + + State = 937; + Match(EXISTS_); + } + } + + State = 940; + Match(OPEN_PAR); + State = 941; + select_stmt(); + State = 942; + Match(CLOSE_PAR); + } + break; + case 9: + { + State = 944; + Match(CASE_); + State = 946; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,118,Context) ) { + case 1: + { + State = 945; + expr(0); + } + break; + } + State = 953; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + do { + { + { + State = 948; + Match(WHEN_); + State = 949; + expr(0); + State = 950; + Match(THEN_); + State = 951; + expr(0); + } + } + State = 955; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } while ( _la==WHEN_ ); + State = 959; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ELSE_) { + { + State = 957; + Match(ELSE_); + State = 958; + expr(0); + } + } + + State = 961; + Match(END_); + } + break; + case 10: + { + State = 963; + raise_function(); + } + break; + } + Context.Stop = TokenStream.LT(-1); + State = 1091; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,138,Context); + while ( _alt!=2 && _alt!=global::Antlr4.Runtime.Atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + if ( ParseListeners!=null ) + TriggerExitRuleEvent(); + _prevctx = _localctx; + { + State = 1089; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,137,Context) ) { + case 1: + { + _localctx = new ExprContext(_parentctx, _parentState); + PushNewRecursionContext(_localctx, _startState, RULE_expr); + State = 966; + if (!(Precpred(Context, 20))) throw new FailedPredicateException(this, "Precpred(Context, 20)"); + State = 967; + Match(PIPE2); + State = 968; + expr(21); + } + break; + case 2: + { + _localctx = new ExprContext(_parentctx, _parentState); + PushNewRecursionContext(_localctx, _startState, RULE_expr); + State = 969; + if (!(Precpred(Context, 19))) throw new FailedPredicateException(this, "Precpred(Context, 19)"); + State = 970; + _la = TokenStream.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 12416L) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 971; + expr(20); + } + break; + case 3: + { + _localctx = new ExprContext(_parentctx, _parentState); + PushNewRecursionContext(_localctx, _startState, RULE_expr); + State = 972; + if (!(Precpred(Context, 18))) throw new FailedPredicateException(this, "Precpred(Context, 18)"); + State = 973; + _la = TokenStream.LA(1); + if ( !(_la==PLUS || _la==MINUS) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 974; + expr(19); + } + break; + case 4: + { + _localctx = new ExprContext(_parentctx, _parentState); + PushNewRecursionContext(_localctx, _startState, RULE_expr); + State = 975; + if (!(Precpred(Context, 17))) throw new FailedPredicateException(this, "Precpred(Context, 17)"); + State = 976; + _la = TokenStream.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 245760L) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 977; + expr(18); + } + break; + case 5: + { + _localctx = new ExprContext(_parentctx, _parentState); + PushNewRecursionContext(_localctx, _startState, RULE_expr); + State = 978; + if (!(Precpred(Context, 16))) throw new FailedPredicateException(this, "Precpred(Context, 16)"); + State = 979; + _la = TokenStream.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 3932160L) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 980; + expr(17); + } + break; + case 6: + { + _localctx = new ExprContext(_parentctx, _parentState); + PushNewRecursionContext(_localctx, _startState, RULE_expr); + State = 981; + if (!(Precpred(Context, 15))) throw new FailedPredicateException(this, "Precpred(Context, 15)"); + State = 1000; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,123,Context) ) { + case 1: + { + State = 982; + Match(ASSIGN); + } + break; + case 2: + { + State = 983; + Match(EQ); + } + break; + case 3: + { + State = 984; + Match(NOT_EQ1); + } + break; + case 4: + { + State = 985; + Match(NOT_EQ2); + } + break; + case 5: + { + State = 986; + Match(IS_); + } + break; + case 6: + { + State = 987; + Match(IS_); + State = 988; + Match(NOT_); + } + break; + case 7: + { + State = 989; + Match(IS_); + State = 991; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==NOT_) { + { + State = 990; + Match(NOT_); + } + } + + State = 993; + Match(DISTINCT_); + State = 994; + Match(FROM_); + } + break; + case 8: + { + State = 995; + Match(IN_); + } + break; + case 9: + { + State = 996; + Match(LIKE_); + } + break; + case 10: + { + State = 997; + Match(GLOB_); + } + break; + case 11: + { + State = 998; + Match(MATCH_); + } + break; + case 12: + { + State = 999; + Match(REGEXP_); + } + break; + } + State = 1002; + expr(16); + } + break; + case 7: + { + _localctx = new ExprContext(_parentctx, _parentState); + PushNewRecursionContext(_localctx, _startState, RULE_expr); + State = 1003; + if (!(Precpred(Context, 14))) throw new FailedPredicateException(this, "Precpred(Context, 14)"); + State = 1004; + Match(AND_); + State = 1005; + expr(15); + } + break; + case 8: + { + _localctx = new ExprContext(_parentctx, _parentState); + PushNewRecursionContext(_localctx, _startState, RULE_expr); + State = 1006; + if (!(Precpred(Context, 13))) throw new FailedPredicateException(this, "Precpred(Context, 13)"); + State = 1007; + Match(OR_); + State = 1008; + expr(14); + } + break; + case 9: + { + _localctx = new ExprContext(_parentctx, _parentState); + PushNewRecursionContext(_localctx, _startState, RULE_expr); + State = 1009; + if (!(Precpred(Context, 6))) throw new FailedPredicateException(this, "Precpred(Context, 6)"); + State = 1010; + Match(IS_); + State = 1012; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,124,Context) ) { + case 1: + { + State = 1011; + Match(NOT_); + } + break; + } + State = 1014; + expr(7); + } + break; + case 10: + { + _localctx = new ExprContext(_parentctx, _parentState); + PushNewRecursionContext(_localctx, _startState, RULE_expr); + State = 1015; + if (!(Precpred(Context, 5))) throw new FailedPredicateException(this, "Precpred(Context, 5)"); + State = 1017; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==NOT_) { + { + State = 1016; + Match(NOT_); + } + } + + State = 1019; + Match(BETWEEN_); + State = 1020; + expr(0); + State = 1021; + Match(AND_); + State = 1022; + expr(6); + } + break; + case 11: + { + _localctx = new ExprContext(_parentctx, _parentState); + PushNewRecursionContext(_localctx, _startState, RULE_expr); + State = 1024; + if (!(Precpred(Context, 9))) throw new FailedPredicateException(this, "Precpred(Context, 9)"); + State = 1025; + Match(COLLATE_); + State = 1026; + collation_name(); + } + break; + case 12: + { + _localctx = new ExprContext(_parentctx, _parentState); + PushNewRecursionContext(_localctx, _startState, RULE_expr); + State = 1027; + if (!(Precpred(Context, 8))) throw new FailedPredicateException(this, "Precpred(Context, 8)"); + State = 1029; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==NOT_) { + { + State = 1028; + Match(NOT_); + } + } + + State = 1031; + _la = TokenStream.LA(1); + if ( !(((((_la - 77)) & ~0x3f) == 0 && ((1L << (_la - 77)) & 2199028498433L) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 1032; + expr(0); + State = 1035; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,127,Context) ) { + case 1: + { + State = 1033; + Match(ESCAPE_); + State = 1034; + expr(0); + } + break; + } + } + break; + case 13: + { + _localctx = new ExprContext(_parentctx, _parentState); + PushNewRecursionContext(_localctx, _startState, RULE_expr); + State = 1037; + if (!(Precpred(Context, 7))) throw new FailedPredicateException(this, "Precpred(Context, 7)"); + State = 1042; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case ISNULL_: + { + State = 1038; + Match(ISNULL_); + } + break; + case NOTNULL_: + { + State = 1039; + Match(NOTNULL_); + } + break; + case NOT_: + { + State = 1040; + Match(NOT_); + State = 1041; + Match(NULL_); + } + break; + default: + throw new NoViableAltException(this); + } + } + break; + case 14: + { + _localctx = new ExprContext(_parentctx, _parentState); + PushNewRecursionContext(_localctx, _startState, RULE_expr); + State = 1044; + if (!(Precpred(Context, 4))) throw new FailedPredicateException(this, "Precpred(Context, 4)"); + State = 1046; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==NOT_) { + { + State = 1045; + Match(NOT_); + } + } + + State = 1048; + Match(IN_); + State = 1087; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,136,Context) ) { + case 1: + { + State = 1049; + Match(OPEN_PAR); + State = 1059; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,131,Context) ) { + case 1: + { + State = 1050; + select_stmt(); + } + break; + case 2: + { + State = 1051; + expr(0); + State = 1056; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1052; + Match(COMMA); + State = 1053; + expr(0); + } + } + State = 1058; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + break; + } + State = 1061; + Match(CLOSE_PAR); + } + break; + case 2: + { + State = 1065; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,132,Context) ) { + case 1: + { + State = 1062; + schema_name(); + State = 1063; + Match(DOT); + } + break; + } + State = 1067; + table_name(); + } + break; + case 3: + { + State = 1071; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,133,Context) ) { + case 1: + { + State = 1068; + schema_name(); + State = 1069; + Match(DOT); + } + break; + } + State = 1073; + table_function_name(); + State = 1074; + Match(OPEN_PAR); + State = 1083; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & -33552632L) != 0) || ((((_la - 64)) & ~0x3f) == 0 && ((1L << (_la - 64)) & -1152921504606846977L) != 0) || ((((_la - 128)) & ~0x3f) == 0 && ((1L << (_la - 128)) & 4476578029606273023L) != 0)) { + { + State = 1075; + expr(0); + State = 1080; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1076; + Match(COMMA); + State = 1077; + expr(0); + } + } + State = 1082; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + + State = 1085; + Match(CLOSE_PAR); + } + break; + } + } + break; + } + } + } + State = 1093; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,138,Context); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + UnrollRecursionContexts(_parentctx); + } + return _localctx; + } + + public partial class Raise_functionContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode RAISE_() { return GetToken(SQLiteParser.RAISE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IGNORE_() { return GetToken(SQLiteParser.IGNORE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA() { return GetToken(SQLiteParser.COMMA, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Error_messageContext error_message() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ROLLBACK_() { return GetToken(SQLiteParser.ROLLBACK_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ABORT_() { return GetToken(SQLiteParser.ABORT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FAIL_() { return GetToken(SQLiteParser.FAIL_, 0); } + public Raise_functionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_raise_function; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterRaise_function(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitRaise_function(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitRaise_function(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Raise_functionContext raise_function() { + Raise_functionContext _localctx = new Raise_functionContext(Context, State); + EnterRule(_localctx, 66, RULE_raise_function); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1094; + Match(RAISE_); + State = 1095; + Match(OPEN_PAR); + State = 1100; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case IGNORE_: + { + State = 1096; + Match(IGNORE_); + } + break; + case ABORT_: + case FAIL_: + case ROLLBACK_: + { + State = 1097; + _la = TokenStream.LA(1); + if ( !(_la==ABORT_ || _la==FAIL_ || _la==ROLLBACK_) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 1098; + Match(COMMA); + State = 1099; + error_message(); + } + break; + default: + throw new NoViableAltException(this); + } + State = 1102; + Match(CLOSE_PAR); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Literal_valueContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NUMERIC_LITERAL() { return GetToken(SQLiteParser.NUMERIC_LITERAL, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode STRING_LITERAL() { return GetToken(SQLiteParser.STRING_LITERAL, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BLOB_LITERAL() { return GetToken(SQLiteParser.BLOB_LITERAL, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NULL_() { return GetToken(SQLiteParser.NULL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TRUE_() { return GetToken(SQLiteParser.TRUE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FALSE_() { return GetToken(SQLiteParser.FALSE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CURRENT_TIME_() { return GetToken(SQLiteParser.CURRENT_TIME_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CURRENT_DATE_() { return GetToken(SQLiteParser.CURRENT_DATE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CURRENT_TIMESTAMP_() { return GetToken(SQLiteParser.CURRENT_TIMESTAMP_, 0); } + public Literal_valueContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_literal_value; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterLiteral_value(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitLiteral_value(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitLiteral_value(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Literal_valueContext literal_value() { + Literal_valueContext _localctx = new Literal_valueContext(Context, State); + EnterRule(_localctx, 68, RULE_literal_value); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1104; + _la = TokenStream.LA(1); + if ( !(((((_la - 52)) & ~0x3f) == 0 && ((1L << (_la - 52)) & 4503599627370503L) != 0) || ((((_la - 172)) & ~0x3f) == 0 && ((1L << (_la - 172)) & 212995L) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Value_rowContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext[] expr() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + public Value_rowContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_value_row; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterValue_row(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitValue_row(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitValue_row(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Value_rowContext value_row() { + Value_rowContext _localctx = new Value_rowContext(Context, State); + EnterRule(_localctx, 70, RULE_value_row); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1106; + Match(OPEN_PAR); + State = 1107; + expr(0); + State = 1112; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1108; + Match(COMMA); + State = 1109; + expr(0); + } + } + State = 1114; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 1115; + Match(CLOSE_PAR); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Values_clauseContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode VALUES_() { return GetToken(SQLiteParser.VALUES_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Value_rowContext[] value_row() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Value_rowContext value_row(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + public Values_clauseContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_values_clause; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterValues_clause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitValues_clause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitValues_clause(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Values_clauseContext values_clause() { + Values_clauseContext _localctx = new Values_clauseContext(Context, State); + EnterRule(_localctx, 72, RULE_values_clause); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1117; + Match(VALUES_); + State = 1118; + value_row(); + State = 1123; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1119; + Match(COMMA); + State = 1120; + value_row(); + } + } + State = 1125; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Insert_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INTO_() { return GetToken(SQLiteParser.INTO_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Table_nameContext table_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INSERT_() { return GetToken(SQLiteParser.INSERT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode REPLACE_() { return GetToken(SQLiteParser.REPLACE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OR_() { return GetToken(SQLiteParser.OR_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DEFAULT_() { return GetToken(SQLiteParser.DEFAULT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode VALUES_() { return GetToken(SQLiteParser.VALUES_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public With_clauseContext with_clause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ROLLBACK_() { return GetToken(SQLiteParser.ROLLBACK_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ABORT_() { return GetToken(SQLiteParser.ABORT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FAIL_() { return GetToken(SQLiteParser.FAIL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IGNORE_() { return GetToken(SQLiteParser.IGNORE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Schema_nameContext schema_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DOT() { return GetToken(SQLiteParser.DOT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AS_() { return GetToken(SQLiteParser.AS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Table_aliasContext table_alias() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext[] column_name() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext column_name(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Returning_clauseContext returning_clause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + [System.Diagnostics.DebuggerNonUserCode] public Values_clauseContext values_clause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Select_stmtContext select_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Upsert_clauseContext upsert_clause() { + return GetRuleContext(0); + } + public Insert_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_insert_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterInsert_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitInsert_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitInsert_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Insert_stmtContext insert_stmt() { + Insert_stmtContext _localctx = new Insert_stmtContext(Context, State); + EnterRule(_localctx, 74, RULE_insert_stmt); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1127; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WITH_) { + { + State = 1126; + with_clause(); + } + } + + State = 1134; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,143,Context) ) { + case 1: + { + State = 1129; + Match(INSERT_); + } + break; + case 2: + { + State = 1130; + Match(REPLACE_); + } + break; + case 3: + { + State = 1131; + Match(INSERT_); + State = 1132; + Match(OR_); + State = 1133; + _la = TokenStream.LA(1); + if ( !(_la==ABORT_ || ((((_la - 72)) & ~0x3f) == 0 && ((1L << (_la - 72)) & 19140298416325121L) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + break; + } + State = 1136; + Match(INTO_); + State = 1140; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,144,Context) ) { + case 1: + { + State = 1137; + schema_name(); + State = 1138; + Match(DOT); + } + break; + } + State = 1142; + table_name(); + State = 1145; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AS_) { + { + State = 1143; + Match(AS_); + State = 1144; + table_alias(); + } + } + + State = 1158; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==OPEN_PAR) { + { + State = 1147; + Match(OPEN_PAR); + State = 1148; + column_name(); + State = 1153; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1149; + Match(COMMA); + State = 1150; + column_name(); + } + } + State = 1155; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 1156; + Match(CLOSE_PAR); + } + } + + State = 1169; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case SELECT_: + case VALUES_: + case WITH_: + { + { + State = 1162; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,148,Context) ) { + case 1: + { + State = 1160; + values_clause(); + } + break; + case 2: + { + State = 1161; + select_stmt(); + } + break; + } + State = 1165; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ON_) { + { + State = 1164; + upsert_clause(); + } + } + + } + } + break; + case DEFAULT_: + { + State = 1167; + Match(DEFAULT_); + State = 1168; + Match(VALUES_); + } + break; + default: + throw new NoViableAltException(this); + } + State = 1172; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==RETURNING_) { + { + State = 1171; + returning_clause(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Returning_clauseContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode RETURNING_() { return GetToken(SQLiteParser.RETURNING_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Result_columnContext[] result_column() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Result_columnContext result_column(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + public Returning_clauseContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_returning_clause; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterReturning_clause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitReturning_clause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitReturning_clause(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Returning_clauseContext returning_clause() { + Returning_clauseContext _localctx = new Returning_clauseContext(Context, State); + EnterRule(_localctx, 76, RULE_returning_clause); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1174; + Match(RETURNING_); + State = 1175; + result_column(); + State = 1180; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1176; + Match(COMMA); + State = 1177; + result_column(); + } + } + State = 1182; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Upsert_clauseContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ON_() { return GetToken(SQLiteParser.ON_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CONFLICT_() { return GetToken(SQLiteParser.CONFLICT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DO_() { return GetToken(SQLiteParser.DO_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NOTHING_() { return GetToken(SQLiteParser.NOTHING_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode UPDATE_() { return GetToken(SQLiteParser.UPDATE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode SET_() { return GetToken(SQLiteParser.SET_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Indexed_columnContext[] indexed_column() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Indexed_columnContext indexed_column(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] ASSIGN() { return GetTokens(SQLiteParser.ASSIGN); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ASSIGN(int i) { + return GetToken(SQLiteParser.ASSIGN, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext[] expr() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] WHERE_() { return GetTokens(SQLiteParser.WHERE_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode WHERE_(int i) { + return GetToken(SQLiteParser.WHERE_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext[] column_name() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext column_name(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_name_listContext[] column_name_list() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_name_listContext column_name_list(int i) { + return GetRuleContext(i); + } + public Upsert_clauseContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_upsert_clause; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterUpsert_clause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitUpsert_clause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitUpsert_clause(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Upsert_clauseContext upsert_clause() { + Upsert_clauseContext _localctx = new Upsert_clauseContext(Context, State); + EnterRule(_localctx, 78, RULE_upsert_clause); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1183; + Match(ON_); + State = 1184; + Match(CONFLICT_); + State = 1199; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==OPEN_PAR) { + { + State = 1185; + Match(OPEN_PAR); + State = 1186; + indexed_column(); + State = 1191; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1187; + Match(COMMA); + State = 1188; + indexed_column(); + } + } + State = 1193; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 1194; + Match(CLOSE_PAR); + State = 1197; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE_) { + { + State = 1195; + Match(WHERE_); + State = 1196; + expr(0); + } + } + + } + } + + State = 1201; + Match(DO_); + State = 1228; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case NOTHING_: + { + State = 1202; + Match(NOTHING_); + } + break; + case UPDATE_: + { + State = 1203; + Match(UPDATE_); + State = 1204; + Match(SET_); + { + State = 1207; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,156,Context) ) { + case 1: + { + State = 1205; + column_name(); + } + break; + case 2: + { + State = 1206; + column_name_list(); + } + break; + } + State = 1209; + Match(ASSIGN); + State = 1210; + expr(0); + State = 1221; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1211; + Match(COMMA); + State = 1214; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,157,Context) ) { + case 1: + { + State = 1212; + column_name(); + } + break; + case 2: + { + State = 1213; + column_name_list(); + } + break; + } + State = 1216; + Match(ASSIGN); + State = 1217; + expr(0); + } + } + State = 1223; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 1226; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE_) { + { + State = 1224; + Match(WHERE_); + State = 1225; + expr(0); + } + } + + } + } + break; + default: + throw new NoViableAltException(this); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Pragma_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PRAGMA_() { return GetToken(SQLiteParser.PRAGMA_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Pragma_nameContext pragma_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Schema_nameContext schema_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DOT() { return GetToken(SQLiteParser.DOT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ASSIGN() { return GetToken(SQLiteParser.ASSIGN, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Pragma_valueContext pragma_value() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + public Pragma_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_pragma_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterPragma_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitPragma_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitPragma_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Pragma_stmtContext pragma_stmt() { + Pragma_stmtContext _localctx = new Pragma_stmtContext(Context, State); + EnterRule(_localctx, 80, RULE_pragma_stmt); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1230; + Match(PRAGMA_); + State = 1234; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,161,Context) ) { + case 1: + { + State = 1231; + schema_name(); + State = 1232; + Match(DOT); + } + break; + } + State = 1236; + pragma_name(); + State = 1243; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case ASSIGN: + { + State = 1237; + Match(ASSIGN); + State = 1238; + pragma_value(); + } + break; + case OPEN_PAR: + { + State = 1239; + Match(OPEN_PAR); + State = 1240; + pragma_value(); + State = 1241; + Match(CLOSE_PAR); + } + break; + case Eof: + case SCOL: + case ALTER_: + case ANALYZE_: + case ATTACH_: + case BEGIN_: + case COMMIT_: + case CREATE_: + case DELETE_: + case DETACH_: + case DROP_: + case END_: + case EXPLAIN_: + case INSERT_: + case PRAGMA_: + case REINDEX_: + case RELEASE_: + case REPLACE_: + case ROLLBACK_: + case SAVEPOINT_: + case SELECT_: + case UPDATE_: + case VACUUM_: + case VALUES_: + case WITH_: + break; + default: + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Pragma_valueContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Signed_numberContext signed_number() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public NameContext name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode STRING_LITERAL() { return GetToken(SQLiteParser.STRING_LITERAL, 0); } + public Pragma_valueContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_pragma_value; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterPragma_value(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitPragma_value(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitPragma_value(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Pragma_valueContext pragma_value() { + Pragma_valueContext _localctx = new Pragma_valueContext(Context, State); + EnterRule(_localctx, 82, RULE_pragma_value); + try { + State = 1248; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,163,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 1245; + signed_number(); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 1246; + name(); + } + break; + case 3: + EnterOuterAlt(_localctx, 3); + { + State = 1247; + Match(STRING_LITERAL); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Reindex_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode REINDEX_() { return GetToken(SQLiteParser.REINDEX_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Collation_nameContext collation_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Table_nameContext table_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Index_nameContext index_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Schema_nameContext schema_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DOT() { return GetToken(SQLiteParser.DOT, 0); } + public Reindex_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_reindex_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterReindex_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitReindex_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitReindex_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Reindex_stmtContext reindex_stmt() { + Reindex_stmtContext _localctx = new Reindex_stmtContext(Context, State); + EnterRule(_localctx, 84, RULE_reindex_stmt); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1250; + Match(REINDEX_); + State = 1261; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,166,Context) ) { + case 1: + { + State = 1251; + collation_name(); + } + break; + case 2: + { + State = 1255; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,164,Context) ) { + case 1: + { + State = 1252; + schema_name(); + State = 1253; + Match(DOT); + } + break; + } + State = 1259; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,165,Context) ) { + case 1: + { + State = 1257; + table_name(); + } + break; + case 2: + { + State = 1258; + index_name(); + } + break; + } + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Select_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Select_coreContext[] select_core() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Select_coreContext select_core(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Common_table_stmtContext common_table_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Compound_operatorContext[] compound_operator() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Compound_operatorContext compound_operator(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Order_by_stmtContext order_by_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Limit_stmtContext limit_stmt() { + return GetRuleContext(0); + } + public Select_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_select_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterSelect_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitSelect_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitSelect_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Select_stmtContext select_stmt() { + Select_stmtContext _localctx = new Select_stmtContext(Context, State); + EnterRule(_localctx, 86, RULE_select_stmt); + int _la; + try { + int _alt; + EnterOuterAlt(_localctx, 1); + { + State = 1264; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WITH_) { + { + State = 1263; + common_table_stmt(); + } + } + + State = 1266; + select_core(); + State = 1272; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,168,Context); + while ( _alt!=2 && _alt!=global::Antlr4.Runtime.Atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + State = 1267; + compound_operator(); + State = 1268; + select_core(); + } + } + } + State = 1274; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,168,Context); + } + State = 1276; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ORDER_) { + { + State = 1275; + order_by_stmt(); + } + } + + State = 1279; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LIMIT_) { + { + State = 1278; + limit_stmt(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Join_clauseContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Table_or_subqueryContext[] table_or_subquery() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Table_or_subqueryContext table_or_subquery(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Join_operatorContext[] join_operator() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Join_operatorContext join_operator(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Join_constraintContext[] join_constraint() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Join_constraintContext join_constraint(int i) { + return GetRuleContext(i); + } + public Join_clauseContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_join_clause; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterJoin_clause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitJoin_clause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitJoin_clause(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Join_clauseContext join_clause() { + Join_clauseContext _localctx = new Join_clauseContext(Context, State); + EnterRule(_localctx, 88, RULE_join_clause); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1281; + table_or_subquery(); + State = 1289; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA || _la==CROSS_ || ((((_la - 76)) & ~0x3f) == 0 && ((1L << (_la - 76)) & 562949971511297L) != 0)) { + { + { + State = 1282; + join_operator(); + State = 1283; + table_or_subquery(); + State = 1285; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,171,Context) ) { + case 1: + { + State = 1284; + join_constraint(); + } + break; + } + } + } + State = 1291; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Select_coreContext : ParserRuleContext { + public ExprContext whereExpr; + public ExprContext _expr; + public IList _groupByExpr = new List(); + public ExprContext havingExpr; + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode SELECT_() { return GetToken(SQLiteParser.SELECT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Result_columnContext[] result_column() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Result_columnContext result_column(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FROM_() { return GetToken(SQLiteParser.FROM_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode WHERE_() { return GetToken(SQLiteParser.WHERE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode GROUP_() { return GetToken(SQLiteParser.GROUP_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BY_() { return GetToken(SQLiteParser.BY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode WINDOW_() { return GetToken(SQLiteParser.WINDOW_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Window_nameContext[] window_name() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Window_nameContext window_name(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] AS_() { return GetTokens(SQLiteParser.AS_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AS_(int i) { + return GetToken(SQLiteParser.AS_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public Window_defnContext[] window_defn() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Window_defnContext window_defn(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DISTINCT_() { return GetToken(SQLiteParser.DISTINCT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ALL_() { return GetToken(SQLiteParser.ALL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext[] expr() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Table_or_subqueryContext[] table_or_subquery() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Table_or_subqueryContext table_or_subquery(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Join_clauseContext join_clause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode HAVING_() { return GetToken(SQLiteParser.HAVING_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Values_clauseContext values_clause() { + return GetRuleContext(0); + } + public Select_coreContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_select_core; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterSelect_core(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitSelect_core(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitSelect_core(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Select_coreContext select_core() { + Select_coreContext _localctx = new Select_coreContext(Context, State); + EnterRule(_localctx, 90, RULE_select_core); + int _la; + try { + State = 1355; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case SELECT_: + EnterOuterAlt(_localctx, 1); + { + { + State = 1292; + Match(SELECT_); + State = 1294; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,173,Context) ) { + case 1: + { + State = 1293; + _la = TokenStream.LA(1); + if ( !(_la==ALL_ || _la==DISTINCT_) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + break; + } + State = 1296; + result_column(); + State = 1301; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1297; + Match(COMMA); + State = 1298; + result_column(); + } + } + State = 1303; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 1316; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==FROM_) { + { + State = 1304; + Match(FROM_); + State = 1314; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,176,Context) ) { + case 1: + { + State = 1305; + table_or_subquery(); + State = 1310; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1306; + Match(COMMA); + State = 1307; + table_or_subquery(); + } + } + State = 1312; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + break; + case 2: + { + State = 1313; + join_clause(); + } + break; + } + } + } + + State = 1320; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE_) { + { + State = 1318; + Match(WHERE_); + State = 1319; + _localctx.whereExpr = expr(0); + } + } + + State = 1336; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==GROUP_) { + { + State = 1322; + Match(GROUP_); + State = 1323; + Match(BY_); + State = 1324; + _localctx._expr = expr(0); + _localctx._groupByExpr.Add(_localctx._expr); + State = 1329; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1325; + Match(COMMA); + State = 1326; + _localctx._expr = expr(0); + _localctx._groupByExpr.Add(_localctx._expr); + } + } + State = 1331; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 1334; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==HAVING_) { + { + State = 1332; + Match(HAVING_); + State = 1333; + _localctx.havingExpr = expr(0); + } + } + + } + } + + State = 1352; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WINDOW_) { + { + State = 1338; + Match(WINDOW_); + State = 1339; + window_name(); + State = 1340; + Match(AS_); + State = 1341; + window_defn(); + State = 1349; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1342; + Match(COMMA); + State = 1343; + window_name(); + State = 1344; + Match(AS_); + State = 1345; + window_defn(); + } + } + State = 1351; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + + } + } + break; + case VALUES_: + EnterOuterAlt(_localctx, 2); + { + State = 1354; + values_clause(); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Factored_select_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Select_stmtContext select_stmt() { + return GetRuleContext(0); + } + public Factored_select_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_factored_select_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterFactored_select_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitFactored_select_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitFactored_select_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Factored_select_stmtContext factored_select_stmt() { + Factored_select_stmtContext _localctx = new Factored_select_stmtContext(Context, State); + EnterRule(_localctx, 92, RULE_factored_select_stmt); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1357; + select_stmt(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Simple_select_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Select_coreContext select_core() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Common_table_stmtContext common_table_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Order_by_stmtContext order_by_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Limit_stmtContext limit_stmt() { + return GetRuleContext(0); + } + public Simple_select_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_simple_select_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterSimple_select_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitSimple_select_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitSimple_select_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Simple_select_stmtContext simple_select_stmt() { + Simple_select_stmtContext _localctx = new Simple_select_stmtContext(Context, State); + EnterRule(_localctx, 94, RULE_simple_select_stmt); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1360; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WITH_) { + { + State = 1359; + common_table_stmt(); + } + } + + State = 1362; + select_core(); + State = 1364; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ORDER_) { + { + State = 1363; + order_by_stmt(); + } + } + + State = 1367; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LIMIT_) { + { + State = 1366; + limit_stmt(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Compound_select_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Select_coreContext[] select_core() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Select_coreContext select_core(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Common_table_stmtContext common_table_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Order_by_stmtContext order_by_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Limit_stmtContext limit_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] UNION_() { return GetTokens(SQLiteParser.UNION_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode UNION_(int i) { + return GetToken(SQLiteParser.UNION_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] INTERSECT_() { return GetTokens(SQLiteParser.INTERSECT_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INTERSECT_(int i) { + return GetToken(SQLiteParser.INTERSECT_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] EXCEPT_() { return GetTokens(SQLiteParser.EXCEPT_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode EXCEPT_(int i) { + return GetToken(SQLiteParser.EXCEPT_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] ALL_() { return GetTokens(SQLiteParser.ALL_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ALL_(int i) { + return GetToken(SQLiteParser.ALL_, i); + } + public Compound_select_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_compound_select_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterCompound_select_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitCompound_select_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitCompound_select_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Compound_select_stmtContext compound_select_stmt() { + Compound_select_stmtContext _localctx = new Compound_select_stmtContext(Context, State); + EnterRule(_localctx, 96, RULE_compound_select_stmt); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1370; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WITH_) { + { + State = 1369; + common_table_stmt(); + } + } + + State = 1372; + select_core(); + State = 1382; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + do { + { + { + State = 1379; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case UNION_: + { + State = 1373; + Match(UNION_); + State = 1375; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ALL_) { + { + State = 1374; + Match(ALL_); + } + } + + } + break; + case INTERSECT_: + { + State = 1377; + Match(INTERSECT_); + } + break; + case EXCEPT_: + { + State = 1378; + Match(EXCEPT_); + } + break; + default: + throw new NoViableAltException(this); + } + State = 1381; + select_core(); + } + } + State = 1384; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } while ( _la==EXCEPT_ || _la==INTERSECT_ || _la==UNION_ ); + State = 1387; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ORDER_) { + { + State = 1386; + order_by_stmt(); + } + } + + State = 1390; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LIMIT_) { + { + State = 1389; + limit_stmt(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Table_or_subqueryContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Table_nameContext table_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Schema_nameContext schema_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DOT() { return GetToken(SQLiteParser.DOT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Table_aliasContext table_alias() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INDEXED_() { return GetToken(SQLiteParser.INDEXED_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BY_() { return GetToken(SQLiteParser.BY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Index_nameContext index_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NOT_() { return GetToken(SQLiteParser.NOT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AS_() { return GetToken(SQLiteParser.AS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Table_function_nameContext table_function_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext[] expr() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + [System.Diagnostics.DebuggerNonUserCode] public Table_or_subqueryContext[] table_or_subquery() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Table_or_subqueryContext table_or_subquery(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Join_clauseContext join_clause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Select_stmtContext select_stmt() { + return GetRuleContext(0); + } + public Table_or_subqueryContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_table_or_subquery; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterTable_or_subquery(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitTable_or_subquery(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitTable_or_subquery(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Table_or_subqueryContext table_or_subquery() { + Table_or_subqueryContext _localctx = new Table_or_subqueryContext(Context, State); + EnterRule(_localctx, 98, RULE_table_or_subquery); + int _la; + try { + State = 1456; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,206,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + { + State = 1395; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,194,Context) ) { + case 1: + { + State = 1392; + schema_name(); + State = 1393; + Match(DOT); + } + break; + } + State = 1397; + table_name(); + State = 1402; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,196,Context) ) { + case 1: + { + State = 1399; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,195,Context) ) { + case 1: + { + State = 1398; + Match(AS_); + } + break; + } + State = 1401; + table_alias(); + } + break; + } + State = 1409; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case INDEXED_: + { + State = 1404; + Match(INDEXED_); + State = 1405; + Match(BY_); + State = 1406; + index_name(); + } + break; + case NOT_: + { + State = 1407; + Match(NOT_); + State = 1408; + Match(INDEXED_); + } + break; + case Eof: + case SCOL: + case CLOSE_PAR: + case COMMA: + case ALTER_: + case ANALYZE_: + case ATTACH_: + case BEGIN_: + case COMMIT_: + case CREATE_: + case CROSS_: + case DELETE_: + case DETACH_: + case DROP_: + case END_: + case EXCEPT_: + case EXPLAIN_: + case FULL_: + case GROUP_: + case INNER_: + case INSERT_: + case INTERSECT_: + case JOIN_: + case LEFT_: + case LIMIT_: + case NATURAL_: + case ON_: + case ORDER_: + case PRAGMA_: + case REINDEX_: + case RELEASE_: + case REPLACE_: + case RETURNING_: + case RIGHT_: + case ROLLBACK_: + case SAVEPOINT_: + case SELECT_: + case UNION_: + case UPDATE_: + case USING_: + case VACUUM_: + case VALUES_: + case WHERE_: + case WITH_: + case WINDOW_: + break; + default: + break; + } + } + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 1414; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,198,Context) ) { + case 1: + { + State = 1411; + schema_name(); + State = 1412; + Match(DOT); + } + break; + } + State = 1416; + table_function_name(); + State = 1417; + Match(OPEN_PAR); + State = 1418; + expr(0); + State = 1423; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1419; + Match(COMMA); + State = 1420; + expr(0); + } + } + State = 1425; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 1426; + Match(CLOSE_PAR); + State = 1431; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,201,Context) ) { + case 1: + { + State = 1428; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,200,Context) ) { + case 1: + { + State = 1427; + Match(AS_); + } + break; + } + State = 1430; + table_alias(); + } + break; + } + } + break; + case 3: + EnterOuterAlt(_localctx, 3); + { + State = 1433; + Match(OPEN_PAR); + State = 1443; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,203,Context) ) { + case 1: + { + State = 1434; + table_or_subquery(); + State = 1439; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1435; + Match(COMMA); + State = 1436; + table_or_subquery(); + } + } + State = 1441; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + break; + case 2: + { + State = 1442; + join_clause(); + } + break; + } + State = 1445; + Match(CLOSE_PAR); + } + break; + case 4: + EnterOuterAlt(_localctx, 4); + { + State = 1447; + Match(OPEN_PAR); + State = 1448; + select_stmt(); + State = 1449; + Match(CLOSE_PAR); + State = 1454; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,205,Context) ) { + case 1: + { + State = 1451; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,204,Context) ) { + case 1: + { + State = 1450; + Match(AS_); + } + break; + } + State = 1453; + table_alias(); + } + break; + } + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Result_columnContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode STAR() { return GetToken(SQLiteParser.STAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Table_nameContext table_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DOT() { return GetToken(SQLiteParser.DOT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_aliasContext column_alias() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AS_() { return GetToken(SQLiteParser.AS_, 0); } + public Result_columnContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_result_column; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterResult_column(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitResult_column(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitResult_column(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Result_columnContext result_column() { + Result_columnContext _localctx = new Result_columnContext(Context, State); + EnterRule(_localctx, 100, RULE_result_column); + int _la; + try { + State = 1470; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,209,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 1458; + Match(STAR); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 1459; + table_name(); + State = 1460; + Match(DOT); + State = 1461; + Match(STAR); + } + break; + case 3: + EnterOuterAlt(_localctx, 3); + { + State = 1463; + expr(0); + State = 1468; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AS_ || _la==IDENTIFIER || _la==STRING_LITERAL) { + { + State = 1465; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AS_) { + { + State = 1464; + Match(AS_); + } + } + + State = 1467; + column_alias(); + } + } + + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Join_operatorContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA() { return GetToken(SQLiteParser.COMMA, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode JOIN_() { return GetToken(SQLiteParser.JOIN_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NATURAL_() { return GetToken(SQLiteParser.NATURAL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INNER_() { return GetToken(SQLiteParser.INNER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CROSS_() { return GetToken(SQLiteParser.CROSS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode LEFT_() { return GetToken(SQLiteParser.LEFT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode RIGHT_() { return GetToken(SQLiteParser.RIGHT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FULL_() { return GetToken(SQLiteParser.FULL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OUTER_() { return GetToken(SQLiteParser.OUTER_, 0); } + public Join_operatorContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_join_operator; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterJoin_operator(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitJoin_operator(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitJoin_operator(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Join_operatorContext join_operator() { + Join_operatorContext _localctx = new Join_operatorContext(Context, State); + EnterRule(_localctx, 102, RULE_join_operator); + int _la; + try { + State = 1485; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case COMMA: + EnterOuterAlt(_localctx, 1); + { + State = 1472; + Match(COMMA); + } + break; + case CROSS_: + case FULL_: + case INNER_: + case JOIN_: + case LEFT_: + case NATURAL_: + case RIGHT_: + EnterOuterAlt(_localctx, 2); + { + State = 1474; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==NATURAL_) { + { + State = 1473; + Match(NATURAL_); + } + } + + State = 1482; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case FULL_: + case LEFT_: + case RIGHT_: + { + State = 1476; + _la = TokenStream.LA(1); + if ( !(((((_la - 76)) & ~0x3f) == 0 && ((1L << (_la - 76)) & 562949954469889L) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 1478; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==OUTER_) { + { + State = 1477; + Match(OUTER_); + } + } + + } + break; + case INNER_: + { + State = 1480; + Match(INNER_); + } + break; + case CROSS_: + { + State = 1481; + Match(CROSS_); + } + break; + case JOIN_: + break; + default: + break; + } + State = 1484; + Match(JOIN_); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Join_constraintContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ON_() { return GetToken(SQLiteParser.ON_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode USING_() { return GetToken(SQLiteParser.USING_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext[] column_name() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext column_name(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + public Join_constraintContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_join_constraint; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterJoin_constraint(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitJoin_constraint(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitJoin_constraint(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Join_constraintContext join_constraint() { + Join_constraintContext _localctx = new Join_constraintContext(Context, State); + EnterRule(_localctx, 104, RULE_join_constraint); + int _la; + try { + State = 1501; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case ON_: + EnterOuterAlt(_localctx, 1); + { + State = 1487; + Match(ON_); + State = 1488; + expr(0); + } + break; + case USING_: + EnterOuterAlt(_localctx, 2); + { + State = 1489; + Match(USING_); + State = 1490; + Match(OPEN_PAR); + State = 1491; + column_name(); + State = 1496; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1492; + Match(COMMA); + State = 1493; + column_name(); + } + } + State = 1498; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 1499; + Match(CLOSE_PAR); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Compound_operatorContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode UNION_() { return GetToken(SQLiteParser.UNION_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ALL_() { return GetToken(SQLiteParser.ALL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INTERSECT_() { return GetToken(SQLiteParser.INTERSECT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode EXCEPT_() { return GetToken(SQLiteParser.EXCEPT_, 0); } + public Compound_operatorContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_compound_operator; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterCompound_operator(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitCompound_operator(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitCompound_operator(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Compound_operatorContext compound_operator() { + Compound_operatorContext _localctx = new Compound_operatorContext(Context, State); + EnterRule(_localctx, 106, RULE_compound_operator); + int _la; + try { + State = 1509; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case UNION_: + EnterOuterAlt(_localctx, 1); + { + State = 1503; + Match(UNION_); + State = 1505; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ALL_) { + { + State = 1504; + Match(ALL_); + } + } + + } + break; + case INTERSECT_: + EnterOuterAlt(_localctx, 2); + { + State = 1507; + Match(INTERSECT_); + } + break; + case EXCEPT_: + EnterOuterAlt(_localctx, 3); + { + State = 1508; + Match(EXCEPT_); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Update_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode UPDATE_() { return GetToken(SQLiteParser.UPDATE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Qualified_table_nameContext qualified_table_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode SET_() { return GetToken(SQLiteParser.SET_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] ASSIGN() { return GetTokens(SQLiteParser.ASSIGN); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ASSIGN(int i) { + return GetToken(SQLiteParser.ASSIGN, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext[] expr() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext[] column_name() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext column_name(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_name_listContext[] column_name_list() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_name_listContext column_name_list(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public With_clauseContext with_clause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OR_() { return GetToken(SQLiteParser.OR_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FROM_() { return GetToken(SQLiteParser.FROM_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode WHERE_() { return GetToken(SQLiteParser.WHERE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Returning_clauseContext returning_clause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ROLLBACK_() { return GetToken(SQLiteParser.ROLLBACK_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ABORT_() { return GetToken(SQLiteParser.ABORT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode REPLACE_() { return GetToken(SQLiteParser.REPLACE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FAIL_() { return GetToken(SQLiteParser.FAIL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IGNORE_() { return GetToken(SQLiteParser.IGNORE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Table_or_subqueryContext[] table_or_subquery() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Table_or_subqueryContext table_or_subquery(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Join_clauseContext join_clause() { + return GetRuleContext(0); + } + public Update_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_update_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterUpdate_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitUpdate_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitUpdate_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Update_stmtContext update_stmt() { + Update_stmtContext _localctx = new Update_stmtContext(Context, State); + EnterRule(_localctx, 108, RULE_update_stmt); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1512; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WITH_) { + { + State = 1511; + with_clause(); + } + } + + State = 1514; + Match(UPDATE_); + State = 1517; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,219,Context) ) { + case 1: + { + State = 1515; + Match(OR_); + State = 1516; + _la = TokenStream.LA(1); + if ( !(_la==ABORT_ || ((((_la - 72)) & ~0x3f) == 0 && ((1L << (_la - 72)) & 19140298416325121L) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + break; + } + State = 1519; + qualified_table_name(); + State = 1520; + Match(SET_); + State = 1523; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,220,Context) ) { + case 1: + { + State = 1521; + column_name(); + } + break; + case 2: + { + State = 1522; + column_name_list(); + } + break; + } + State = 1525; + Match(ASSIGN); + State = 1526; + expr(0); + State = 1537; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1527; + Match(COMMA); + State = 1530; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,221,Context) ) { + case 1: + { + State = 1528; + column_name(); + } + break; + case 2: + { + State = 1529; + column_name_list(); + } + break; + } + State = 1532; + Match(ASSIGN); + State = 1533; + expr(0); + } + } + State = 1539; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 1552; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==FROM_) { + { + State = 1540; + Match(FROM_); + State = 1550; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,224,Context) ) { + case 1: + { + State = 1541; + table_or_subquery(); + State = 1546; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1542; + Match(COMMA); + State = 1543; + table_or_subquery(); + } + } + State = 1548; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + break; + case 2: + { + State = 1549; + join_clause(); + } + break; + } + } + } + + State = 1556; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE_) { + { + State = 1554; + Match(WHERE_); + State = 1555; + expr(0); + } + } + + State = 1559; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==RETURNING_) { + { + State = 1558; + returning_clause(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Column_name_listContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext[] column_name() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext column_name(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + public Column_name_listContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_column_name_list; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterColumn_name_list(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitColumn_name_list(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitColumn_name_list(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Column_name_listContext column_name_list() { + Column_name_listContext _localctx = new Column_name_listContext(Context, State); + EnterRule(_localctx, 110, RULE_column_name_list); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1561; + Match(OPEN_PAR); + State = 1562; + column_name(); + State = 1567; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1563; + Match(COMMA); + State = 1564; + column_name(); + } + } + State = 1569; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 1570; + Match(CLOSE_PAR); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Update_stmt_limitedContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode UPDATE_() { return GetToken(SQLiteParser.UPDATE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Qualified_table_nameContext qualified_table_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode SET_() { return GetToken(SQLiteParser.SET_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] ASSIGN() { return GetTokens(SQLiteParser.ASSIGN); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ASSIGN(int i) { + return GetToken(SQLiteParser.ASSIGN, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext[] expr() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext[] column_name() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_nameContext column_name(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_name_listContext[] column_name_list() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_name_listContext column_name_list(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public With_clauseContext with_clause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OR_() { return GetToken(SQLiteParser.OR_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode WHERE_() { return GetToken(SQLiteParser.WHERE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Returning_clauseContext returning_clause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Limit_stmtContext limit_stmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ROLLBACK_() { return GetToken(SQLiteParser.ROLLBACK_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ABORT_() { return GetToken(SQLiteParser.ABORT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode REPLACE_() { return GetToken(SQLiteParser.REPLACE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FAIL_() { return GetToken(SQLiteParser.FAIL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IGNORE_() { return GetToken(SQLiteParser.IGNORE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Order_by_stmtContext order_by_stmt() { + return GetRuleContext(0); + } + public Update_stmt_limitedContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_update_stmt_limited; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterUpdate_stmt_limited(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitUpdate_stmt_limited(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitUpdate_stmt_limited(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Update_stmt_limitedContext update_stmt_limited() { + Update_stmt_limitedContext _localctx = new Update_stmt_limitedContext(Context, State); + EnterRule(_localctx, 112, RULE_update_stmt_limited); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1573; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WITH_) { + { + State = 1572; + with_clause(); + } + } + + State = 1575; + Match(UPDATE_); + State = 1578; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,230,Context) ) { + case 1: + { + State = 1576; + Match(OR_); + State = 1577; + _la = TokenStream.LA(1); + if ( !(_la==ABORT_ || ((((_la - 72)) & ~0x3f) == 0 && ((1L << (_la - 72)) & 19140298416325121L) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + break; + } + State = 1580; + qualified_table_name(); + State = 1581; + Match(SET_); + State = 1584; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,231,Context) ) { + case 1: + { + State = 1582; + column_name(); + } + break; + case 2: + { + State = 1583; + column_name_list(); + } + break; + } + State = 1586; + Match(ASSIGN); + State = 1587; + expr(0); + State = 1598; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1588; + Match(COMMA); + State = 1591; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,232,Context) ) { + case 1: + { + State = 1589; + column_name(); + } + break; + case 2: + { + State = 1590; + column_name_list(); + } + break; + } + State = 1593; + Match(ASSIGN); + State = 1594; + expr(0); + } + } + State = 1600; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 1603; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==WHERE_) { + { + State = 1601; + Match(WHERE_); + State = 1602; + expr(0); + } + } + + State = 1606; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==RETURNING_) { + { + State = 1605; + returning_clause(); + } + } + + State = 1612; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==LIMIT_ || _la==ORDER_) { + { + State = 1609; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ORDER_) { + { + State = 1608; + order_by_stmt(); + } + } + + State = 1611; + limit_stmt(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Qualified_table_nameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Table_nameContext table_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Schema_nameContext schema_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DOT() { return GetToken(SQLiteParser.DOT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AS_() { return GetToken(SQLiteParser.AS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public AliasContext alias() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INDEXED_() { return GetToken(SQLiteParser.INDEXED_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BY_() { return GetToken(SQLiteParser.BY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Index_nameContext index_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NOT_() { return GetToken(SQLiteParser.NOT_, 0); } + public Qualified_table_nameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_qualified_table_name; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterQualified_table_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitQualified_table_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitQualified_table_name(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Qualified_table_nameContext qualified_table_name() { + Qualified_table_nameContext _localctx = new Qualified_table_nameContext(Context, State); + EnterRule(_localctx, 114, RULE_qualified_table_name); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1617; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,238,Context) ) { + case 1: + { + State = 1614; + schema_name(); + State = 1615; + Match(DOT); + } + break; + } + State = 1619; + table_name(); + State = 1622; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AS_) { + { + State = 1620; + Match(AS_); + State = 1621; + alias(); + } + } + + State = 1629; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case INDEXED_: + { + State = 1624; + Match(INDEXED_); + State = 1625; + Match(BY_); + State = 1626; + index_name(); + } + break; + case NOT_: + { + State = 1627; + Match(NOT_); + State = 1628; + Match(INDEXED_); + } + break; + case Eof: + case SCOL: + case ALTER_: + case ANALYZE_: + case ATTACH_: + case BEGIN_: + case COMMIT_: + case CREATE_: + case DELETE_: + case DETACH_: + case DROP_: + case END_: + case EXPLAIN_: + case INSERT_: + case LIMIT_: + case ORDER_: + case PRAGMA_: + case REINDEX_: + case RELEASE_: + case REPLACE_: + case RETURNING_: + case ROLLBACK_: + case SAVEPOINT_: + case SELECT_: + case SET_: + case UPDATE_: + case VACUUM_: + case VALUES_: + case WHERE_: + case WITH_: + break; + default: + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Vacuum_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode VACUUM_() { return GetToken(SQLiteParser.VACUUM_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Schema_nameContext schema_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INTO_() { return GetToken(SQLiteParser.INTO_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public FilenameContext filename() { + return GetRuleContext(0); + } + public Vacuum_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_vacuum_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterVacuum_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitVacuum_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitVacuum_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Vacuum_stmtContext vacuum_stmt() { + Vacuum_stmtContext _localctx = new Vacuum_stmtContext(Context, State); + EnterRule(_localctx, 116, RULE_vacuum_stmt); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1631; + Match(VACUUM_); + State = 1633; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,241,Context) ) { + case 1: + { + State = 1632; + schema_name(); + } + break; + } + State = 1637; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==INTO_) { + { + State = 1635; + Match(INTO_); + State = 1636; + filename(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Filter_clauseContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FILTER_() { return GetToken(SQLiteParser.FILTER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode WHERE_() { return GetToken(SQLiteParser.WHERE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + public Filter_clauseContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_filter_clause; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterFilter_clause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitFilter_clause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitFilter_clause(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Filter_clauseContext filter_clause() { + Filter_clauseContext _localctx = new Filter_clauseContext(Context, State); + EnterRule(_localctx, 118, RULE_filter_clause); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1639; + Match(FILTER_); + State = 1640; + Match(OPEN_PAR); + State = 1641; + Match(WHERE_); + State = 1642; + expr(0); + State = 1643; + Match(CLOSE_PAR); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Window_defnContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ORDER_() { return GetToken(SQLiteParser.ORDER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] BY_() { return GetTokens(SQLiteParser.BY_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BY_(int i) { + return GetToken(SQLiteParser.BY_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public Ordering_termContext[] ordering_term() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Ordering_termContext ordering_term(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Base_window_nameContext base_window_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PARTITION_() { return GetToken(SQLiteParser.PARTITION_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext[] expr() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Frame_specContext frame_spec() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + public Window_defnContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_window_defn; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterWindow_defn(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitWindow_defn(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitWindow_defn(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Window_defnContext window_defn() { + Window_defnContext _localctx = new Window_defnContext(Context, State); + EnterRule(_localctx, 120, RULE_window_defn); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1645; + Match(OPEN_PAR); + State = 1647; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,243,Context) ) { + case 1: + { + State = 1646; + base_window_name(); + } + break; + } + State = 1659; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==PARTITION_) { + { + State = 1649; + Match(PARTITION_); + State = 1650; + Match(BY_); + State = 1651; + expr(0); + State = 1656; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1652; + Match(COMMA); + State = 1653; + expr(0); + } + } + State = 1658; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + + { + State = 1661; + Match(ORDER_); + State = 1662; + Match(BY_); + State = 1663; + ordering_term(); + State = 1668; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1664; + Match(COMMA); + State = 1665; + ordering_term(); + } + } + State = 1670; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + State = 1672; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 128)) & ~0x3f) == 0 && ((1L << (_la - 128)) & 2251799880794113L) != 0)) { + { + State = 1671; + frame_spec(); + } + } + + State = 1674; + Match(CLOSE_PAR); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Over_clauseContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OVER_() { return GetToken(SQLiteParser.OVER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Window_nameContext window_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Base_window_nameContext base_window_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PARTITION_() { return GetToken(SQLiteParser.PARTITION_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] BY_() { return GetTokens(SQLiteParser.BY_); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BY_(int i) { + return GetToken(SQLiteParser.BY_, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext[] expr() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ORDER_() { return GetToken(SQLiteParser.ORDER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Ordering_termContext[] ordering_term() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Ordering_termContext ordering_term(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Frame_specContext frame_spec() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + public Over_clauseContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_over_clause; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterOver_clause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitOver_clause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitOver_clause(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Over_clauseContext over_clause() { + Over_clauseContext _localctx = new Over_clauseContext(Context, State); + EnterRule(_localctx, 122, RULE_over_clause); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1676; + Match(OVER_); + State = 1710; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,254,Context) ) { + case 1: + { + State = 1677; + window_name(); + } + break; + case 2: + { + State = 1678; + Match(OPEN_PAR); + State = 1680; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,248,Context) ) { + case 1: + { + State = 1679; + base_window_name(); + } + break; + } + State = 1692; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==PARTITION_) { + { + State = 1682; + Match(PARTITION_); + State = 1683; + Match(BY_); + State = 1684; + expr(0); + State = 1689; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1685; + Match(COMMA); + State = 1686; + expr(0); + } + } + State = 1691; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + + State = 1704; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ORDER_) { + { + State = 1694; + Match(ORDER_); + State = 1695; + Match(BY_); + State = 1696; + ordering_term(); + State = 1701; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1697; + Match(COMMA); + State = 1698; + ordering_term(); + } + } + State = 1703; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + + State = 1707; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 128)) & ~0x3f) == 0 && ((1L << (_la - 128)) & 2251799880794113L) != 0)) { + { + State = 1706; + frame_spec(); + } + } + + State = 1709; + Match(CLOSE_PAR); + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Frame_specContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Frame_clauseContext frame_clause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode EXCLUDE_() { return GetToken(SQLiteParser.EXCLUDE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NO_() { return GetToken(SQLiteParser.NO_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OTHERS_() { return GetToken(SQLiteParser.OTHERS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CURRENT_() { return GetToken(SQLiteParser.CURRENT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ROW_() { return GetToken(SQLiteParser.ROW_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode GROUP_() { return GetToken(SQLiteParser.GROUP_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TIES_() { return GetToken(SQLiteParser.TIES_, 0); } + public Frame_specContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_frame_spec; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterFrame_spec(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitFrame_spec(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitFrame_spec(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Frame_specContext frame_spec() { + Frame_specContext _localctx = new Frame_specContext(Context, State); + EnterRule(_localctx, 124, RULE_frame_spec); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1712; + frame_clause(); + State = 1722; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==EXCLUDE_) { + { + State = 1713; + Match(EXCLUDE_); + State = 1720; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case NO_: + { + State = 1714; + Match(NO_); + State = 1715; + Match(OTHERS_); + } + break; + case CURRENT_: + { + State = 1716; + Match(CURRENT_); + State = 1717; + Match(ROW_); + } + break; + case GROUP_: + { + State = 1718; + Match(GROUP_); + } + break; + case TIES_: + { + State = 1719; + Match(TIES_); + } + break; + default: + throw new NoViableAltException(this); + } + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Frame_clauseContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode RANGE_() { return GetToken(SQLiteParser.RANGE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ROWS_() { return GetToken(SQLiteParser.ROWS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode GROUPS_() { return GetToken(SQLiteParser.GROUPS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Frame_singleContext frame_single() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BETWEEN_() { return GetToken(SQLiteParser.BETWEEN_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Frame_leftContext frame_left() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AND_() { return GetToken(SQLiteParser.AND_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Frame_rightContext frame_right() { + return GetRuleContext(0); + } + public Frame_clauseContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_frame_clause; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterFrame_clause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitFrame_clause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitFrame_clause(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Frame_clauseContext frame_clause() { + Frame_clauseContext _localctx = new Frame_clauseContext(Context, State); + EnterRule(_localctx, 126, RULE_frame_clause); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1724; + _la = TokenStream.LA(1); + if ( !(((((_la - 128)) & ~0x3f) == 0 && ((1L << (_la - 128)) & 2251799880794113L) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 1731; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,257,Context) ) { + case 1: + { + State = 1725; + frame_single(); + } + break; + case 2: + { + State = 1726; + Match(BETWEEN_); + State = 1727; + frame_left(); + State = 1728; + Match(AND_); + State = 1729; + frame_right(); + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Simple_function_invocationContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Simple_funcContext simple_func() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext[] expr() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode STAR() { return GetToken(SQLiteParser.STAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + public Simple_function_invocationContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_simple_function_invocation; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterSimple_function_invocation(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitSimple_function_invocation(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitSimple_function_invocation(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Simple_function_invocationContext simple_function_invocation() { + Simple_function_invocationContext _localctx = new Simple_function_invocationContext(Context, State); + EnterRule(_localctx, 128, RULE_simple_function_invocation); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1733; + simple_func(); + State = 1734; + Match(OPEN_PAR); + State = 1744; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case OPEN_PAR: + case PLUS: + case MINUS: + case TILDE: + case ABORT_: + case ACTION_: + case ADD_: + case AFTER_: + case ALL_: + case ALTER_: + case ANALYZE_: + case AND_: + case AS_: + case ASC_: + case ATTACH_: + case AUTOINCREMENT_: + case BEFORE_: + case BEGIN_: + case BETWEEN_: + case BY_: + case CASCADE_: + case CASE_: + case CAST_: + case CHECK_: + case COLLATE_: + case COLUMN_: + case COMMIT_: + case CONFLICT_: + case CONSTRAINT_: + case CREATE_: + case CROSS_: + case CURRENT_DATE_: + case CURRENT_TIME_: + case CURRENT_TIMESTAMP_: + case DATABASE_: + case DEFAULT_: + case DEFERRABLE_: + case DEFERRED_: + case DELETE_: + case DESC_: + case DETACH_: + case DISTINCT_: + case DROP_: + case EACH_: + case ELSE_: + case END_: + case ESCAPE_: + case EXCEPT_: + case EXCLUSIVE_: + case EXISTS_: + case EXPLAIN_: + case FAIL_: + case FOR_: + case FOREIGN_: + case FROM_: + case FULL_: + case GLOB_: + case GROUP_: + case HAVING_: + case IF_: + case IGNORE_: + case IMMEDIATE_: + case IN_: + case INDEX_: + case INDEXED_: + case INITIALLY_: + case INNER_: + case INSERT_: + case INSTEAD_: + case INTERSECT_: + case INTO_: + case IS_: + case ISNULL_: + case JOIN_: + case KEY_: + case LEFT_: + case LIKE_: + case LIMIT_: + case MATCH_: + case NATURAL_: + case NO_: + case NOT_: + case NOTNULL_: + case NULL_: + case OF_: + case OFFSET_: + case ON_: + case OR_: + case ORDER_: + case OUTER_: + case PLAN_: + case PRAGMA_: + case PRIMARY_: + case QUERY_: + case RAISE_: + case RECURSIVE_: + case REFERENCES_: + case REGEXP_: + case REINDEX_: + case RELEASE_: + case RENAME_: + case REPLACE_: + case RESTRICT_: + case RIGHT_: + case ROLLBACK_: + case ROW_: + case ROWS_: + case SAVEPOINT_: + case SELECT_: + case SET_: + case TABLE_: + case TEMP_: + case TEMPORARY_: + case THEN_: + case TO_: + case TRANSACTION_: + case TRIGGER_: + case UNION_: + case UNIQUE_: + case UPDATE_: + case USING_: + case VACUUM_: + case VALUES_: + case VIEW_: + case VIRTUAL_: + case WHEN_: + case WHERE_: + case WITH_: + case WITHOUT_: + case FIRST_VALUE_: + case OVER_: + case PARTITION_: + case RANGE_: + case PRECEDING_: + case UNBOUNDED_: + case CURRENT_: + case FOLLOWING_: + case CUME_DIST_: + case DENSE_RANK_: + case LAG_: + case LAST_VALUE_: + case LEAD_: + case NTH_VALUE_: + case NTILE_: + case PERCENT_RANK_: + case RANK_: + case ROW_NUMBER_: + case GENERATED_: + case ALWAYS_: + case STORED_: + case TRUE_: + case FALSE_: + case WINDOW_: + case NULLS_: + case FIRST_: + case LAST_: + case FILTER_: + case GROUPS_: + case EXCLUDE_: + case IDENTIFIER: + case NUMERIC_LITERAL: + case BIND_PARAMETER: + case STRING_LITERAL: + case BLOB_LITERAL: + { + State = 1735; + expr(0); + State = 1740; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1736; + Match(COMMA); + State = 1737; + expr(0); + } + } + State = 1742; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + break; + case STAR: + { + State = 1743; + Match(STAR); + } + break; + default: + throw new NoViableAltException(this); + } + State = 1746; + Match(CLOSE_PAR); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Aggregate_function_invocationContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Aggregate_funcContext aggregate_func() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext[] expr() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode STAR() { return GetToken(SQLiteParser.STAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Filter_clauseContext filter_clause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DISTINCT_() { return GetToken(SQLiteParser.DISTINCT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + public Aggregate_function_invocationContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_aggregate_function_invocation; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterAggregate_function_invocation(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitAggregate_function_invocation(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitAggregate_function_invocation(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Aggregate_function_invocationContext aggregate_function_invocation() { + Aggregate_function_invocationContext _localctx = new Aggregate_function_invocationContext(Context, State); + EnterRule(_localctx, 130, RULE_aggregate_function_invocation); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1748; + aggregate_func(); + State = 1749; + Match(OPEN_PAR); + State = 1762; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case OPEN_PAR: + case PLUS: + case MINUS: + case TILDE: + case ABORT_: + case ACTION_: + case ADD_: + case AFTER_: + case ALL_: + case ALTER_: + case ANALYZE_: + case AND_: + case AS_: + case ASC_: + case ATTACH_: + case AUTOINCREMENT_: + case BEFORE_: + case BEGIN_: + case BETWEEN_: + case BY_: + case CASCADE_: + case CASE_: + case CAST_: + case CHECK_: + case COLLATE_: + case COLUMN_: + case COMMIT_: + case CONFLICT_: + case CONSTRAINT_: + case CREATE_: + case CROSS_: + case CURRENT_DATE_: + case CURRENT_TIME_: + case CURRENT_TIMESTAMP_: + case DATABASE_: + case DEFAULT_: + case DEFERRABLE_: + case DEFERRED_: + case DELETE_: + case DESC_: + case DETACH_: + case DISTINCT_: + case DROP_: + case EACH_: + case ELSE_: + case END_: + case ESCAPE_: + case EXCEPT_: + case EXCLUSIVE_: + case EXISTS_: + case EXPLAIN_: + case FAIL_: + case FOR_: + case FOREIGN_: + case FROM_: + case FULL_: + case GLOB_: + case GROUP_: + case HAVING_: + case IF_: + case IGNORE_: + case IMMEDIATE_: + case IN_: + case INDEX_: + case INDEXED_: + case INITIALLY_: + case INNER_: + case INSERT_: + case INSTEAD_: + case INTERSECT_: + case INTO_: + case IS_: + case ISNULL_: + case JOIN_: + case KEY_: + case LEFT_: + case LIKE_: + case LIMIT_: + case MATCH_: + case NATURAL_: + case NO_: + case NOT_: + case NOTNULL_: + case NULL_: + case OF_: + case OFFSET_: + case ON_: + case OR_: + case ORDER_: + case OUTER_: + case PLAN_: + case PRAGMA_: + case PRIMARY_: + case QUERY_: + case RAISE_: + case RECURSIVE_: + case REFERENCES_: + case REGEXP_: + case REINDEX_: + case RELEASE_: + case RENAME_: + case REPLACE_: + case RESTRICT_: + case RIGHT_: + case ROLLBACK_: + case ROW_: + case ROWS_: + case SAVEPOINT_: + case SELECT_: + case SET_: + case TABLE_: + case TEMP_: + case TEMPORARY_: + case THEN_: + case TO_: + case TRANSACTION_: + case TRIGGER_: + case UNION_: + case UNIQUE_: + case UPDATE_: + case USING_: + case VACUUM_: + case VALUES_: + case VIEW_: + case VIRTUAL_: + case WHEN_: + case WHERE_: + case WITH_: + case WITHOUT_: + case FIRST_VALUE_: + case OVER_: + case PARTITION_: + case RANGE_: + case PRECEDING_: + case UNBOUNDED_: + case CURRENT_: + case FOLLOWING_: + case CUME_DIST_: + case DENSE_RANK_: + case LAG_: + case LAST_VALUE_: + case LEAD_: + case NTH_VALUE_: + case NTILE_: + case PERCENT_RANK_: + case RANK_: + case ROW_NUMBER_: + case GENERATED_: + case ALWAYS_: + case STORED_: + case TRUE_: + case FALSE_: + case WINDOW_: + case NULLS_: + case FIRST_: + case LAST_: + case FILTER_: + case GROUPS_: + case EXCLUDE_: + case IDENTIFIER: + case NUMERIC_LITERAL: + case BIND_PARAMETER: + case STRING_LITERAL: + case BLOB_LITERAL: + { + State = 1751; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,260,Context) ) { + case 1: + { + State = 1750; + Match(DISTINCT_); + } + break; + } + State = 1753; + expr(0); + State = 1758; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1754; + Match(COMMA); + State = 1755; + expr(0); + } + } + State = 1760; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + break; + case STAR: + { + State = 1761; + Match(STAR); + } + break; + case CLOSE_PAR: + break; + default: + break; + } + State = 1764; + Match(CLOSE_PAR); + State = 1766; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==FILTER_) { + { + State = 1765; + filter_clause(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Window_function_invocationContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Window_functionContext window_function() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OVER_() { return GetToken(SQLiteParser.OVER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Window_defnContext window_defn() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Window_nameContext window_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext[] expr() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode STAR() { return GetToken(SQLiteParser.STAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Filter_clauseContext filter_clause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + public Window_function_invocationContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_window_function_invocation; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterWindow_function_invocation(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitWindow_function_invocation(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitWindow_function_invocation(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Window_function_invocationContext window_function_invocation() { + Window_function_invocationContext _localctx = new Window_function_invocationContext(Context, State); + EnterRule(_localctx, 132, RULE_window_function_invocation); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1768; + window_function(); + State = 1769; + Match(OPEN_PAR); + State = 1779; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case OPEN_PAR: + case PLUS: + case MINUS: + case TILDE: + case ABORT_: + case ACTION_: + case ADD_: + case AFTER_: + case ALL_: + case ALTER_: + case ANALYZE_: + case AND_: + case AS_: + case ASC_: + case ATTACH_: + case AUTOINCREMENT_: + case BEFORE_: + case BEGIN_: + case BETWEEN_: + case BY_: + case CASCADE_: + case CASE_: + case CAST_: + case CHECK_: + case COLLATE_: + case COLUMN_: + case COMMIT_: + case CONFLICT_: + case CONSTRAINT_: + case CREATE_: + case CROSS_: + case CURRENT_DATE_: + case CURRENT_TIME_: + case CURRENT_TIMESTAMP_: + case DATABASE_: + case DEFAULT_: + case DEFERRABLE_: + case DEFERRED_: + case DELETE_: + case DESC_: + case DETACH_: + case DISTINCT_: + case DROP_: + case EACH_: + case ELSE_: + case END_: + case ESCAPE_: + case EXCEPT_: + case EXCLUSIVE_: + case EXISTS_: + case EXPLAIN_: + case FAIL_: + case FOR_: + case FOREIGN_: + case FROM_: + case FULL_: + case GLOB_: + case GROUP_: + case HAVING_: + case IF_: + case IGNORE_: + case IMMEDIATE_: + case IN_: + case INDEX_: + case INDEXED_: + case INITIALLY_: + case INNER_: + case INSERT_: + case INSTEAD_: + case INTERSECT_: + case INTO_: + case IS_: + case ISNULL_: + case JOIN_: + case KEY_: + case LEFT_: + case LIKE_: + case LIMIT_: + case MATCH_: + case NATURAL_: + case NO_: + case NOT_: + case NOTNULL_: + case NULL_: + case OF_: + case OFFSET_: + case ON_: + case OR_: + case ORDER_: + case OUTER_: + case PLAN_: + case PRAGMA_: + case PRIMARY_: + case QUERY_: + case RAISE_: + case RECURSIVE_: + case REFERENCES_: + case REGEXP_: + case REINDEX_: + case RELEASE_: + case RENAME_: + case REPLACE_: + case RESTRICT_: + case RIGHT_: + case ROLLBACK_: + case ROW_: + case ROWS_: + case SAVEPOINT_: + case SELECT_: + case SET_: + case TABLE_: + case TEMP_: + case TEMPORARY_: + case THEN_: + case TO_: + case TRANSACTION_: + case TRIGGER_: + case UNION_: + case UNIQUE_: + case UPDATE_: + case USING_: + case VACUUM_: + case VALUES_: + case VIEW_: + case VIRTUAL_: + case WHEN_: + case WHERE_: + case WITH_: + case WITHOUT_: + case FIRST_VALUE_: + case OVER_: + case PARTITION_: + case RANGE_: + case PRECEDING_: + case UNBOUNDED_: + case CURRENT_: + case FOLLOWING_: + case CUME_DIST_: + case DENSE_RANK_: + case LAG_: + case LAST_VALUE_: + case LEAD_: + case NTH_VALUE_: + case NTILE_: + case PERCENT_RANK_: + case RANK_: + case ROW_NUMBER_: + case GENERATED_: + case ALWAYS_: + case STORED_: + case TRUE_: + case FALSE_: + case WINDOW_: + case NULLS_: + case FIRST_: + case LAST_: + case FILTER_: + case GROUPS_: + case EXCLUDE_: + case IDENTIFIER: + case NUMERIC_LITERAL: + case BIND_PARAMETER: + case STRING_LITERAL: + case BLOB_LITERAL: + { + State = 1770; + expr(0); + State = 1775; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1771; + Match(COMMA); + State = 1772; + expr(0); + } + } + State = 1777; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + break; + case STAR: + { + State = 1778; + Match(STAR); + } + break; + case CLOSE_PAR: + break; + default: + break; + } + State = 1781; + Match(CLOSE_PAR); + State = 1783; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==FILTER_) { + { + State = 1782; + filter_clause(); + } + } + + State = 1785; + Match(OVER_); + State = 1788; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,267,Context) ) { + case 1: + { + State = 1786; + window_defn(); + } + break; + case 2: + { + State = 1787; + window_name(); + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Common_table_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode WITH_() { return GetToken(SQLiteParser.WITH_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Common_table_expressionContext[] common_table_expression() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Common_table_expressionContext common_table_expression(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode RECURSIVE_() { return GetToken(SQLiteParser.RECURSIVE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + public Common_table_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_common_table_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterCommon_table_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitCommon_table_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitCommon_table_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Common_table_stmtContext common_table_stmt() { + Common_table_stmtContext _localctx = new Common_table_stmtContext(Context, State); + EnterRule(_localctx, 134, RULE_common_table_stmt); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1790; + Match(WITH_); + State = 1792; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,268,Context) ) { + case 1: + { + State = 1791; + Match(RECURSIVE_); + } + break; + } + State = 1794; + common_table_expression(); + State = 1799; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1795; + Match(COMMA); + State = 1796; + common_table_expression(); + } + } + State = 1801; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Order_by_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ORDER_() { return GetToken(SQLiteParser.ORDER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BY_() { return GetToken(SQLiteParser.BY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Ordering_termContext[] ordering_term() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Ordering_termContext ordering_term(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + public Order_by_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_order_by_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterOrder_by_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitOrder_by_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitOrder_by_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Order_by_stmtContext order_by_stmt() { + Order_by_stmtContext _localctx = new Order_by_stmtContext(Context, State); + EnterRule(_localctx, 136, RULE_order_by_stmt); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1802; + Match(ORDER_); + State = 1803; + Match(BY_); + State = 1804; + ordering_term(); + State = 1809; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1805; + Match(COMMA); + State = 1806; + ordering_term(); + } + } + State = 1811; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Limit_stmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode LIMIT_() { return GetToken(SQLiteParser.LIMIT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext[] expr() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OFFSET_() { return GetToken(SQLiteParser.OFFSET_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA() { return GetToken(SQLiteParser.COMMA, 0); } + public Limit_stmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_limit_stmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterLimit_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitLimit_stmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitLimit_stmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Limit_stmtContext limit_stmt() { + Limit_stmtContext _localctx = new Limit_stmtContext(Context, State); + EnterRule(_localctx, 138, RULE_limit_stmt); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1812; + Match(LIMIT_); + State = 1813; + expr(0); + State = 1816; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==COMMA || _la==OFFSET_) { + { + State = 1814; + _la = TokenStream.LA(1); + if ( !(_la==COMMA || _la==OFFSET_) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 1815; + expr(0); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Ordering_termContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COLLATE_() { return GetToken(SQLiteParser.COLLATE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Collation_nameContext collation_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Asc_descContext asc_desc() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NULLS_() { return GetToken(SQLiteParser.NULLS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FIRST_() { return GetToken(SQLiteParser.FIRST_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode LAST_() { return GetToken(SQLiteParser.LAST_, 0); } + public Ordering_termContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_ordering_term; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterOrdering_term(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitOrdering_term(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitOrdering_term(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Ordering_termContext ordering_term() { + Ordering_termContext _localctx = new Ordering_termContext(Context, State); + EnterRule(_localctx, 140, RULE_ordering_term); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1818; + expr(0); + State = 1821; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==COLLATE_) { + { + State = 1819; + Match(COLLATE_); + State = 1820; + collation_name(); + } + } + + State = 1824; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ASC_ || _la==DESC_) { + { + State = 1823; + asc_desc(); + } + } + + State = 1828; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==NULLS_) { + { + State = 1826; + Match(NULLS_); + State = 1827; + _la = TokenStream.LA(1); + if ( !(_la==FIRST_ || _la==LAST_) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Asc_descContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ASC_() { return GetToken(SQLiteParser.ASC_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DESC_() { return GetToken(SQLiteParser.DESC_, 0); } + public Asc_descContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_asc_desc; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterAsc_desc(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitAsc_desc(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitAsc_desc(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Asc_descContext asc_desc() { + Asc_descContext _localctx = new Asc_descContext(Context, State); + EnterRule(_localctx, 142, RULE_asc_desc); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1830; + _la = TokenStream.LA(1); + if ( !(_la==ASC_ || _la==DESC_) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Frame_leftContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PRECEDING_() { return GetToken(SQLiteParser.PRECEDING_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FOLLOWING_() { return GetToken(SQLiteParser.FOLLOWING_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CURRENT_() { return GetToken(SQLiteParser.CURRENT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ROW_() { return GetToken(SQLiteParser.ROW_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode UNBOUNDED_() { return GetToken(SQLiteParser.UNBOUNDED_, 0); } + public Frame_leftContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_frame_left; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterFrame_left(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitFrame_left(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitFrame_left(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Frame_leftContext frame_left() { + Frame_leftContext _localctx = new Frame_leftContext(Context, State); + EnterRule(_localctx, 144, RULE_frame_left); + try { + State = 1842; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,275,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 1832; + expr(0); + State = 1833; + Match(PRECEDING_); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 1835; + expr(0); + State = 1836; + Match(FOLLOWING_); + } + break; + case 3: + EnterOuterAlt(_localctx, 3); + { + State = 1838; + Match(CURRENT_); + State = 1839; + Match(ROW_); + } + break; + case 4: + EnterOuterAlt(_localctx, 4); + { + State = 1840; + Match(UNBOUNDED_); + State = 1841; + Match(PRECEDING_); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Frame_rightContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PRECEDING_() { return GetToken(SQLiteParser.PRECEDING_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FOLLOWING_() { return GetToken(SQLiteParser.FOLLOWING_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CURRENT_() { return GetToken(SQLiteParser.CURRENT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ROW_() { return GetToken(SQLiteParser.ROW_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode UNBOUNDED_() { return GetToken(SQLiteParser.UNBOUNDED_, 0); } + public Frame_rightContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_frame_right; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterFrame_right(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitFrame_right(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitFrame_right(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Frame_rightContext frame_right() { + Frame_rightContext _localctx = new Frame_rightContext(Context, State); + EnterRule(_localctx, 146, RULE_frame_right); + try { + State = 1854; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,276,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 1844; + expr(0); + State = 1845; + Match(PRECEDING_); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 1847; + expr(0); + State = 1848; + Match(FOLLOWING_); + } + break; + case 3: + EnterOuterAlt(_localctx, 3); + { + State = 1850; + Match(CURRENT_); + State = 1851; + Match(ROW_); + } + break; + case 4: + EnterOuterAlt(_localctx, 4); + { + State = 1852; + Match(UNBOUNDED_); + State = 1853; + Match(FOLLOWING_); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Frame_singleContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PRECEDING_() { return GetToken(SQLiteParser.PRECEDING_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode UNBOUNDED_() { return GetToken(SQLiteParser.UNBOUNDED_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CURRENT_() { return GetToken(SQLiteParser.CURRENT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ROW_() { return GetToken(SQLiteParser.ROW_, 0); } + public Frame_singleContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_frame_single; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterFrame_single(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitFrame_single(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitFrame_single(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Frame_singleContext frame_single() { + Frame_singleContext _localctx = new Frame_singleContext(Context, State); + EnterRule(_localctx, 148, RULE_frame_single); + try { + State = 1863; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,277,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 1856; + expr(0); + State = 1857; + Match(PRECEDING_); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 1859; + Match(UNBOUNDED_); + State = 1860; + Match(PRECEDING_); + } + break; + case 3: + EnterOuterAlt(_localctx, 3); + { + State = 1861; + Match(CURRENT_); + State = 1862; + Match(ROW_); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Window_functionContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] OPEN_PAR() { return GetTokens(SQLiteParser.OPEN_PAR); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR(int i) { + return GetToken(SQLiteParser.OPEN_PAR, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] CLOSE_PAR() { return GetTokens(SQLiteParser.CLOSE_PAR); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR(int i) { + return GetToken(SQLiteParser.CLOSE_PAR, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OVER_() { return GetToken(SQLiteParser.OVER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Order_by_expr_asc_descContext order_by_expr_asc_desc() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FIRST_VALUE_() { return GetToken(SQLiteParser.FIRST_VALUE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode LAST_VALUE_() { return GetToken(SQLiteParser.LAST_VALUE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Partition_byContext partition_by() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Frame_clauseContext frame_clause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CUME_DIST_() { return GetToken(SQLiteParser.CUME_DIST_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PERCENT_RANK_() { return GetToken(SQLiteParser.PERCENT_RANK_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Order_by_exprContext order_by_expr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DENSE_RANK_() { return GetToken(SQLiteParser.DENSE_RANK_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode RANK_() { return GetToken(SQLiteParser.RANK_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ROW_NUMBER_() { return GetToken(SQLiteParser.ROW_NUMBER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode LAG_() { return GetToken(SQLiteParser.LAG_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode LEAD_() { return GetToken(SQLiteParser.LEAD_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public OffsetContext offset() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Default_valueContext default_value() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NTH_VALUE_() { return GetToken(SQLiteParser.NTH_VALUE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA() { return GetToken(SQLiteParser.COMMA, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Signed_numberContext signed_number() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NTILE_() { return GetToken(SQLiteParser.NTILE_, 0); } + public Window_functionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_window_function; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterWindow_function(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitWindow_function(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitWindow_function(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Window_functionContext window_function() { + Window_functionContext _localctx = new Window_functionContext(Context, State); + EnterRule(_localctx, 150, RULE_window_function); + int _la; + try { + State = 1950; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case FIRST_VALUE_: + case LAST_VALUE_: + EnterOuterAlt(_localctx, 1); + { + State = 1865; + _la = TokenStream.LA(1); + if ( !(_la==FIRST_VALUE_ || _la==LAST_VALUE_) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 1866; + Match(OPEN_PAR); + State = 1867; + expr(0); + State = 1868; + Match(CLOSE_PAR); + State = 1869; + Match(OVER_); + State = 1870; + Match(OPEN_PAR); + State = 1872; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==PARTITION_) { + { + State = 1871; + partition_by(); + } + } + + State = 1874; + order_by_expr_asc_desc(); + State = 1876; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 128)) & ~0x3f) == 0 && ((1L << (_la - 128)) & 2251799880794113L) != 0)) { + { + State = 1875; + frame_clause(); + } + } + + State = 1878; + Match(CLOSE_PAR); + } + break; + case CUME_DIST_: + case PERCENT_RANK_: + EnterOuterAlt(_localctx, 2); + { + State = 1880; + _la = TokenStream.LA(1); + if ( !(_la==CUME_DIST_ || _la==PERCENT_RANK_) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 1881; + Match(OPEN_PAR); + State = 1882; + Match(CLOSE_PAR); + State = 1883; + Match(OVER_); + State = 1884; + Match(OPEN_PAR); + State = 1886; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==PARTITION_) { + { + State = 1885; + partition_by(); + } + } + + State = 1889; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ORDER_) { + { + State = 1888; + order_by_expr(); + } + } + + State = 1891; + Match(CLOSE_PAR); + } + break; + case DENSE_RANK_: + case RANK_: + case ROW_NUMBER_: + EnterOuterAlt(_localctx, 3); + { + State = 1892; + _la = TokenStream.LA(1); + if ( !(((((_la - 160)) & ~0x3f) == 0 && ((1L << (_la - 160)) & 385L) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 1893; + Match(OPEN_PAR); + State = 1894; + Match(CLOSE_PAR); + State = 1895; + Match(OVER_); + State = 1896; + Match(OPEN_PAR); + State = 1898; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==PARTITION_) { + { + State = 1897; + partition_by(); + } + } + + State = 1900; + order_by_expr_asc_desc(); + State = 1901; + Match(CLOSE_PAR); + } + break; + case LAG_: + case LEAD_: + EnterOuterAlt(_localctx, 4); + { + State = 1903; + _la = TokenStream.LA(1); + if ( !(_la==LAG_ || _la==LEAD_) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 1904; + Match(OPEN_PAR); + State = 1905; + expr(0); + State = 1907; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,283,Context) ) { + case 1: + { + State = 1906; + offset(); + } + break; + } + State = 1910; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==COMMA) { + { + State = 1909; + default_value(); + } + } + + State = 1912; + Match(CLOSE_PAR); + State = 1913; + Match(OVER_); + State = 1914; + Match(OPEN_PAR); + State = 1916; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==PARTITION_) { + { + State = 1915; + partition_by(); + } + } + + State = 1918; + order_by_expr_asc_desc(); + State = 1919; + Match(CLOSE_PAR); + } + break; + case NTH_VALUE_: + EnterOuterAlt(_localctx, 5); + { + State = 1921; + Match(NTH_VALUE_); + State = 1922; + Match(OPEN_PAR); + State = 1923; + expr(0); + State = 1924; + Match(COMMA); + State = 1925; + signed_number(); + State = 1926; + Match(CLOSE_PAR); + State = 1927; + Match(OVER_); + State = 1928; + Match(OPEN_PAR); + State = 1930; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==PARTITION_) { + { + State = 1929; + partition_by(); + } + } + + State = 1932; + order_by_expr_asc_desc(); + State = 1934; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (((((_la - 128)) & ~0x3f) == 0 && ((1L << (_la - 128)) & 2251799880794113L) != 0)) { + { + State = 1933; + frame_clause(); + } + } + + State = 1936; + Match(CLOSE_PAR); + } + break; + case NTILE_: + EnterOuterAlt(_localctx, 6); + { + State = 1938; + Match(NTILE_); + State = 1939; + Match(OPEN_PAR); + State = 1940; + expr(0); + State = 1941; + Match(CLOSE_PAR); + State = 1942; + Match(OVER_); + State = 1943; + Match(OPEN_PAR); + State = 1945; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==PARTITION_) { + { + State = 1944; + partition_by(); + } + } + + State = 1947; + order_by_expr_asc_desc(); + State = 1948; + Match(CLOSE_PAR); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OffsetContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA() { return GetToken(SQLiteParser.COMMA, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Signed_numberContext signed_number() { + return GetRuleContext(0); + } + public OffsetContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_offset; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterOffset(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitOffset(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitOffset(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OffsetContext offset() { + OffsetContext _localctx = new OffsetContext(Context, State); + EnterRule(_localctx, 152, RULE_offset); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1952; + Match(COMMA); + State = 1953; + signed_number(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Default_valueContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA() { return GetToken(SQLiteParser.COMMA, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Signed_numberContext signed_number() { + return GetRuleContext(0); + } + public Default_valueContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_default_value; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterDefault_value(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitDefault_value(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitDefault_value(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Default_valueContext default_value() { + Default_valueContext _localctx = new Default_valueContext(Context, State); + EnterRule(_localctx, 154, RULE_default_value); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1955; + Match(COMMA); + State = 1956; + signed_number(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Partition_byContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PARTITION_() { return GetToken(SQLiteParser.PARTITION_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BY_() { return GetToken(SQLiteParser.BY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext[] expr() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr(int i) { + return GetRuleContext(i); + } + public Partition_byContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_partition_by; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterPartition_by(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitPartition_by(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitPartition_by(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Partition_byContext partition_by() { + Partition_byContext _localctx = new Partition_byContext(Context, State); + EnterRule(_localctx, 156, RULE_partition_by); + try { + int _alt; + EnterOuterAlt(_localctx, 1); + { + State = 1958; + Match(PARTITION_); + State = 1959; + Match(BY_); + State = 1961; + ErrorHandler.Sync(this); + _alt = 1; + do { + switch (_alt) { + case 1: + { + { + State = 1960; + expr(0); + } + } + break; + default: + throw new NoViableAltException(this); + } + State = 1963; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,290,Context); + } while ( _alt!=2 && _alt!=global::Antlr4.Runtime.Atn.ATN.INVALID_ALT_NUMBER ); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Order_by_exprContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ORDER_() { return GetToken(SQLiteParser.ORDER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BY_() { return GetToken(SQLiteParser.BY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext[] expr() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr(int i) { + return GetRuleContext(i); + } + public Order_by_exprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_order_by_expr; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterOrder_by_expr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitOrder_by_expr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitOrder_by_expr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Order_by_exprContext order_by_expr() { + Order_by_exprContext _localctx = new Order_by_exprContext(Context, State); + EnterRule(_localctx, 158, RULE_order_by_expr); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1965; + Match(ORDER_); + State = 1966; + Match(BY_); + State = 1968; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + do { + { + { + State = 1967; + expr(0); + } + } + State = 1970; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } while ( (((_la) & ~0x3f) == 0 && ((1L << _la) & -33552632L) != 0) || ((((_la - 64)) & ~0x3f) == 0 && ((1L << (_la - 64)) & -1152921504606846977L) != 0) || ((((_la - 128)) & ~0x3f) == 0 && ((1L << (_la - 128)) & 4476578029606273023L) != 0) ); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Order_by_expr_asc_descContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ORDER_() { return GetToken(SQLiteParser.ORDER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BY_() { return GetToken(SQLiteParser.BY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Expr_asc_descContext expr_asc_desc() { + return GetRuleContext(0); + } + public Order_by_expr_asc_descContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_order_by_expr_asc_desc; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterOrder_by_expr_asc_desc(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitOrder_by_expr_asc_desc(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitOrder_by_expr_asc_desc(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Order_by_expr_asc_descContext order_by_expr_asc_desc() { + Order_by_expr_asc_descContext _localctx = new Order_by_expr_asc_descContext(Context, State); + EnterRule(_localctx, 160, RULE_order_by_expr_asc_desc); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1972; + Match(ORDER_); + State = 1973; + Match(BY_); + State = 1974; + expr_asc_desc(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Expr_asc_descContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ExprContext[] expr() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public Asc_descContext[] asc_desc() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public Asc_descContext asc_desc(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] COMMA() { return GetTokens(SQLiteParser.COMMA); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMA(int i) { + return GetToken(SQLiteParser.COMMA, i); + } + public Expr_asc_descContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_expr_asc_desc; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterExpr_asc_desc(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitExpr_asc_desc(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitExpr_asc_desc(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Expr_asc_descContext expr_asc_desc() { + Expr_asc_descContext _localctx = new Expr_asc_descContext(Context, State); + EnterRule(_localctx, 162, RULE_expr_asc_desc); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1976; + expr(0); + State = 1978; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ASC_ || _la==DESC_) { + { + State = 1977; + asc_desc(); + } + } + + State = 1987; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==COMMA) { + { + { + State = 1980; + Match(COMMA); + State = 1981; + expr(0); + State = 1983; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ASC_ || _la==DESC_) { + { + State = 1982; + asc_desc(); + } + } + + } + } + State = 1989; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Initial_selectContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Select_stmtContext select_stmt() { + return GetRuleContext(0); + } + public Initial_selectContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_initial_select; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterInitial_select(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitInitial_select(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitInitial_select(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Initial_selectContext initial_select() { + Initial_selectContext _localctx = new Initial_selectContext(Context, State); + EnterRule(_localctx, 164, RULE_initial_select); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1990; + select_stmt(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Recursive_selectContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Select_stmtContext select_stmt() { + return GetRuleContext(0); + } + public Recursive_selectContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_recursive_select; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterRecursive_select(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitRecursive_select(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitRecursive_select(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Recursive_selectContext recursive_select() { + Recursive_selectContext _localctx = new Recursive_selectContext(Context, State); + EnterRule(_localctx, 166, RULE_recursive_select); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1992; + select_stmt(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Unary_operatorContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode MINUS() { return GetToken(SQLiteParser.MINUS, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PLUS() { return GetToken(SQLiteParser.PLUS, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TILDE() { return GetToken(SQLiteParser.TILDE, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NOT_() { return GetToken(SQLiteParser.NOT_, 0); } + public Unary_operatorContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_unary_operator; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterUnary_operator(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitUnary_operator(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitUnary_operator(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Unary_operatorContext unary_operator() { + Unary_operatorContext _localctx = new Unary_operatorContext(Context, State); + EnterRule(_localctx, 168, RULE_unary_operator); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 1994; + _la = TokenStream.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 1792L) != 0) || _la==NOT_) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Error_messageContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode STRING_LITERAL() { return GetToken(SQLiteParser.STRING_LITERAL, 0); } + public Error_messageContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_error_message; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterError_message(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitError_message(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitError_message(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Error_messageContext error_message() { + Error_messageContext _localctx = new Error_messageContext(Context, State); + EnterRule(_localctx, 170, RULE_error_message); + try { + EnterOuterAlt(_localctx, 1); + { + State = 1996; + Match(STRING_LITERAL); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Module_argumentContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public Column_defContext column_def() { + return GetRuleContext(0); + } + public Module_argumentContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_module_argument; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterModule_argument(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitModule_argument(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitModule_argument(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Module_argumentContext module_argument() { + Module_argumentContext _localctx = new Module_argumentContext(Context, State); + EnterRule(_localctx, 172, RULE_module_argument); + try { + State = 2000; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,295,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 1998; + expr(0); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 1999; + column_def(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Column_aliasContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IDENTIFIER() { return GetToken(SQLiteParser.IDENTIFIER, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode STRING_LITERAL() { return GetToken(SQLiteParser.STRING_LITERAL, 0); } + public Column_aliasContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_column_alias; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterColumn_alias(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitColumn_alias(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitColumn_alias(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Column_aliasContext column_alias() { + Column_aliasContext _localctx = new Column_aliasContext(Context, State); + EnterRule(_localctx, 174, RULE_column_alias); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2002; + _la = TokenStream.LA(1); + if ( !(_la==IDENTIFIER || _la==STRING_LITERAL) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class KeywordContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ABORT_() { return GetToken(SQLiteParser.ABORT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ACTION_() { return GetToken(SQLiteParser.ACTION_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ADD_() { return GetToken(SQLiteParser.ADD_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AFTER_() { return GetToken(SQLiteParser.AFTER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ALL_() { return GetToken(SQLiteParser.ALL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ALTER_() { return GetToken(SQLiteParser.ALTER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ANALYZE_() { return GetToken(SQLiteParser.ANALYZE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AND_() { return GetToken(SQLiteParser.AND_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AS_() { return GetToken(SQLiteParser.AS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ASC_() { return GetToken(SQLiteParser.ASC_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ATTACH_() { return GetToken(SQLiteParser.ATTACH_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AUTOINCREMENT_() { return GetToken(SQLiteParser.AUTOINCREMENT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BEFORE_() { return GetToken(SQLiteParser.BEFORE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BEGIN_() { return GetToken(SQLiteParser.BEGIN_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BETWEEN_() { return GetToken(SQLiteParser.BETWEEN_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BY_() { return GetToken(SQLiteParser.BY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CASCADE_() { return GetToken(SQLiteParser.CASCADE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CASE_() { return GetToken(SQLiteParser.CASE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CAST_() { return GetToken(SQLiteParser.CAST_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CHECK_() { return GetToken(SQLiteParser.CHECK_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COLLATE_() { return GetToken(SQLiteParser.COLLATE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COLUMN_() { return GetToken(SQLiteParser.COLUMN_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode COMMIT_() { return GetToken(SQLiteParser.COMMIT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CONFLICT_() { return GetToken(SQLiteParser.CONFLICT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CONSTRAINT_() { return GetToken(SQLiteParser.CONSTRAINT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CREATE_() { return GetToken(SQLiteParser.CREATE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CROSS_() { return GetToken(SQLiteParser.CROSS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CURRENT_DATE_() { return GetToken(SQLiteParser.CURRENT_DATE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CURRENT_TIME_() { return GetToken(SQLiteParser.CURRENT_TIME_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CURRENT_TIMESTAMP_() { return GetToken(SQLiteParser.CURRENT_TIMESTAMP_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DATABASE_() { return GetToken(SQLiteParser.DATABASE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DEFAULT_() { return GetToken(SQLiteParser.DEFAULT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DEFERRABLE_() { return GetToken(SQLiteParser.DEFERRABLE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DEFERRED_() { return GetToken(SQLiteParser.DEFERRED_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DELETE_() { return GetToken(SQLiteParser.DELETE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DESC_() { return GetToken(SQLiteParser.DESC_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DETACH_() { return GetToken(SQLiteParser.DETACH_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DISTINCT_() { return GetToken(SQLiteParser.DISTINCT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DROP_() { return GetToken(SQLiteParser.DROP_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode EACH_() { return GetToken(SQLiteParser.EACH_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ELSE_() { return GetToken(SQLiteParser.ELSE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode END_() { return GetToken(SQLiteParser.END_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ESCAPE_() { return GetToken(SQLiteParser.ESCAPE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode EXCEPT_() { return GetToken(SQLiteParser.EXCEPT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode EXCLUSIVE_() { return GetToken(SQLiteParser.EXCLUSIVE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode EXISTS_() { return GetToken(SQLiteParser.EXISTS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode EXPLAIN_() { return GetToken(SQLiteParser.EXPLAIN_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FAIL_() { return GetToken(SQLiteParser.FAIL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FOR_() { return GetToken(SQLiteParser.FOR_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FOREIGN_() { return GetToken(SQLiteParser.FOREIGN_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FROM_() { return GetToken(SQLiteParser.FROM_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FULL_() { return GetToken(SQLiteParser.FULL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode GLOB_() { return GetToken(SQLiteParser.GLOB_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode GROUP_() { return GetToken(SQLiteParser.GROUP_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode HAVING_() { return GetToken(SQLiteParser.HAVING_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IF_() { return GetToken(SQLiteParser.IF_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IGNORE_() { return GetToken(SQLiteParser.IGNORE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IMMEDIATE_() { return GetToken(SQLiteParser.IMMEDIATE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IN_() { return GetToken(SQLiteParser.IN_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INDEX_() { return GetToken(SQLiteParser.INDEX_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INDEXED_() { return GetToken(SQLiteParser.INDEXED_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INITIALLY_() { return GetToken(SQLiteParser.INITIALLY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INNER_() { return GetToken(SQLiteParser.INNER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INSERT_() { return GetToken(SQLiteParser.INSERT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INSTEAD_() { return GetToken(SQLiteParser.INSTEAD_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INTERSECT_() { return GetToken(SQLiteParser.INTERSECT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INTO_() { return GetToken(SQLiteParser.INTO_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IS_() { return GetToken(SQLiteParser.IS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ISNULL_() { return GetToken(SQLiteParser.ISNULL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode JOIN_() { return GetToken(SQLiteParser.JOIN_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode KEY_() { return GetToken(SQLiteParser.KEY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode LEFT_() { return GetToken(SQLiteParser.LEFT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode LIKE_() { return GetToken(SQLiteParser.LIKE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode LIMIT_() { return GetToken(SQLiteParser.LIMIT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode MATCH_() { return GetToken(SQLiteParser.MATCH_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NATURAL_() { return GetToken(SQLiteParser.NATURAL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NO_() { return GetToken(SQLiteParser.NO_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NOT_() { return GetToken(SQLiteParser.NOT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NOTNULL_() { return GetToken(SQLiteParser.NOTNULL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NULL_() { return GetToken(SQLiteParser.NULL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OF_() { return GetToken(SQLiteParser.OF_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OFFSET_() { return GetToken(SQLiteParser.OFFSET_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ON_() { return GetToken(SQLiteParser.ON_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OR_() { return GetToken(SQLiteParser.OR_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ORDER_() { return GetToken(SQLiteParser.ORDER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OUTER_() { return GetToken(SQLiteParser.OUTER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PLAN_() { return GetToken(SQLiteParser.PLAN_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PRAGMA_() { return GetToken(SQLiteParser.PRAGMA_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PRIMARY_() { return GetToken(SQLiteParser.PRIMARY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode QUERY_() { return GetToken(SQLiteParser.QUERY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode RAISE_() { return GetToken(SQLiteParser.RAISE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode RECURSIVE_() { return GetToken(SQLiteParser.RECURSIVE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode REFERENCES_() { return GetToken(SQLiteParser.REFERENCES_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode REGEXP_() { return GetToken(SQLiteParser.REGEXP_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode REINDEX_() { return GetToken(SQLiteParser.REINDEX_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode RELEASE_() { return GetToken(SQLiteParser.RELEASE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode RENAME_() { return GetToken(SQLiteParser.RENAME_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode REPLACE_() { return GetToken(SQLiteParser.REPLACE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode RESTRICT_() { return GetToken(SQLiteParser.RESTRICT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode RIGHT_() { return GetToken(SQLiteParser.RIGHT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ROLLBACK_() { return GetToken(SQLiteParser.ROLLBACK_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ROW_() { return GetToken(SQLiteParser.ROW_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ROWS_() { return GetToken(SQLiteParser.ROWS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode SAVEPOINT_() { return GetToken(SQLiteParser.SAVEPOINT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode SELECT_() { return GetToken(SQLiteParser.SELECT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode SET_() { return GetToken(SQLiteParser.SET_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TABLE_() { return GetToken(SQLiteParser.TABLE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TEMP_() { return GetToken(SQLiteParser.TEMP_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TEMPORARY_() { return GetToken(SQLiteParser.TEMPORARY_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode THEN_() { return GetToken(SQLiteParser.THEN_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TO_() { return GetToken(SQLiteParser.TO_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TRANSACTION_() { return GetToken(SQLiteParser.TRANSACTION_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TRIGGER_() { return GetToken(SQLiteParser.TRIGGER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode UNION_() { return GetToken(SQLiteParser.UNION_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode UNIQUE_() { return GetToken(SQLiteParser.UNIQUE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode UPDATE_() { return GetToken(SQLiteParser.UPDATE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode USING_() { return GetToken(SQLiteParser.USING_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode VACUUM_() { return GetToken(SQLiteParser.VACUUM_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode VALUES_() { return GetToken(SQLiteParser.VALUES_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode VIEW_() { return GetToken(SQLiteParser.VIEW_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode VIRTUAL_() { return GetToken(SQLiteParser.VIRTUAL_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode WHEN_() { return GetToken(SQLiteParser.WHEN_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode WHERE_() { return GetToken(SQLiteParser.WHERE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode WITH_() { return GetToken(SQLiteParser.WITH_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode WITHOUT_() { return GetToken(SQLiteParser.WITHOUT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FIRST_VALUE_() { return GetToken(SQLiteParser.FIRST_VALUE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OVER_() { return GetToken(SQLiteParser.OVER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PARTITION_() { return GetToken(SQLiteParser.PARTITION_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode RANGE_() { return GetToken(SQLiteParser.RANGE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PRECEDING_() { return GetToken(SQLiteParser.PRECEDING_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode UNBOUNDED_() { return GetToken(SQLiteParser.UNBOUNDED_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CURRENT_() { return GetToken(SQLiteParser.CURRENT_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FOLLOWING_() { return GetToken(SQLiteParser.FOLLOWING_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CUME_DIST_() { return GetToken(SQLiteParser.CUME_DIST_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DENSE_RANK_() { return GetToken(SQLiteParser.DENSE_RANK_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode LAG_() { return GetToken(SQLiteParser.LAG_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode LAST_VALUE_() { return GetToken(SQLiteParser.LAST_VALUE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode LEAD_() { return GetToken(SQLiteParser.LEAD_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NTH_VALUE_() { return GetToken(SQLiteParser.NTH_VALUE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NTILE_() { return GetToken(SQLiteParser.NTILE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PERCENT_RANK_() { return GetToken(SQLiteParser.PERCENT_RANK_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode RANK_() { return GetToken(SQLiteParser.RANK_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ROW_NUMBER_() { return GetToken(SQLiteParser.ROW_NUMBER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode GENERATED_() { return GetToken(SQLiteParser.GENERATED_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ALWAYS_() { return GetToken(SQLiteParser.ALWAYS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode STORED_() { return GetToken(SQLiteParser.STORED_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode TRUE_() { return GetToken(SQLiteParser.TRUE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FALSE_() { return GetToken(SQLiteParser.FALSE_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode WINDOW_() { return GetToken(SQLiteParser.WINDOW_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NULLS_() { return GetToken(SQLiteParser.NULLS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FIRST_() { return GetToken(SQLiteParser.FIRST_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode LAST_() { return GetToken(SQLiteParser.LAST_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode FILTER_() { return GetToken(SQLiteParser.FILTER_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode GROUPS_() { return GetToken(SQLiteParser.GROUPS_, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode EXCLUDE_() { return GetToken(SQLiteParser.EXCLUDE_, 0); } + public KeywordContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_keyword; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterKeyword(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitKeyword(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitKeyword(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public KeywordContext keyword() { + KeywordContext _localctx = new KeywordContext(Context, State); + EnterRule(_localctx, 176, RULE_keyword); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 2004; + _la = TokenStream.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & -33554432L) != 0) || ((((_la - 64)) & ~0x3f) == 0 && ((1L << (_la - 64)) & -1152921504606846977L) != 0) || ((((_la - 128)) & ~0x3f) == 0 && ((1L << (_la - 128)) & 9007199254740991L) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class NameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public NameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_name; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterName(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitName(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitName(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public NameContext name() { + NameContext _localctx = new NameContext(Context, State); + EnterRule(_localctx, 178, RULE_name); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2006; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Function_nameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public Function_nameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_function_name; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterFunction_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitFunction_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitFunction_name(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Function_nameContext function_name() { + Function_nameContext _localctx = new Function_nameContext(Context, State); + EnterRule(_localctx, 180, RULE_function_name); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2008; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Schema_nameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public Schema_nameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_schema_name; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterSchema_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitSchema_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitSchema_name(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Schema_nameContext schema_name() { + Schema_nameContext _localctx = new Schema_nameContext(Context, State); + EnterRule(_localctx, 182, RULE_schema_name); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2010; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Table_nameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public Table_nameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_table_name; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterTable_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitTable_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitTable_name(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Table_nameContext table_name() { + Table_nameContext _localctx = new Table_nameContext(Context, State); + EnterRule(_localctx, 184, RULE_table_name); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2012; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Table_or_index_nameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public Table_or_index_nameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_table_or_index_name; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterTable_or_index_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitTable_or_index_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitTable_or_index_name(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Table_or_index_nameContext table_or_index_name() { + Table_or_index_nameContext _localctx = new Table_or_index_nameContext(Context, State); + EnterRule(_localctx, 186, RULE_table_or_index_name); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2014; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Column_nameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public Column_nameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_column_name; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterColumn_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitColumn_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitColumn_name(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Column_nameContext column_name() { + Column_nameContext _localctx = new Column_nameContext(Context, State); + EnterRule(_localctx, 188, RULE_column_name); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2016; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Collation_nameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public Collation_nameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_collation_name; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterCollation_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitCollation_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitCollation_name(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Collation_nameContext collation_name() { + Collation_nameContext _localctx = new Collation_nameContext(Context, State); + EnterRule(_localctx, 190, RULE_collation_name); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2018; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Foreign_tableContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public Foreign_tableContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_foreign_table; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterForeign_table(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitForeign_table(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitForeign_table(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Foreign_tableContext foreign_table() { + Foreign_tableContext _localctx = new Foreign_tableContext(Context, State); + EnterRule(_localctx, 192, RULE_foreign_table); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2020; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Index_nameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public Index_nameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_index_name; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterIndex_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitIndex_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitIndex_name(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Index_nameContext index_name() { + Index_nameContext _localctx = new Index_nameContext(Context, State); + EnterRule(_localctx, 194, RULE_index_name); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2022; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Trigger_nameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public Trigger_nameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_trigger_name; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterTrigger_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitTrigger_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitTrigger_name(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Trigger_nameContext trigger_name() { + Trigger_nameContext _localctx = new Trigger_nameContext(Context, State); + EnterRule(_localctx, 196, RULE_trigger_name); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2024; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class View_nameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public View_nameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_view_name; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterView_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitView_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitView_name(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public View_nameContext view_name() { + View_nameContext _localctx = new View_nameContext(Context, State); + EnterRule(_localctx, 198, RULE_view_name); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2026; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Module_nameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public Module_nameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_module_name; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterModule_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitModule_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitModule_name(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Module_nameContext module_name() { + Module_nameContext _localctx = new Module_nameContext(Context, State); + EnterRule(_localctx, 200, RULE_module_name); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2028; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Pragma_nameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public Pragma_nameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_pragma_name; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterPragma_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitPragma_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitPragma_name(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Pragma_nameContext pragma_name() { + Pragma_nameContext _localctx = new Pragma_nameContext(Context, State); + EnterRule(_localctx, 202, RULE_pragma_name); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2030; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Savepoint_nameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public Savepoint_nameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_savepoint_name; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterSavepoint_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitSavepoint_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitSavepoint_name(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Savepoint_nameContext savepoint_name() { + Savepoint_nameContext _localctx = new Savepoint_nameContext(Context, State); + EnterRule(_localctx, 204, RULE_savepoint_name); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2032; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Table_aliasContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public Table_aliasContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_table_alias; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterTable_alias(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitTable_alias(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitTable_alias(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Table_aliasContext table_alias() { + Table_aliasContext _localctx = new Table_aliasContext(Context, State); + EnterRule(_localctx, 206, RULE_table_alias); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2034; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Transaction_nameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public Transaction_nameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_transaction_name; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterTransaction_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitTransaction_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitTransaction_name(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Transaction_nameContext transaction_name() { + Transaction_nameContext _localctx = new Transaction_nameContext(Context, State); + EnterRule(_localctx, 208, RULE_transaction_name); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2036; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Window_nameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public Window_nameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_window_name; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterWindow_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitWindow_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitWindow_name(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Window_nameContext window_name() { + Window_nameContext _localctx = new Window_nameContext(Context, State); + EnterRule(_localctx, 210, RULE_window_name); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2038; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class AliasContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public AliasContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_alias; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterAlias(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitAlias(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitAlias(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public AliasContext alias() { + AliasContext _localctx = new AliasContext(Context, State); + EnterRule(_localctx, 212, RULE_alias); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2040; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class FilenameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public FilenameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_filename; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterFilename(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitFilename(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitFilename(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public FilenameContext filename() { + FilenameContext _localctx = new FilenameContext(Context, State); + EnterRule(_localctx, 214, RULE_filename); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2042; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Base_window_nameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public Base_window_nameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_base_window_name; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterBase_window_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitBase_window_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitBase_window_name(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Base_window_nameContext base_window_name() { + Base_window_nameContext _localctx = new Base_window_nameContext(Context, State); + EnterRule(_localctx, 216, RULE_base_window_name); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2044; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Simple_funcContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public Simple_funcContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_simple_func; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterSimple_func(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitSimple_func(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitSimple_func(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Simple_funcContext simple_func() { + Simple_funcContext _localctx = new Simple_funcContext(Context, State); + EnterRule(_localctx, 218, RULE_simple_func); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2046; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Aggregate_funcContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public Aggregate_funcContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_aggregate_func; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterAggregate_func(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitAggregate_func(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitAggregate_func(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Aggregate_funcContext aggregate_func() { + Aggregate_funcContext _localctx = new Aggregate_funcContext(Context, State); + EnterRule(_localctx, 220, RULE_aggregate_func); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2048; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Table_function_nameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + public Table_function_nameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_table_function_name; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterTable_function_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitTable_function_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitTable_function_name(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Table_function_nameContext table_function_name() { + Table_function_nameContext _localctx = new Table_function_nameContext(Context, State); + EnterRule(_localctx, 222, RULE_table_function_name); + try { + EnterOuterAlt(_localctx, 1); + { + State = 2050; + any_name(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class Any_nameContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IDENTIFIER() { return GetToken(SQLiteParser.IDENTIFIER, 0); } + [System.Diagnostics.DebuggerNonUserCode] public KeywordContext keyword() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode STRING_LITERAL() { return GetToken(SQLiteParser.STRING_LITERAL, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OPEN_PAR() { return GetToken(SQLiteParser.OPEN_PAR, 0); } + [System.Diagnostics.DebuggerNonUserCode] public Any_nameContext any_name() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CLOSE_PAR() { return GetToken(SQLiteParser.CLOSE_PAR, 0); } + public Any_nameContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_any_name; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.EnterAny_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ISQLiteParserListener typedListener = listener as ISQLiteParserListener; + if (typedListener != null) typedListener.ExitAny_name(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ISQLiteParserVisitor typedVisitor = visitor as ISQLiteParserVisitor; + if (typedVisitor != null) return typedVisitor.VisitAny_name(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public Any_nameContext any_name() { + Any_nameContext _localctx = new Any_nameContext(Context, State); + EnterRule(_localctx, 224, RULE_any_name); + try { + State = 2059; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case IDENTIFIER: + EnterOuterAlt(_localctx, 1); + { + State = 2052; + Match(IDENTIFIER); + } + break; + case ABORT_: + case ACTION_: + case ADD_: + case AFTER_: + case ALL_: + case ALTER_: + case ANALYZE_: + case AND_: + case AS_: + case ASC_: + case ATTACH_: + case AUTOINCREMENT_: + case BEFORE_: + case BEGIN_: + case BETWEEN_: + case BY_: + case CASCADE_: + case CASE_: + case CAST_: + case CHECK_: + case COLLATE_: + case COLUMN_: + case COMMIT_: + case CONFLICT_: + case CONSTRAINT_: + case CREATE_: + case CROSS_: + case CURRENT_DATE_: + case CURRENT_TIME_: + case CURRENT_TIMESTAMP_: + case DATABASE_: + case DEFAULT_: + case DEFERRABLE_: + case DEFERRED_: + case DELETE_: + case DESC_: + case DETACH_: + case DISTINCT_: + case DROP_: + case EACH_: + case ELSE_: + case END_: + case ESCAPE_: + case EXCEPT_: + case EXCLUSIVE_: + case EXISTS_: + case EXPLAIN_: + case FAIL_: + case FOR_: + case FOREIGN_: + case FROM_: + case FULL_: + case GLOB_: + case GROUP_: + case HAVING_: + case IF_: + case IGNORE_: + case IMMEDIATE_: + case IN_: + case INDEX_: + case INDEXED_: + case INITIALLY_: + case INNER_: + case INSERT_: + case INSTEAD_: + case INTERSECT_: + case INTO_: + case IS_: + case ISNULL_: + case JOIN_: + case KEY_: + case LEFT_: + case LIKE_: + case LIMIT_: + case MATCH_: + case NATURAL_: + case NO_: + case NOT_: + case NOTNULL_: + case NULL_: + case OF_: + case OFFSET_: + case ON_: + case OR_: + case ORDER_: + case OUTER_: + case PLAN_: + case PRAGMA_: + case PRIMARY_: + case QUERY_: + case RAISE_: + case RECURSIVE_: + case REFERENCES_: + case REGEXP_: + case REINDEX_: + case RELEASE_: + case RENAME_: + case REPLACE_: + case RESTRICT_: + case RIGHT_: + case ROLLBACK_: + case ROW_: + case ROWS_: + case SAVEPOINT_: + case SELECT_: + case SET_: + case TABLE_: + case TEMP_: + case TEMPORARY_: + case THEN_: + case TO_: + case TRANSACTION_: + case TRIGGER_: + case UNION_: + case UNIQUE_: + case UPDATE_: + case USING_: + case VACUUM_: + case VALUES_: + case VIEW_: + case VIRTUAL_: + case WHEN_: + case WHERE_: + case WITH_: + case WITHOUT_: + case FIRST_VALUE_: + case OVER_: + case PARTITION_: + case RANGE_: + case PRECEDING_: + case UNBOUNDED_: + case CURRENT_: + case FOLLOWING_: + case CUME_DIST_: + case DENSE_RANK_: + case LAG_: + case LAST_VALUE_: + case LEAD_: + case NTH_VALUE_: + case NTILE_: + case PERCENT_RANK_: + case RANK_: + case ROW_NUMBER_: + case GENERATED_: + case ALWAYS_: + case STORED_: + case TRUE_: + case FALSE_: + case WINDOW_: + case NULLS_: + case FIRST_: + case LAST_: + case FILTER_: + case GROUPS_: + case EXCLUDE_: + EnterOuterAlt(_localctx, 2); + { + State = 2053; + keyword(); + } + break; + case STRING_LITERAL: + EnterOuterAlt(_localctx, 3); + { + State = 2054; + Match(STRING_LITERAL); + } + break; + case OPEN_PAR: + EnterOuterAlt(_localctx, 4); + { + State = 2055; + Match(OPEN_PAR); + State = 2056; + any_name(); + State = 2057; + Match(CLOSE_PAR); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public override bool Sempred(RuleContext _localctx, int ruleIndex, int predIndex) { + switch (ruleIndex) { + case 32: return expr_sempred((ExprContext)_localctx, predIndex); + } + return true; + } + private bool expr_sempred(ExprContext _localctx, int predIndex) { + switch (predIndex) { + case 0: return Precpred(Context, 20); + case 1: return Precpred(Context, 19); + case 2: return Precpred(Context, 18); + case 3: return Precpred(Context, 17); + case 4: return Precpred(Context, 16); + case 5: return Precpred(Context, 15); + case 6: return Precpred(Context, 14); + case 7: return Precpred(Context, 13); + case 8: return Precpred(Context, 6); + case 9: return Precpred(Context, 5); + case 10: return Precpred(Context, 9); + case 11: return Precpred(Context, 8); + case 12: return Precpred(Context, 7); + case 13: return Precpred(Context, 4); + } + return true; + } + + private static int[] _serializedATN = { + 4,1,193,2062,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,2, + 7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14,7,14, + 2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7,19,2,20,7,20,2,21,7,21, + 2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,2,26,7,26,2,27,7,27,2,28,7,28, + 2,29,7,29,2,30,7,30,2,31,7,31,2,32,7,32,2,33,7,33,2,34,7,34,2,35,7,35, + 2,36,7,36,2,37,7,37,2,38,7,38,2,39,7,39,2,40,7,40,2,41,7,41,2,42,7,42, + 2,43,7,43,2,44,7,44,2,45,7,45,2,46,7,46,2,47,7,47,2,48,7,48,2,49,7,49, + 2,50,7,50,2,51,7,51,2,52,7,52,2,53,7,53,2,54,7,54,2,55,7,55,2,56,7,56, + 2,57,7,57,2,58,7,58,2,59,7,59,2,60,7,60,2,61,7,61,2,62,7,62,2,63,7,63, + 2,64,7,64,2,65,7,65,2,66,7,66,2,67,7,67,2,68,7,68,2,69,7,69,2,70,7,70, + 2,71,7,71,2,72,7,72,2,73,7,73,2,74,7,74,2,75,7,75,2,76,7,76,2,77,7,77, + 2,78,7,78,2,79,7,79,2,80,7,80,2,81,7,81,2,82,7,82,2,83,7,83,2,84,7,84, + 2,85,7,85,2,86,7,86,2,87,7,87,2,88,7,88,2,89,7,89,2,90,7,90,2,91,7,91, + 2,92,7,92,2,93,7,93,2,94,7,94,2,95,7,95,2,96,7,96,2,97,7,97,2,98,7,98, + 2,99,7,99,2,100,7,100,2,101,7,101,2,102,7,102,2,103,7,103,2,104,7,104, + 2,105,7,105,2,106,7,106,2,107,7,107,2,108,7,108,2,109,7,109,2,110,7,110, + 2,111,7,111,2,112,7,112,1,0,5,0,228,8,0,10,0,12,0,231,9,0,1,0,1,0,1,1, + 5,1,236,8,1,10,1,12,1,239,9,1,1,1,1,1,4,1,243,8,1,11,1,12,1,244,1,1,5, + 1,248,8,1,10,1,12,1,251,9,1,1,1,5,1,254,8,1,10,1,12,1,257,9,1,1,2,1,2, + 1,2,3,2,262,8,2,3,2,264,8,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1, + 2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,1,2,3,2,290,8,2,1,3, + 1,3,1,3,1,3,1,3,3,3,297,8,3,1,3,1,3,1,3,1,3,1,3,3,3,304,8,3,1,3,1,3,1, + 3,1,3,3,3,310,8,3,1,3,1,3,3,3,314,8,3,1,3,1,3,1,3,3,3,319,8,3,1,3,3,3, + 322,8,3,1,4,1,4,1,4,1,4,1,4,3,4,329,8,4,1,4,3,4,332,8,4,1,5,1,5,3,5,336, + 8,5,1,5,1,5,1,5,1,5,1,6,1,6,3,6,344,8,6,1,6,1,6,3,6,348,8,6,3,6,350,8, + 6,1,7,1,7,3,7,354,8,7,1,8,1,8,3,8,358,8,8,1,8,1,8,3,8,362,8,8,1,8,3,8, + 365,8,8,1,9,1,9,1,9,1,10,1,10,3,10,372,8,10,1,10,1,10,1,11,1,11,3,11,378, + 8,11,1,11,1,11,1,11,1,11,3,11,384,8,11,1,11,1,11,1,11,3,11,389,8,11,1, + 11,1,11,1,11,1,11,1,11,1,11,1,11,5,11,398,8,11,10,11,12,11,401,9,11,1, + 11,1,11,1,11,3,11,406,8,11,1,12,1,12,3,12,410,8,12,1,12,1,12,3,12,414, + 8,12,1,12,3,12,417,8,12,1,13,1,13,3,13,421,8,13,1,13,1,13,1,13,1,13,3, + 13,427,8,13,1,13,1,13,1,13,3,13,432,8,13,1,13,1,13,1,13,1,13,1,13,5,13, + 439,8,13,10,13,12,13,442,9,13,1,13,1,13,5,13,446,8,13,10,13,12,13,449, + 9,13,1,13,1,13,1,13,3,13,454,8,13,1,13,1,13,3,13,458,8,13,1,14,1,14,3, + 14,462,8,14,1,14,5,14,465,8,14,10,14,12,14,468,9,14,1,15,4,15,471,8,15, + 11,15,12,15,472,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,3,15, + 485,8,15,1,16,1,16,3,16,489,8,16,1,16,1,16,1,16,3,16,494,8,16,1,16,3,16, + 497,8,16,1,16,3,16,500,8,16,1,16,3,16,503,8,16,1,16,1,16,3,16,507,8,16, + 1,16,3,16,510,8,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1,16,1, + 16,1,16,3,16,524,8,16,1,16,1,16,1,16,1,16,1,16,3,16,531,8,16,1,16,1,16, + 1,16,1,16,1,16,3,16,538,8,16,3,16,540,8,16,1,17,3,17,543,8,17,1,17,1,17, + 1,18,1,18,3,18,549,8,18,1,18,1,18,1,18,3,18,554,8,18,1,18,1,18,1,18,1, + 18,5,18,560,8,18,10,18,12,18,563,9,18,1,18,1,18,3,18,567,8,18,1,18,1,18, + 1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,1,18,5,18,580,8,18,10,18,12,18, + 583,9,18,1,18,1,18,1,18,3,18,588,8,18,1,19,1,19,1,19,1,19,1,19,1,19,5, + 19,596,8,19,10,19,12,19,599,9,19,1,19,1,19,3,19,603,8,19,1,19,1,19,1,19, + 1,19,1,19,1,19,1,19,1,19,3,19,613,8,19,1,19,1,19,5,19,617,8,19,10,19,12, + 19,620,9,19,1,19,3,19,623,8,19,1,19,1,19,1,19,3,19,628,8,19,3,19,630,8, + 19,1,20,1,20,1,20,1,20,1,21,1,21,3,21,638,8,21,1,21,1,21,1,21,1,21,3,21, + 644,8,21,1,21,1,21,1,21,3,21,649,8,21,1,21,1,21,1,21,1,21,1,21,3,21,656, + 8,21,1,21,1,21,1,21,1,21,1,21,1,21,1,21,5,21,665,8,21,10,21,12,21,668, + 9,21,3,21,670,8,21,3,21,672,8,21,1,21,1,21,1,21,1,21,1,21,3,21,679,8,21, + 1,21,1,21,3,21,683,8,21,1,21,1,21,1,21,1,21,1,21,3,21,690,8,21,1,21,1, + 21,4,21,694,8,21,11,21,12,21,695,1,21,1,21,1,22,1,22,3,22,702,8,22,1,22, + 1,22,1,22,1,22,3,22,708,8,22,1,22,1,22,1,22,3,22,713,8,22,1,22,1,22,1, + 22,1,22,1,22,5,22,720,8,22,10,22,12,22,723,9,22,1,22,1,22,3,22,727,8,22, + 1,22,1,22,1,22,1,23,1,23,1,23,1,23,1,23,1,23,3,23,738,8,23,1,23,1,23,1, + 23,3,23,743,8,23,1,23,1,23,1,23,1,23,1,23,1,23,1,23,5,23,752,8,23,10,23, + 12,23,755,9,23,1,23,1,23,3,23,759,8,23,1,24,1,24,3,24,763,8,24,1,24,1, + 24,1,24,1,24,1,24,1,24,1,24,1,24,1,24,1,24,1,24,1,24,5,24,777,8,24,10, + 24,12,24,780,9,24,1,25,1,25,1,25,1,25,1,25,5,25,787,8,25,10,25,12,25,790, + 9,25,1,25,1,25,3,25,794,8,25,1,26,1,26,1,26,1,26,1,26,1,26,3,26,802,8, + 26,1,26,1,26,1,26,1,27,1,27,1,27,1,27,1,27,5,27,812,8,27,10,27,12,27,815, + 9,27,1,27,1,27,3,27,819,8,27,1,27,1,27,1,27,1,27,1,27,1,28,3,28,827,8, + 28,1,28,1,28,1,28,1,28,1,28,3,28,834,8,28,1,28,3,28,837,8,28,1,29,3,29, + 840,8,29,1,29,1,29,1,29,1,29,1,29,3,29,847,8,29,1,29,3,29,850,8,29,1,29, + 3,29,853,8,29,1,29,3,29,856,8,29,1,30,1,30,3,30,860,8,30,1,30,1,30,1,31, + 1,31,1,31,1,31,3,31,868,8,31,1,31,1,31,1,31,3,31,873,8,31,1,31,1,31,1, + 32,1,32,1,32,1,32,1,32,1,32,3,32,883,8,32,1,32,1,32,1,32,3,32,888,8,32, + 1,32,1,32,1,32,1,32,1,32,1,32,1,32,3,32,897,8,32,1,32,1,32,1,32,5,32,902, + 8,32,10,32,12,32,905,9,32,1,32,3,32,908,8,32,1,32,1,32,3,32,912,8,32,1, + 32,3,32,915,8,32,1,32,1,32,1,32,1,32,5,32,921,8,32,10,32,12,32,924,9,32, + 1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,3,32,936,8,32,1,32,3, + 32,939,8,32,1,32,1,32,1,32,1,32,1,32,1,32,3,32,947,8,32,1,32,1,32,1,32, + 1,32,1,32,4,32,954,8,32,11,32,12,32,955,1,32,1,32,3,32,960,8,32,1,32,1, + 32,1,32,3,32,965,8,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32, + 1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32, + 1,32,3,32,992,8,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,3,32,1001,8,32,1, + 32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,3,32,1013,8,32,1,32,1, + 32,1,32,3,32,1018,8,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1,32,1, + 32,3,32,1030,8,32,1,32,1,32,1,32,1,32,3,32,1036,8,32,1,32,1,32,1,32,1, + 32,1,32,3,32,1043,8,32,1,32,1,32,3,32,1047,8,32,1,32,1,32,1,32,1,32,1, + 32,1,32,5,32,1055,8,32,10,32,12,32,1058,9,32,3,32,1060,8,32,1,32,1,32, + 1,32,1,32,3,32,1066,8,32,1,32,1,32,1,32,1,32,3,32,1072,8,32,1,32,1,32, + 1,32,1,32,1,32,5,32,1079,8,32,10,32,12,32,1082,9,32,3,32,1084,8,32,1,32, + 1,32,3,32,1088,8,32,5,32,1090,8,32,10,32,12,32,1093,9,32,1,33,1,33,1,33, + 1,33,1,33,1,33,3,33,1101,8,33,1,33,1,33,1,34,1,34,1,35,1,35,1,35,1,35, + 5,35,1111,8,35,10,35,12,35,1114,9,35,1,35,1,35,1,36,1,36,1,36,1,36,5,36, + 1122,8,36,10,36,12,36,1125,9,36,1,37,3,37,1128,8,37,1,37,1,37,1,37,1,37, + 1,37,3,37,1135,8,37,1,37,1,37,1,37,1,37,3,37,1141,8,37,1,37,1,37,1,37, + 3,37,1146,8,37,1,37,1,37,1,37,1,37,5,37,1152,8,37,10,37,12,37,1155,9,37, + 1,37,1,37,3,37,1159,8,37,1,37,1,37,3,37,1163,8,37,1,37,3,37,1166,8,37, + 1,37,1,37,3,37,1170,8,37,1,37,3,37,1173,8,37,1,38,1,38,1,38,1,38,5,38, + 1179,8,38,10,38,12,38,1182,9,38,1,39,1,39,1,39,1,39,1,39,1,39,5,39,1190, + 8,39,10,39,12,39,1193,9,39,1,39,1,39,1,39,3,39,1198,8,39,3,39,1200,8,39, + 1,39,1,39,1,39,1,39,1,39,1,39,3,39,1208,8,39,1,39,1,39,1,39,1,39,1,39, + 3,39,1215,8,39,1,39,1,39,1,39,5,39,1220,8,39,10,39,12,39,1223,9,39,1,39, + 1,39,3,39,1227,8,39,3,39,1229,8,39,1,40,1,40,1,40,1,40,3,40,1235,8,40, + 1,40,1,40,1,40,1,40,1,40,1,40,1,40,3,40,1244,8,40,1,41,1,41,1,41,3,41, + 1249,8,41,1,42,1,42,1,42,1,42,1,42,3,42,1256,8,42,1,42,1,42,3,42,1260, + 8,42,3,42,1262,8,42,1,43,3,43,1265,8,43,1,43,1,43,1,43,1,43,5,43,1271, + 8,43,10,43,12,43,1274,9,43,1,43,3,43,1277,8,43,1,43,3,43,1280,8,43,1,44, + 1,44,1,44,1,44,3,44,1286,8,44,5,44,1288,8,44,10,44,12,44,1291,9,44,1,45, + 1,45,3,45,1295,8,45,1,45,1,45,1,45,5,45,1300,8,45,10,45,12,45,1303,9,45, + 1,45,1,45,1,45,1,45,5,45,1309,8,45,10,45,12,45,1312,9,45,1,45,3,45,1315, + 8,45,3,45,1317,8,45,1,45,1,45,3,45,1321,8,45,1,45,1,45,1,45,1,45,1,45, + 5,45,1328,8,45,10,45,12,45,1331,9,45,1,45,1,45,3,45,1335,8,45,3,45,1337, + 8,45,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,45,5,45,1348,8,45,10,45, + 12,45,1351,9,45,3,45,1353,8,45,1,45,3,45,1356,8,45,1,46,1,46,1,47,3,47, + 1361,8,47,1,47,1,47,3,47,1365,8,47,1,47,3,47,1368,8,47,1,48,3,48,1371, + 8,48,1,48,1,48,1,48,3,48,1376,8,48,1,48,1,48,3,48,1380,8,48,1,48,4,48, + 1383,8,48,11,48,12,48,1384,1,48,3,48,1388,8,48,1,48,3,48,1391,8,48,1,49, + 1,49,1,49,3,49,1396,8,49,1,49,1,49,3,49,1400,8,49,1,49,3,49,1403,8,49, + 1,49,1,49,1,49,1,49,1,49,3,49,1410,8,49,1,49,1,49,1,49,3,49,1415,8,49, + 1,49,1,49,1,49,1,49,1,49,5,49,1422,8,49,10,49,12,49,1425,9,49,1,49,1,49, + 3,49,1429,8,49,1,49,3,49,1432,8,49,1,49,1,49,1,49,1,49,5,49,1438,8,49, + 10,49,12,49,1441,9,49,1,49,3,49,1444,8,49,1,49,1,49,1,49,1,49,1,49,1,49, + 3,49,1452,8,49,1,49,3,49,1455,8,49,3,49,1457,8,49,1,50,1,50,1,50,1,50, + 1,50,1,50,1,50,3,50,1466,8,50,1,50,3,50,1469,8,50,3,50,1471,8,50,1,51, + 1,51,3,51,1475,8,51,1,51,1,51,3,51,1479,8,51,1,51,1,51,3,51,1483,8,51, + 1,51,3,51,1486,8,51,1,52,1,52,1,52,1,52,1,52,1,52,1,52,5,52,1495,8,52, + 10,52,12,52,1498,9,52,1,52,1,52,3,52,1502,8,52,1,53,1,53,3,53,1506,8,53, + 1,53,1,53,3,53,1510,8,53,1,54,3,54,1513,8,54,1,54,1,54,1,54,3,54,1518, + 8,54,1,54,1,54,1,54,1,54,3,54,1524,8,54,1,54,1,54,1,54,1,54,1,54,3,54, + 1531,8,54,1,54,1,54,1,54,5,54,1536,8,54,10,54,12,54,1539,9,54,1,54,1,54, + 1,54,1,54,5,54,1545,8,54,10,54,12,54,1548,9,54,1,54,3,54,1551,8,54,3,54, + 1553,8,54,1,54,1,54,3,54,1557,8,54,1,54,3,54,1560,8,54,1,55,1,55,1,55, + 1,55,5,55,1566,8,55,10,55,12,55,1569,9,55,1,55,1,55,1,56,3,56,1574,8,56, + 1,56,1,56,1,56,3,56,1579,8,56,1,56,1,56,1,56,1,56,3,56,1585,8,56,1,56, + 1,56,1,56,1,56,1,56,3,56,1592,8,56,1,56,1,56,1,56,5,56,1597,8,56,10,56, + 12,56,1600,9,56,1,56,1,56,3,56,1604,8,56,1,56,3,56,1607,8,56,1,56,3,56, + 1610,8,56,1,56,3,56,1613,8,56,1,57,1,57,1,57,3,57,1618,8,57,1,57,1,57, + 1,57,3,57,1623,8,57,1,57,1,57,1,57,1,57,1,57,3,57,1630,8,57,1,58,1,58, + 3,58,1634,8,58,1,58,1,58,3,58,1638,8,58,1,59,1,59,1,59,1,59,1,59,1,59, + 1,60,1,60,3,60,1648,8,60,1,60,1,60,1,60,1,60,1,60,5,60,1655,8,60,10,60, + 12,60,1658,9,60,3,60,1660,8,60,1,60,1,60,1,60,1,60,1,60,5,60,1667,8,60, + 10,60,12,60,1670,9,60,1,60,3,60,1673,8,60,1,60,1,60,1,61,1,61,1,61,1,61, + 3,61,1681,8,61,1,61,1,61,1,61,1,61,1,61,5,61,1688,8,61,10,61,12,61,1691, + 9,61,3,61,1693,8,61,1,61,1,61,1,61,1,61,1,61,5,61,1700,8,61,10,61,12,61, + 1703,9,61,3,61,1705,8,61,1,61,3,61,1708,8,61,1,61,3,61,1711,8,61,1,62, + 1,62,1,62,1,62,1,62,1,62,1,62,1,62,3,62,1721,8,62,3,62,1723,8,62,1,63, + 1,63,1,63,1,63,1,63,1,63,1,63,3,63,1732,8,63,1,64,1,64,1,64,1,64,1,64, + 5,64,1739,8,64,10,64,12,64,1742,9,64,1,64,3,64,1745,8,64,1,64,1,64,1,65, + 1,65,1,65,3,65,1752,8,65,1,65,1,65,1,65,5,65,1757,8,65,10,65,12,65,1760, + 9,65,1,65,3,65,1763,8,65,1,65,1,65,3,65,1767,8,65,1,66,1,66,1,66,1,66, + 1,66,5,66,1774,8,66,10,66,12,66,1777,9,66,1,66,3,66,1780,8,66,1,66,1,66, + 3,66,1784,8,66,1,66,1,66,1,66,3,66,1789,8,66,1,67,1,67,3,67,1793,8,67, + 1,67,1,67,1,67,5,67,1798,8,67,10,67,12,67,1801,9,67,1,68,1,68,1,68,1,68, + 1,68,5,68,1808,8,68,10,68,12,68,1811,9,68,1,69,1,69,1,69,1,69,3,69,1817, + 8,69,1,70,1,70,1,70,3,70,1822,8,70,1,70,3,70,1825,8,70,1,70,1,70,3,70, + 1829,8,70,1,71,1,71,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72,1,72, + 3,72,1843,8,72,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,1,73,3,73, + 1855,8,73,1,74,1,74,1,74,1,74,1,74,1,74,1,74,3,74,1864,8,74,1,75,1,75, + 1,75,1,75,1,75,1,75,1,75,3,75,1873,8,75,1,75,1,75,3,75,1877,8,75,1,75, + 1,75,1,75,1,75,1,75,1,75,1,75,1,75,3,75,1887,8,75,1,75,3,75,1890,8,75, + 1,75,1,75,1,75,1,75,1,75,1,75,1,75,3,75,1899,8,75,1,75,1,75,1,75,1,75, + 1,75,1,75,1,75,3,75,1908,8,75,1,75,3,75,1911,8,75,1,75,1,75,1,75,1,75, + 3,75,1917,8,75,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1,75,1,75, + 1,75,3,75,1931,8,75,1,75,1,75,3,75,1935,8,75,1,75,1,75,1,75,1,75,1,75, + 1,75,1,75,1,75,1,75,3,75,1946,8,75,1,75,1,75,1,75,3,75,1951,8,75,1,76, + 1,76,1,76,1,77,1,77,1,77,1,78,1,78,1,78,4,78,1962,8,78,11,78,12,78,1963, + 1,79,1,79,1,79,4,79,1969,8,79,11,79,12,79,1970,1,80,1,80,1,80,1,80,1,81, + 1,81,3,81,1979,8,81,1,81,1,81,1,81,3,81,1984,8,81,5,81,1986,8,81,10,81, + 12,81,1989,9,81,1,82,1,82,1,83,1,83,1,84,1,84,1,85,1,85,1,86,1,86,3,86, + 2001,8,86,1,87,1,87,1,88,1,88,1,89,1,89,1,90,1,90,1,91,1,91,1,92,1,92, + 1,93,1,93,1,94,1,94,1,95,1,95,1,96,1,96,1,97,1,97,1,98,1,98,1,99,1,99, + 1,100,1,100,1,101,1,101,1,102,1,102,1,103,1,103,1,104,1,104,1,105,1,105, + 1,106,1,106,1,107,1,107,1,108,1,108,1,109,1,109,1,110,1,110,1,111,1,111, + 1,112,1,112,1,112,1,112,1,112,1,112,1,112,3,112,2060,8,112,1,112,2,440, + 472,1,64,113,0,2,4,6,8,10,12,14,16,18,20,22,24,26,28,30,32,34,36,38,40, + 42,44,46,48,50,52,54,56,58,60,62,64,66,68,70,72,74,76,78,80,82,84,86,88, + 90,92,94,96,98,100,102,104,106,108,110,112,114,116,118,120,122,124,126, + 128,130,132,134,136,138,140,142,144,146,148,150,152,154,156,158,160,162, + 164,166,168,170,172,174,176,178,180,182,184,186,188,190,192,194,196,198, + 200,202,204,206,208,210,212,214,216,218,220,222,224,0,29,3,0,58,58,69, + 69,82,82,2,0,47,47,66,66,1,0,133,134,2,0,146,146,171,171,1,0,8,9,2,0,59, + 59,141,141,2,0,56,56,104,104,2,0,58,58,82,82,5,0,25,25,72,72,81,81,122, + 122,126,126,4,0,84,84,132,132,138,138,145,145,2,0,7,7,12,13,1,0,14,17, + 1,0,18,21,4,0,77,77,97,97,99,99,118,118,3,0,25,25,72,72,126,126,5,0,52, + 54,104,104,172,173,186,186,188,189,2,0,29,29,62,62,3,0,76,76,96,96,125, + 125,3,0,128,128,154,154,179,179,2,0,5,5,106,106,1,0,176,177,2,0,34,34, + 60,60,2,0,151,151,162,162,2,0,159,159,166,166,2,0,160,160,167,168,2,0, + 161,161,163,163,2,0,8,10,102,102,2,0,185,185,188,188,2,0,25,123,125,180, + 2346,0,229,1,0,0,0,2,237,1,0,0,0,4,263,1,0,0,0,6,291,1,0,0,0,8,323,1,0, + 0,0,10,333,1,0,0,0,12,341,1,0,0,0,14,351,1,0,0,0,16,355,1,0,0,0,18,366, + 1,0,0,0,20,369,1,0,0,0,22,375,1,0,0,0,24,409,1,0,0,0,26,418,1,0,0,0,28, + 459,1,0,0,0,30,470,1,0,0,0,32,488,1,0,0,0,34,542,1,0,0,0,36,548,1,0,0, + 0,38,589,1,0,0,0,40,631,1,0,0,0,42,635,1,0,0,0,44,699,1,0,0,0,46,731,1, + 0,0,0,48,760,1,0,0,0,50,781,1,0,0,0,52,795,1,0,0,0,54,806,1,0,0,0,56,826, + 1,0,0,0,58,839,1,0,0,0,60,857,1,0,0,0,62,863,1,0,0,0,64,964,1,0,0,0,66, + 1094,1,0,0,0,68,1104,1,0,0,0,70,1106,1,0,0,0,72,1117,1,0,0,0,74,1127,1, + 0,0,0,76,1174,1,0,0,0,78,1183,1,0,0,0,80,1230,1,0,0,0,82,1248,1,0,0,0, + 84,1250,1,0,0,0,86,1264,1,0,0,0,88,1281,1,0,0,0,90,1355,1,0,0,0,92,1357, + 1,0,0,0,94,1360,1,0,0,0,96,1370,1,0,0,0,98,1456,1,0,0,0,100,1470,1,0,0, + 0,102,1485,1,0,0,0,104,1501,1,0,0,0,106,1509,1,0,0,0,108,1512,1,0,0,0, + 110,1561,1,0,0,0,112,1573,1,0,0,0,114,1617,1,0,0,0,116,1631,1,0,0,0,118, + 1639,1,0,0,0,120,1645,1,0,0,0,122,1676,1,0,0,0,124,1712,1,0,0,0,126,1724, + 1,0,0,0,128,1733,1,0,0,0,130,1748,1,0,0,0,132,1768,1,0,0,0,134,1790,1, + 0,0,0,136,1802,1,0,0,0,138,1812,1,0,0,0,140,1818,1,0,0,0,142,1830,1,0, + 0,0,144,1842,1,0,0,0,146,1854,1,0,0,0,148,1863,1,0,0,0,150,1950,1,0,0, + 0,152,1952,1,0,0,0,154,1955,1,0,0,0,156,1958,1,0,0,0,158,1965,1,0,0,0, + 160,1972,1,0,0,0,162,1976,1,0,0,0,164,1990,1,0,0,0,166,1992,1,0,0,0,168, + 1994,1,0,0,0,170,1996,1,0,0,0,172,2000,1,0,0,0,174,2002,1,0,0,0,176,2004, + 1,0,0,0,178,2006,1,0,0,0,180,2008,1,0,0,0,182,2010,1,0,0,0,184,2012,1, + 0,0,0,186,2014,1,0,0,0,188,2016,1,0,0,0,190,2018,1,0,0,0,192,2020,1,0, + 0,0,194,2022,1,0,0,0,196,2024,1,0,0,0,198,2026,1,0,0,0,200,2028,1,0,0, + 0,202,2030,1,0,0,0,204,2032,1,0,0,0,206,2034,1,0,0,0,208,2036,1,0,0,0, + 210,2038,1,0,0,0,212,2040,1,0,0,0,214,2042,1,0,0,0,216,2044,1,0,0,0,218, + 2046,1,0,0,0,220,2048,1,0,0,0,222,2050,1,0,0,0,224,2059,1,0,0,0,226,228, + 3,2,1,0,227,226,1,0,0,0,228,231,1,0,0,0,229,227,1,0,0,0,229,230,1,0,0, + 0,230,232,1,0,0,0,231,229,1,0,0,0,232,233,5,0,0,1,233,1,1,0,0,0,234,236, + 5,1,0,0,235,234,1,0,0,0,236,239,1,0,0,0,237,235,1,0,0,0,237,238,1,0,0, + 0,238,240,1,0,0,0,239,237,1,0,0,0,240,249,3,4,2,0,241,243,5,1,0,0,242, + 241,1,0,0,0,243,244,1,0,0,0,244,242,1,0,0,0,244,245,1,0,0,0,245,246,1, + 0,0,0,246,248,3,4,2,0,247,242,1,0,0,0,248,251,1,0,0,0,249,247,1,0,0,0, + 249,250,1,0,0,0,250,255,1,0,0,0,251,249,1,0,0,0,252,254,5,1,0,0,253,252, + 1,0,0,0,254,257,1,0,0,0,255,253,1,0,0,0,255,256,1,0,0,0,256,3,1,0,0,0, + 257,255,1,0,0,0,258,261,5,71,0,0,259,260,5,114,0,0,260,262,5,111,0,0,261, + 259,1,0,0,0,261,262,1,0,0,0,262,264,1,0,0,0,263,258,1,0,0,0,263,264,1, + 0,0,0,264,289,1,0,0,0,265,290,3,6,3,0,266,290,3,8,4,0,267,290,3,10,5,0, + 268,290,3,12,6,0,269,290,3,14,7,0,270,290,3,22,11,0,271,290,3,26,13,0, + 272,290,3,42,21,0,273,290,3,44,22,0,274,290,3,46,23,0,275,290,3,56,28, + 0,276,290,3,58,29,0,277,290,3,60,30,0,278,290,3,62,31,0,279,290,3,74,37, + 0,280,290,3,80,40,0,281,290,3,84,42,0,282,290,3,20,10,0,283,290,3,16,8, + 0,284,290,3,18,9,0,285,290,3,86,43,0,286,290,3,108,54,0,287,290,3,112, + 56,0,288,290,3,116,58,0,289,265,1,0,0,0,289,266,1,0,0,0,289,267,1,0,0, + 0,289,268,1,0,0,0,289,269,1,0,0,0,289,270,1,0,0,0,289,271,1,0,0,0,289, + 272,1,0,0,0,289,273,1,0,0,0,289,274,1,0,0,0,289,275,1,0,0,0,289,276,1, + 0,0,0,289,277,1,0,0,0,289,278,1,0,0,0,289,279,1,0,0,0,289,280,1,0,0,0, + 289,281,1,0,0,0,289,282,1,0,0,0,289,283,1,0,0,0,289,284,1,0,0,0,289,285, + 1,0,0,0,289,286,1,0,0,0,289,287,1,0,0,0,289,288,1,0,0,0,290,5,1,0,0,0, + 291,292,5,30,0,0,292,296,5,132,0,0,293,294,3,182,91,0,294,295,5,2,0,0, + 295,297,1,0,0,0,296,293,1,0,0,0,296,297,1,0,0,0,297,298,1,0,0,0,298,321, + 3,184,92,0,299,309,5,121,0,0,300,301,5,136,0,0,301,310,3,184,92,0,302, + 304,5,46,0,0,303,302,1,0,0,0,303,304,1,0,0,0,304,305,1,0,0,0,305,306,3, + 188,94,0,306,307,5,136,0,0,307,308,3,188,94,0,308,310,1,0,0,0,309,300, + 1,0,0,0,309,303,1,0,0,0,310,322,1,0,0,0,311,313,5,27,0,0,312,314,5,46, + 0,0,313,312,1,0,0,0,313,314,1,0,0,0,314,315,1,0,0,0,315,322,3,28,14,0, + 316,318,5,63,0,0,317,319,5,46,0,0,318,317,1,0,0,0,318,319,1,0,0,0,319, + 320,1,0,0,0,320,322,3,188,94,0,321,299,1,0,0,0,321,311,1,0,0,0,321,316, + 1,0,0,0,322,7,1,0,0,0,323,331,5,31,0,0,324,332,3,182,91,0,325,326,3,182, + 91,0,326,327,5,2,0,0,327,329,1,0,0,0,328,325,1,0,0,0,328,329,1,0,0,0,329, + 330,1,0,0,0,330,332,3,186,93,0,331,324,1,0,0,0,331,328,1,0,0,0,331,332, + 1,0,0,0,332,9,1,0,0,0,333,335,5,35,0,0,334,336,5,55,0,0,335,334,1,0,0, + 0,335,336,1,0,0,0,336,337,1,0,0,0,337,338,3,64,32,0,338,339,5,33,0,0,339, + 340,3,182,91,0,340,11,1,0,0,0,341,343,5,38,0,0,342,344,7,0,0,0,343,342, + 1,0,0,0,343,344,1,0,0,0,344,349,1,0,0,0,345,347,5,137,0,0,346,348,3,208, + 104,0,347,346,1,0,0,0,347,348,1,0,0,0,348,350,1,0,0,0,349,345,1,0,0,0, + 349,350,1,0,0,0,350,13,1,0,0,0,351,353,7,1,0,0,352,354,5,137,0,0,353,352, + 1,0,0,0,353,354,1,0,0,0,354,15,1,0,0,0,355,357,5,126,0,0,356,358,5,137, + 0,0,357,356,1,0,0,0,357,358,1,0,0,0,358,364,1,0,0,0,359,361,5,136,0,0, + 360,362,5,129,0,0,361,360,1,0,0,0,361,362,1,0,0,0,362,363,1,0,0,0,363, + 365,3,204,102,0,364,359,1,0,0,0,364,365,1,0,0,0,365,17,1,0,0,0,366,367, + 5,129,0,0,367,368,3,204,102,0,368,19,1,0,0,0,369,371,5,120,0,0,370,372, + 5,129,0,0,371,370,1,0,0,0,371,372,1,0,0,0,372,373,1,0,0,0,373,374,3,204, + 102,0,374,21,1,0,0,0,375,377,5,50,0,0,376,378,5,140,0,0,377,376,1,0,0, + 0,377,378,1,0,0,0,378,379,1,0,0,0,379,383,5,84,0,0,380,381,5,80,0,0,381, + 382,5,102,0,0,382,384,5,70,0,0,383,380,1,0,0,0,383,384,1,0,0,0,384,388, + 1,0,0,0,385,386,3,182,91,0,386,387,5,2,0,0,387,389,1,0,0,0,388,385,1,0, + 0,0,388,389,1,0,0,0,389,390,1,0,0,0,390,391,3,194,97,0,391,392,5,107,0, + 0,392,393,3,184,92,0,393,394,5,3,0,0,394,399,3,24,12,0,395,396,5,5,0,0, + 396,398,3,24,12,0,397,395,1,0,0,0,398,401,1,0,0,0,399,397,1,0,0,0,399, + 400,1,0,0,0,400,402,1,0,0,0,401,399,1,0,0,0,402,405,5,4,0,0,403,404,5, + 148,0,0,404,406,3,64,32,0,405,403,1,0,0,0,405,406,1,0,0,0,406,23,1,0,0, + 0,407,410,3,188,94,0,408,410,3,64,32,0,409,407,1,0,0,0,409,408,1,0,0,0, + 410,413,1,0,0,0,411,412,5,45,0,0,412,414,3,190,95,0,413,411,1,0,0,0,413, + 414,1,0,0,0,414,416,1,0,0,0,415,417,3,142,71,0,416,415,1,0,0,0,416,417, + 1,0,0,0,417,25,1,0,0,0,418,420,5,50,0,0,419,421,7,2,0,0,420,419,1,0,0, + 0,420,421,1,0,0,0,421,422,1,0,0,0,422,426,5,132,0,0,423,424,5,80,0,0,424, + 425,5,102,0,0,425,427,5,70,0,0,426,423,1,0,0,0,426,427,1,0,0,0,427,431, + 1,0,0,0,428,429,3,182,91,0,429,430,5,2,0,0,430,432,1,0,0,0,431,428,1,0, + 0,0,431,432,1,0,0,0,432,433,1,0,0,0,433,457,3,184,92,0,434,435,5,3,0,0, + 435,440,3,28,14,0,436,437,5,5,0,0,437,439,3,28,14,0,438,436,1,0,0,0,439, + 442,1,0,0,0,440,441,1,0,0,0,440,438,1,0,0,0,441,447,1,0,0,0,442,440,1, + 0,0,0,443,444,5,5,0,0,444,446,3,36,18,0,445,443,1,0,0,0,446,449,1,0,0, + 0,447,445,1,0,0,0,447,448,1,0,0,0,448,450,1,0,0,0,449,447,1,0,0,0,450, + 453,5,4,0,0,451,452,5,150,0,0,452,454,5,185,0,0,453,451,1,0,0,0,453,454, + 1,0,0,0,454,458,1,0,0,0,455,456,5,33,0,0,456,458,3,86,43,0,457,434,1,0, + 0,0,457,455,1,0,0,0,458,27,1,0,0,0,459,461,3,188,94,0,460,462,3,30,15, + 0,461,460,1,0,0,0,461,462,1,0,0,0,462,466,1,0,0,0,463,465,3,32,16,0,464, + 463,1,0,0,0,465,468,1,0,0,0,466,464,1,0,0,0,466,467,1,0,0,0,467,29,1,0, + 0,0,468,466,1,0,0,0,469,471,3,178,89,0,470,469,1,0,0,0,471,472,1,0,0,0, + 472,473,1,0,0,0,472,470,1,0,0,0,473,484,1,0,0,0,474,475,5,3,0,0,475,476, + 3,34,17,0,476,477,5,4,0,0,477,485,1,0,0,0,478,479,5,3,0,0,479,480,3,34, + 17,0,480,481,5,5,0,0,481,482,3,34,17,0,482,483,5,4,0,0,483,485,1,0,0,0, + 484,474,1,0,0,0,484,478,1,0,0,0,484,485,1,0,0,0,485,31,1,0,0,0,486,487, + 5,49,0,0,487,489,3,178,89,0,488,486,1,0,0,0,488,489,1,0,0,0,489,539,1, + 0,0,0,490,491,5,113,0,0,491,493,5,95,0,0,492,494,3,142,71,0,493,492,1, + 0,0,0,493,494,1,0,0,0,494,496,1,0,0,0,495,497,3,40,20,0,496,495,1,0,0, + 0,496,497,1,0,0,0,497,499,1,0,0,0,498,500,5,36,0,0,499,498,1,0,0,0,499, + 500,1,0,0,0,500,540,1,0,0,0,501,503,5,102,0,0,502,501,1,0,0,0,502,503, + 1,0,0,0,503,504,1,0,0,0,504,507,5,104,0,0,505,507,5,140,0,0,506,502,1, + 0,0,0,506,505,1,0,0,0,507,509,1,0,0,0,508,510,3,40,20,0,509,508,1,0,0, + 0,509,510,1,0,0,0,510,540,1,0,0,0,511,512,5,44,0,0,512,513,5,3,0,0,513, + 514,3,64,32,0,514,515,5,4,0,0,515,540,1,0,0,0,516,523,5,56,0,0,517,524, + 3,34,17,0,518,524,3,68,34,0,519,520,5,3,0,0,520,521,3,64,32,0,521,522, + 5,4,0,0,522,524,1,0,0,0,523,517,1,0,0,0,523,518,1,0,0,0,523,519,1,0,0, + 0,524,540,1,0,0,0,525,526,5,45,0,0,526,540,3,190,95,0,527,540,3,38,19, + 0,528,529,5,169,0,0,529,531,5,170,0,0,530,528,1,0,0,0,530,531,1,0,0,0, + 531,532,1,0,0,0,532,533,5,33,0,0,533,534,5,3,0,0,534,535,3,64,32,0,535, + 537,5,4,0,0,536,538,7,3,0,0,537,536,1,0,0,0,537,538,1,0,0,0,538,540,1, + 0,0,0,539,490,1,0,0,0,539,506,1,0,0,0,539,511,1,0,0,0,539,516,1,0,0,0, + 539,525,1,0,0,0,539,527,1,0,0,0,539,530,1,0,0,0,540,33,1,0,0,0,541,543, + 7,4,0,0,542,541,1,0,0,0,542,543,1,0,0,0,543,544,1,0,0,0,544,545,5,186, + 0,0,545,35,1,0,0,0,546,547,5,49,0,0,547,549,3,178,89,0,548,546,1,0,0,0, + 548,549,1,0,0,0,549,587,1,0,0,0,550,551,5,113,0,0,551,554,5,95,0,0,552, + 554,5,140,0,0,553,550,1,0,0,0,553,552,1,0,0,0,554,555,1,0,0,0,555,556, + 5,3,0,0,556,561,3,24,12,0,557,558,5,5,0,0,558,560,3,24,12,0,559,557,1, + 0,0,0,560,563,1,0,0,0,561,559,1,0,0,0,561,562,1,0,0,0,562,564,1,0,0,0, + 563,561,1,0,0,0,564,566,5,4,0,0,565,567,3,40,20,0,566,565,1,0,0,0,566, + 567,1,0,0,0,567,588,1,0,0,0,568,569,5,44,0,0,569,570,5,3,0,0,570,571,3, + 64,32,0,571,572,5,4,0,0,572,588,1,0,0,0,573,574,5,74,0,0,574,575,5,95, + 0,0,575,576,5,3,0,0,576,581,3,188,94,0,577,578,5,5,0,0,578,580,3,188,94, + 0,579,577,1,0,0,0,580,583,1,0,0,0,581,579,1,0,0,0,581,582,1,0,0,0,582, + 584,1,0,0,0,583,581,1,0,0,0,584,585,5,4,0,0,585,586,3,38,19,0,586,588, + 1,0,0,0,587,553,1,0,0,0,587,568,1,0,0,0,587,573,1,0,0,0,588,37,1,0,0,0, + 589,590,5,117,0,0,590,602,3,192,96,0,591,592,5,3,0,0,592,597,3,188,94, + 0,593,594,5,5,0,0,594,596,3,188,94,0,595,593,1,0,0,0,596,599,1,0,0,0,597, + 595,1,0,0,0,597,598,1,0,0,0,598,600,1,0,0,0,599,597,1,0,0,0,600,601,5, + 4,0,0,601,603,1,0,0,0,602,591,1,0,0,0,602,603,1,0,0,0,603,618,1,0,0,0, + 604,605,5,107,0,0,605,612,7,5,0,0,606,607,5,131,0,0,607,613,7,6,0,0,608, + 613,5,41,0,0,609,613,5,123,0,0,610,611,5,101,0,0,611,613,5,26,0,0,612, + 606,1,0,0,0,612,608,1,0,0,0,612,609,1,0,0,0,612,610,1,0,0,0,613,617,1, + 0,0,0,614,615,5,99,0,0,615,617,3,178,89,0,616,604,1,0,0,0,616,614,1,0, + 0,0,617,620,1,0,0,0,618,616,1,0,0,0,618,619,1,0,0,0,619,629,1,0,0,0,620, + 618,1,0,0,0,621,623,5,102,0,0,622,621,1,0,0,0,622,623,1,0,0,0,623,624, + 1,0,0,0,624,627,5,57,0,0,625,626,5,86,0,0,626,628,7,7,0,0,627,625,1,0, + 0,0,627,628,1,0,0,0,628,630,1,0,0,0,629,622,1,0,0,0,629,630,1,0,0,0,630, + 39,1,0,0,0,631,632,5,107,0,0,632,633,5,48,0,0,633,634,7,8,0,0,634,41,1, + 0,0,0,635,637,5,50,0,0,636,638,7,2,0,0,637,636,1,0,0,0,637,638,1,0,0,0, + 638,639,1,0,0,0,639,643,5,138,0,0,640,641,5,80,0,0,641,642,5,102,0,0,642, + 644,5,70,0,0,643,640,1,0,0,0,643,644,1,0,0,0,644,648,1,0,0,0,645,646,3, + 182,91,0,646,647,5,2,0,0,647,649,1,0,0,0,648,645,1,0,0,0,648,649,1,0,0, + 0,649,650,1,0,0,0,650,655,3,196,98,0,651,656,5,37,0,0,652,656,5,28,0,0, + 653,654,5,89,0,0,654,656,5,105,0,0,655,651,1,0,0,0,655,652,1,0,0,0,655, + 653,1,0,0,0,655,656,1,0,0,0,656,671,1,0,0,0,657,672,5,59,0,0,658,672,5, + 88,0,0,659,669,5,141,0,0,660,661,5,105,0,0,661,666,3,188,94,0,662,663, + 5,5,0,0,663,665,3,188,94,0,664,662,1,0,0,0,665,668,1,0,0,0,666,664,1,0, + 0,0,666,667,1,0,0,0,667,670,1,0,0,0,668,666,1,0,0,0,669,660,1,0,0,0,669, + 670,1,0,0,0,670,672,1,0,0,0,671,657,1,0,0,0,671,658,1,0,0,0,671,659,1, + 0,0,0,672,673,1,0,0,0,673,674,5,107,0,0,674,678,3,184,92,0,675,676,5,73, + 0,0,676,677,5,64,0,0,677,679,5,127,0,0,678,675,1,0,0,0,678,679,1,0,0,0, + 679,682,1,0,0,0,680,681,5,147,0,0,681,683,3,64,32,0,682,680,1,0,0,0,682, + 683,1,0,0,0,683,684,1,0,0,0,684,693,5,38,0,0,685,690,3,108,54,0,686,690, + 3,74,37,0,687,690,3,56,28,0,688,690,3,86,43,0,689,685,1,0,0,0,689,686, + 1,0,0,0,689,687,1,0,0,0,689,688,1,0,0,0,690,691,1,0,0,0,691,692,5,1,0, + 0,692,694,1,0,0,0,693,689,1,0,0,0,694,695,1,0,0,0,695,693,1,0,0,0,695, + 696,1,0,0,0,696,697,1,0,0,0,697,698,5,66,0,0,698,43,1,0,0,0,699,701,5, + 50,0,0,700,702,7,2,0,0,701,700,1,0,0,0,701,702,1,0,0,0,702,703,1,0,0,0, + 703,707,5,145,0,0,704,705,5,80,0,0,705,706,5,102,0,0,706,708,5,70,0,0, + 707,704,1,0,0,0,707,708,1,0,0,0,708,712,1,0,0,0,709,710,3,182,91,0,710, + 711,5,2,0,0,711,713,1,0,0,0,712,709,1,0,0,0,712,713,1,0,0,0,713,714,1, + 0,0,0,714,726,3,198,99,0,715,716,5,3,0,0,716,721,3,188,94,0,717,718,5, + 5,0,0,718,720,3,188,94,0,719,717,1,0,0,0,720,723,1,0,0,0,721,719,1,0,0, + 0,721,722,1,0,0,0,722,724,1,0,0,0,723,721,1,0,0,0,724,725,5,4,0,0,725, + 727,1,0,0,0,726,715,1,0,0,0,726,727,1,0,0,0,727,728,1,0,0,0,728,729,5, + 33,0,0,729,730,3,86,43,0,730,45,1,0,0,0,731,732,5,50,0,0,732,733,5,146, + 0,0,733,737,5,132,0,0,734,735,5,80,0,0,735,736,5,102,0,0,736,738,5,70, + 0,0,737,734,1,0,0,0,737,738,1,0,0,0,738,742,1,0,0,0,739,740,3,182,91,0, + 740,741,5,2,0,0,741,743,1,0,0,0,742,739,1,0,0,0,742,743,1,0,0,0,743,744, + 1,0,0,0,744,745,3,184,92,0,745,746,5,142,0,0,746,758,3,200,100,0,747,748, + 5,3,0,0,748,753,3,172,86,0,749,750,5,5,0,0,750,752,3,172,86,0,751,749, + 1,0,0,0,752,755,1,0,0,0,753,751,1,0,0,0,753,754,1,0,0,0,754,756,1,0,0, + 0,755,753,1,0,0,0,756,757,5,4,0,0,757,759,1,0,0,0,758,747,1,0,0,0,758, + 759,1,0,0,0,759,47,1,0,0,0,760,762,5,149,0,0,761,763,5,116,0,0,762,761, + 1,0,0,0,762,763,1,0,0,0,763,764,1,0,0,0,764,765,3,50,25,0,765,766,5,33, + 0,0,766,767,5,3,0,0,767,768,3,86,43,0,768,778,5,4,0,0,769,770,5,5,0,0, + 770,771,3,50,25,0,771,772,5,33,0,0,772,773,5,3,0,0,773,774,3,86,43,0,774, + 775,5,4,0,0,775,777,1,0,0,0,776,769,1,0,0,0,777,780,1,0,0,0,778,776,1, + 0,0,0,778,779,1,0,0,0,779,49,1,0,0,0,780,778,1,0,0,0,781,793,3,184,92, + 0,782,783,5,3,0,0,783,788,3,188,94,0,784,785,5,5,0,0,785,787,3,188,94, + 0,786,784,1,0,0,0,787,790,1,0,0,0,788,786,1,0,0,0,788,789,1,0,0,0,789, + 791,1,0,0,0,790,788,1,0,0,0,791,792,5,4,0,0,792,794,1,0,0,0,793,782,1, + 0,0,0,793,794,1,0,0,0,794,51,1,0,0,0,795,796,3,50,25,0,796,797,5,33,0, + 0,797,798,5,3,0,0,798,799,3,164,82,0,799,801,5,139,0,0,800,802,5,29,0, + 0,801,800,1,0,0,0,801,802,1,0,0,0,802,803,1,0,0,0,803,804,3,166,83,0,804, + 805,5,4,0,0,805,53,1,0,0,0,806,818,3,184,92,0,807,808,5,3,0,0,808,813, + 3,188,94,0,809,810,5,5,0,0,810,812,3,188,94,0,811,809,1,0,0,0,812,815, + 1,0,0,0,813,811,1,0,0,0,813,814,1,0,0,0,814,816,1,0,0,0,815,813,1,0,0, + 0,816,817,5,4,0,0,817,819,1,0,0,0,818,807,1,0,0,0,818,819,1,0,0,0,819, + 820,1,0,0,0,820,821,5,33,0,0,821,822,5,3,0,0,822,823,3,86,43,0,823,824, + 5,4,0,0,824,55,1,0,0,0,825,827,3,48,24,0,826,825,1,0,0,0,826,827,1,0,0, + 0,827,828,1,0,0,0,828,829,5,59,0,0,829,830,5,75,0,0,830,833,3,114,57,0, + 831,832,5,148,0,0,832,834,3,64,32,0,833,831,1,0,0,0,833,834,1,0,0,0,834, + 836,1,0,0,0,835,837,3,76,38,0,836,835,1,0,0,0,836,837,1,0,0,0,837,57,1, + 0,0,0,838,840,3,48,24,0,839,838,1,0,0,0,839,840,1,0,0,0,840,841,1,0,0, + 0,841,842,5,59,0,0,842,843,5,75,0,0,843,846,3,114,57,0,844,845,5,148,0, + 0,845,847,3,64,32,0,846,844,1,0,0,0,846,847,1,0,0,0,847,849,1,0,0,0,848, + 850,3,76,38,0,849,848,1,0,0,0,849,850,1,0,0,0,850,855,1,0,0,0,851,853, + 3,136,68,0,852,851,1,0,0,0,852,853,1,0,0,0,853,854,1,0,0,0,854,856,3,138, + 69,0,855,852,1,0,0,0,855,856,1,0,0,0,856,59,1,0,0,0,857,859,5,61,0,0,858, + 860,5,55,0,0,859,858,1,0,0,0,859,860,1,0,0,0,860,861,1,0,0,0,861,862,3, + 182,91,0,862,61,1,0,0,0,863,864,5,63,0,0,864,867,7,9,0,0,865,866,5,80, + 0,0,866,868,5,70,0,0,867,865,1,0,0,0,867,868,1,0,0,0,868,872,1,0,0,0,869, + 870,3,182,91,0,870,871,5,2,0,0,871,873,1,0,0,0,872,869,1,0,0,0,872,873, + 1,0,0,0,873,874,1,0,0,0,874,875,3,224,112,0,875,63,1,0,0,0,876,877,6,32, + -1,0,877,965,3,68,34,0,878,965,5,187,0,0,879,880,3,182,91,0,880,881,5, + 2,0,0,881,883,1,0,0,0,882,879,1,0,0,0,882,883,1,0,0,0,883,884,1,0,0,0, + 884,885,3,184,92,0,885,886,5,2,0,0,886,888,1,0,0,0,887,882,1,0,0,0,887, + 888,1,0,0,0,888,889,1,0,0,0,889,965,3,188,94,0,890,891,3,168,84,0,891, + 892,3,64,32,21,892,965,1,0,0,0,893,894,3,180,90,0,894,907,5,3,0,0,895, + 897,5,62,0,0,896,895,1,0,0,0,896,897,1,0,0,0,897,898,1,0,0,0,898,903,3, + 64,32,0,899,900,5,5,0,0,900,902,3,64,32,0,901,899,1,0,0,0,902,905,1,0, + 0,0,903,901,1,0,0,0,903,904,1,0,0,0,904,908,1,0,0,0,905,903,1,0,0,0,906, + 908,5,7,0,0,907,896,1,0,0,0,907,906,1,0,0,0,907,908,1,0,0,0,908,909,1, + 0,0,0,909,911,5,4,0,0,910,912,3,118,59,0,911,910,1,0,0,0,911,912,1,0,0, + 0,912,914,1,0,0,0,913,915,3,122,61,0,914,913,1,0,0,0,914,915,1,0,0,0,915, + 965,1,0,0,0,916,917,5,3,0,0,917,922,3,64,32,0,918,919,5,5,0,0,919,921, + 3,64,32,0,920,918,1,0,0,0,921,924,1,0,0,0,922,920,1,0,0,0,922,923,1,0, + 0,0,923,925,1,0,0,0,924,922,1,0,0,0,925,926,5,4,0,0,926,965,1,0,0,0,927, + 928,5,43,0,0,928,929,5,3,0,0,929,930,3,64,32,0,930,931,5,33,0,0,931,932, + 3,30,15,0,932,933,5,4,0,0,933,965,1,0,0,0,934,936,5,102,0,0,935,934,1, + 0,0,0,935,936,1,0,0,0,936,937,1,0,0,0,937,939,5,70,0,0,938,935,1,0,0,0, + 938,939,1,0,0,0,939,940,1,0,0,0,940,941,5,3,0,0,941,942,3,86,43,0,942, + 943,5,4,0,0,943,965,1,0,0,0,944,946,5,42,0,0,945,947,3,64,32,0,946,945, + 1,0,0,0,946,947,1,0,0,0,947,953,1,0,0,0,948,949,5,147,0,0,949,950,3,64, + 32,0,950,951,5,135,0,0,951,952,3,64,32,0,952,954,1,0,0,0,953,948,1,0,0, + 0,954,955,1,0,0,0,955,953,1,0,0,0,955,956,1,0,0,0,956,959,1,0,0,0,957, + 958,5,65,0,0,958,960,3,64,32,0,959,957,1,0,0,0,959,960,1,0,0,0,960,961, + 1,0,0,0,961,962,5,66,0,0,962,965,1,0,0,0,963,965,3,66,33,0,964,876,1,0, + 0,0,964,878,1,0,0,0,964,887,1,0,0,0,964,890,1,0,0,0,964,893,1,0,0,0,964, + 916,1,0,0,0,964,927,1,0,0,0,964,938,1,0,0,0,964,944,1,0,0,0,964,963,1, + 0,0,0,965,1091,1,0,0,0,966,967,10,20,0,0,967,968,5,11,0,0,968,1090,3,64, + 32,21,969,970,10,19,0,0,970,971,7,10,0,0,971,1090,3,64,32,20,972,973,10, + 18,0,0,973,974,7,4,0,0,974,1090,3,64,32,19,975,976,10,17,0,0,976,977,7, + 11,0,0,977,1090,3,64,32,18,978,979,10,16,0,0,979,980,7,12,0,0,980,1090, + 3,64,32,17,981,1000,10,15,0,0,982,1001,5,6,0,0,983,1001,5,22,0,0,984,1001, + 5,23,0,0,985,1001,5,24,0,0,986,1001,5,92,0,0,987,988,5,92,0,0,988,1001, + 5,102,0,0,989,991,5,92,0,0,990,992,5,102,0,0,991,990,1,0,0,0,991,992,1, + 0,0,0,992,993,1,0,0,0,993,994,5,62,0,0,994,1001,5,75,0,0,995,1001,5,83, + 0,0,996,1001,5,97,0,0,997,1001,5,77,0,0,998,1001,5,99,0,0,999,1001,5,118, + 0,0,1000,982,1,0,0,0,1000,983,1,0,0,0,1000,984,1,0,0,0,1000,985,1,0,0, + 0,1000,986,1,0,0,0,1000,987,1,0,0,0,1000,989,1,0,0,0,1000,995,1,0,0,0, + 1000,996,1,0,0,0,1000,997,1,0,0,0,1000,998,1,0,0,0,1000,999,1,0,0,0,1001, + 1002,1,0,0,0,1002,1090,3,64,32,16,1003,1004,10,14,0,0,1004,1005,5,32,0, + 0,1005,1090,3,64,32,15,1006,1007,10,13,0,0,1007,1008,5,108,0,0,1008,1090, + 3,64,32,14,1009,1010,10,6,0,0,1010,1012,5,92,0,0,1011,1013,5,102,0,0,1012, + 1011,1,0,0,0,1012,1013,1,0,0,0,1013,1014,1,0,0,0,1014,1090,3,64,32,7,1015, + 1017,10,5,0,0,1016,1018,5,102,0,0,1017,1016,1,0,0,0,1017,1018,1,0,0,0, + 1018,1019,1,0,0,0,1019,1020,5,39,0,0,1020,1021,3,64,32,0,1021,1022,5,32, + 0,0,1022,1023,3,64,32,6,1023,1090,1,0,0,0,1024,1025,10,9,0,0,1025,1026, + 5,45,0,0,1026,1090,3,190,95,0,1027,1029,10,8,0,0,1028,1030,5,102,0,0,1029, + 1028,1,0,0,0,1029,1030,1,0,0,0,1030,1031,1,0,0,0,1031,1032,7,13,0,0,1032, + 1035,3,64,32,0,1033,1034,5,67,0,0,1034,1036,3,64,32,0,1035,1033,1,0,0, + 0,1035,1036,1,0,0,0,1036,1090,1,0,0,0,1037,1042,10,7,0,0,1038,1043,5,93, + 0,0,1039,1043,5,103,0,0,1040,1041,5,102,0,0,1041,1043,5,104,0,0,1042,1038, + 1,0,0,0,1042,1039,1,0,0,0,1042,1040,1,0,0,0,1043,1090,1,0,0,0,1044,1046, + 10,4,0,0,1045,1047,5,102,0,0,1046,1045,1,0,0,0,1046,1047,1,0,0,0,1047, + 1048,1,0,0,0,1048,1087,5,83,0,0,1049,1059,5,3,0,0,1050,1060,3,86,43,0, + 1051,1056,3,64,32,0,1052,1053,5,5,0,0,1053,1055,3,64,32,0,1054,1052,1, + 0,0,0,1055,1058,1,0,0,0,1056,1054,1,0,0,0,1056,1057,1,0,0,0,1057,1060, + 1,0,0,0,1058,1056,1,0,0,0,1059,1050,1,0,0,0,1059,1051,1,0,0,0,1059,1060, + 1,0,0,0,1060,1061,1,0,0,0,1061,1088,5,4,0,0,1062,1063,3,182,91,0,1063, + 1064,5,2,0,0,1064,1066,1,0,0,0,1065,1062,1,0,0,0,1065,1066,1,0,0,0,1066, + 1067,1,0,0,0,1067,1088,3,184,92,0,1068,1069,3,182,91,0,1069,1070,5,2,0, + 0,1070,1072,1,0,0,0,1071,1068,1,0,0,0,1071,1072,1,0,0,0,1072,1073,1,0, + 0,0,1073,1074,3,222,111,0,1074,1083,5,3,0,0,1075,1080,3,64,32,0,1076,1077, + 5,5,0,0,1077,1079,3,64,32,0,1078,1076,1,0,0,0,1079,1082,1,0,0,0,1080,1078, + 1,0,0,0,1080,1081,1,0,0,0,1081,1084,1,0,0,0,1082,1080,1,0,0,0,1083,1075, + 1,0,0,0,1083,1084,1,0,0,0,1084,1085,1,0,0,0,1085,1086,5,4,0,0,1086,1088, + 1,0,0,0,1087,1049,1,0,0,0,1087,1065,1,0,0,0,1087,1071,1,0,0,0,1088,1090, + 1,0,0,0,1089,966,1,0,0,0,1089,969,1,0,0,0,1089,972,1,0,0,0,1089,975,1, + 0,0,0,1089,978,1,0,0,0,1089,981,1,0,0,0,1089,1003,1,0,0,0,1089,1006,1, + 0,0,0,1089,1009,1,0,0,0,1089,1015,1,0,0,0,1089,1024,1,0,0,0,1089,1027, + 1,0,0,0,1089,1037,1,0,0,0,1089,1044,1,0,0,0,1090,1093,1,0,0,0,1091,1089, + 1,0,0,0,1091,1092,1,0,0,0,1092,65,1,0,0,0,1093,1091,1,0,0,0,1094,1095, + 5,115,0,0,1095,1100,5,3,0,0,1096,1101,5,81,0,0,1097,1098,7,14,0,0,1098, + 1099,5,5,0,0,1099,1101,3,170,85,0,1100,1096,1,0,0,0,1100,1097,1,0,0,0, + 1101,1102,1,0,0,0,1102,1103,5,4,0,0,1103,67,1,0,0,0,1104,1105,7,15,0,0, + 1105,69,1,0,0,0,1106,1107,5,3,0,0,1107,1112,3,64,32,0,1108,1109,5,5,0, + 0,1109,1111,3,64,32,0,1110,1108,1,0,0,0,1111,1114,1,0,0,0,1112,1110,1, + 0,0,0,1112,1113,1,0,0,0,1113,1115,1,0,0,0,1114,1112,1,0,0,0,1115,1116, + 5,4,0,0,1116,71,1,0,0,0,1117,1118,5,144,0,0,1118,1123,3,70,35,0,1119,1120, + 5,5,0,0,1120,1122,3,70,35,0,1121,1119,1,0,0,0,1122,1125,1,0,0,0,1123,1121, + 1,0,0,0,1123,1124,1,0,0,0,1124,73,1,0,0,0,1125,1123,1,0,0,0,1126,1128, + 3,48,24,0,1127,1126,1,0,0,0,1127,1128,1,0,0,0,1128,1134,1,0,0,0,1129,1135, + 5,88,0,0,1130,1135,5,122,0,0,1131,1132,5,88,0,0,1132,1133,5,108,0,0,1133, + 1135,7,8,0,0,1134,1129,1,0,0,0,1134,1130,1,0,0,0,1134,1131,1,0,0,0,1135, + 1136,1,0,0,0,1136,1140,5,91,0,0,1137,1138,3,182,91,0,1138,1139,5,2,0,0, + 1139,1141,1,0,0,0,1140,1137,1,0,0,0,1140,1141,1,0,0,0,1141,1142,1,0,0, + 0,1142,1145,3,184,92,0,1143,1144,5,33,0,0,1144,1146,3,206,103,0,1145,1143, + 1,0,0,0,1145,1146,1,0,0,0,1146,1158,1,0,0,0,1147,1148,5,3,0,0,1148,1153, + 3,188,94,0,1149,1150,5,5,0,0,1150,1152,3,188,94,0,1151,1149,1,0,0,0,1152, + 1155,1,0,0,0,1153,1151,1,0,0,0,1153,1154,1,0,0,0,1154,1156,1,0,0,0,1155, + 1153,1,0,0,0,1156,1157,5,4,0,0,1157,1159,1,0,0,0,1158,1147,1,0,0,0,1158, + 1159,1,0,0,0,1159,1169,1,0,0,0,1160,1163,3,72,36,0,1161,1163,3,86,43,0, + 1162,1160,1,0,0,0,1162,1161,1,0,0,0,1163,1165,1,0,0,0,1164,1166,3,78,39, + 0,1165,1164,1,0,0,0,1165,1166,1,0,0,0,1166,1170,1,0,0,0,1167,1168,5,56, + 0,0,1168,1170,5,144,0,0,1169,1162,1,0,0,0,1169,1167,1,0,0,0,1170,1172, + 1,0,0,0,1171,1173,3,76,38,0,1172,1171,1,0,0,0,1172,1173,1,0,0,0,1173,75, + 1,0,0,0,1174,1175,5,124,0,0,1175,1180,3,100,50,0,1176,1177,5,5,0,0,1177, + 1179,3,100,50,0,1178,1176,1,0,0,0,1179,1182,1,0,0,0,1180,1178,1,0,0,0, + 1180,1181,1,0,0,0,1181,77,1,0,0,0,1182,1180,1,0,0,0,1183,1184,5,107,0, + 0,1184,1199,5,48,0,0,1185,1186,5,3,0,0,1186,1191,3,24,12,0,1187,1188,5, + 5,0,0,1188,1190,3,24,12,0,1189,1187,1,0,0,0,1190,1193,1,0,0,0,1191,1189, + 1,0,0,0,1191,1192,1,0,0,0,1192,1194,1,0,0,0,1193,1191,1,0,0,0,1194,1197, + 5,4,0,0,1195,1196,5,148,0,0,1196,1198,3,64,32,0,1197,1195,1,0,0,0,1197, + 1198,1,0,0,0,1198,1200,1,0,0,0,1199,1185,1,0,0,0,1199,1200,1,0,0,0,1200, + 1201,1,0,0,0,1201,1228,5,183,0,0,1202,1229,5,184,0,0,1203,1204,5,141,0, + 0,1204,1207,5,131,0,0,1205,1208,3,188,94,0,1206,1208,3,110,55,0,1207,1205, + 1,0,0,0,1207,1206,1,0,0,0,1208,1209,1,0,0,0,1209,1210,5,6,0,0,1210,1221, + 3,64,32,0,1211,1214,5,5,0,0,1212,1215,3,188,94,0,1213,1215,3,110,55,0, + 1214,1212,1,0,0,0,1214,1213,1,0,0,0,1215,1216,1,0,0,0,1216,1217,5,6,0, + 0,1217,1218,3,64,32,0,1218,1220,1,0,0,0,1219,1211,1,0,0,0,1220,1223,1, + 0,0,0,1221,1219,1,0,0,0,1221,1222,1,0,0,0,1222,1226,1,0,0,0,1223,1221, + 1,0,0,0,1224,1225,5,148,0,0,1225,1227,3,64,32,0,1226,1224,1,0,0,0,1226, + 1227,1,0,0,0,1227,1229,1,0,0,0,1228,1202,1,0,0,0,1228,1203,1,0,0,0,1229, + 79,1,0,0,0,1230,1234,5,112,0,0,1231,1232,3,182,91,0,1232,1233,5,2,0,0, + 1233,1235,1,0,0,0,1234,1231,1,0,0,0,1234,1235,1,0,0,0,1235,1236,1,0,0, + 0,1236,1243,3,202,101,0,1237,1238,5,6,0,0,1238,1244,3,82,41,0,1239,1240, + 5,3,0,0,1240,1241,3,82,41,0,1241,1242,5,4,0,0,1242,1244,1,0,0,0,1243,1237, + 1,0,0,0,1243,1239,1,0,0,0,1243,1244,1,0,0,0,1244,81,1,0,0,0,1245,1249, + 3,34,17,0,1246,1249,3,178,89,0,1247,1249,5,188,0,0,1248,1245,1,0,0,0,1248, + 1246,1,0,0,0,1248,1247,1,0,0,0,1249,83,1,0,0,0,1250,1261,5,119,0,0,1251, + 1262,3,190,95,0,1252,1253,3,182,91,0,1253,1254,5,2,0,0,1254,1256,1,0,0, + 0,1255,1252,1,0,0,0,1255,1256,1,0,0,0,1256,1259,1,0,0,0,1257,1260,3,184, + 92,0,1258,1260,3,194,97,0,1259,1257,1,0,0,0,1259,1258,1,0,0,0,1260,1262, + 1,0,0,0,1261,1251,1,0,0,0,1261,1255,1,0,0,0,1261,1262,1,0,0,0,1262,85, + 1,0,0,0,1263,1265,3,134,67,0,1264,1263,1,0,0,0,1264,1265,1,0,0,0,1265, + 1266,1,0,0,0,1266,1272,3,90,45,0,1267,1268,3,106,53,0,1268,1269,3,90,45, + 0,1269,1271,1,0,0,0,1270,1267,1,0,0,0,1271,1274,1,0,0,0,1272,1270,1,0, + 0,0,1272,1273,1,0,0,0,1273,1276,1,0,0,0,1274,1272,1,0,0,0,1275,1277,3, + 136,68,0,1276,1275,1,0,0,0,1276,1277,1,0,0,0,1277,1279,1,0,0,0,1278,1280, + 3,138,69,0,1279,1278,1,0,0,0,1279,1280,1,0,0,0,1280,87,1,0,0,0,1281,1289, + 3,98,49,0,1282,1283,3,102,51,0,1283,1285,3,98,49,0,1284,1286,3,104,52, + 0,1285,1284,1,0,0,0,1285,1286,1,0,0,0,1286,1288,1,0,0,0,1287,1282,1,0, + 0,0,1288,1291,1,0,0,0,1289,1287,1,0,0,0,1289,1290,1,0,0,0,1290,89,1,0, + 0,0,1291,1289,1,0,0,0,1292,1294,5,130,0,0,1293,1295,7,16,0,0,1294,1293, + 1,0,0,0,1294,1295,1,0,0,0,1295,1296,1,0,0,0,1296,1301,3,100,50,0,1297, + 1298,5,5,0,0,1298,1300,3,100,50,0,1299,1297,1,0,0,0,1300,1303,1,0,0,0, + 1301,1299,1,0,0,0,1301,1302,1,0,0,0,1302,1316,1,0,0,0,1303,1301,1,0,0, + 0,1304,1314,5,75,0,0,1305,1310,3,98,49,0,1306,1307,5,5,0,0,1307,1309,3, + 98,49,0,1308,1306,1,0,0,0,1309,1312,1,0,0,0,1310,1308,1,0,0,0,1310,1311, + 1,0,0,0,1311,1315,1,0,0,0,1312,1310,1,0,0,0,1313,1315,3,88,44,0,1314,1305, + 1,0,0,0,1314,1313,1,0,0,0,1315,1317,1,0,0,0,1316,1304,1,0,0,0,1316,1317, + 1,0,0,0,1317,1320,1,0,0,0,1318,1319,5,148,0,0,1319,1321,3,64,32,0,1320, + 1318,1,0,0,0,1320,1321,1,0,0,0,1321,1336,1,0,0,0,1322,1323,5,78,0,0,1323, + 1324,5,40,0,0,1324,1329,3,64,32,0,1325,1326,5,5,0,0,1326,1328,3,64,32, + 0,1327,1325,1,0,0,0,1328,1331,1,0,0,0,1329,1327,1,0,0,0,1329,1330,1,0, + 0,0,1330,1334,1,0,0,0,1331,1329,1,0,0,0,1332,1333,5,79,0,0,1333,1335,3, + 64,32,0,1334,1332,1,0,0,0,1334,1335,1,0,0,0,1335,1337,1,0,0,0,1336,1322, + 1,0,0,0,1336,1337,1,0,0,0,1337,1352,1,0,0,0,1338,1339,5,174,0,0,1339,1340, + 3,210,105,0,1340,1341,5,33,0,0,1341,1349,3,120,60,0,1342,1343,5,5,0,0, + 1343,1344,3,210,105,0,1344,1345,5,33,0,0,1345,1346,3,120,60,0,1346,1348, + 1,0,0,0,1347,1342,1,0,0,0,1348,1351,1,0,0,0,1349,1347,1,0,0,0,1349,1350, + 1,0,0,0,1350,1353,1,0,0,0,1351,1349,1,0,0,0,1352,1338,1,0,0,0,1352,1353, + 1,0,0,0,1353,1356,1,0,0,0,1354,1356,3,72,36,0,1355,1292,1,0,0,0,1355,1354, + 1,0,0,0,1356,91,1,0,0,0,1357,1358,3,86,43,0,1358,93,1,0,0,0,1359,1361, + 3,134,67,0,1360,1359,1,0,0,0,1360,1361,1,0,0,0,1361,1362,1,0,0,0,1362, + 1364,3,90,45,0,1363,1365,3,136,68,0,1364,1363,1,0,0,0,1364,1365,1,0,0, + 0,1365,1367,1,0,0,0,1366,1368,3,138,69,0,1367,1366,1,0,0,0,1367,1368,1, + 0,0,0,1368,95,1,0,0,0,1369,1371,3,134,67,0,1370,1369,1,0,0,0,1370,1371, + 1,0,0,0,1371,1372,1,0,0,0,1372,1382,3,90,45,0,1373,1375,5,139,0,0,1374, + 1376,5,29,0,0,1375,1374,1,0,0,0,1375,1376,1,0,0,0,1376,1380,1,0,0,0,1377, + 1380,5,90,0,0,1378,1380,5,68,0,0,1379,1373,1,0,0,0,1379,1377,1,0,0,0,1379, + 1378,1,0,0,0,1380,1381,1,0,0,0,1381,1383,3,90,45,0,1382,1379,1,0,0,0,1383, + 1384,1,0,0,0,1384,1382,1,0,0,0,1384,1385,1,0,0,0,1385,1387,1,0,0,0,1386, + 1388,3,136,68,0,1387,1386,1,0,0,0,1387,1388,1,0,0,0,1388,1390,1,0,0,0, + 1389,1391,3,138,69,0,1390,1389,1,0,0,0,1390,1391,1,0,0,0,1391,97,1,0,0, + 0,1392,1393,3,182,91,0,1393,1394,5,2,0,0,1394,1396,1,0,0,0,1395,1392,1, + 0,0,0,1395,1396,1,0,0,0,1396,1397,1,0,0,0,1397,1402,3,184,92,0,1398,1400, + 5,33,0,0,1399,1398,1,0,0,0,1399,1400,1,0,0,0,1400,1401,1,0,0,0,1401,1403, + 3,206,103,0,1402,1399,1,0,0,0,1402,1403,1,0,0,0,1403,1409,1,0,0,0,1404, + 1405,5,85,0,0,1405,1406,5,40,0,0,1406,1410,3,194,97,0,1407,1408,5,102, + 0,0,1408,1410,5,85,0,0,1409,1404,1,0,0,0,1409,1407,1,0,0,0,1409,1410,1, + 0,0,0,1410,1457,1,0,0,0,1411,1412,3,182,91,0,1412,1413,5,2,0,0,1413,1415, + 1,0,0,0,1414,1411,1,0,0,0,1414,1415,1,0,0,0,1415,1416,1,0,0,0,1416,1417, + 3,222,111,0,1417,1418,5,3,0,0,1418,1423,3,64,32,0,1419,1420,5,5,0,0,1420, + 1422,3,64,32,0,1421,1419,1,0,0,0,1422,1425,1,0,0,0,1423,1421,1,0,0,0,1423, + 1424,1,0,0,0,1424,1426,1,0,0,0,1425,1423,1,0,0,0,1426,1431,5,4,0,0,1427, + 1429,5,33,0,0,1428,1427,1,0,0,0,1428,1429,1,0,0,0,1429,1430,1,0,0,0,1430, + 1432,3,206,103,0,1431,1428,1,0,0,0,1431,1432,1,0,0,0,1432,1457,1,0,0,0, + 1433,1443,5,3,0,0,1434,1439,3,98,49,0,1435,1436,5,5,0,0,1436,1438,3,98, + 49,0,1437,1435,1,0,0,0,1438,1441,1,0,0,0,1439,1437,1,0,0,0,1439,1440,1, + 0,0,0,1440,1444,1,0,0,0,1441,1439,1,0,0,0,1442,1444,3,88,44,0,1443,1434, + 1,0,0,0,1443,1442,1,0,0,0,1444,1445,1,0,0,0,1445,1446,5,4,0,0,1446,1457, + 1,0,0,0,1447,1448,5,3,0,0,1448,1449,3,86,43,0,1449,1454,5,4,0,0,1450,1452, + 5,33,0,0,1451,1450,1,0,0,0,1451,1452,1,0,0,0,1452,1453,1,0,0,0,1453,1455, + 3,206,103,0,1454,1451,1,0,0,0,1454,1455,1,0,0,0,1455,1457,1,0,0,0,1456, + 1395,1,0,0,0,1456,1414,1,0,0,0,1456,1433,1,0,0,0,1456,1447,1,0,0,0,1457, + 99,1,0,0,0,1458,1471,5,7,0,0,1459,1460,3,184,92,0,1460,1461,5,2,0,0,1461, + 1462,5,7,0,0,1462,1471,1,0,0,0,1463,1468,3,64,32,0,1464,1466,5,33,0,0, + 1465,1464,1,0,0,0,1465,1466,1,0,0,0,1466,1467,1,0,0,0,1467,1469,3,174, + 87,0,1468,1465,1,0,0,0,1468,1469,1,0,0,0,1469,1471,1,0,0,0,1470,1458,1, + 0,0,0,1470,1459,1,0,0,0,1470,1463,1,0,0,0,1471,101,1,0,0,0,1472,1486,5, + 5,0,0,1473,1475,5,100,0,0,1474,1473,1,0,0,0,1474,1475,1,0,0,0,1475,1482, + 1,0,0,0,1476,1478,7,17,0,0,1477,1479,5,110,0,0,1478,1477,1,0,0,0,1478, + 1479,1,0,0,0,1479,1483,1,0,0,0,1480,1483,5,87,0,0,1481,1483,5,51,0,0,1482, + 1476,1,0,0,0,1482,1480,1,0,0,0,1482,1481,1,0,0,0,1482,1483,1,0,0,0,1483, + 1484,1,0,0,0,1484,1486,5,94,0,0,1485,1472,1,0,0,0,1485,1474,1,0,0,0,1486, + 103,1,0,0,0,1487,1488,5,107,0,0,1488,1502,3,64,32,0,1489,1490,5,142,0, + 0,1490,1491,5,3,0,0,1491,1496,3,188,94,0,1492,1493,5,5,0,0,1493,1495,3, + 188,94,0,1494,1492,1,0,0,0,1495,1498,1,0,0,0,1496,1494,1,0,0,0,1496,1497, + 1,0,0,0,1497,1499,1,0,0,0,1498,1496,1,0,0,0,1499,1500,5,4,0,0,1500,1502, + 1,0,0,0,1501,1487,1,0,0,0,1501,1489,1,0,0,0,1502,105,1,0,0,0,1503,1505, + 5,139,0,0,1504,1506,5,29,0,0,1505,1504,1,0,0,0,1505,1506,1,0,0,0,1506, + 1510,1,0,0,0,1507,1510,5,90,0,0,1508,1510,5,68,0,0,1509,1503,1,0,0,0,1509, + 1507,1,0,0,0,1509,1508,1,0,0,0,1510,107,1,0,0,0,1511,1513,3,48,24,0,1512, + 1511,1,0,0,0,1512,1513,1,0,0,0,1513,1514,1,0,0,0,1514,1517,5,141,0,0,1515, + 1516,5,108,0,0,1516,1518,7,8,0,0,1517,1515,1,0,0,0,1517,1518,1,0,0,0,1518, + 1519,1,0,0,0,1519,1520,3,114,57,0,1520,1523,5,131,0,0,1521,1524,3,188, + 94,0,1522,1524,3,110,55,0,1523,1521,1,0,0,0,1523,1522,1,0,0,0,1524,1525, + 1,0,0,0,1525,1526,5,6,0,0,1526,1537,3,64,32,0,1527,1530,5,5,0,0,1528,1531, + 3,188,94,0,1529,1531,3,110,55,0,1530,1528,1,0,0,0,1530,1529,1,0,0,0,1531, + 1532,1,0,0,0,1532,1533,5,6,0,0,1533,1534,3,64,32,0,1534,1536,1,0,0,0,1535, + 1527,1,0,0,0,1536,1539,1,0,0,0,1537,1535,1,0,0,0,1537,1538,1,0,0,0,1538, + 1552,1,0,0,0,1539,1537,1,0,0,0,1540,1550,5,75,0,0,1541,1546,3,98,49,0, + 1542,1543,5,5,0,0,1543,1545,3,98,49,0,1544,1542,1,0,0,0,1545,1548,1,0, + 0,0,1546,1544,1,0,0,0,1546,1547,1,0,0,0,1547,1551,1,0,0,0,1548,1546,1, + 0,0,0,1549,1551,3,88,44,0,1550,1541,1,0,0,0,1550,1549,1,0,0,0,1551,1553, + 1,0,0,0,1552,1540,1,0,0,0,1552,1553,1,0,0,0,1553,1556,1,0,0,0,1554,1555, + 5,148,0,0,1555,1557,3,64,32,0,1556,1554,1,0,0,0,1556,1557,1,0,0,0,1557, + 1559,1,0,0,0,1558,1560,3,76,38,0,1559,1558,1,0,0,0,1559,1560,1,0,0,0,1560, + 109,1,0,0,0,1561,1562,5,3,0,0,1562,1567,3,188,94,0,1563,1564,5,5,0,0,1564, + 1566,3,188,94,0,1565,1563,1,0,0,0,1566,1569,1,0,0,0,1567,1565,1,0,0,0, + 1567,1568,1,0,0,0,1568,1570,1,0,0,0,1569,1567,1,0,0,0,1570,1571,5,4,0, + 0,1571,111,1,0,0,0,1572,1574,3,48,24,0,1573,1572,1,0,0,0,1573,1574,1,0, + 0,0,1574,1575,1,0,0,0,1575,1578,5,141,0,0,1576,1577,5,108,0,0,1577,1579, + 7,8,0,0,1578,1576,1,0,0,0,1578,1579,1,0,0,0,1579,1580,1,0,0,0,1580,1581, + 3,114,57,0,1581,1584,5,131,0,0,1582,1585,3,188,94,0,1583,1585,3,110,55, + 0,1584,1582,1,0,0,0,1584,1583,1,0,0,0,1585,1586,1,0,0,0,1586,1587,5,6, + 0,0,1587,1598,3,64,32,0,1588,1591,5,5,0,0,1589,1592,3,188,94,0,1590,1592, + 3,110,55,0,1591,1589,1,0,0,0,1591,1590,1,0,0,0,1592,1593,1,0,0,0,1593, + 1594,5,6,0,0,1594,1595,3,64,32,0,1595,1597,1,0,0,0,1596,1588,1,0,0,0,1597, + 1600,1,0,0,0,1598,1596,1,0,0,0,1598,1599,1,0,0,0,1599,1603,1,0,0,0,1600, + 1598,1,0,0,0,1601,1602,5,148,0,0,1602,1604,3,64,32,0,1603,1601,1,0,0,0, + 1603,1604,1,0,0,0,1604,1606,1,0,0,0,1605,1607,3,76,38,0,1606,1605,1,0, + 0,0,1606,1607,1,0,0,0,1607,1612,1,0,0,0,1608,1610,3,136,68,0,1609,1608, + 1,0,0,0,1609,1610,1,0,0,0,1610,1611,1,0,0,0,1611,1613,3,138,69,0,1612, + 1609,1,0,0,0,1612,1613,1,0,0,0,1613,113,1,0,0,0,1614,1615,3,182,91,0,1615, + 1616,5,2,0,0,1616,1618,1,0,0,0,1617,1614,1,0,0,0,1617,1618,1,0,0,0,1618, + 1619,1,0,0,0,1619,1622,3,184,92,0,1620,1621,5,33,0,0,1621,1623,3,212,106, + 0,1622,1620,1,0,0,0,1622,1623,1,0,0,0,1623,1629,1,0,0,0,1624,1625,5,85, + 0,0,1625,1626,5,40,0,0,1626,1630,3,194,97,0,1627,1628,5,102,0,0,1628,1630, + 5,85,0,0,1629,1624,1,0,0,0,1629,1627,1,0,0,0,1629,1630,1,0,0,0,1630,115, + 1,0,0,0,1631,1633,5,143,0,0,1632,1634,3,182,91,0,1633,1632,1,0,0,0,1633, + 1634,1,0,0,0,1634,1637,1,0,0,0,1635,1636,5,91,0,0,1636,1638,3,214,107, + 0,1637,1635,1,0,0,0,1637,1638,1,0,0,0,1638,117,1,0,0,0,1639,1640,5,178, + 0,0,1640,1641,5,3,0,0,1641,1642,5,148,0,0,1642,1643,3,64,32,0,1643,1644, + 5,4,0,0,1644,119,1,0,0,0,1645,1647,5,3,0,0,1646,1648,3,216,108,0,1647, + 1646,1,0,0,0,1647,1648,1,0,0,0,1648,1659,1,0,0,0,1649,1650,5,153,0,0,1650, + 1651,5,40,0,0,1651,1656,3,64,32,0,1652,1653,5,5,0,0,1653,1655,3,64,32, + 0,1654,1652,1,0,0,0,1655,1658,1,0,0,0,1656,1654,1,0,0,0,1656,1657,1,0, + 0,0,1657,1660,1,0,0,0,1658,1656,1,0,0,0,1659,1649,1,0,0,0,1659,1660,1, + 0,0,0,1660,1661,1,0,0,0,1661,1662,5,109,0,0,1662,1663,5,40,0,0,1663,1668, + 3,140,70,0,1664,1665,5,5,0,0,1665,1667,3,140,70,0,1666,1664,1,0,0,0,1667, + 1670,1,0,0,0,1668,1666,1,0,0,0,1668,1669,1,0,0,0,1669,1672,1,0,0,0,1670, + 1668,1,0,0,0,1671,1673,3,124,62,0,1672,1671,1,0,0,0,1672,1673,1,0,0,0, + 1673,1674,1,0,0,0,1674,1675,5,4,0,0,1675,121,1,0,0,0,1676,1710,5,152,0, + 0,1677,1711,3,210,105,0,1678,1680,5,3,0,0,1679,1681,3,216,108,0,1680,1679, + 1,0,0,0,1680,1681,1,0,0,0,1681,1692,1,0,0,0,1682,1683,5,153,0,0,1683,1684, + 5,40,0,0,1684,1689,3,64,32,0,1685,1686,5,5,0,0,1686,1688,3,64,32,0,1687, + 1685,1,0,0,0,1688,1691,1,0,0,0,1689,1687,1,0,0,0,1689,1690,1,0,0,0,1690, + 1693,1,0,0,0,1691,1689,1,0,0,0,1692,1682,1,0,0,0,1692,1693,1,0,0,0,1693, + 1704,1,0,0,0,1694,1695,5,109,0,0,1695,1696,5,40,0,0,1696,1701,3,140,70, + 0,1697,1698,5,5,0,0,1698,1700,3,140,70,0,1699,1697,1,0,0,0,1700,1703,1, + 0,0,0,1701,1699,1,0,0,0,1701,1702,1,0,0,0,1702,1705,1,0,0,0,1703,1701, + 1,0,0,0,1704,1694,1,0,0,0,1704,1705,1,0,0,0,1705,1707,1,0,0,0,1706,1708, + 3,124,62,0,1707,1706,1,0,0,0,1707,1708,1,0,0,0,1708,1709,1,0,0,0,1709, + 1711,5,4,0,0,1710,1677,1,0,0,0,1710,1678,1,0,0,0,1711,123,1,0,0,0,1712, + 1722,3,126,63,0,1713,1720,5,180,0,0,1714,1715,5,101,0,0,1715,1721,5,182, + 0,0,1716,1717,5,157,0,0,1717,1721,5,127,0,0,1718,1721,5,78,0,0,1719,1721, + 5,181,0,0,1720,1714,1,0,0,0,1720,1716,1,0,0,0,1720,1718,1,0,0,0,1720,1719, + 1,0,0,0,1721,1723,1,0,0,0,1722,1713,1,0,0,0,1722,1723,1,0,0,0,1723,125, + 1,0,0,0,1724,1731,7,18,0,0,1725,1732,3,148,74,0,1726,1727,5,39,0,0,1727, + 1728,3,144,72,0,1728,1729,5,32,0,0,1729,1730,3,146,73,0,1730,1732,1,0, + 0,0,1731,1725,1,0,0,0,1731,1726,1,0,0,0,1732,127,1,0,0,0,1733,1734,3,218, + 109,0,1734,1744,5,3,0,0,1735,1740,3,64,32,0,1736,1737,5,5,0,0,1737,1739, + 3,64,32,0,1738,1736,1,0,0,0,1739,1742,1,0,0,0,1740,1738,1,0,0,0,1740,1741, + 1,0,0,0,1741,1745,1,0,0,0,1742,1740,1,0,0,0,1743,1745,5,7,0,0,1744,1735, + 1,0,0,0,1744,1743,1,0,0,0,1745,1746,1,0,0,0,1746,1747,5,4,0,0,1747,129, + 1,0,0,0,1748,1749,3,220,110,0,1749,1762,5,3,0,0,1750,1752,5,62,0,0,1751, + 1750,1,0,0,0,1751,1752,1,0,0,0,1752,1753,1,0,0,0,1753,1758,3,64,32,0,1754, + 1755,5,5,0,0,1755,1757,3,64,32,0,1756,1754,1,0,0,0,1757,1760,1,0,0,0,1758, + 1756,1,0,0,0,1758,1759,1,0,0,0,1759,1763,1,0,0,0,1760,1758,1,0,0,0,1761, + 1763,5,7,0,0,1762,1751,1,0,0,0,1762,1761,1,0,0,0,1762,1763,1,0,0,0,1763, + 1764,1,0,0,0,1764,1766,5,4,0,0,1765,1767,3,118,59,0,1766,1765,1,0,0,0, + 1766,1767,1,0,0,0,1767,131,1,0,0,0,1768,1769,3,150,75,0,1769,1779,5,3, + 0,0,1770,1775,3,64,32,0,1771,1772,5,5,0,0,1772,1774,3,64,32,0,1773,1771, + 1,0,0,0,1774,1777,1,0,0,0,1775,1773,1,0,0,0,1775,1776,1,0,0,0,1776,1780, + 1,0,0,0,1777,1775,1,0,0,0,1778,1780,5,7,0,0,1779,1770,1,0,0,0,1779,1778, + 1,0,0,0,1779,1780,1,0,0,0,1780,1781,1,0,0,0,1781,1783,5,4,0,0,1782,1784, + 3,118,59,0,1783,1782,1,0,0,0,1783,1784,1,0,0,0,1784,1785,1,0,0,0,1785, + 1788,5,152,0,0,1786,1789,3,120,60,0,1787,1789,3,210,105,0,1788,1786,1, + 0,0,0,1788,1787,1,0,0,0,1789,133,1,0,0,0,1790,1792,5,149,0,0,1791,1793, + 5,116,0,0,1792,1791,1,0,0,0,1792,1793,1,0,0,0,1793,1794,1,0,0,0,1794,1799, + 3,54,27,0,1795,1796,5,5,0,0,1796,1798,3,54,27,0,1797,1795,1,0,0,0,1798, + 1801,1,0,0,0,1799,1797,1,0,0,0,1799,1800,1,0,0,0,1800,135,1,0,0,0,1801, + 1799,1,0,0,0,1802,1803,5,109,0,0,1803,1804,5,40,0,0,1804,1809,3,140,70, + 0,1805,1806,5,5,0,0,1806,1808,3,140,70,0,1807,1805,1,0,0,0,1808,1811,1, + 0,0,0,1809,1807,1,0,0,0,1809,1810,1,0,0,0,1810,137,1,0,0,0,1811,1809,1, + 0,0,0,1812,1813,5,98,0,0,1813,1816,3,64,32,0,1814,1815,7,19,0,0,1815,1817, + 3,64,32,0,1816,1814,1,0,0,0,1816,1817,1,0,0,0,1817,139,1,0,0,0,1818,1821, + 3,64,32,0,1819,1820,5,45,0,0,1820,1822,3,190,95,0,1821,1819,1,0,0,0,1821, + 1822,1,0,0,0,1822,1824,1,0,0,0,1823,1825,3,142,71,0,1824,1823,1,0,0,0, + 1824,1825,1,0,0,0,1825,1828,1,0,0,0,1826,1827,5,175,0,0,1827,1829,7,20, + 0,0,1828,1826,1,0,0,0,1828,1829,1,0,0,0,1829,141,1,0,0,0,1830,1831,7,21, + 0,0,1831,143,1,0,0,0,1832,1833,3,64,32,0,1833,1834,5,155,0,0,1834,1843, + 1,0,0,0,1835,1836,3,64,32,0,1836,1837,5,158,0,0,1837,1843,1,0,0,0,1838, + 1839,5,157,0,0,1839,1843,5,127,0,0,1840,1841,5,156,0,0,1841,1843,5,155, + 0,0,1842,1832,1,0,0,0,1842,1835,1,0,0,0,1842,1838,1,0,0,0,1842,1840,1, + 0,0,0,1843,145,1,0,0,0,1844,1845,3,64,32,0,1845,1846,5,155,0,0,1846,1855, + 1,0,0,0,1847,1848,3,64,32,0,1848,1849,5,158,0,0,1849,1855,1,0,0,0,1850, + 1851,5,157,0,0,1851,1855,5,127,0,0,1852,1853,5,156,0,0,1853,1855,5,158, + 0,0,1854,1844,1,0,0,0,1854,1847,1,0,0,0,1854,1850,1,0,0,0,1854,1852,1, + 0,0,0,1855,147,1,0,0,0,1856,1857,3,64,32,0,1857,1858,5,155,0,0,1858,1864, + 1,0,0,0,1859,1860,5,156,0,0,1860,1864,5,155,0,0,1861,1862,5,157,0,0,1862, + 1864,5,127,0,0,1863,1856,1,0,0,0,1863,1859,1,0,0,0,1863,1861,1,0,0,0,1864, + 149,1,0,0,0,1865,1866,7,22,0,0,1866,1867,5,3,0,0,1867,1868,3,64,32,0,1868, + 1869,5,4,0,0,1869,1870,5,152,0,0,1870,1872,5,3,0,0,1871,1873,3,156,78, + 0,1872,1871,1,0,0,0,1872,1873,1,0,0,0,1873,1874,1,0,0,0,1874,1876,3,160, + 80,0,1875,1877,3,126,63,0,1876,1875,1,0,0,0,1876,1877,1,0,0,0,1877,1878, + 1,0,0,0,1878,1879,5,4,0,0,1879,1951,1,0,0,0,1880,1881,7,23,0,0,1881,1882, + 5,3,0,0,1882,1883,5,4,0,0,1883,1884,5,152,0,0,1884,1886,5,3,0,0,1885,1887, + 3,156,78,0,1886,1885,1,0,0,0,1886,1887,1,0,0,0,1887,1889,1,0,0,0,1888, + 1890,3,158,79,0,1889,1888,1,0,0,0,1889,1890,1,0,0,0,1890,1891,1,0,0,0, + 1891,1951,5,4,0,0,1892,1893,7,24,0,0,1893,1894,5,3,0,0,1894,1895,5,4,0, + 0,1895,1896,5,152,0,0,1896,1898,5,3,0,0,1897,1899,3,156,78,0,1898,1897, + 1,0,0,0,1898,1899,1,0,0,0,1899,1900,1,0,0,0,1900,1901,3,160,80,0,1901, + 1902,5,4,0,0,1902,1951,1,0,0,0,1903,1904,7,25,0,0,1904,1905,5,3,0,0,1905, + 1907,3,64,32,0,1906,1908,3,152,76,0,1907,1906,1,0,0,0,1907,1908,1,0,0, + 0,1908,1910,1,0,0,0,1909,1911,3,154,77,0,1910,1909,1,0,0,0,1910,1911,1, + 0,0,0,1911,1912,1,0,0,0,1912,1913,5,4,0,0,1913,1914,5,152,0,0,1914,1916, + 5,3,0,0,1915,1917,3,156,78,0,1916,1915,1,0,0,0,1916,1917,1,0,0,0,1917, + 1918,1,0,0,0,1918,1919,3,160,80,0,1919,1920,5,4,0,0,1920,1951,1,0,0,0, + 1921,1922,5,164,0,0,1922,1923,5,3,0,0,1923,1924,3,64,32,0,1924,1925,5, + 5,0,0,1925,1926,3,34,17,0,1926,1927,5,4,0,0,1927,1928,5,152,0,0,1928,1930, + 5,3,0,0,1929,1931,3,156,78,0,1930,1929,1,0,0,0,1930,1931,1,0,0,0,1931, + 1932,1,0,0,0,1932,1934,3,160,80,0,1933,1935,3,126,63,0,1934,1933,1,0,0, + 0,1934,1935,1,0,0,0,1935,1936,1,0,0,0,1936,1937,5,4,0,0,1937,1951,1,0, + 0,0,1938,1939,5,165,0,0,1939,1940,5,3,0,0,1940,1941,3,64,32,0,1941,1942, + 5,4,0,0,1942,1943,5,152,0,0,1943,1945,5,3,0,0,1944,1946,3,156,78,0,1945, + 1944,1,0,0,0,1945,1946,1,0,0,0,1946,1947,1,0,0,0,1947,1948,3,160,80,0, + 1948,1949,5,4,0,0,1949,1951,1,0,0,0,1950,1865,1,0,0,0,1950,1880,1,0,0, + 0,1950,1892,1,0,0,0,1950,1903,1,0,0,0,1950,1921,1,0,0,0,1950,1938,1,0, + 0,0,1951,151,1,0,0,0,1952,1953,5,5,0,0,1953,1954,3,34,17,0,1954,153,1, + 0,0,0,1955,1956,5,5,0,0,1956,1957,3,34,17,0,1957,155,1,0,0,0,1958,1959, + 5,153,0,0,1959,1961,5,40,0,0,1960,1962,3,64,32,0,1961,1960,1,0,0,0,1962, + 1963,1,0,0,0,1963,1961,1,0,0,0,1963,1964,1,0,0,0,1964,157,1,0,0,0,1965, + 1966,5,109,0,0,1966,1968,5,40,0,0,1967,1969,3,64,32,0,1968,1967,1,0,0, + 0,1969,1970,1,0,0,0,1970,1968,1,0,0,0,1970,1971,1,0,0,0,1971,159,1,0,0, + 0,1972,1973,5,109,0,0,1973,1974,5,40,0,0,1974,1975,3,162,81,0,1975,161, + 1,0,0,0,1976,1978,3,64,32,0,1977,1979,3,142,71,0,1978,1977,1,0,0,0,1978, + 1979,1,0,0,0,1979,1987,1,0,0,0,1980,1981,5,5,0,0,1981,1983,3,64,32,0,1982, + 1984,3,142,71,0,1983,1982,1,0,0,0,1983,1984,1,0,0,0,1984,1986,1,0,0,0, + 1985,1980,1,0,0,0,1986,1989,1,0,0,0,1987,1985,1,0,0,0,1987,1988,1,0,0, + 0,1988,163,1,0,0,0,1989,1987,1,0,0,0,1990,1991,3,86,43,0,1991,165,1,0, + 0,0,1992,1993,3,86,43,0,1993,167,1,0,0,0,1994,1995,7,26,0,0,1995,169,1, + 0,0,0,1996,1997,5,188,0,0,1997,171,1,0,0,0,1998,2001,3,64,32,0,1999,2001, + 3,28,14,0,2000,1998,1,0,0,0,2000,1999,1,0,0,0,2001,173,1,0,0,0,2002,2003, + 7,27,0,0,2003,175,1,0,0,0,2004,2005,7,28,0,0,2005,177,1,0,0,0,2006,2007, + 3,224,112,0,2007,179,1,0,0,0,2008,2009,3,224,112,0,2009,181,1,0,0,0,2010, + 2011,3,224,112,0,2011,183,1,0,0,0,2012,2013,3,224,112,0,2013,185,1,0,0, + 0,2014,2015,3,224,112,0,2015,187,1,0,0,0,2016,2017,3,224,112,0,2017,189, + 1,0,0,0,2018,2019,3,224,112,0,2019,191,1,0,0,0,2020,2021,3,224,112,0,2021, + 193,1,0,0,0,2022,2023,3,224,112,0,2023,195,1,0,0,0,2024,2025,3,224,112, + 0,2025,197,1,0,0,0,2026,2027,3,224,112,0,2027,199,1,0,0,0,2028,2029,3, + 224,112,0,2029,201,1,0,0,0,2030,2031,3,224,112,0,2031,203,1,0,0,0,2032, + 2033,3,224,112,0,2033,205,1,0,0,0,2034,2035,3,224,112,0,2035,207,1,0,0, + 0,2036,2037,3,224,112,0,2037,209,1,0,0,0,2038,2039,3,224,112,0,2039,211, + 1,0,0,0,2040,2041,3,224,112,0,2041,213,1,0,0,0,2042,2043,3,224,112,0,2043, + 215,1,0,0,0,2044,2045,3,224,112,0,2045,217,1,0,0,0,2046,2047,3,224,112, + 0,2047,219,1,0,0,0,2048,2049,3,224,112,0,2049,221,1,0,0,0,2050,2051,3, + 224,112,0,2051,223,1,0,0,0,2052,2060,5,185,0,0,2053,2060,3,176,88,0,2054, + 2060,5,188,0,0,2055,2056,5,3,0,0,2056,2057,3,224,112,0,2057,2058,5,4,0, + 0,2058,2060,1,0,0,0,2059,2052,1,0,0,0,2059,2053,1,0,0,0,2059,2054,1,0, + 0,0,2059,2055,1,0,0,0,2060,225,1,0,0,0,297,229,237,244,249,255,261,263, + 289,296,303,309,313,318,321,328,331,335,343,347,349,353,357,361,364,371, + 377,383,388,399,405,409,413,416,420,426,431,440,447,453,457,461,466,472, + 484,488,493,496,499,502,506,509,523,530,537,539,542,548,553,561,566,581, + 587,597,602,612,616,618,622,627,629,637,643,648,655,666,669,671,678,682, + 689,695,701,707,712,721,726,737,742,753,758,762,778,788,793,801,813,818, + 826,833,836,839,846,849,852,855,859,867,872,882,887,896,903,907,911,914, + 922,935,938,946,955,959,964,991,1000,1012,1017,1029,1035,1042,1046,1056, + 1059,1065,1071,1080,1083,1087,1089,1091,1100,1112,1123,1127,1134,1140, + 1145,1153,1158,1162,1165,1169,1172,1180,1191,1197,1199,1207,1214,1221, + 1226,1228,1234,1243,1248,1255,1259,1261,1264,1272,1276,1279,1285,1289, + 1294,1301,1310,1314,1316,1320,1329,1334,1336,1349,1352,1355,1360,1364, + 1367,1370,1375,1379,1384,1387,1390,1395,1399,1402,1409,1414,1423,1428, + 1431,1439,1443,1451,1454,1456,1465,1468,1470,1474,1478,1482,1485,1496, + 1501,1505,1509,1512,1517,1523,1530,1537,1546,1550,1552,1556,1559,1567, + 1573,1578,1584,1591,1598,1603,1606,1609,1612,1617,1622,1629,1633,1637, + 1647,1656,1659,1668,1672,1680,1689,1692,1701,1704,1707,1710,1720,1722, + 1731,1740,1744,1751,1758,1762,1766,1775,1779,1783,1788,1792,1799,1809, + 1816,1821,1824,1828,1842,1854,1863,1872,1876,1886,1889,1898,1907,1910, + 1916,1930,1934,1945,1950,1963,1970,1978,1983,1987,2000,2059 + }; + + public static readonly ATN _ATN = + new ATNDeserializer().Deserialize(_serializedATN); + + +} +} // namespace DataProvider.SQLite.Parsing diff --git a/DataProvider/DataProvider.SQLite/Parsing/SQLiteParserBaseListener.cs b/DataProvider/DataProvider.SQLite/Parsing/SQLiteParserBaseListener.cs new file mode 100644 index 00000000..222da9ef --- /dev/null +++ b/DataProvider/DataProvider.SQLite/Parsing/SQLiteParserBaseListener.cs @@ -0,0 +1,1409 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// ANTLR Version: 4.13.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// Generated from SQLiteParser.g4 by ANTLR 4.13.1 + +// Unreachable code detected +#pragma warning disable 0162 +// The variable '...' is assigned but its value is never used +#pragma warning disable 0219 +// Missing XML comment for publicly visible type or member '...' +#pragma warning disable 1591 +// Ambiguous reference in cref attribute +#pragma warning disable 419 + +namespace DataProvider.SQLite.Parsing { + +using Antlr4.Runtime.Misc; +using IErrorNode = Antlr4.Runtime.Tree.IErrorNode; +using ITerminalNode = Antlr4.Runtime.Tree.ITerminalNode; +using IToken = Antlr4.Runtime.IToken; +using ParserRuleContext = Antlr4.Runtime.ParserRuleContext; + +/// +/// This class provides an empty implementation of , +/// which can be extended to create a listener which only needs to handle a subset +/// of the available methods. +/// +[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.13.1")] +[System.Diagnostics.DebuggerNonUserCode] +[System.CLSCompliant(false)] +public partial class SQLiteParserBaseListener : ISQLiteParserListener { + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterParse([NotNull] SQLiteParser.ParseContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitParse([NotNull] SQLiteParser.ParseContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSql_stmt_list([NotNull] SQLiteParser.Sql_stmt_listContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSql_stmt_list([NotNull] SQLiteParser.Sql_stmt_listContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSql_stmt([NotNull] SQLiteParser.Sql_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSql_stmt([NotNull] SQLiteParser.Sql_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterAlter_table_stmt([NotNull] SQLiteParser.Alter_table_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitAlter_table_stmt([NotNull] SQLiteParser.Alter_table_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterAnalyze_stmt([NotNull] SQLiteParser.Analyze_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitAnalyze_stmt([NotNull] SQLiteParser.Analyze_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterAttach_stmt([NotNull] SQLiteParser.Attach_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitAttach_stmt([NotNull] SQLiteParser.Attach_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBegin_stmt([NotNull] SQLiteParser.Begin_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBegin_stmt([NotNull] SQLiteParser.Begin_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCommit_stmt([NotNull] SQLiteParser.Commit_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCommit_stmt([NotNull] SQLiteParser.Commit_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterRollback_stmt([NotNull] SQLiteParser.Rollback_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitRollback_stmt([NotNull] SQLiteParser.Rollback_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSavepoint_stmt([NotNull] SQLiteParser.Savepoint_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSavepoint_stmt([NotNull] SQLiteParser.Savepoint_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterRelease_stmt([NotNull] SQLiteParser.Release_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitRelease_stmt([NotNull] SQLiteParser.Release_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreate_index_stmt([NotNull] SQLiteParser.Create_index_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreate_index_stmt([NotNull] SQLiteParser.Create_index_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterIndexed_column([NotNull] SQLiteParser.Indexed_columnContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitIndexed_column([NotNull] SQLiteParser.Indexed_columnContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreate_table_stmt([NotNull] SQLiteParser.Create_table_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreate_table_stmt([NotNull] SQLiteParser.Create_table_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterColumn_def([NotNull] SQLiteParser.Column_defContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitColumn_def([NotNull] SQLiteParser.Column_defContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterType_name([NotNull] SQLiteParser.Type_nameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitType_name([NotNull] SQLiteParser.Type_nameContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterColumn_constraint([NotNull] SQLiteParser.Column_constraintContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitColumn_constraint([NotNull] SQLiteParser.Column_constraintContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSigned_number([NotNull] SQLiteParser.Signed_numberContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSigned_number([NotNull] SQLiteParser.Signed_numberContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterTable_constraint([NotNull] SQLiteParser.Table_constraintContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitTable_constraint([NotNull] SQLiteParser.Table_constraintContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterForeign_key_clause([NotNull] SQLiteParser.Foreign_key_clauseContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitForeign_key_clause([NotNull] SQLiteParser.Foreign_key_clauseContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterConflict_clause([NotNull] SQLiteParser.Conflict_clauseContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitConflict_clause([NotNull] SQLiteParser.Conflict_clauseContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreate_trigger_stmt([NotNull] SQLiteParser.Create_trigger_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreate_trigger_stmt([NotNull] SQLiteParser.Create_trigger_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreate_view_stmt([NotNull] SQLiteParser.Create_view_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreate_view_stmt([NotNull] SQLiteParser.Create_view_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCreate_virtual_table_stmt([NotNull] SQLiteParser.Create_virtual_table_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCreate_virtual_table_stmt([NotNull] SQLiteParser.Create_virtual_table_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterWith_clause([NotNull] SQLiteParser.With_clauseContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitWith_clause([NotNull] SQLiteParser.With_clauseContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCte_table_name([NotNull] SQLiteParser.Cte_table_nameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCte_table_name([NotNull] SQLiteParser.Cte_table_nameContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterRecursive_cte([NotNull] SQLiteParser.Recursive_cteContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitRecursive_cte([NotNull] SQLiteParser.Recursive_cteContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCommon_table_expression([NotNull] SQLiteParser.Common_table_expressionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCommon_table_expression([NotNull] SQLiteParser.Common_table_expressionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterDelete_stmt([NotNull] SQLiteParser.Delete_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitDelete_stmt([NotNull] SQLiteParser.Delete_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterDelete_stmt_limited([NotNull] SQLiteParser.Delete_stmt_limitedContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitDelete_stmt_limited([NotNull] SQLiteParser.Delete_stmt_limitedContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterDetach_stmt([NotNull] SQLiteParser.Detach_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitDetach_stmt([NotNull] SQLiteParser.Detach_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterDrop_stmt([NotNull] SQLiteParser.Drop_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitDrop_stmt([NotNull] SQLiteParser.Drop_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExpr([NotNull] SQLiteParser.ExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExpr([NotNull] SQLiteParser.ExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterRaise_function([NotNull] SQLiteParser.Raise_functionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitRaise_function([NotNull] SQLiteParser.Raise_functionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterLiteral_value([NotNull] SQLiteParser.Literal_valueContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitLiteral_value([NotNull] SQLiteParser.Literal_valueContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterValue_row([NotNull] SQLiteParser.Value_rowContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitValue_row([NotNull] SQLiteParser.Value_rowContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterValues_clause([NotNull] SQLiteParser.Values_clauseContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitValues_clause([NotNull] SQLiteParser.Values_clauseContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterInsert_stmt([NotNull] SQLiteParser.Insert_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitInsert_stmt([NotNull] SQLiteParser.Insert_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterReturning_clause([NotNull] SQLiteParser.Returning_clauseContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitReturning_clause([NotNull] SQLiteParser.Returning_clauseContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterUpsert_clause([NotNull] SQLiteParser.Upsert_clauseContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitUpsert_clause([NotNull] SQLiteParser.Upsert_clauseContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterPragma_stmt([NotNull] SQLiteParser.Pragma_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitPragma_stmt([NotNull] SQLiteParser.Pragma_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterPragma_value([NotNull] SQLiteParser.Pragma_valueContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitPragma_value([NotNull] SQLiteParser.Pragma_valueContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterReindex_stmt([NotNull] SQLiteParser.Reindex_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitReindex_stmt([NotNull] SQLiteParser.Reindex_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSelect_stmt([NotNull] SQLiteParser.Select_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSelect_stmt([NotNull] SQLiteParser.Select_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterJoin_clause([NotNull] SQLiteParser.Join_clauseContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitJoin_clause([NotNull] SQLiteParser.Join_clauseContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSelect_core([NotNull] SQLiteParser.Select_coreContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSelect_core([NotNull] SQLiteParser.Select_coreContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterFactored_select_stmt([NotNull] SQLiteParser.Factored_select_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitFactored_select_stmt([NotNull] SQLiteParser.Factored_select_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSimple_select_stmt([NotNull] SQLiteParser.Simple_select_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSimple_select_stmt([NotNull] SQLiteParser.Simple_select_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCompound_select_stmt([NotNull] SQLiteParser.Compound_select_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCompound_select_stmt([NotNull] SQLiteParser.Compound_select_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterTable_or_subquery([NotNull] SQLiteParser.Table_or_subqueryContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitTable_or_subquery([NotNull] SQLiteParser.Table_or_subqueryContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterResult_column([NotNull] SQLiteParser.Result_columnContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitResult_column([NotNull] SQLiteParser.Result_columnContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterJoin_operator([NotNull] SQLiteParser.Join_operatorContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitJoin_operator([NotNull] SQLiteParser.Join_operatorContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterJoin_constraint([NotNull] SQLiteParser.Join_constraintContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitJoin_constraint([NotNull] SQLiteParser.Join_constraintContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCompound_operator([NotNull] SQLiteParser.Compound_operatorContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCompound_operator([NotNull] SQLiteParser.Compound_operatorContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterUpdate_stmt([NotNull] SQLiteParser.Update_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitUpdate_stmt([NotNull] SQLiteParser.Update_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterColumn_name_list([NotNull] SQLiteParser.Column_name_listContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitColumn_name_list([NotNull] SQLiteParser.Column_name_listContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterUpdate_stmt_limited([NotNull] SQLiteParser.Update_stmt_limitedContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitUpdate_stmt_limited([NotNull] SQLiteParser.Update_stmt_limitedContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterQualified_table_name([NotNull] SQLiteParser.Qualified_table_nameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitQualified_table_name([NotNull] SQLiteParser.Qualified_table_nameContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterVacuum_stmt([NotNull] SQLiteParser.Vacuum_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitVacuum_stmt([NotNull] SQLiteParser.Vacuum_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterFilter_clause([NotNull] SQLiteParser.Filter_clauseContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitFilter_clause([NotNull] SQLiteParser.Filter_clauseContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterWindow_defn([NotNull] SQLiteParser.Window_defnContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitWindow_defn([NotNull] SQLiteParser.Window_defnContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOver_clause([NotNull] SQLiteParser.Over_clauseContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOver_clause([NotNull] SQLiteParser.Over_clauseContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterFrame_spec([NotNull] SQLiteParser.Frame_specContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitFrame_spec([NotNull] SQLiteParser.Frame_specContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterFrame_clause([NotNull] SQLiteParser.Frame_clauseContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitFrame_clause([NotNull] SQLiteParser.Frame_clauseContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSimple_function_invocation([NotNull] SQLiteParser.Simple_function_invocationContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSimple_function_invocation([NotNull] SQLiteParser.Simple_function_invocationContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterAggregate_function_invocation([NotNull] SQLiteParser.Aggregate_function_invocationContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitAggregate_function_invocation([NotNull] SQLiteParser.Aggregate_function_invocationContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterWindow_function_invocation([NotNull] SQLiteParser.Window_function_invocationContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitWindow_function_invocation([NotNull] SQLiteParser.Window_function_invocationContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCommon_table_stmt([NotNull] SQLiteParser.Common_table_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCommon_table_stmt([NotNull] SQLiteParser.Common_table_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOrder_by_stmt([NotNull] SQLiteParser.Order_by_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOrder_by_stmt([NotNull] SQLiteParser.Order_by_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterLimit_stmt([NotNull] SQLiteParser.Limit_stmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitLimit_stmt([NotNull] SQLiteParser.Limit_stmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOrdering_term([NotNull] SQLiteParser.Ordering_termContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOrdering_term([NotNull] SQLiteParser.Ordering_termContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterAsc_desc([NotNull] SQLiteParser.Asc_descContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitAsc_desc([NotNull] SQLiteParser.Asc_descContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterFrame_left([NotNull] SQLiteParser.Frame_leftContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitFrame_left([NotNull] SQLiteParser.Frame_leftContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterFrame_right([NotNull] SQLiteParser.Frame_rightContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitFrame_right([NotNull] SQLiteParser.Frame_rightContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterFrame_single([NotNull] SQLiteParser.Frame_singleContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitFrame_single([NotNull] SQLiteParser.Frame_singleContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterWindow_function([NotNull] SQLiteParser.Window_functionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitWindow_function([NotNull] SQLiteParser.Window_functionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOffset([NotNull] SQLiteParser.OffsetContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOffset([NotNull] SQLiteParser.OffsetContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterDefault_value([NotNull] SQLiteParser.Default_valueContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitDefault_value([NotNull] SQLiteParser.Default_valueContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterPartition_by([NotNull] SQLiteParser.Partition_byContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitPartition_by([NotNull] SQLiteParser.Partition_byContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOrder_by_expr([NotNull] SQLiteParser.Order_by_exprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOrder_by_expr([NotNull] SQLiteParser.Order_by_exprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOrder_by_expr_asc_desc([NotNull] SQLiteParser.Order_by_expr_asc_descContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOrder_by_expr_asc_desc([NotNull] SQLiteParser.Order_by_expr_asc_descContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExpr_asc_desc([NotNull] SQLiteParser.Expr_asc_descContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExpr_asc_desc([NotNull] SQLiteParser.Expr_asc_descContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterInitial_select([NotNull] SQLiteParser.Initial_selectContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitInitial_select([NotNull] SQLiteParser.Initial_selectContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterRecursive_select([NotNull] SQLiteParser.Recursive_selectContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitRecursive_select([NotNull] SQLiteParser.Recursive_selectContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterUnary_operator([NotNull] SQLiteParser.Unary_operatorContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitUnary_operator([NotNull] SQLiteParser.Unary_operatorContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterError_message([NotNull] SQLiteParser.Error_messageContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitError_message([NotNull] SQLiteParser.Error_messageContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterModule_argument([NotNull] SQLiteParser.Module_argumentContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitModule_argument([NotNull] SQLiteParser.Module_argumentContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterColumn_alias([NotNull] SQLiteParser.Column_aliasContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitColumn_alias([NotNull] SQLiteParser.Column_aliasContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterKeyword([NotNull] SQLiteParser.KeywordContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitKeyword([NotNull] SQLiteParser.KeywordContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterName([NotNull] SQLiteParser.NameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitName([NotNull] SQLiteParser.NameContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterFunction_name([NotNull] SQLiteParser.Function_nameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitFunction_name([NotNull] SQLiteParser.Function_nameContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSchema_name([NotNull] SQLiteParser.Schema_nameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSchema_name([NotNull] SQLiteParser.Schema_nameContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterTable_name([NotNull] SQLiteParser.Table_nameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitTable_name([NotNull] SQLiteParser.Table_nameContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterTable_or_index_name([NotNull] SQLiteParser.Table_or_index_nameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitTable_or_index_name([NotNull] SQLiteParser.Table_or_index_nameContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterColumn_name([NotNull] SQLiteParser.Column_nameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitColumn_name([NotNull] SQLiteParser.Column_nameContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCollation_name([NotNull] SQLiteParser.Collation_nameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCollation_name([NotNull] SQLiteParser.Collation_nameContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterForeign_table([NotNull] SQLiteParser.Foreign_tableContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitForeign_table([NotNull] SQLiteParser.Foreign_tableContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterIndex_name([NotNull] SQLiteParser.Index_nameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitIndex_name([NotNull] SQLiteParser.Index_nameContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterTrigger_name([NotNull] SQLiteParser.Trigger_nameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitTrigger_name([NotNull] SQLiteParser.Trigger_nameContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterView_name([NotNull] SQLiteParser.View_nameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitView_name([NotNull] SQLiteParser.View_nameContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterModule_name([NotNull] SQLiteParser.Module_nameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitModule_name([NotNull] SQLiteParser.Module_nameContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterPragma_name([NotNull] SQLiteParser.Pragma_nameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitPragma_name([NotNull] SQLiteParser.Pragma_nameContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSavepoint_name([NotNull] SQLiteParser.Savepoint_nameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSavepoint_name([NotNull] SQLiteParser.Savepoint_nameContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterTable_alias([NotNull] SQLiteParser.Table_aliasContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitTable_alias([NotNull] SQLiteParser.Table_aliasContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterTransaction_name([NotNull] SQLiteParser.Transaction_nameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitTransaction_name([NotNull] SQLiteParser.Transaction_nameContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterWindow_name([NotNull] SQLiteParser.Window_nameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitWindow_name([NotNull] SQLiteParser.Window_nameContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterAlias([NotNull] SQLiteParser.AliasContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitAlias([NotNull] SQLiteParser.AliasContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterFilename([NotNull] SQLiteParser.FilenameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitFilename([NotNull] SQLiteParser.FilenameContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterBase_window_name([NotNull] SQLiteParser.Base_window_nameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitBase_window_name([NotNull] SQLiteParser.Base_window_nameContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterSimple_func([NotNull] SQLiteParser.Simple_funcContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitSimple_func([NotNull] SQLiteParser.Simple_funcContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterAggregate_func([NotNull] SQLiteParser.Aggregate_funcContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitAggregate_func([NotNull] SQLiteParser.Aggregate_funcContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterTable_function_name([NotNull] SQLiteParser.Table_function_nameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitTable_function_name([NotNull] SQLiteParser.Table_function_nameContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterAny_name([NotNull] SQLiteParser.Any_nameContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitAny_name([NotNull] SQLiteParser.Any_nameContext context) { } + + /// + /// The default implementation does nothing. + public virtual void EnterEveryRule([NotNull] ParserRuleContext context) { } + /// + /// The default implementation does nothing. + public virtual void ExitEveryRule([NotNull] ParserRuleContext context) { } + /// + /// The default implementation does nothing. + public virtual void VisitTerminal([NotNull] ITerminalNode node) { } + /// + /// The default implementation does nothing. + public virtual void VisitErrorNode([NotNull] IErrorNode node) { } +} +} // namespace DataProvider.SQLite.Parsing diff --git a/DataProvider/DataProvider.SQLite/Parsing/SQLiteParserBaseVisitor.cs b/DataProvider/DataProvider.SQLite/Parsing/SQLiteParserBaseVisitor.cs new file mode 100644 index 00000000..be1b6add --- /dev/null +++ b/DataProvider/DataProvider.SQLite/Parsing/SQLiteParserBaseVisitor.cs @@ -0,0 +1,1169 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// ANTLR Version: 4.13.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// Generated from SQLiteParser.g4 by ANTLR 4.13.1 + +// Unreachable code detected +#pragma warning disable 0162 +// The variable '...' is assigned but its value is never used +#pragma warning disable 0219 +// Missing XML comment for publicly visible type or member '...' +#pragma warning disable 1591 +// Ambiguous reference in cref attribute +#pragma warning disable 419 + +namespace DataProvider.SQLite.Parsing { +using Antlr4.Runtime.Misc; +using Antlr4.Runtime.Tree; +using IToken = Antlr4.Runtime.IToken; +using ParserRuleContext = Antlr4.Runtime.ParserRuleContext; + +/// +/// This class provides an empty implementation of , +/// which can be extended to create a visitor which only needs to handle a subset +/// of the available methods. +/// +/// The return type of the visit operation. +[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.13.1")] +[System.Diagnostics.DebuggerNonUserCode] +[System.CLSCompliant(false)] +public partial class SQLiteParserBaseVisitor : AbstractParseTreeVisitor, ISQLiteParserVisitor { + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitParse([NotNull] SQLiteParser.ParseContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSql_stmt_list([NotNull] SQLiteParser.Sql_stmt_listContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSql_stmt([NotNull] SQLiteParser.Sql_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitAlter_table_stmt([NotNull] SQLiteParser.Alter_table_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitAnalyze_stmt([NotNull] SQLiteParser.Analyze_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitAttach_stmt([NotNull] SQLiteParser.Attach_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBegin_stmt([NotNull] SQLiteParser.Begin_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCommit_stmt([NotNull] SQLiteParser.Commit_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitRollback_stmt([NotNull] SQLiteParser.Rollback_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSavepoint_stmt([NotNull] SQLiteParser.Savepoint_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitRelease_stmt([NotNull] SQLiteParser.Release_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreate_index_stmt([NotNull] SQLiteParser.Create_index_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitIndexed_column([NotNull] SQLiteParser.Indexed_columnContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreate_table_stmt([NotNull] SQLiteParser.Create_table_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitColumn_def([NotNull] SQLiteParser.Column_defContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitType_name([NotNull] SQLiteParser.Type_nameContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitColumn_constraint([NotNull] SQLiteParser.Column_constraintContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSigned_number([NotNull] SQLiteParser.Signed_numberContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitTable_constraint([NotNull] SQLiteParser.Table_constraintContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitForeign_key_clause([NotNull] SQLiteParser.Foreign_key_clauseContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitConflict_clause([NotNull] SQLiteParser.Conflict_clauseContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreate_trigger_stmt([NotNull] SQLiteParser.Create_trigger_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreate_view_stmt([NotNull] SQLiteParser.Create_view_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCreate_virtual_table_stmt([NotNull] SQLiteParser.Create_virtual_table_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitWith_clause([NotNull] SQLiteParser.With_clauseContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCte_table_name([NotNull] SQLiteParser.Cte_table_nameContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitRecursive_cte([NotNull] SQLiteParser.Recursive_cteContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCommon_table_expression([NotNull] SQLiteParser.Common_table_expressionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitDelete_stmt([NotNull] SQLiteParser.Delete_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitDelete_stmt_limited([NotNull] SQLiteParser.Delete_stmt_limitedContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitDetach_stmt([NotNull] SQLiteParser.Detach_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitDrop_stmt([NotNull] SQLiteParser.Drop_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExpr([NotNull] SQLiteParser.ExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitRaise_function([NotNull] SQLiteParser.Raise_functionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitLiteral_value([NotNull] SQLiteParser.Literal_valueContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitValue_row([NotNull] SQLiteParser.Value_rowContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitValues_clause([NotNull] SQLiteParser.Values_clauseContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitInsert_stmt([NotNull] SQLiteParser.Insert_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitReturning_clause([NotNull] SQLiteParser.Returning_clauseContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitUpsert_clause([NotNull] SQLiteParser.Upsert_clauseContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitPragma_stmt([NotNull] SQLiteParser.Pragma_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitPragma_value([NotNull] SQLiteParser.Pragma_valueContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitReindex_stmt([NotNull] SQLiteParser.Reindex_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSelect_stmt([NotNull] SQLiteParser.Select_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitJoin_clause([NotNull] SQLiteParser.Join_clauseContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSelect_core([NotNull] SQLiteParser.Select_coreContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitFactored_select_stmt([NotNull] SQLiteParser.Factored_select_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSimple_select_stmt([NotNull] SQLiteParser.Simple_select_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCompound_select_stmt([NotNull] SQLiteParser.Compound_select_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitTable_or_subquery([NotNull] SQLiteParser.Table_or_subqueryContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitResult_column([NotNull] SQLiteParser.Result_columnContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitJoin_operator([NotNull] SQLiteParser.Join_operatorContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitJoin_constraint([NotNull] SQLiteParser.Join_constraintContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCompound_operator([NotNull] SQLiteParser.Compound_operatorContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitUpdate_stmt([NotNull] SQLiteParser.Update_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitColumn_name_list([NotNull] SQLiteParser.Column_name_listContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitUpdate_stmt_limited([NotNull] SQLiteParser.Update_stmt_limitedContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitQualified_table_name([NotNull] SQLiteParser.Qualified_table_nameContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitVacuum_stmt([NotNull] SQLiteParser.Vacuum_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitFilter_clause([NotNull] SQLiteParser.Filter_clauseContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitWindow_defn([NotNull] SQLiteParser.Window_defnContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOver_clause([NotNull] SQLiteParser.Over_clauseContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitFrame_spec([NotNull] SQLiteParser.Frame_specContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitFrame_clause([NotNull] SQLiteParser.Frame_clauseContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSimple_function_invocation([NotNull] SQLiteParser.Simple_function_invocationContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitAggregate_function_invocation([NotNull] SQLiteParser.Aggregate_function_invocationContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitWindow_function_invocation([NotNull] SQLiteParser.Window_function_invocationContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCommon_table_stmt([NotNull] SQLiteParser.Common_table_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOrder_by_stmt([NotNull] SQLiteParser.Order_by_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitLimit_stmt([NotNull] SQLiteParser.Limit_stmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOrdering_term([NotNull] SQLiteParser.Ordering_termContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitAsc_desc([NotNull] SQLiteParser.Asc_descContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitFrame_left([NotNull] SQLiteParser.Frame_leftContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitFrame_right([NotNull] SQLiteParser.Frame_rightContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitFrame_single([NotNull] SQLiteParser.Frame_singleContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitWindow_function([NotNull] SQLiteParser.Window_functionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOffset([NotNull] SQLiteParser.OffsetContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitDefault_value([NotNull] SQLiteParser.Default_valueContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitPartition_by([NotNull] SQLiteParser.Partition_byContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOrder_by_expr([NotNull] SQLiteParser.Order_by_exprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOrder_by_expr_asc_desc([NotNull] SQLiteParser.Order_by_expr_asc_descContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExpr_asc_desc([NotNull] SQLiteParser.Expr_asc_descContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitInitial_select([NotNull] SQLiteParser.Initial_selectContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitRecursive_select([NotNull] SQLiteParser.Recursive_selectContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitUnary_operator([NotNull] SQLiteParser.Unary_operatorContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitError_message([NotNull] SQLiteParser.Error_messageContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitModule_argument([NotNull] SQLiteParser.Module_argumentContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitColumn_alias([NotNull] SQLiteParser.Column_aliasContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitKeyword([NotNull] SQLiteParser.KeywordContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitName([NotNull] SQLiteParser.NameContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitFunction_name([NotNull] SQLiteParser.Function_nameContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSchema_name([NotNull] SQLiteParser.Schema_nameContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitTable_name([NotNull] SQLiteParser.Table_nameContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitTable_or_index_name([NotNull] SQLiteParser.Table_or_index_nameContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitColumn_name([NotNull] SQLiteParser.Column_nameContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCollation_name([NotNull] SQLiteParser.Collation_nameContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitForeign_table([NotNull] SQLiteParser.Foreign_tableContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitIndex_name([NotNull] SQLiteParser.Index_nameContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitTrigger_name([NotNull] SQLiteParser.Trigger_nameContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitView_name([NotNull] SQLiteParser.View_nameContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitModule_name([NotNull] SQLiteParser.Module_nameContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitPragma_name([NotNull] SQLiteParser.Pragma_nameContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSavepoint_name([NotNull] SQLiteParser.Savepoint_nameContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitTable_alias([NotNull] SQLiteParser.Table_aliasContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitTransaction_name([NotNull] SQLiteParser.Transaction_nameContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitWindow_name([NotNull] SQLiteParser.Window_nameContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitAlias([NotNull] SQLiteParser.AliasContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitFilename([NotNull] SQLiteParser.FilenameContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitBase_window_name([NotNull] SQLiteParser.Base_window_nameContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitSimple_func([NotNull] SQLiteParser.Simple_funcContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitAggregate_func([NotNull] SQLiteParser.Aggregate_funcContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitTable_function_name([NotNull] SQLiteParser.Table_function_nameContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitAny_name([NotNull] SQLiteParser.Any_nameContext context) { return VisitChildren(context); } +} +} // namespace DataProvider.SQLite.Parsing diff --git a/DataProvider/DataProvider.SQLite/Parsing/SQLiteParserListener.cs b/DataProvider/DataProvider.SQLite/Parsing/SQLiteParserListener.cs new file mode 100644 index 00000000..0dcaaa26 --- /dev/null +++ b/DataProvider/DataProvider.SQLite/Parsing/SQLiteParserListener.cs @@ -0,0 +1,1165 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// ANTLR Version: 4.13.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// Generated from SQLiteParser.g4 by ANTLR 4.13.1 + +// Unreachable code detected +#pragma warning disable 0162 +// The variable '...' is assigned but its value is never used +#pragma warning disable 0219 +// Missing XML comment for publicly visible type or member '...' +#pragma warning disable 1591 +// Ambiguous reference in cref attribute +#pragma warning disable 419 + +namespace DataProvider.SQLite.Parsing { +using Antlr4.Runtime.Misc; +using IParseTreeListener = Antlr4.Runtime.Tree.IParseTreeListener; +using IToken = Antlr4.Runtime.IToken; + +/// +/// This interface defines a complete listener for a parse tree produced by +/// . +/// +[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.13.1")] +[System.CLSCompliant(false)] +public interface ISQLiteParserListener : IParseTreeListener { + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterParse([NotNull] SQLiteParser.ParseContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitParse([NotNull] SQLiteParser.ParseContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSql_stmt_list([NotNull] SQLiteParser.Sql_stmt_listContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSql_stmt_list([NotNull] SQLiteParser.Sql_stmt_listContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSql_stmt([NotNull] SQLiteParser.Sql_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSql_stmt([NotNull] SQLiteParser.Sql_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterAlter_table_stmt([NotNull] SQLiteParser.Alter_table_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitAlter_table_stmt([NotNull] SQLiteParser.Alter_table_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterAnalyze_stmt([NotNull] SQLiteParser.Analyze_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitAnalyze_stmt([NotNull] SQLiteParser.Analyze_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterAttach_stmt([NotNull] SQLiteParser.Attach_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitAttach_stmt([NotNull] SQLiteParser.Attach_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterBegin_stmt([NotNull] SQLiteParser.Begin_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitBegin_stmt([NotNull] SQLiteParser.Begin_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCommit_stmt([NotNull] SQLiteParser.Commit_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCommit_stmt([NotNull] SQLiteParser.Commit_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterRollback_stmt([NotNull] SQLiteParser.Rollback_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitRollback_stmt([NotNull] SQLiteParser.Rollback_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSavepoint_stmt([NotNull] SQLiteParser.Savepoint_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSavepoint_stmt([NotNull] SQLiteParser.Savepoint_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterRelease_stmt([NotNull] SQLiteParser.Release_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitRelease_stmt([NotNull] SQLiteParser.Release_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreate_index_stmt([NotNull] SQLiteParser.Create_index_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreate_index_stmt([NotNull] SQLiteParser.Create_index_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterIndexed_column([NotNull] SQLiteParser.Indexed_columnContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitIndexed_column([NotNull] SQLiteParser.Indexed_columnContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreate_table_stmt([NotNull] SQLiteParser.Create_table_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreate_table_stmt([NotNull] SQLiteParser.Create_table_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterColumn_def([NotNull] SQLiteParser.Column_defContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitColumn_def([NotNull] SQLiteParser.Column_defContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterType_name([NotNull] SQLiteParser.Type_nameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitType_name([NotNull] SQLiteParser.Type_nameContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterColumn_constraint([NotNull] SQLiteParser.Column_constraintContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitColumn_constraint([NotNull] SQLiteParser.Column_constraintContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSigned_number([NotNull] SQLiteParser.Signed_numberContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSigned_number([NotNull] SQLiteParser.Signed_numberContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterTable_constraint([NotNull] SQLiteParser.Table_constraintContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitTable_constraint([NotNull] SQLiteParser.Table_constraintContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterForeign_key_clause([NotNull] SQLiteParser.Foreign_key_clauseContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitForeign_key_clause([NotNull] SQLiteParser.Foreign_key_clauseContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterConflict_clause([NotNull] SQLiteParser.Conflict_clauseContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitConflict_clause([NotNull] SQLiteParser.Conflict_clauseContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreate_trigger_stmt([NotNull] SQLiteParser.Create_trigger_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreate_trigger_stmt([NotNull] SQLiteParser.Create_trigger_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreate_view_stmt([NotNull] SQLiteParser.Create_view_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreate_view_stmt([NotNull] SQLiteParser.Create_view_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCreate_virtual_table_stmt([NotNull] SQLiteParser.Create_virtual_table_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCreate_virtual_table_stmt([NotNull] SQLiteParser.Create_virtual_table_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterWith_clause([NotNull] SQLiteParser.With_clauseContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitWith_clause([NotNull] SQLiteParser.With_clauseContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCte_table_name([NotNull] SQLiteParser.Cte_table_nameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCte_table_name([NotNull] SQLiteParser.Cte_table_nameContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterRecursive_cte([NotNull] SQLiteParser.Recursive_cteContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitRecursive_cte([NotNull] SQLiteParser.Recursive_cteContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCommon_table_expression([NotNull] SQLiteParser.Common_table_expressionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCommon_table_expression([NotNull] SQLiteParser.Common_table_expressionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterDelete_stmt([NotNull] SQLiteParser.Delete_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitDelete_stmt([NotNull] SQLiteParser.Delete_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterDelete_stmt_limited([NotNull] SQLiteParser.Delete_stmt_limitedContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitDelete_stmt_limited([NotNull] SQLiteParser.Delete_stmt_limitedContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterDetach_stmt([NotNull] SQLiteParser.Detach_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitDetach_stmt([NotNull] SQLiteParser.Detach_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterDrop_stmt([NotNull] SQLiteParser.Drop_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitDrop_stmt([NotNull] SQLiteParser.Drop_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExpr([NotNull] SQLiteParser.ExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExpr([NotNull] SQLiteParser.ExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterRaise_function([NotNull] SQLiteParser.Raise_functionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitRaise_function([NotNull] SQLiteParser.Raise_functionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterLiteral_value([NotNull] SQLiteParser.Literal_valueContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitLiteral_value([NotNull] SQLiteParser.Literal_valueContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterValue_row([NotNull] SQLiteParser.Value_rowContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitValue_row([NotNull] SQLiteParser.Value_rowContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterValues_clause([NotNull] SQLiteParser.Values_clauseContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitValues_clause([NotNull] SQLiteParser.Values_clauseContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterInsert_stmt([NotNull] SQLiteParser.Insert_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitInsert_stmt([NotNull] SQLiteParser.Insert_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterReturning_clause([NotNull] SQLiteParser.Returning_clauseContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitReturning_clause([NotNull] SQLiteParser.Returning_clauseContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterUpsert_clause([NotNull] SQLiteParser.Upsert_clauseContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitUpsert_clause([NotNull] SQLiteParser.Upsert_clauseContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterPragma_stmt([NotNull] SQLiteParser.Pragma_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitPragma_stmt([NotNull] SQLiteParser.Pragma_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterPragma_value([NotNull] SQLiteParser.Pragma_valueContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitPragma_value([NotNull] SQLiteParser.Pragma_valueContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterReindex_stmt([NotNull] SQLiteParser.Reindex_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitReindex_stmt([NotNull] SQLiteParser.Reindex_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSelect_stmt([NotNull] SQLiteParser.Select_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSelect_stmt([NotNull] SQLiteParser.Select_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterJoin_clause([NotNull] SQLiteParser.Join_clauseContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitJoin_clause([NotNull] SQLiteParser.Join_clauseContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSelect_core([NotNull] SQLiteParser.Select_coreContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSelect_core([NotNull] SQLiteParser.Select_coreContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterFactored_select_stmt([NotNull] SQLiteParser.Factored_select_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitFactored_select_stmt([NotNull] SQLiteParser.Factored_select_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSimple_select_stmt([NotNull] SQLiteParser.Simple_select_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSimple_select_stmt([NotNull] SQLiteParser.Simple_select_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCompound_select_stmt([NotNull] SQLiteParser.Compound_select_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCompound_select_stmt([NotNull] SQLiteParser.Compound_select_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterTable_or_subquery([NotNull] SQLiteParser.Table_or_subqueryContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitTable_or_subquery([NotNull] SQLiteParser.Table_or_subqueryContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterResult_column([NotNull] SQLiteParser.Result_columnContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitResult_column([NotNull] SQLiteParser.Result_columnContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterJoin_operator([NotNull] SQLiteParser.Join_operatorContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitJoin_operator([NotNull] SQLiteParser.Join_operatorContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterJoin_constraint([NotNull] SQLiteParser.Join_constraintContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitJoin_constraint([NotNull] SQLiteParser.Join_constraintContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCompound_operator([NotNull] SQLiteParser.Compound_operatorContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCompound_operator([NotNull] SQLiteParser.Compound_operatorContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterUpdate_stmt([NotNull] SQLiteParser.Update_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitUpdate_stmt([NotNull] SQLiteParser.Update_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterColumn_name_list([NotNull] SQLiteParser.Column_name_listContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitColumn_name_list([NotNull] SQLiteParser.Column_name_listContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterUpdate_stmt_limited([NotNull] SQLiteParser.Update_stmt_limitedContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitUpdate_stmt_limited([NotNull] SQLiteParser.Update_stmt_limitedContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterQualified_table_name([NotNull] SQLiteParser.Qualified_table_nameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitQualified_table_name([NotNull] SQLiteParser.Qualified_table_nameContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterVacuum_stmt([NotNull] SQLiteParser.Vacuum_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitVacuum_stmt([NotNull] SQLiteParser.Vacuum_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterFilter_clause([NotNull] SQLiteParser.Filter_clauseContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitFilter_clause([NotNull] SQLiteParser.Filter_clauseContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterWindow_defn([NotNull] SQLiteParser.Window_defnContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitWindow_defn([NotNull] SQLiteParser.Window_defnContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOver_clause([NotNull] SQLiteParser.Over_clauseContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOver_clause([NotNull] SQLiteParser.Over_clauseContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterFrame_spec([NotNull] SQLiteParser.Frame_specContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitFrame_spec([NotNull] SQLiteParser.Frame_specContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterFrame_clause([NotNull] SQLiteParser.Frame_clauseContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitFrame_clause([NotNull] SQLiteParser.Frame_clauseContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSimple_function_invocation([NotNull] SQLiteParser.Simple_function_invocationContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSimple_function_invocation([NotNull] SQLiteParser.Simple_function_invocationContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterAggregate_function_invocation([NotNull] SQLiteParser.Aggregate_function_invocationContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitAggregate_function_invocation([NotNull] SQLiteParser.Aggregate_function_invocationContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterWindow_function_invocation([NotNull] SQLiteParser.Window_function_invocationContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitWindow_function_invocation([NotNull] SQLiteParser.Window_function_invocationContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCommon_table_stmt([NotNull] SQLiteParser.Common_table_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCommon_table_stmt([NotNull] SQLiteParser.Common_table_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOrder_by_stmt([NotNull] SQLiteParser.Order_by_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOrder_by_stmt([NotNull] SQLiteParser.Order_by_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterLimit_stmt([NotNull] SQLiteParser.Limit_stmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitLimit_stmt([NotNull] SQLiteParser.Limit_stmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOrdering_term([NotNull] SQLiteParser.Ordering_termContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOrdering_term([NotNull] SQLiteParser.Ordering_termContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterAsc_desc([NotNull] SQLiteParser.Asc_descContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitAsc_desc([NotNull] SQLiteParser.Asc_descContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterFrame_left([NotNull] SQLiteParser.Frame_leftContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitFrame_left([NotNull] SQLiteParser.Frame_leftContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterFrame_right([NotNull] SQLiteParser.Frame_rightContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitFrame_right([NotNull] SQLiteParser.Frame_rightContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterFrame_single([NotNull] SQLiteParser.Frame_singleContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitFrame_single([NotNull] SQLiteParser.Frame_singleContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterWindow_function([NotNull] SQLiteParser.Window_functionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitWindow_function([NotNull] SQLiteParser.Window_functionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOffset([NotNull] SQLiteParser.OffsetContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOffset([NotNull] SQLiteParser.OffsetContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterDefault_value([NotNull] SQLiteParser.Default_valueContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitDefault_value([NotNull] SQLiteParser.Default_valueContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterPartition_by([NotNull] SQLiteParser.Partition_byContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitPartition_by([NotNull] SQLiteParser.Partition_byContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOrder_by_expr([NotNull] SQLiteParser.Order_by_exprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOrder_by_expr([NotNull] SQLiteParser.Order_by_exprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOrder_by_expr_asc_desc([NotNull] SQLiteParser.Order_by_expr_asc_descContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOrder_by_expr_asc_desc([NotNull] SQLiteParser.Order_by_expr_asc_descContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExpr_asc_desc([NotNull] SQLiteParser.Expr_asc_descContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExpr_asc_desc([NotNull] SQLiteParser.Expr_asc_descContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterInitial_select([NotNull] SQLiteParser.Initial_selectContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitInitial_select([NotNull] SQLiteParser.Initial_selectContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterRecursive_select([NotNull] SQLiteParser.Recursive_selectContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitRecursive_select([NotNull] SQLiteParser.Recursive_selectContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterUnary_operator([NotNull] SQLiteParser.Unary_operatorContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitUnary_operator([NotNull] SQLiteParser.Unary_operatorContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterError_message([NotNull] SQLiteParser.Error_messageContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitError_message([NotNull] SQLiteParser.Error_messageContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterModule_argument([NotNull] SQLiteParser.Module_argumentContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitModule_argument([NotNull] SQLiteParser.Module_argumentContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterColumn_alias([NotNull] SQLiteParser.Column_aliasContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitColumn_alias([NotNull] SQLiteParser.Column_aliasContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterKeyword([NotNull] SQLiteParser.KeywordContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitKeyword([NotNull] SQLiteParser.KeywordContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterName([NotNull] SQLiteParser.NameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitName([NotNull] SQLiteParser.NameContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterFunction_name([NotNull] SQLiteParser.Function_nameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitFunction_name([NotNull] SQLiteParser.Function_nameContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSchema_name([NotNull] SQLiteParser.Schema_nameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSchema_name([NotNull] SQLiteParser.Schema_nameContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterTable_name([NotNull] SQLiteParser.Table_nameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitTable_name([NotNull] SQLiteParser.Table_nameContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterTable_or_index_name([NotNull] SQLiteParser.Table_or_index_nameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitTable_or_index_name([NotNull] SQLiteParser.Table_or_index_nameContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterColumn_name([NotNull] SQLiteParser.Column_nameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitColumn_name([NotNull] SQLiteParser.Column_nameContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCollation_name([NotNull] SQLiteParser.Collation_nameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCollation_name([NotNull] SQLiteParser.Collation_nameContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterForeign_table([NotNull] SQLiteParser.Foreign_tableContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitForeign_table([NotNull] SQLiteParser.Foreign_tableContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterIndex_name([NotNull] SQLiteParser.Index_nameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitIndex_name([NotNull] SQLiteParser.Index_nameContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterTrigger_name([NotNull] SQLiteParser.Trigger_nameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitTrigger_name([NotNull] SQLiteParser.Trigger_nameContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterView_name([NotNull] SQLiteParser.View_nameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitView_name([NotNull] SQLiteParser.View_nameContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterModule_name([NotNull] SQLiteParser.Module_nameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitModule_name([NotNull] SQLiteParser.Module_nameContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterPragma_name([NotNull] SQLiteParser.Pragma_nameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitPragma_name([NotNull] SQLiteParser.Pragma_nameContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSavepoint_name([NotNull] SQLiteParser.Savepoint_nameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSavepoint_name([NotNull] SQLiteParser.Savepoint_nameContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterTable_alias([NotNull] SQLiteParser.Table_aliasContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitTable_alias([NotNull] SQLiteParser.Table_aliasContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterTransaction_name([NotNull] SQLiteParser.Transaction_nameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitTransaction_name([NotNull] SQLiteParser.Transaction_nameContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterWindow_name([NotNull] SQLiteParser.Window_nameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitWindow_name([NotNull] SQLiteParser.Window_nameContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterAlias([NotNull] SQLiteParser.AliasContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitAlias([NotNull] SQLiteParser.AliasContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterFilename([NotNull] SQLiteParser.FilenameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitFilename([NotNull] SQLiteParser.FilenameContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterBase_window_name([NotNull] SQLiteParser.Base_window_nameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitBase_window_name([NotNull] SQLiteParser.Base_window_nameContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterSimple_func([NotNull] SQLiteParser.Simple_funcContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitSimple_func([NotNull] SQLiteParser.Simple_funcContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterAggregate_func([NotNull] SQLiteParser.Aggregate_funcContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitAggregate_func([NotNull] SQLiteParser.Aggregate_funcContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterTable_function_name([NotNull] SQLiteParser.Table_function_nameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitTable_function_name([NotNull] SQLiteParser.Table_function_nameContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterAny_name([NotNull] SQLiteParser.Any_nameContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitAny_name([NotNull] SQLiteParser.Any_nameContext context); +} +} // namespace DataProvider.SQLite.Parsing diff --git a/DataProvider/DataProvider.SQLite/Parsing/SQLiteParserVisitor.cs b/DataProvider/DataProvider.SQLite/Parsing/SQLiteParserVisitor.cs new file mode 100644 index 00000000..f17b3217 --- /dev/null +++ b/DataProvider/DataProvider.SQLite/Parsing/SQLiteParserVisitor.cs @@ -0,0 +1,714 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// ANTLR Version: 4.13.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// Generated from SQLiteParser.g4 by ANTLR 4.13.1 + +// Unreachable code detected +#pragma warning disable 0162 +// The variable '...' is assigned but its value is never used +#pragma warning disable 0219 +// Missing XML comment for publicly visible type or member '...' +#pragma warning disable 1591 +// Ambiguous reference in cref attribute +#pragma warning disable 419 + +namespace DataProvider.SQLite.Parsing { +using Antlr4.Runtime.Misc; +using Antlr4.Runtime.Tree; +using IToken = Antlr4.Runtime.IToken; + +/// +/// This interface defines a complete generic visitor for a parse tree produced +/// by . +/// +/// The return type of the visit operation. +[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.13.1")] +[System.CLSCompliant(false)] +public interface ISQLiteParserVisitor : IParseTreeVisitor { + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitParse([NotNull] SQLiteParser.ParseContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSql_stmt_list([NotNull] SQLiteParser.Sql_stmt_listContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSql_stmt([NotNull] SQLiteParser.Sql_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitAlter_table_stmt([NotNull] SQLiteParser.Alter_table_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitAnalyze_stmt([NotNull] SQLiteParser.Analyze_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitAttach_stmt([NotNull] SQLiteParser.Attach_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitBegin_stmt([NotNull] SQLiteParser.Begin_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCommit_stmt([NotNull] SQLiteParser.Commit_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitRollback_stmt([NotNull] SQLiteParser.Rollback_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSavepoint_stmt([NotNull] SQLiteParser.Savepoint_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitRelease_stmt([NotNull] SQLiteParser.Release_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreate_index_stmt([NotNull] SQLiteParser.Create_index_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitIndexed_column([NotNull] SQLiteParser.Indexed_columnContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreate_table_stmt([NotNull] SQLiteParser.Create_table_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitColumn_def([NotNull] SQLiteParser.Column_defContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitType_name([NotNull] SQLiteParser.Type_nameContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitColumn_constraint([NotNull] SQLiteParser.Column_constraintContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSigned_number([NotNull] SQLiteParser.Signed_numberContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitTable_constraint([NotNull] SQLiteParser.Table_constraintContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitForeign_key_clause([NotNull] SQLiteParser.Foreign_key_clauseContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitConflict_clause([NotNull] SQLiteParser.Conflict_clauseContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreate_trigger_stmt([NotNull] SQLiteParser.Create_trigger_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreate_view_stmt([NotNull] SQLiteParser.Create_view_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCreate_virtual_table_stmt([NotNull] SQLiteParser.Create_virtual_table_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitWith_clause([NotNull] SQLiteParser.With_clauseContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCte_table_name([NotNull] SQLiteParser.Cte_table_nameContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitRecursive_cte([NotNull] SQLiteParser.Recursive_cteContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCommon_table_expression([NotNull] SQLiteParser.Common_table_expressionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitDelete_stmt([NotNull] SQLiteParser.Delete_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitDelete_stmt_limited([NotNull] SQLiteParser.Delete_stmt_limitedContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitDetach_stmt([NotNull] SQLiteParser.Detach_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitDrop_stmt([NotNull] SQLiteParser.Drop_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExpr([NotNull] SQLiteParser.ExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitRaise_function([NotNull] SQLiteParser.Raise_functionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitLiteral_value([NotNull] SQLiteParser.Literal_valueContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitValue_row([NotNull] SQLiteParser.Value_rowContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitValues_clause([NotNull] SQLiteParser.Values_clauseContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitInsert_stmt([NotNull] SQLiteParser.Insert_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitReturning_clause([NotNull] SQLiteParser.Returning_clauseContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitUpsert_clause([NotNull] SQLiteParser.Upsert_clauseContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitPragma_stmt([NotNull] SQLiteParser.Pragma_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitPragma_value([NotNull] SQLiteParser.Pragma_valueContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitReindex_stmt([NotNull] SQLiteParser.Reindex_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSelect_stmt([NotNull] SQLiteParser.Select_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitJoin_clause([NotNull] SQLiteParser.Join_clauseContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSelect_core([NotNull] SQLiteParser.Select_coreContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitFactored_select_stmt([NotNull] SQLiteParser.Factored_select_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSimple_select_stmt([NotNull] SQLiteParser.Simple_select_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCompound_select_stmt([NotNull] SQLiteParser.Compound_select_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitTable_or_subquery([NotNull] SQLiteParser.Table_or_subqueryContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitResult_column([NotNull] SQLiteParser.Result_columnContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitJoin_operator([NotNull] SQLiteParser.Join_operatorContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitJoin_constraint([NotNull] SQLiteParser.Join_constraintContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCompound_operator([NotNull] SQLiteParser.Compound_operatorContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitUpdate_stmt([NotNull] SQLiteParser.Update_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitColumn_name_list([NotNull] SQLiteParser.Column_name_listContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitUpdate_stmt_limited([NotNull] SQLiteParser.Update_stmt_limitedContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitQualified_table_name([NotNull] SQLiteParser.Qualified_table_nameContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitVacuum_stmt([NotNull] SQLiteParser.Vacuum_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitFilter_clause([NotNull] SQLiteParser.Filter_clauseContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitWindow_defn([NotNull] SQLiteParser.Window_defnContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOver_clause([NotNull] SQLiteParser.Over_clauseContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitFrame_spec([NotNull] SQLiteParser.Frame_specContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitFrame_clause([NotNull] SQLiteParser.Frame_clauseContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSimple_function_invocation([NotNull] SQLiteParser.Simple_function_invocationContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitAggregate_function_invocation([NotNull] SQLiteParser.Aggregate_function_invocationContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitWindow_function_invocation([NotNull] SQLiteParser.Window_function_invocationContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCommon_table_stmt([NotNull] SQLiteParser.Common_table_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOrder_by_stmt([NotNull] SQLiteParser.Order_by_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitLimit_stmt([NotNull] SQLiteParser.Limit_stmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOrdering_term([NotNull] SQLiteParser.Ordering_termContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitAsc_desc([NotNull] SQLiteParser.Asc_descContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitFrame_left([NotNull] SQLiteParser.Frame_leftContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitFrame_right([NotNull] SQLiteParser.Frame_rightContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitFrame_single([NotNull] SQLiteParser.Frame_singleContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitWindow_function([NotNull] SQLiteParser.Window_functionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOffset([NotNull] SQLiteParser.OffsetContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitDefault_value([NotNull] SQLiteParser.Default_valueContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitPartition_by([NotNull] SQLiteParser.Partition_byContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOrder_by_expr([NotNull] SQLiteParser.Order_by_exprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOrder_by_expr_asc_desc([NotNull] SQLiteParser.Order_by_expr_asc_descContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExpr_asc_desc([NotNull] SQLiteParser.Expr_asc_descContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitInitial_select([NotNull] SQLiteParser.Initial_selectContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitRecursive_select([NotNull] SQLiteParser.Recursive_selectContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitUnary_operator([NotNull] SQLiteParser.Unary_operatorContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitError_message([NotNull] SQLiteParser.Error_messageContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitModule_argument([NotNull] SQLiteParser.Module_argumentContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitColumn_alias([NotNull] SQLiteParser.Column_aliasContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitKeyword([NotNull] SQLiteParser.KeywordContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitName([NotNull] SQLiteParser.NameContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitFunction_name([NotNull] SQLiteParser.Function_nameContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSchema_name([NotNull] SQLiteParser.Schema_nameContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitTable_name([NotNull] SQLiteParser.Table_nameContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitTable_or_index_name([NotNull] SQLiteParser.Table_or_index_nameContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitColumn_name([NotNull] SQLiteParser.Column_nameContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCollation_name([NotNull] SQLiteParser.Collation_nameContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitForeign_table([NotNull] SQLiteParser.Foreign_tableContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitIndex_name([NotNull] SQLiteParser.Index_nameContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitTrigger_name([NotNull] SQLiteParser.Trigger_nameContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitView_name([NotNull] SQLiteParser.View_nameContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitModule_name([NotNull] SQLiteParser.Module_nameContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitPragma_name([NotNull] SQLiteParser.Pragma_nameContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSavepoint_name([NotNull] SQLiteParser.Savepoint_nameContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitTable_alias([NotNull] SQLiteParser.Table_aliasContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitTransaction_name([NotNull] SQLiteParser.Transaction_nameContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitWindow_name([NotNull] SQLiteParser.Window_nameContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitAlias([NotNull] SQLiteParser.AliasContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitFilename([NotNull] SQLiteParser.FilenameContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitBase_window_name([NotNull] SQLiteParser.Base_window_nameContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitSimple_func([NotNull] SQLiteParser.Simple_funcContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitAggregate_func([NotNull] SQLiteParser.Aggregate_funcContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitTable_function_name([NotNull] SQLiteParser.Table_function_nameContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitAny_name([NotNull] SQLiteParser.Any_nameContext context); +} +} // namespace DataProvider.SQLite.Parsing diff --git a/DataProvider/DataProvider.Tests/BulkOperationsTests.cs b/DataProvider/DataProvider.Tests/BulkOperationsTests.cs new file mode 100644 index 00000000..4cb068bb --- /dev/null +++ b/DataProvider/DataProvider.Tests/BulkOperationsTests.cs @@ -0,0 +1,456 @@ +using DataProvider.CodeGeneration; +using Xunit; + +namespace DataProvider.Tests; + +/// +/// Tests for bulk insert and upsert code generation +/// +public sealed class BulkOperationsTests +{ + private static DatabaseTable CreateTestTable() => + new() + { + Schema = "public", + Name = "Products", + Columns = new List + { + new() + { + Name = "Id", + CSharpType = "Guid", + IsPrimaryKey = true, + IsIdentity = false, + }, + new() + { + Name = "Name", + CSharpType = "string", + IsNullable = false, + }, + new() + { + Name = "Price", + CSharpType = "decimal", + IsNullable = false, + }, + new() + { + Name = "Category", + CSharpType = "string", + IsNullable = true, + }, + }.AsReadOnly(), + }; + + private static DatabaseTable CreateTableWithIdentity() => + new() + { + Schema = "public", + Name = "Orders", + Columns = new List + { + new() + { + Name = "Id", + CSharpType = "int", + IsPrimaryKey = true, + IsIdentity = true, + }, + new() + { + Name = "CustomerId", + CSharpType = "Guid", + IsNullable = false, + }, + new() + { + Name = "Total", + CSharpType = "decimal", + IsNullable = false, + }, + }.AsReadOnly(), + }; + + [Fact] + public void GenerateBulkInsertMethod_WithValidTable_ReturnsSuccess() + { + // Arrange + var table = CreateTestTable(); + + // Act + var result = DataAccessGenerator.GenerateBulkInsertMethod(table); + + // Assert + Assert.True(result is StringOk); + var code = ((StringOk)result).Value; + Assert.Contains("BulkInsertProductsAsync", code); + Assert.Contains("IEnumerable<(", code); + Assert.Contains("INSERT INTO Products", code); + Assert.Contains("batchSize", code); + } + + [Fact] + public void GenerateBulkInsertMethod_WithNullTable_ReturnsError() + { + // Act + var result = DataAccessGenerator.GenerateBulkInsertMethod(null!); + + // Assert + Assert.True(result is StringError); + var error = ((StringError)result).Value; + Assert.Contains("table cannot be null", error.Message); + } + + [Fact] + public void GenerateBulkInsertMethod_WithIdentityColumn_ExcludesIdentityFromInsert() + { + // Arrange + var table = CreateTableWithIdentity(); + + // Act + var result = DataAccessGenerator.GenerateBulkInsertMethod(table); + + // Assert + Assert.True(result is StringOk); + var code = ((StringOk)result).Value; + Assert.Contains("BulkInsertOrdersAsync", code); + Assert.Contains("CustomerId", code); + Assert.Contains("Total", code); + Assert.DoesNotContain("Id Id", code); + } + + [Fact] + public void GenerateBulkInsertMethod_GeneratesBatchHelper() + { + // Arrange + var table = CreateTestTable(); + + // Act + var result = DataAccessGenerator.GenerateBulkInsertMethod(table); + + // Assert + Assert.True(result is StringOk); + var code = ((StringOk)result).Value; + Assert.Contains("ExecuteBulkInsertProductsBatchAsync", code); + Assert.Contains("StringBuilder", code); + Assert.Contains("parameters.Add", code); + } + + [Fact] + public void GenerateBulkInsertMethod_WithCustomBatchSize_UsesBatchSize() + { + // Arrange + var table = CreateTestTable(); + + // Act + var result = DataAccessGenerator.GenerateBulkInsertMethod(table, batchSize: 500); + + // Assert + Assert.True(result is StringOk); + var code = ((StringOk)result).Value; + Assert.Contains("const int batchSize = 500;", code); + } + + [Fact] + public void GenerateBulkUpsertMethod_WithValidTable_ReturnsSuccess() + { + // Arrange + var table = CreateTestTable(); + + // Act + var result = DataAccessGenerator.GenerateBulkUpsertMethod(table); + + // Assert + Assert.True(result is StringOk); + var code = ((StringOk)result).Value; + Assert.Contains("BulkUpsertProductsAsync", code); + Assert.Contains("INSERT OR REPLACE INTO Products", code); + } + + [Fact] + public void GenerateBulkUpsertMethod_ForPostgres_UsesOnConflict() + { + // Arrange + var table = CreateTestTable(); + + // Act + var result = DataAccessGenerator.GenerateBulkUpsertMethod( + table, + databaseType: "Postgres", + connectionType: "NpgsqlConnection" + ); + + // Assert + Assert.True(result is StringOk); + var code = ((StringOk)result).Value; + Assert.Contains("ON CONFLICT", code); + Assert.Contains("DO UPDATE SET", code); + Assert.Contains("EXCLUDED.", code); + Assert.Contains("NpgsqlCommand", code); + } + + [Fact] + public void GenerateBulkUpsertMethod_ForSQLite_UsesReplaceInto() + { + // Arrange + var table = CreateTestTable(); + + // Act + var result = DataAccessGenerator.GenerateBulkUpsertMethod(table, databaseType: "SQLite"); + + // Assert + Assert.True(result is StringOk); + var code = ((StringOk)result).Value; + Assert.Contains("INSERT OR REPLACE INTO", code); + Assert.DoesNotContain("ON CONFLICT", code); + } + + [Fact] + public void GenerateBulkUpsertMethod_WithNullTable_ReturnsError() + { + // Act + var result = DataAccessGenerator.GenerateBulkUpsertMethod(null!); + + // Assert + Assert.True(result is StringError); + var error = ((StringError)result).Value; + Assert.Contains("table cannot be null", error.Message); + } + + [Fact] + public void GenerateBulkUpsertMethod_WithNoPrimaryKey_ReturnsEmpty() + { + // Arrange + var table = new DatabaseTable + { + Schema = "public", + Name = "Logs", + Columns = new List + { + new() + { + Name = "Message", + CSharpType = "string", + IsPrimaryKey = false, + }, + new() + { + Name = "Timestamp", + CSharpType = "DateTime", + IsPrimaryKey = false, + }, + }.AsReadOnly(), + }; + + // Act + var result = DataAccessGenerator.GenerateBulkUpsertMethod(table); + + // Assert + Assert.True(result is StringOk); + var code = ((StringOk)result).Value; + Assert.Empty(code); + } + + [Fact] + public void GenerateBulkInsertMethod_IncludesErrorHandling() + { + // Arrange + var table = CreateTestTable(); + + // Act + var result = DataAccessGenerator.GenerateBulkInsertMethod(table); + + // Assert + Assert.True(result is StringOk); + var code = ((StringOk)result).Value; + Assert.Contains("try", code); + Assert.Contains("catch (Exception ex)", code); + Assert.Contains("Result.Error", code); + Assert.Contains("Bulk insert failed", code); + } + + [Fact] + public void GenerateBulkUpsertMethod_IncludesErrorHandling() + { + // Arrange + var table = CreateTestTable(); + + // Act + var result = DataAccessGenerator.GenerateBulkUpsertMethod(table); + + // Assert + Assert.True(result is StringOk); + var code = ((StringOk)result).Value; + Assert.Contains("try", code); + Assert.Contains("catch (Exception ex)", code); + Assert.Contains("Result.Error", code); + Assert.Contains("Bulk upsert failed", code); + } + + [Fact] + public void GenerateBulkInsertMethod_GeneratesParameterizedQueries() + { + // Arrange + var table = CreateTestTable(); + + // Act + var result = DataAccessGenerator.GenerateBulkInsertMethod(table); + + // Assert + Assert.True(result is StringOk); + var code = ((StringOk)result).Value; + Assert.Contains("@p", code); + Assert.Contains("AddWithValue", code); + Assert.Contains("DBNull.Value", code); + } + + [Fact] + public void GenerateBulkUpsertMethod_IncludesAllColumns() + { + // Arrange + var table = CreateTestTable(); + + // Act + var result = DataAccessGenerator.GenerateBulkUpsertMethod(table, databaseType: "Postgres"); + + // Assert + Assert.True(result is StringOk); + var code = ((StringOk)result).Value; + Assert.Contains("Id", code); + Assert.Contains("Name", code); + Assert.Contains("Price", code); + Assert.Contains("Category", code); + } + + [Fact] + public void GenerateBulkInsertMethod_GeneratesProperTupleType() + { + // Arrange + var table = CreateTestTable(); + + // Act + var result = DataAccessGenerator.GenerateBulkInsertMethod(table); + + // Assert + Assert.True(result is StringOk); + var code = ((StringOk)result).Value; + Assert.Contains("Guid Id", code); + Assert.Contains("string Name", code); + Assert.Contains("decimal Price", code); + Assert.Contains("string Category", code); + } + + [Fact] + public void GenerateBulkInsertMethod_HandlesNullableTypes() + { + // Arrange + var table = CreateTestTable(); + + // Act + var result = DataAccessGenerator.GenerateBulkInsertMethod(table); + + // Assert + Assert.True(result is StringOk); + var code = ((StringOk)result).Value; + Assert.Contains("?? (object)DBNull.Value", code); + } + + [Fact] + public void GenerateBulkUpsertMethod_Postgres_UpdatesNonKeyColumns() + { + // Arrange + var table = CreateTestTable(); + + // Act + var result = DataAccessGenerator.GenerateBulkUpsertMethod(table, databaseType: "Postgres"); + + // Assert + Assert.True(result is StringOk); + var code = ((StringOk)result).Value; + Assert.Contains("Name = EXCLUDED.Name", code); + Assert.Contains("Price = EXCLUDED.Price", code); + Assert.Contains("Category = EXCLUDED.Category", code); + Assert.DoesNotContain("Id = EXCLUDED.Id", code); + } + + [Fact] + public void GenerateBulkInsertMethod_GeneratesAsyncMethods() + { + // Arrange + var table = CreateTestTable(); + + // Act + var result = DataAccessGenerator.GenerateBulkInsertMethod(table); + + // Assert + Assert.True(result is StringOk); + var code = ((StringOk)result).Value; + Assert.Contains("async Task>", code); + Assert.Contains("await", code); + Assert.Contains("ConfigureAwait(false)", code); + } + + [Fact] + public void GenerateBulkInsertMethod_UsesExtensionMethodPattern() + { + // Arrange + var table = CreateTestTable(); + + // Act + var result = DataAccessGenerator.GenerateBulkInsertMethod(table); + + // Assert + Assert.True(result is StringOk); + var code = ((StringOk)result).Value; + Assert.Contains("this IDbTransaction transaction", code); + } + + [Fact] + public void GenerateBulkUpsertMethod_WithCompositeKey_HandlesCorrectly() + { + // Arrange + var table = new DatabaseTable + { + Schema = "public", + Name = "OrderItems", + Columns = new List + { + new() + { + Name = "OrderId", + CSharpType = "Guid", + IsPrimaryKey = true, + }, + new() + { + Name = "ProductId", + CSharpType = "Guid", + IsPrimaryKey = true, + }, + new() + { + Name = "Quantity", + CSharpType = "int", + IsNullable = false, + }, + new() + { + Name = "UnitPrice", + CSharpType = "decimal", + IsNullable = false, + }, + }.AsReadOnly(), + }; + + // Act + var result = DataAccessGenerator.GenerateBulkUpsertMethod(table, databaseType: "Postgres"); + + // Assert + Assert.True(result is StringOk); + var code = ((StringOk)result).Value; + Assert.Contains("ON CONFLICT (OrderId, ProductId)", code); + Assert.Contains("Quantity = EXCLUDED.Quantity", code); + Assert.Contains("UnitPrice = EXCLUDED.UnitPrice", code); + } +} diff --git a/DataProvider/DataProvider.Tests/CustomCodeGenerationTests.cs b/DataProvider/DataProvider.Tests/CustomCodeGenerationTests.cs index b90bd29a..a2c92bbe 100644 --- a/DataProvider/DataProvider.Tests/CustomCodeGenerationTests.cs +++ b/DataProvider/DataProvider.Tests/CustomCodeGenerationTests.cs @@ -621,14 +621,14 @@ public async Task CreateAsync(UserEntity entity) { using var connection = new SqliteConnection(_connectionString); await connection.OpenAsync(); - - var sql = ""INSERT INTO Users (Name, Email) VALUES (@Name, @Email); SELECT last_insert_rowid()""; + + var sql = ""INSERT INTO Users (Name, Email) VALUES (@Name, @Email)""; using var command = new SqliteCommand(sql, connection); command.Parameters.AddWithValue(""@Name"", entity.Name); command.Parameters.AddWithValue(""@Email"", entity.Email); - - var newId = Convert.ToInt32(await command.ExecuteScalarAsync()); - return entity with { Id = newId }; + + await command.ExecuteNonQueryAsync(); + return entity; } public async Task UpdateAsync(UserEntity entity) { @@ -798,13 +798,13 @@ private static string GenerateRepositoryInsertMethod(DatabaseTable table, string {{ using var connection = new SqliteConnection(_connectionString); await connection.OpenAsync(); - - var sql = ""INSERT INTO {table.Name} ({columnNames}) VALUES ({parameterNames}); SELECT last_insert_rowid()""; + + var sql = ""INSERT INTO {table.Name} ({columnNames}) VALUES ({parameterNames})""; using var command = new SqliteCommand(sql, connection); {string.Join("\n", insertableColumns.Select(c => $" command.Parameters.AddWithValue(\"@{c.Name}\", entity.{c.Name});"))} - - var newId = Convert.ToInt32(await command.ExecuteScalarAsync()); - return entity with {{ Id = newId }}; + + await command.ExecuteNonQueryAsync(); + return entity; }}"; } diff --git a/DataProvider/DataProvider.Tests/DbTransactTests.cs b/DataProvider/DataProvider.Tests/DbTransactTests.cs index 1ad4f338..adb122e8 100644 --- a/DataProvider/DataProvider.Tests/DbTransactTests.cs +++ b/DataProvider/DataProvider.Tests/DbTransactTests.cs @@ -26,13 +26,15 @@ public async Task Transact_SqliteConnection_CommitsSuccessfulTransaction() await CreateTestTable(); // Act + var testId = Guid.NewGuid().ToString(); await _connection.Transact(async tx => { using var command = new SqliteCommand( - "INSERT INTO TestTable (Name) VALUES ('Test1')", + "INSERT INTO TestTable (Id, Name) VALUES (@id, 'Test1')", _connection, tx as SqliteTransaction ); + command.Parameters.AddWithValue("@id", testId); await command.ExecuteNonQueryAsync().ConfigureAwait(false); }); @@ -59,13 +61,14 @@ await _connection .Transact(async tx => { using var command1 = new SqliteCommand( - "INSERT INTO TestTable (Name) VALUES ('Test1')", + "INSERT INTO TestTable (Id, Name) VALUES (@id, 'Test1')", _connection, tx as SqliteTransaction ); + command1.Parameters.AddWithValue("@id", Guid.NewGuid().ToString()); await command1.ExecuteNonQueryAsync().ConfigureAwait(false); - // This will fail due to constraint violation (assuming we make Name unique) + // This will fail because InvalidTable doesn't exist using var command2 = new SqliteCommand( "INSERT INTO InvalidTable (Name) VALUES ('Test2')", _connection, @@ -93,19 +96,20 @@ public async Task Transact_SqliteConnection_WithReturnValue_ReturnsCorrectValue( await CreateTestTable(); // Act + var testId = Guid.NewGuid().ToString(); var result = await _connection.Transact(async tx => { using var command = new SqliteCommand( - "INSERT INTO TestTable (Name) VALUES ('Test1'); SELECT last_insert_rowid();", + "INSERT INTO TestTable (Id, Name) VALUES (@id, 'Test1')", _connection, tx as SqliteTransaction ); - var id = await command.ExecuteScalarAsync().ConfigureAwait(false); - return Convert.ToInt64(id, System.Globalization.CultureInfo.InvariantCulture); + command.Parameters.AddWithValue("@id", testId); + return await command.ExecuteNonQueryAsync().ConfigureAwait(false); }); - // Assert - Assert.Equal(1L, result); + // Assert - 1 row affected + Assert.Equal(1, result); } [Fact] @@ -119,13 +123,14 @@ public async Task Transact_SqliteConnection_WithReturnValue_RollsBackOnException await Assert.ThrowsAsync(async () => { await _connection - .Transact(async tx => + .Transact(async tx => { using var command = new SqliteCommand( - "INSERT INTO TestTable (Name) VALUES ('Test1')", + "INSERT INTO TestTable (Id, Name) VALUES (@id, 'Test1')", _connection, tx as SqliteTransaction ); + command.Parameters.AddWithValue("@id", Guid.NewGuid().ToString()); await command.ExecuteNonQueryAsync().ConfigureAwait(false); throw new InvalidOperationException("Test exception"); @@ -153,10 +158,11 @@ await _connection.Transact(async tx => { await CreateTestTable(tx as SqliteTransaction).ConfigureAwait(false); using var command = new SqliteCommand( - "INSERT INTO TestTable (Name) VALUES ('Test1')", + "INSERT INTO TestTable (Id, Name) VALUES (@id, 'Test1')", _connection, tx as SqliteTransaction ); + command.Parameters.AddWithValue("@id", Guid.NewGuid().ToString()); await command.ExecuteNonQueryAsync().ConfigureAwait(false); }); @@ -226,7 +232,7 @@ private async Task CreateTestTable(SqliteTransaction? transaction = null) using var command = new SqliteCommand( @" CREATE TABLE IF NOT EXISTS TestTable ( - Id INTEGER PRIMARY KEY AUTOINCREMENT, + Id TEXT PRIMARY KEY, Name TEXT NOT NULL )", _connection, diff --git a/DataProvider/DataProvider.Tests/DbTransactionExtensionsTests.cs b/DataProvider/DataProvider.Tests/DbTransactionExtensionsTests.cs index 02dc3653..a8dc8abf 100644 --- a/DataProvider/DataProvider.Tests/DbTransactionExtensionsTests.cs +++ b/DataProvider/DataProvider.Tests/DbTransactionExtensionsTests.cs @@ -1,18 +1,22 @@ using Microsoft.Data.Sqlite; -using Outcome; using Xunit; +using TestRecordListError = Outcome.Result< + System.Collections.Generic.IReadOnlyList, + Selecta.SqlError +>.Error< + System.Collections.Generic.IReadOnlyList, + Selecta.SqlError +>; +using TestRecordListOk = Outcome.Result< + System.Collections.Generic.IReadOnlyList, + Selecta.SqlError +>.Ok< + System.Collections.Generic.IReadOnlyList, + Selecta.SqlError +>; namespace DataProvider.Tests; -using TestRecordListError = Result< - IReadOnlyList, - SqlError ->.Error, SqlError>; -using TestRecordListOk = Result< - IReadOnlyList, - SqlError ->.Ok, SqlError>; - /// /// Tests for DbTransactionExtensions Query method to improve coverage /// diff --git a/DataProvider/DataProvider/CodeGeneration/DataAccessGenerator.cs b/DataProvider/DataProvider/CodeGeneration/DataAccessGenerator.cs index 7e7041ab..96723e16 100644 --- a/DataProvider/DataProvider/CodeGeneration/DataAccessGenerator.cs +++ b/DataProvider/DataProvider/CodeGeneration/DataAccessGenerator.cs @@ -313,7 +313,7 @@ public static Result GenerateInsertMethod( insertableColumns.Select(c => string.Create( CultureInfo.InvariantCulture, - $"{c.CSharpType} {EscapeReservedKeyword(c.Name)}" + $"{c.CSharpType}{(c.IsNullable && c.CSharpType == "string" ? "?" : "")} {EscapeReservedKeyword(c.Name)}" ) ) ); @@ -327,17 +327,22 @@ public static Result GenerateInsertMethod( sb.AppendLine(" /// "); sb.AppendLine( CultureInfo.InvariantCulture, - $" public static async Task> Insert{table.Name}Async(this IDbTransaction transaction, {parameterList})" + $" public static async Task> Insert{table.Name}Async(this IDbTransaction transaction, {parameterList})" ); sb.AppendLine(" {"); - // Generate INSERT SQL + // Generate INSERT SQL (no last_insert_rowid - all PKs are UUIDs) var columnNames = string.Join(", ", insertableColumns.Select(c => c.Name)); var parameterNames = string.Join(", ", insertableColumns.Select(c => $"@{c.Name}")); sb.AppendLine( CultureInfo.InvariantCulture, - $" const string sql = \"INSERT INTO {table.Name} ({columnNames}) VALUES ({parameterNames}); SELECT last_insert_rowid()\";" + $" const string sql = \"INSERT INTO {table.Name} ({columnNames}) VALUES ({parameterNames})\";" + ); + sb.AppendLine(); + sb.AppendLine(" if (transaction.Connection is null)"); + sb.AppendLine( + " return new Result.Error(new SqlError(\"Transaction has no connection\"));" ); sb.AppendLine(); sb.AppendLine(" try"); @@ -351,7 +356,7 @@ public static Result GenerateInsertMethod( ); sb.AppendLine( CultureInfo.InvariantCulture, - $" using (var command = new {commandType}(sql, ({connectionType})transaction.Connection, ({transactionType})transaction))" + $" using (var command = new {commandType}(sql, ({connectionType})transaction.Connection!, ({transactionType})transaction))" ); sb.AppendLine(" {"); @@ -377,24 +382,17 @@ public static Result GenerateInsertMethod( sb.AppendLine(); sb.AppendLine( - " var result = await command.ExecuteScalarAsync().ConfigureAwait(false);" - ); - sb.AppendLine(" if (result == null || result == DBNull.Value)"); - sb.AppendLine( - " return new Result.Error(new SqlError(\"Insert failed: no ID returned\"));" - ); - sb.AppendLine( - " var newId = Convert.ToInt64(result, CultureInfo.InvariantCulture);" + " var rowsAffected = await command.ExecuteNonQueryAsync().ConfigureAwait(false);" ); sb.AppendLine( - " return new Result.Ok(newId);" + " return new Result.Ok(rowsAffected);" ); sb.AppendLine(" }"); sb.AppendLine(" }"); sb.AppendLine(" catch (Exception ex)"); sb.AppendLine(" {"); sb.AppendLine( - " return new Result.Error(new SqlError(\"Insert failed\", ex));" + " return new Result.Error(new SqlError(\"Insert failed\", ex));" ); sb.AppendLine(" }"); sb.AppendLine(" }"); @@ -579,6 +577,11 @@ public static Result GenerateUpdateMethod( $" const string sql = \"UPDATE {table.Name} SET {setClause} WHERE {whereClause}\";" ); sb.AppendLine(); + sb.AppendLine(" if (transaction.Connection is null)"); + sb.AppendLine( + " return new Result.Error(new SqlError(\"Transaction has no connection\"));" + ); + sb.AppendLine(); sb.AppendLine(" try"); sb.AppendLine(" {"); @@ -590,7 +593,7 @@ public static Result GenerateUpdateMethod( ); sb.AppendLine( CultureInfo.InvariantCulture, - $" using (var command = new {commandType}(sql, ({connectionType})transaction.Connection, ({transactionType})transaction))" + $" using (var command = new {commandType}(sql, ({connectionType})transaction.Connection!, ({transactionType})transaction))" ); sb.AppendLine(" {"); @@ -622,4 +625,419 @@ public static Result GenerateUpdateMethod( return new Result.Ok(sb.ToString()); } + + /// + /// Generates a bulk INSERT method for a database table. + /// Uses multi-row VALUES syntax for database-independent bulk inserts. + /// + /// Database table metadata + /// Maximum rows per batch (default 1000) + /// Database connection type + /// Generated bulk INSERT method code + public static Result GenerateBulkInsertMethod( + DatabaseTable table, + int batchSize = 1000, + string connectionType = "SqliteConnection" + ) + { + if (table == null) + return new Result.Error( + new SqlError("table cannot be null") + ); + + var insertableColumns = table.InsertableColumns; + if (insertableColumns.Count == 0) + return new Result.Ok(""); + + var sb = new StringBuilder(); + var tupleType = string.Join( + ", ", + insertableColumns.Select(c => + string.Create(CultureInfo.InvariantCulture, $"{c.CSharpType} {c.Name}") + ) + ); + + sb.AppendLine(); + sb.AppendLine(" /// "); + sb.AppendLine( + CultureInfo.InvariantCulture, + $" /// Bulk inserts rows into the {table.Name} table using batched multi-row VALUES." + ); + sb.AppendLine(" /// "); + sb.AppendLine(" /// Active database transaction."); + sb.AppendLine(" /// Records to insert."); + sb.AppendLine(" /// Result with total rows inserted or SQL error."); + sb.AppendLine( + CultureInfo.InvariantCulture, + $" public static async Task> BulkInsert{table.Name}Async(this IDbTransaction transaction, IEnumerable<({tupleType})> records)" + ); + sb.AppendLine(" {"); + sb.AppendLine(CultureInfo.InvariantCulture, $" const int batchSize = {batchSize};"); + sb.AppendLine(" var totalInserted = 0;"); + sb.AppendLine(" var batch = new List<(" + tupleType + ")>(batchSize);"); + sb.AppendLine(); + sb.AppendLine(" try"); + sb.AppendLine(" {"); + sb.AppendLine(" foreach (var record in records)"); + sb.AppendLine(" {"); + sb.AppendLine(" batch.Add(record);"); + sb.AppendLine(" if (batch.Count >= batchSize)"); + sb.AppendLine(" {"); + sb.AppendLine( + CultureInfo.InvariantCulture, + $" var result = await ExecuteBulkInsert{table.Name}BatchAsync(transaction, batch).ConfigureAwait(false);" + ); + sb.AppendLine( + " if (result is Result.Error err)" + ); + sb.AppendLine(" return err;"); + sb.AppendLine( + " totalInserted += ((Result.Ok)result).Value;" + ); + sb.AppendLine(" batch.Clear();"); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine(" if (batch.Count > 0)"); + sb.AppendLine(" {"); + sb.AppendLine( + CultureInfo.InvariantCulture, + $" var finalResult = await ExecuteBulkInsert{table.Name}BatchAsync(transaction, batch).ConfigureAwait(false);" + ); + sb.AppendLine( + " if (finalResult is Result.Error finalErr)" + ); + sb.AppendLine(" return finalErr;"); + sb.AppendLine( + " totalInserted += ((Result.Ok)finalResult).Value;" + ); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine( + " return new Result.Ok(totalInserted);" + ); + sb.AppendLine(" }"); + sb.AppendLine(" catch (Exception ex)"); + sb.AppendLine(" {"); + sb.AppendLine( + " return new Result.Error(new SqlError(\"Bulk insert failed\", ex));" + ); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + sb.AppendLine(); + + // Generate the batch execution helper method + sb.AppendLine(" /// "); + sb.AppendLine( + CultureInfo.InvariantCulture, + $" /// Executes a single batch of bulk inserts for {table.Name}." + ); + sb.AppendLine(" /// "); + sb.AppendLine( + CultureInfo.InvariantCulture, + $" private static async Task> ExecuteBulkInsert{table.Name}BatchAsync(IDbTransaction transaction, List<({tupleType})> batch)" + ); + sb.AppendLine(" {"); + sb.AppendLine(" if (batch.Count == 0)"); + sb.AppendLine(" return new Result.Ok(0);"); + sb.AppendLine(); + sb.AppendLine(" if (transaction.Connection is null)"); + sb.AppendLine( + " return new Result.Error(new SqlError(\"Transaction has no connection\"));" + ); + sb.AppendLine(); + + // Build the SQL with placeholders + var columnNames = string.Join(", ", insertableColumns.Select(c => c.Name)); + sb.AppendLine( + CultureInfo.InvariantCulture, + $" var sql = new StringBuilder(\"INSERT INTO {table.Name} ({columnNames}) VALUES \");" + ); + sb.AppendLine(" var parameters = new List();"); + sb.AppendLine(); + sb.AppendLine(" for (int i = 0; i < batch.Count; i++)"); + sb.AppendLine(" {"); + sb.AppendLine(" if (i > 0) sql.Append(\", \");"); + + // Build VALUES clause with parameter placeholders + var paramPlaceholders = string.Join( + ", ", + insertableColumns.Select( + (c, idx) => $"@p\" + (i * {insertableColumns.Count} + {idx}) + \"" + ) + ); + sb.AppendLine( + CultureInfo.InvariantCulture, + $" sql.Append(\"({paramPlaceholders})\");" + ); + sb.AppendLine(" var rec = batch[i];"); + + // Add parameters from tuple + for (int i = 0; i < insertableColumns.Count; i++) + { + var col = insertableColumns[i]; + sb.AppendLine( + CultureInfo.InvariantCulture, + $" parameters.Add(rec.{col.Name});" + ); + } + sb.AppendLine(" }"); + sb.AppendLine(); + + var commandType = connectionType.Replace("Connection", "Command", StringComparison.Ordinal); + var transactionType = connectionType.Replace( + "Connection", + "Transaction", + StringComparison.Ordinal + ); + sb.AppendLine( + CultureInfo.InvariantCulture, + $" using (var command = new {commandType}(sql.ToString(), ({connectionType})transaction.Connection!, ({transactionType})transaction))" + ); + sb.AppendLine(" {"); + sb.AppendLine(" for (int i = 0; i < parameters.Count; i++)"); + sb.AppendLine(" {"); + sb.AppendLine( + " command.Parameters.AddWithValue(\"@p\" + i, parameters[i] ?? (object)DBNull.Value);" + ); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine( + " var rowsAffected = await command.ExecuteNonQueryAsync().ConfigureAwait(false);" + ); + sb.AppendLine( + " return new Result.Ok(rowsAffected);" + ); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + + return new Result.Ok(sb.ToString()); + } + + /// + /// Generates a bulk UPSERT method for a database table. + /// Uses INSERT ... ON CONFLICT DO UPDATE for PostgreSQL, REPLACE INTO for SQLite. + /// + /// Database table metadata + /// Target database type (Postgres or SQLite) + /// Maximum rows per batch (default 1000) + /// Database connection type + /// Generated bulk UPSERT method code + public static Result GenerateBulkUpsertMethod( + DatabaseTable table, + string databaseType = "SQLite", + int batchSize = 1000, + string connectionType = "SqliteConnection" + ) + { + if (table == null) + return new Result.Error( + new SqlError("table cannot be null") + ); + + var insertableColumns = table.InsertableColumns; + var primaryKeyColumns = table.PrimaryKeyColumns; + + if (insertableColumns.Count == 0 || primaryKeyColumns.Count == 0) + return new Result.Ok(""); + + var sb = new StringBuilder(); + var allColumns = primaryKeyColumns + .Concat(insertableColumns.Where(c => !primaryKeyColumns.Any(pk => pk.Name == c.Name))) + .ToList(); + var tupleType = string.Join( + ", ", + allColumns.Select(c => + string.Create(CultureInfo.InvariantCulture, $"{c.CSharpType} {c.Name}") + ) + ); + + sb.AppendLine(); + sb.AppendLine(" /// "); + sb.AppendLine( + CultureInfo.InvariantCulture, + $" /// Bulk upserts rows into the {table.Name} table (insert or update on conflict)." + ); + sb.AppendLine(" /// "); + sb.AppendLine(" /// Active database transaction."); + sb.AppendLine(" /// Records to upsert."); + sb.AppendLine(" /// Result with total rows affected or SQL error."); + sb.AppendLine( + CultureInfo.InvariantCulture, + $" public static async Task> BulkUpsert{table.Name}Async(this IDbTransaction transaction, IEnumerable<({tupleType})> records)" + ); + sb.AppendLine(" {"); + sb.AppendLine(CultureInfo.InvariantCulture, $" const int batchSize = {batchSize};"); + sb.AppendLine(" var totalAffected = 0;"); + sb.AppendLine(" var batch = new List<(" + tupleType + ")>(batchSize);"); + sb.AppendLine(); + sb.AppendLine(" try"); + sb.AppendLine(" {"); + sb.AppendLine(" foreach (var record in records)"); + sb.AppendLine(" {"); + sb.AppendLine(" batch.Add(record);"); + sb.AppendLine(" if (batch.Count >= batchSize)"); + sb.AppendLine(" {"); + sb.AppendLine( + CultureInfo.InvariantCulture, + $" var result = await ExecuteBulkUpsert{table.Name}BatchAsync(transaction, batch).ConfigureAwait(false);" + ); + sb.AppendLine( + " if (result is Result.Error err)" + ); + sb.AppendLine(" return err;"); + sb.AppendLine( + " totalAffected += ((Result.Ok)result).Value;" + ); + sb.AppendLine(" batch.Clear();"); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine(" if (batch.Count > 0)"); + sb.AppendLine(" {"); + sb.AppendLine( + CultureInfo.InvariantCulture, + $" var finalResult = await ExecuteBulkUpsert{table.Name}BatchAsync(transaction, batch).ConfigureAwait(false);" + ); + sb.AppendLine( + " if (finalResult is Result.Error finalErr)" + ); + sb.AppendLine(" return finalErr;"); + sb.AppendLine( + " totalAffected += ((Result.Ok)finalResult).Value;" + ); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine( + " return new Result.Ok(totalAffected);" + ); + sb.AppendLine(" }"); + sb.AppendLine(" catch (Exception ex)"); + sb.AppendLine(" {"); + sb.AppendLine( + " return new Result.Error(new SqlError(\"Bulk upsert failed\", ex));" + ); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + sb.AppendLine(); + + // Generate the batch execution helper method + sb.AppendLine(" /// "); + sb.AppendLine( + CultureInfo.InvariantCulture, + $" /// Executes a single batch of bulk upserts for {table.Name}." + ); + sb.AppendLine(" /// "); + sb.AppendLine( + CultureInfo.InvariantCulture, + $" private static async Task> ExecuteBulkUpsert{table.Name}BatchAsync(IDbTransaction transaction, List<({tupleType})> batch)" + ); + sb.AppendLine(" {"); + sb.AppendLine(" if (batch.Count == 0)"); + sb.AppendLine(" return new Result.Ok(0);"); + sb.AppendLine(); + sb.AppendLine(" if (transaction.Connection is null)"); + sb.AppendLine( + " return new Result.Error(new SqlError(\"Transaction has no connection\"));" + ); + sb.AppendLine(); + + // Build the SQL with placeholders - database-specific upsert syntax + var columnNames = string.Join(", ", allColumns.Select(c => c.Name)); + var pkColumnNames = string.Join(", ", primaryKeyColumns.Select(c => c.Name)); + var updateColumns = allColumns + .Where(c => !primaryKeyColumns.Any(pk => pk.Name == c.Name)) + .ToList(); + var updateSet = string.Join( + ", ", + updateColumns.Select(c => $"{c.Name} = EXCLUDED.{c.Name}") + ); + + if (databaseType.Equals("Postgres", StringComparison.OrdinalIgnoreCase)) + { + sb.AppendLine( + CultureInfo.InvariantCulture, + $" var sql = new StringBuilder(\"INSERT INTO {table.Name} ({columnNames}) VALUES \");" + ); + } + else + { + // SQLite uses INSERT OR REPLACE + sb.AppendLine( + CultureInfo.InvariantCulture, + $" var sql = new StringBuilder(\"INSERT OR REPLACE INTO {table.Name} ({columnNames}) VALUES \");" + ); + } + + sb.AppendLine(" var parameters = new List();"); + sb.AppendLine(); + sb.AppendLine(" for (int i = 0; i < batch.Count; i++)"); + sb.AppendLine(" {"); + sb.AppendLine(" if (i > 0) sql.Append(\", \");"); + + // Build VALUES clause with parameter placeholders + var paramPlaceholders = string.Join( + ", ", + allColumns.Select((c, idx) => $"@p\" + (i * {allColumns.Count} + {idx}) + \"") + ); + sb.AppendLine( + CultureInfo.InvariantCulture, + $" sql.Append(\"({paramPlaceholders})\");" + ); + sb.AppendLine(" var rec = batch[i];"); + + // Add parameters from tuple + foreach (var col in allColumns) + { + sb.AppendLine( + CultureInfo.InvariantCulture, + $" parameters.Add(rec.{col.Name});" + ); + } + sb.AppendLine(" }"); + + // Add ON CONFLICT clause for Postgres + if ( + databaseType.Equals("Postgres", StringComparison.OrdinalIgnoreCase) + && updateColumns.Count > 0 + ) + { + sb.AppendLine(); + sb.AppendLine( + CultureInfo.InvariantCulture, + $" sql.Append(\" ON CONFLICT ({pkColumnNames}) DO UPDATE SET {updateSet}\");" + ); + } + + sb.AppendLine(); + + var commandType = connectionType.Replace("Connection", "Command", StringComparison.Ordinal); + var transactionType = connectionType.Replace( + "Connection", + "Transaction", + StringComparison.Ordinal + ); + sb.AppendLine( + CultureInfo.InvariantCulture, + $" using (var command = new {commandType}(sql.ToString(), ({connectionType})transaction.Connection!, ({transactionType})transaction))" + ); + sb.AppendLine(" {"); + sb.AppendLine(" for (int i = 0; i < parameters.Count; i++)"); + sb.AppendLine(" {"); + sb.AppendLine( + " command.Parameters.AddWithValue(\"@p\" + i, parameters[i] ?? (object)DBNull.Value);" + ); + sb.AppendLine(" }"); + sb.AppendLine(); + sb.AppendLine( + " var rowsAffected = await command.ExecuteNonQueryAsync().ConfigureAwait(false);" + ); + sb.AppendLine( + " return new Result.Ok(rowsAffected);" + ); + sb.AppendLine(" }"); + sb.AppendLine(" }"); + + return new Result.Ok(sb.ToString()); + } } diff --git a/DataProvider/DataProvider/CodeGeneration/DefaultTableOperationGenerator.cs b/DataProvider/DataProvider/CodeGeneration/DefaultTableOperationGenerator.cs index db4560cf..1a8dcb8a 100644 --- a/DataProvider/DataProvider/CodeGeneration/DefaultTableOperationGenerator.cs +++ b/DataProvider/DataProvider/CodeGeneration/DefaultTableOperationGenerator.cs @@ -40,6 +40,7 @@ TableConfig config ); var sb = new StringBuilder(); + sb.AppendLine("#nullable enable"); sb.AppendLine("using System;"); sb.AppendLine("using System.Collections.Generic;"); sb.AppendLine("using System.Collections.Immutable;"); diff --git a/DataProvider/README.md b/DataProvider/README.md index 15212c3f..46b12ca4 100644 --- a/DataProvider/README.md +++ b/DataProvider/README.md @@ -30,6 +30,108 @@ A .NET source generator that creates compile-time safe database extension method ``` +## Database Schema Setup (Migrations) + +DataProvider requires a database with schema to exist **before** code generation runs. The schema allows the generator to introspect table structures and generate correct types. + +### Required Build Order + +``` +1. Export C# Schema to YAML (if schema defined in code) +2. Run Migration.Cli to create database from YAML +3. Run DataProvider code generation +``` + +### Using Migration.Cli + +Migration.Cli is the **single canonical tool** for creating databases from schema definitions. All projects that need a build-time database MUST use this tool. + +```bash +dotnet run --project Migration/Migration.Cli/Migration.Cli.csproj -- \ + --schema path/to/schema.yaml \ + --output path/to/database.db \ + --provider sqlite +``` + +### MSBuild Integration + +Configure your `.csproj` to run migrations before code generation: + +```xml + + + + + + + + + +``` + +### YAML Schema Format + +See [this](Migration/migration_exe_spec.md) + +### Exporting C# Schemas to YAML + +If your schema is defined in C# code using the Migration fluent API: + +```csharp +var schema = Schema.Define("my_schema") + .Table("Customer", t => t + .Column("Id", Text, c => c.PrimaryKey()) + .Column("Name", Text, c => c.NotNull()) + ) + .Build(); + +// Export to YAML +SchemaYamlSerializer.ToYamlFile(schema, "schema.yaml"); +``` + +### Avoiding Circular Dependencies + +**CRITICAL:** Projects that use DataProvider code generation MUST NOT have circular dependencies with the CLI tools. + +**The Problem:** If your project references `DataProvider.csproj` AND runs `DataProvider.SQLite.Cli` as a build target, you create an infinite build loop: +``` +YourProject → DataProvider → (build target) DataProvider.SQLite.Cli → DataProvider → ... +``` + +**How to Fix:** +1. **Remove the `ProjectReference` to DataProvider.csproj** from projects that run the CLI as a build target +2. **Use raw YAML schemas checked into git** - do NOT export C# schemas to YAML at build time +3. **Migration.Cli is safe** - it does NOT depend on DataProvider, only on Migration projects + +**The Rule:** YAML schema files are source of truth. Check them into git. Never generate them at build time. The C# → YAML export is a one-time developer action, not a build step. + +**Correct pattern:** +```xml + + + + + + + + + +``` + +**Wrong pattern:** +```xml + + +``` + +### Forbidden Patterns + +- **NO raw SQL DDL files** - Use Migration.Cli with YAML +- **NO individual BuildDb projects** - Use Migration.Cli (single tool) +- **NO `schema.sql` files** - YAML schemas only +- **NO code generation before schema creation** - Migration MUST run first +- **NO C# schema export at build time** - Export once, commit YAML to git + ## Configuration Create a `DataProvider.json` file in your project root: diff --git a/Directory.Build.props b/Directory.Build.props index 81d4d5e7..242fcc6b 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -7,7 +7,7 @@ enable enable true - IDE0301;IDE0063;IDE0005 + IDE0301;IDE0063;IDE0005;NU1603;MSB3243 CA1016;CA1303;EPS06;IDE0290;CA1062;CA1002;IDE0090;CA1017;CS8509;IDE0037 $(WarningsNotAsErrors);CA1303;EPS06;CA1016;IDE0290;CA1062;CA1002;CA1017;CS8509;IDE0037 9999 @@ -82,7 +82,7 @@ $(WarningsAsErrors);IDE0001;IDE0042;IDE0051;IDE0052;IDE0056;IDE0060;IDE0022;IDE0002;IDE0130;IDE0060;IDE0002 - $(WarningsAsErrors);CA1805;CA1806;CA1810;CA1812;CA1813;CA1814;CA1815;CA1816;CA1819;CA1820;CA1821;CA1822;CA1823;CA1824;CA1825;CA1826;CA1827;CA1828;CA1829;CA1830;CA1831;CA1832;CA1833;CA1834;CA1835;CA1836;CA1837;CA1838;CA1839;CA1840;CA1841;CA1842;CA1843;CA1844;CA1845;CA1846;CA1847;CA1848;CA1849;CA1850;CA1851;CA1852;CA1853;CA1854;CA1855;CA1856;CA1857;CA1858;CA1859;CA1860;CA1861;CA1862;CA1863;CA1864;CA1865;CA1866;CA1867;CA1868;CA1869;CA1870 + $(WarningsAsErrors);CA1805;CA1806;CA1810;CA1812;CA1813;CA1814;CA1815;CA1816;CA1819;CA1820;CA1821;CA1822;CA1823;CA1824;CA1825;CA1826;CA1827;CA1828;CA1829;CA1830;CA1831;CA1832;CA1833;CA1834;CA1835;CA1836;CA1837;CA1838;CA1839;CA1840;CA1841;CA1842;CA1843;CA1844;CA1845;CA1846;CA1847;CA1849;CA1850;CA1851;CA1852;CA1853;CA1854;CA1855;CA1856;CA1857;CA1858;CA1859;CA1860;CA1861;CA1862;CA1863;CA1864;CA1865;CA1866;CA1867;CA1868;CA1869;CA1870 $(WarningsAsErrors);CA2100;CA2101;CA2102;CA2103;CA2104;CA2105;CA2106;CA2107;CA2108;CA2109;CA2110;CA2111;CA2112;CA2113;CA2114;CA2115;CA2116;CA2117;CA2118;CA2119;CA2120;CA2121;CA2122;CA2123;CA2124;CA2125;CA2126;CA2127;CA2128;CA2129;CA2130;CA2131;CA2132;CA2133;CA2134;CA2135;CA2136;CA2137;CA2138;CA2139;CA2140;CA2141;CA2142;CA2143;CA2144;CA2145;CA2146;CA2147;CA2148;CA2149;CA2150;CA2151;CA2152;CA2153;CA2154;CA2155;CA2156;CA2157;CA2158;CA2159;CA2160 diff --git a/Gatekeeper/Gatekeeper.Api.Tests/AuthorizationTests.cs b/Gatekeeper/Gatekeeper.Api.Tests/AuthorizationTests.cs index 4951f978..cbeda034 100644 --- a/Gatekeeper/Gatekeeper.Api.Tests/AuthorizationTests.cs +++ b/Gatekeeper/Gatekeeper.Api.Tests/AuthorizationTests.cs @@ -1,10 +1,9 @@ -namespace Gatekeeper.Api.Tests; - using System.Globalization; -using Gatekeeper.Api; -using Generated; using Microsoft.Data.Sqlite; using Microsoft.Extensions.DependencyInjection; +using Outcome; + +namespace Gatekeeper.Api.Tests; /// /// Integration tests for Gatekeeper authorization endpoints. @@ -440,9 +439,7 @@ await tx.Insertgk_user_roleAsync( tx.Commit(); // Force WAL checkpoint to ensure changes are visible to other connections - using var wal = conn.CreateCommand(); - wal.CommandText = "PRAGMA wal_checkpoint(FULL)"; - wal.ExecuteNonQuery(); + _ = await conn.WalCheckpointAsync().ConfigureAwait(false); var token = TokenService.CreateToken( userId, @@ -493,9 +490,7 @@ await tx.Insertgk_user_roleAsync( tx.Commit(); // Force WAL checkpoint to ensure changes are visible to other connections - using var wal = conn.CreateCommand(); - wal.CommandText = "PRAGMA wal_checkpoint(FULL)"; - wal.ExecuteNonQuery(); + _ = await conn.WalCheckpointAsync().ConfigureAwait(false); var token = TokenService.CreateToken( userId, @@ -521,26 +516,30 @@ string permissionCode ) { using var conn = OpenConnection(); - using var tx = conn.BeginTransaction(); + // Look up existing permission by code BEFORE starting transaction + var permLookupResult = await conn.GetPermissionByCodeAsync(permissionCode) + .ConfigureAwait(false); + var existingPerm = permLookupResult switch + { + GetPermissionByCodeOk ok => ok.Value.FirstOrDefault(), + GetPermissionByCodeError err => throw new InvalidOperationException( + $"Permission lookup failed: {err.Value.Message}, Exception: {err.Value.InnerException?.Message}" + ), + }; + + var permId = + existingPerm?.id + ?? throw new InvalidOperationException( + $"Permission '{permissionCode}' not found in seeded database" + ); + + using var tx = conn.BeginTransaction(); var now = DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture); var grantId = Guid.NewGuid().ToString(); - var permId = $"perm-{permissionCode}-{Guid.NewGuid():N}"; - var action = permissionCode.Split(':').LastOrDefault() ?? "read"; - - // First ensure the permission exists using DataProvider generated method - await tx.Insertgk_permissionAsync( - permId, - permissionCode, - resourceType, - action, - null, // description - now - ) - .ConfigureAwait(false); - // Then grant access using DataProvider generated method - await tx.Insertgk_resource_grantAsync( + // Grant access using DataProvider generated method + var grantResult = await tx.Insertgk_resource_grantAsync( grantId, userId, resourceType, @@ -552,12 +551,17 @@ await tx.Insertgk_resource_grantAsync( ) .ConfigureAwait(false); + if (grantResult is Result.Error grantErr) + { + throw new InvalidOperationException( + $"Failed to insert grant: {grantErr.Value.Message}" + ); + } + tx.Commit(); // Force WAL checkpoint to ensure changes are visible to other connections - using var wal = conn.CreateCommand(); - wal.CommandText = "PRAGMA wal_checkpoint(FULL)"; - wal.ExecuteNonQuery(); + _ = await conn.WalCheckpointAsync().ConfigureAwait(false); } /// @@ -572,26 +576,30 @@ string permissionCode ) { using var conn = OpenConnection(); - using var tx = conn.BeginTransaction(); + // Look up existing permission by code BEFORE starting transaction + var permLookupResult = await conn.GetPermissionByCodeAsync(permissionCode) + .ConfigureAwait(false); + var existingPerm = permLookupResult switch + { + GetPermissionByCodeOk ok => ok.Value.FirstOrDefault(), + GetPermissionByCodeError err => throw new InvalidOperationException( + $"Permission lookup failed: {err.Value.Message}" + ), + }; + + var permId = + existingPerm?.id + ?? throw new InvalidOperationException( + $"Permission '{permissionCode}' not found in seeded database" + ); + + using var tx = conn.BeginTransaction(); var now = DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture); var expired = DateTime.UtcNow.AddHours(-1).ToString("o", CultureInfo.InvariantCulture); var grantId = Guid.NewGuid().ToString(); - var permId = $"perm-{permissionCode}-{Guid.NewGuid():N}"; - var action = permissionCode.Split(':').LastOrDefault() ?? "read"; - - // First ensure the permission exists using DataProvider generated method - await tx.Insertgk_permissionAsync( - permId, - permissionCode, - resourceType, - action, - null, // description - now - ) - .ConfigureAwait(false); - // Then grant access with expired timestamp using DataProvider generated method + // Grant access with expired timestamp using DataProvider generated method await tx.Insertgk_resource_grantAsync( grantId, userId, @@ -607,9 +615,7 @@ await tx.Insertgk_resource_grantAsync( tx.Commit(); // Force WAL checkpoint to ensure changes are visible to other connections - using var wal = conn.CreateCommand(); - wal.CommandText = "PRAGMA wal_checkpoint(FULL)"; - wal.ExecuteNonQuery(); + _ = await conn.WalCheckpointAsync().ConfigureAwait(false); } /// Disposes the test fixture. diff --git a/Gatekeeper/Gatekeeper.Api.Tests/Gatekeeper.Api.Tests.csproj b/Gatekeeper/Gatekeeper.Api.Tests/Gatekeeper.Api.Tests.csproj index f41e3e8d..afa8fddc 100644 --- a/Gatekeeper/Gatekeeper.Api.Tests/Gatekeeper.Api.Tests.csproj +++ b/Gatekeeper/Gatekeeper.Api.Tests/Gatekeeper.Api.Tests.csproj @@ -4,7 +4,7 @@ Library true Gatekeeper.Api.Tests - CS1591;CA1707;CA1307;CA1062;CA1515;CA2100;CA1305;CA1822;CA1859;CA1848;CA1849;CA2234;CA1812;CA2007;CA2000;xUnit1030 + CS1591;CA1707;CA1307;CA1062;CA1515;CA2100;CA1822;CA1859;CA1849;CA2234;CA1812;CA2007;CA2000;xUnit1030 @@ -24,6 +24,14 @@ + + + + + + + PreserveNewest + diff --git a/Gatekeeper/Gatekeeper.Api.Tests/GlobalUsings.cs b/Gatekeeper/Gatekeeper.Api.Tests/GlobalUsings.cs index 6188f707..90439cda 100644 --- a/Gatekeeper/Gatekeeper.Api.Tests/GlobalUsings.cs +++ b/Gatekeeper/Gatekeeper.Api.Tests/GlobalUsings.cs @@ -5,14 +5,32 @@ global using System.Text.Json; global using Generated; global using Microsoft.AspNetCore.Mvc.Testing; -global using Outcome; global using Selecta; global using Xunit; -global using GetRolePermissionsOk = Outcome.Result< - System.Collections.Immutable.ImmutableList, +global using GetPermissionByCodeError = Outcome.Result< + System.Collections.Immutable.ImmutableList, Selecta.SqlError ->.Ok, Selecta.SqlError>; +>.Error< + System.Collections.Immutable.ImmutableList, + Selecta.SqlError +>; +global using GetPermissionByCodeOk = Outcome.Result< + System.Collections.Immutable.ImmutableList, + Selecta.SqlError +>.Ok, Selecta.SqlError>; global using GetRolePermissionsError = Outcome.Result< System.Collections.Immutable.ImmutableList, Selecta.SqlError >.Error, Selecta.SqlError>; +global using GetRolePermissionsOk = Outcome.Result< + System.Collections.Immutable.ImmutableList, + Selecta.SqlError +>.Ok, Selecta.SqlError>; +global using GetSessionRevokedError = Outcome.Result< + System.Collections.Immutable.ImmutableList, + Selecta.SqlError +>.Error, Selecta.SqlError>; +global using GetSessionRevokedOk = Outcome.Result< + System.Collections.Immutable.ImmutableList, + Selecta.SqlError +>.Ok, Selecta.SqlError>; diff --git a/Gatekeeper/Gatekeeper.Api.Tests/TokenServiceTests.cs b/Gatekeeper/Gatekeeper.Api.Tests/TokenServiceTests.cs index eac8e284..eee498a2 100644 --- a/Gatekeeper/Gatekeeper.Api.Tests/TokenServiceTests.cs +++ b/Gatekeeper/Gatekeeper.Api.Tests/TokenServiceTests.cs @@ -1,8 +1,9 @@ -namespace Gatekeeper.Api.Tests; - using System.Globalization; -using Generated; using Microsoft.Data.Sqlite; +using Migration; +using Migration.SQLite; + +namespace Gatekeeper.Api.Tests; /// /// Unit tests for TokenService JWT creation, validation, and revocation. @@ -263,30 +264,32 @@ public async Task ValidateTokenAsync_RevokedToken_ReturnsError() var now = DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture); var exp = DateTime.UtcNow.AddHours(1).ToString("o", CultureInfo.InvariantCulture); - // Insert user and revoked session using DataProvider methods + // Insert user and revoked session using raw SQL (consistent with other tests) using var tx = conn.BeginTransaction(); - await tx.Insertgk_userAsync( - "user-revoked", - "Revoked User", - null!, // email - now, - null!, // last_login_at - 1, // is_active - null! // metadata - ) - .ConfigureAwait(false); - await tx.Insertgk_sessionAsync( - jti, - "user-revoked", - null!, // credential_id - now, - exp, - now, - null!, // ip_address - null!, // user_agent - 1 // is_revoked = true - ) - .ConfigureAwait(false); + + using var userCmd = conn.CreateCommand(); + userCmd.Transaction = tx; + userCmd.CommandText = + @"INSERT INTO gk_user (id, display_name, email, created_at, last_login_at, is_active, metadata) + VALUES (@id, @name, @email, @now, NULL, 1, NULL)"; + userCmd.Parameters.AddWithValue("@id", "user-revoked"); + userCmd.Parameters.AddWithValue("@name", "Revoked User"); + userCmd.Parameters.AddWithValue("@email", DBNull.Value); + userCmd.Parameters.AddWithValue("@now", now); + await userCmd.ExecuteNonQueryAsync().ConfigureAwait(false); + + using var sessionCmd = conn.CreateCommand(); + sessionCmd.Transaction = tx; + sessionCmd.CommandText = + @"INSERT INTO gk_session (id, user_id, credential_id, created_at, expires_at, last_activity_at, ip_address, user_agent, is_revoked) + VALUES (@id, @user_id, NULL, @created, @expires, @activity, NULL, NULL, 1)"; + sessionCmd.Parameters.AddWithValue("@id", jti); + sessionCmd.Parameters.AddWithValue("@user_id", "user-revoked"); + sessionCmd.Parameters.AddWithValue("@created", now); + sessionCmd.Parameters.AddWithValue("@expires", exp); + sessionCmd.Parameters.AddWithValue("@activity", now); + await sessionCmd.ExecuteNonQueryAsync().ConfigureAwait(false); + tx.Commit(); var result = await TokenService.ValidateTokenAsync( @@ -324,30 +327,32 @@ public async Task ValidateTokenAsync_RevokedToken_IgnoredWhenCheckRevocationFals var now = DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture); var exp = DateTime.UtcNow.AddHours(1).ToString("o", CultureInfo.InvariantCulture); - // Insert user and revoked session using DataProvider methods + // Insert user and revoked session using raw SQL (consistent with other tests) using var tx = conn.BeginTransaction(); - await tx.Insertgk_userAsync( - "user-revoked2", - "Revoked User 2", - null!, // email - now, - null!, // last_login_at - 1, // is_active - null! // metadata - ) - .ConfigureAwait(false); - await tx.Insertgk_sessionAsync( - jti, - "user-revoked2", - null!, // credential_id - now, - exp, - now, - null!, // ip_address - null!, // user_agent - 1 // is_revoked = true - ) - .ConfigureAwait(false); + + using var userCmd = conn.CreateCommand(); + userCmd.Transaction = tx; + userCmd.CommandText = + @"INSERT INTO gk_user (id, display_name, email, created_at, last_login_at, is_active, metadata) + VALUES (@id, @name, @email, @now, NULL, 1, NULL)"; + userCmd.Parameters.AddWithValue("@id", "user-revoked2"); + userCmd.Parameters.AddWithValue("@name", "Revoked User 2"); + userCmd.Parameters.AddWithValue("@email", DBNull.Value); + userCmd.Parameters.AddWithValue("@now", now); + await userCmd.ExecuteNonQueryAsync().ConfigureAwait(false); + + using var sessionCmd = conn.CreateCommand(); + sessionCmd.Transaction = tx; + sessionCmd.CommandText = + @"INSERT INTO gk_session (id, user_id, credential_id, created_at, expires_at, last_activity_at, ip_address, user_agent, is_revoked) + VALUES (@id, @user_id, NULL, @created, @expires, @activity, NULL, NULL, 1)"; + sessionCmd.Parameters.AddWithValue("@id", jti); + sessionCmd.Parameters.AddWithValue("@user_id", "user-revoked2"); + sessionCmd.Parameters.AddWithValue("@created", now); + sessionCmd.Parameters.AddWithValue("@expires", exp); + sessionCmd.Parameters.AddWithValue("@activity", now); + await sessionCmd.ExecuteNonQueryAsync().ConfigureAwait(false); + tx.Commit(); // With checkRevocation: false, should still validate @@ -371,40 +376,46 @@ public async Task RevokeTokenAsync_SetsIsRevokedFlag() var now = DateTime.UtcNow.ToString("o", CultureInfo.InvariantCulture); var exp = DateTime.UtcNow.AddHours(1).ToString("o", CultureInfo.InvariantCulture); - // Insert user and session using DataProvider methods + // Insert user and session using raw SQL (TEXT PK doesn't return rowid) using var tx = conn.BeginTransaction(); - await tx.Insertgk_userAsync( - userId, - "Test User", - null!, // email - now, - null!, // last_login_at - 1, // is_active - null! // metadata - ) - .ConfigureAwait(false); - await tx.Insertgk_sessionAsync( - jti, - userId, - null!, // credential_id - now, - exp, - now, - null!, // ip_address - null!, // user_agent - 0 // is_revoked = false - ) - .ConfigureAwait(false); + + using var userCmd = conn.CreateCommand(); + userCmd.Transaction = tx; + userCmd.CommandText = + @"INSERT INTO gk_user (id, display_name, email, created_at, last_login_at, is_active, metadata) + VALUES (@id, @name, @email, @now, NULL, 1, NULL)"; + userCmd.Parameters.AddWithValue("@id", userId); + userCmd.Parameters.AddWithValue("@name", "Test User"); + userCmd.Parameters.AddWithValue("@email", DBNull.Value); + userCmd.Parameters.AddWithValue("@now", now); + await userCmd.ExecuteNonQueryAsync().ConfigureAwait(false); + + using var sessionCmd = conn.CreateCommand(); + sessionCmd.Transaction = tx; + sessionCmd.CommandText = + @"INSERT INTO gk_session (id, user_id, credential_id, created_at, expires_at, last_activity_at, ip_address, user_agent, is_revoked) + VALUES (@id, @user_id, NULL, @created, @expires, @activity, NULL, NULL, 0)"; + sessionCmd.Parameters.AddWithValue("@id", jti); + sessionCmd.Parameters.AddWithValue("@user_id", userId); + sessionCmd.Parameters.AddWithValue("@created", now); + sessionCmd.Parameters.AddWithValue("@expires", exp); + sessionCmd.Parameters.AddWithValue("@activity", now); + await sessionCmd.ExecuteNonQueryAsync().ConfigureAwait(false); + tx.Commit(); // Revoke await TokenService.RevokeTokenAsync(conn, jti); - // Verify - using var checkCmd = conn.CreateCommand(); - checkCmd.CommandText = "SELECT is_revoked FROM gk_session WHERE id = @jti"; - checkCmd.Parameters.AddWithValue("@jti", jti); - var isRevoked = await checkCmd.ExecuteScalarAsync(); + // Verify using DataProvider generated method + var revokedResult = await conn.GetSessionRevokedAsync(jti); + var isRevoked = revokedResult switch + { + GetSessionRevokedOk ok => ok.Value.FirstOrDefault()?.is_revoked ?? -1L, + GetSessionRevokedError err => throw new InvalidOperationException( + $"GetSessionRevoked failed: {err.Value.Message}, {err.Value.InnerException?.Message}" + ), + }; Assert.Equal(1L, isRevoked); } @@ -454,31 +465,31 @@ private static SqliteConnection CreateInMemoryDb() var conn = new SqliteConnection("Data Source=:memory:"); conn.Open(); - using var cmd = conn.CreateCommand(); - cmd.CommandText = """ - CREATE TABLE IF NOT EXISTS gk_user ( - id TEXT PRIMARY KEY, - display_name TEXT NOT NULL, - email TEXT, - created_at TEXT NOT NULL, - last_login_at TEXT, - is_active INTEGER NOT NULL DEFAULT 1, - metadata TEXT - ); - - CREATE TABLE IF NOT EXISTS gk_session ( - id TEXT PRIMARY KEY, - user_id TEXT NOT NULL REFERENCES gk_user(id) ON DELETE CASCADE, - credential_id TEXT, - created_at TEXT NOT NULL, - expires_at TEXT NOT NULL, - last_activity_at TEXT NOT NULL, - ip_address TEXT, - user_agent TEXT, - is_revoked INTEGER NOT NULL DEFAULT 0 - ); - """; - cmd.ExecuteNonQuery(); + // Use the YAML schema to create only the needed tables + // gk_credential is needed because gk_session has a FK to it + var yamlPath = Path.Combine(AppContext.BaseDirectory, "gatekeeper-schema.yaml"); + var schema = SchemaYamlSerializer.FromYamlFile(yamlPath); + var neededTables = new[] { "gk_user", "gk_credential", "gk_session" }; + + foreach (var table in schema.Tables.Where(t => neededTables.Contains(t.Name))) + { + var ddl = SqliteDdlGenerator.Generate(new CreateTableOperation(table)); + foreach ( + var statement in ddl.Split( + ';', + StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries + ) + ) + { + if (string.IsNullOrWhiteSpace(statement)) + { + continue; + } + using var cmd = conn.CreateCommand(); + cmd.CommandText = statement; + cmd.ExecuteNonQuery(); + } + } return conn; } diff --git a/Gatekeeper/Gatekeeper.Api/AuthorizationService.cs b/Gatekeeper/Gatekeeper.Api/AuthorizationService.cs index 306511b3..2f59f18c 100644 --- a/Gatekeeper/Gatekeeper.Api/AuthorizationService.cs +++ b/Gatekeeper/Gatekeeper.Api/AuthorizationService.cs @@ -1,9 +1,7 @@ -#pragma warning disable CS8509 // Non-exhaustive switch +using System.Text; namespace Gatekeeper.Api; -using System.Text; - /// /// Service for evaluating authorization decisions. /// @@ -24,17 +22,16 @@ string now // Step 1: Check resource-level grants first (most specific) if (!string.IsNullOrEmpty(resourceType) && !string.IsNullOrEmpty(resourceId)) { - // Generated param order: user_id, resource_type, resource_id, now, permission_code var grantResult = await conn.CheckResourceGrantAsync( - userId, - resourceType, - resourceId, - now, - permissionCode + now: now, + resource_id: resourceId, + user_id: userId, + resource_type: resourceType, + permission_code: permissionCode ) .ConfigureAwait(false); - if (grantResult is CheckResourceGrantOk { Value.Count: > 0 }) + if (grantResult is CheckResourceGrantOk grantOk && grantOk.Value.Count > 0) { return (true, $"resource-grant:{resourceType}/{resourceId}"); } diff --git a/Gatekeeper/Gatekeeper.Api/DatabaseSetup.cs b/Gatekeeper/Gatekeeper.Api/DatabaseSetup.cs index 04600bd5..0931d799 100644 --- a/Gatekeeper/Gatekeeper.Api/DatabaseSetup.cs +++ b/Gatekeeper/Gatekeeper.Api/DatabaseSetup.cs @@ -1,4 +1,3 @@ -using Gatekeeper.Migration; using Migration; using Migration.SQLite; @@ -20,7 +19,7 @@ public static void Initialize(SqliteConnection conn, ILogger logger) private static void CreateSchemaFromMigration(SqliteConnection conn, ILogger logger) { - logger.LogInformation("Creating database schema from GatekeeperSchema"); + logger.LogInformation("Creating database schema from gatekeeper-schema.yaml"); try { @@ -28,7 +27,11 @@ private static void CreateSchemaFromMigration(SqliteConnection conn, ILogger log using var pragmaCmd = conn.CreateCommand(); pragmaCmd.CommandText = "PRAGMA journal_mode = DELETE; PRAGMA synchronous = FULL;"; pragmaCmd.ExecuteNonQuery(); - var schema = GatekeeperSchema.Build(); + + // Load schema from YAML (source of truth) + var yamlPath = Path.Combine(AppContext.BaseDirectory, "gatekeeper-schema.yaml"); + var schema = SchemaYamlSerializer.FromYamlFile(yamlPath); + foreach (var table in schema.Tables) { var ddl = SqliteDdlGenerator.Generate(new CreateTableOperation(table)); @@ -52,9 +55,7 @@ var statement in ddl.Split( logger.LogDebug("Created table {TableName}", table.Name); } - logger.LogInformation( - "Created Gatekeeper database schema from GatekeeperSchema metadata" - ); + logger.LogInformation("Created Gatekeeper database schema from YAML"); } catch (Exception ex) { @@ -95,7 +96,11 @@ INSERT INTO gk_role (id, name, description, is_system, created_at) INSERT INTO gk_permission (id, code, resource_type, action, description, created_at) VALUES ('perm-admin-all', 'admin:*', 'admin', '*', 'Full admin access', @now), ('perm-user-profile', 'user:profile', 'user', 'read', 'View own profile', @now), - ('perm-user-credentials', 'user:credentials', 'user', 'manage', 'Manage own passkeys', @now) + ('perm-user-credentials', 'user:credentials', 'user', 'manage', 'Manage own passkeys', @now), + ('perm-patient-read', 'patient:read', 'patient', 'read', 'Read patient records', @now), + ('perm-order-read', 'order:read', 'order', 'read', 'Read order records', @now), + ('perm-sync-read', 'sync:read', 'sync', 'read', 'Read sync data', @now), + ('perm-sync-write', 'sync:write', 'sync', 'write', 'Write sync data', @now) """, ("@now", now) ); @@ -105,6 +110,8 @@ INSERT INTO gk_permission (id, code, resource_type, action, description, created """ INSERT INTO gk_role_permission (role_id, permission_id, granted_at) VALUES ('role-admin', 'perm-admin-all', @now), + ('role-admin', 'perm-sync-read', @now), + ('role-admin', 'perm-sync-write', @now), ('role-user', 'perm-user-profile', @now), ('role-user', 'perm-user-credentials', @now) """, diff --git a/Gatekeeper/Gatekeeper.Api/Gatekeeper.Api.csproj b/Gatekeeper/Gatekeeper.Api/Gatekeeper.Api.csproj index f18bc315..d5b0435e 100644 --- a/Gatekeeper/Gatekeeper.Api/Gatekeeper.Api.csproj +++ b/Gatekeeper/Gatekeeper.Api/Gatekeeper.Api.csproj @@ -2,9 +2,14 @@ Exe - CA1848;CA1515;CA2100;RS1035;CA1508;CA2234;CA1034;CA1819;CA2007;EXHAUSTION001;EPC12 + CA1515;CA2100;RS1035;CA1508;CA2234;CA1819;CA2007;EPC12 + + + + + @@ -12,26 +17,35 @@ - + - + + + PreserveNewest + + + + + + - - - - + + + + + - + diff --git a/Gatekeeper/Gatekeeper.Api/GlobalUsings.cs b/Gatekeeper/Gatekeeper.Api/GlobalUsings.cs index 871d95a5..afe3110b 100644 --- a/Gatekeeper/Gatekeeper.Api/GlobalUsings.cs +++ b/Gatekeeper/Gatekeeper.Api/GlobalUsings.cs @@ -1,7 +1,6 @@ #pragma warning disable IDE0005 // Using directive is unnecessary (some are unused but needed for tests) global using System; -global using System.Collections.Immutable; global using System.Globalization; global using System.Text.Json; global using Fido2NetLib; @@ -15,6 +14,11 @@ System.Collections.Immutable.ImmutableList, Selecta.SqlError >.Ok, Selecta.SqlError>; +// Insert result type alias +global using CountSystemRolesOk = Outcome.Result< + System.Collections.Immutable.ImmutableList, + Selecta.SqlError +>.Ok, Selecta.SqlError>; global using GetChallengeByIdOk = Outcome.Result< System.Collections.Immutable.ImmutableList, Selecta.SqlError @@ -24,6 +28,10 @@ System.Collections.Immutable.ImmutableList, Selecta.SqlError >.Ok, Selecta.SqlError>; +global using GetSessionRevokedOk = Outcome.Result< + System.Collections.Immutable.ImmutableList, + Selecta.SqlError +>.Ok, Selecta.SqlError>; // Query result type aliases global using GetUserByEmailOk = Outcome.Result< System.Collections.Immutable.ImmutableList, @@ -33,6 +41,10 @@ System.Collections.Immutable.ImmutableList, Selecta.SqlError >.Ok, Selecta.SqlError>; +global using GetUserCredentialsError = Outcome.Result< + System.Collections.Immutable.ImmutableList, + Selecta.SqlError +>.Error, Selecta.SqlError>; global using GetUserCredentialsOk = Outcome.Result< System.Collections.Immutable.ImmutableList, Selecta.SqlError @@ -45,4 +57,3 @@ System.Collections.Immutable.ImmutableList, Selecta.SqlError >.Ok, Selecta.SqlError>; -// Insert result type alias diff --git a/Gatekeeper/Gatekeeper.Api/Program.cs b/Gatekeeper/Gatekeeper.Api/Program.cs index f192d734..8a98e90e 100644 --- a/Gatekeeper/Gatekeeper.Api/Program.cs +++ b/Gatekeeper/Gatekeeper.Api/Program.cs @@ -1,4 +1,3 @@ -#pragma warning disable CS8509 // Exhaustive switch #pragma warning disable IDE0037 // Use inferred member name using System.Security.Cryptography; @@ -110,7 +109,7 @@ static SqliteConnection OpenConnection(DbConfig db) Convert.FromBase64String(c.id) )) .ToList(), - _ => [], + GetUserCredentialsError _ => [], }; var user = new Fido2User diff --git a/Gatekeeper/Gatekeeper.Api/Sql/CountSystemRoles.sql b/Gatekeeper/Gatekeeper.Api/Sql/CountSystemRoles.sql new file mode 100644 index 00000000..6239c654 --- /dev/null +++ b/Gatekeeper/Gatekeeper.Api/Sql/CountSystemRoles.sql @@ -0,0 +1,2 @@ +-- name: CountSystemRoles +SELECT COUNT(*) as cnt FROM gk_role WHERE is_system = 1; diff --git a/Gatekeeper/Gatekeeper.Api/Sql/GetPermissionByCode.sql b/Gatekeeper/Gatekeeper.Api/Sql/GetPermissionByCode.sql new file mode 100644 index 00000000..cfd75b93 --- /dev/null +++ b/Gatekeeper/Gatekeeper.Api/Sql/GetPermissionByCode.sql @@ -0,0 +1,4 @@ +-- name: GetPermissionByCode +SELECT id, code, resource_type, action, description, created_at +FROM gk_permission +WHERE code = @code; diff --git a/Gatekeeper/Gatekeeper.Api/Sql/SetPragmas.sql b/Gatekeeper/Gatekeeper.Api/Sql/SetPragmas.sql new file mode 100644 index 00000000..dad6372b --- /dev/null +++ b/Gatekeeper/Gatekeeper.Api/Sql/SetPragmas.sql @@ -0,0 +1,2 @@ +-- name: SetPragmas +PRAGMA journal_mode = DELETE; diff --git a/Gatekeeper/Gatekeeper.Api/Sql/WalCheckpoint.sql b/Gatekeeper/Gatekeeper.Api/Sql/WalCheckpoint.sql new file mode 100644 index 00000000..1a24657a --- /dev/null +++ b/Gatekeeper/Gatekeeper.Api/Sql/WalCheckpoint.sql @@ -0,0 +1,2 @@ +-- name: WalCheckpoint +PRAGMA wal_checkpoint(FULL); diff --git a/Gatekeeper/Gatekeeper.Api/gatekeeper-schema.yaml b/Gatekeeper/Gatekeeper.Api/gatekeeper-schema.yaml new file mode 100644 index 00000000..1a653cc1 --- /dev/null +++ b/Gatekeeper/Gatekeeper.Api/gatekeeper-schema.yaml @@ -0,0 +1,397 @@ +name: gatekeeper +tables: +- name: gk_user + columns: + - name: id + type: Text + - name: display_name + type: Text + - name: email + type: Text + - name: created_at + type: Text + - name: last_login_at + type: Text + - name: is_active + type: Boolean + defaultValue: 1 + - name: metadata + type: Json + indexes: + - name: idx_user_email + columns: + - email + isUnique: true + primaryKey: + name: PK_gk_user + columns: + - id +- name: gk_credential + columns: + - name: id + type: Text + - name: user_id + type: Text + - name: public_key + type: Blob + - name: sign_count + type: Int + defaultValue: 0 + - name: aaguid + type: Text + - name: credential_type + type: Text + - name: transports + type: Json + - name: attestation_format + type: Text + - name: created_at + type: Text + - name: last_used_at + type: Text + - name: device_name + type: Text + - name: is_backup_eligible + type: Boolean + - name: is_backed_up + type: Boolean + indexes: + - name: idx_credential_user + columns: + - user_id + foreignKeys: + - name: FK_gk_credential_user_id + columns: + - user_id + referencedTable: gk_user + referencedColumns: + - id + onDelete: Cascade + primaryKey: + name: PK_gk_credential + columns: + - id +- name: gk_session + columns: + - name: id + type: Text + - name: user_id + type: Text + - name: credential_id + type: Text + - name: created_at + type: Text + - name: expires_at + type: Text + - name: last_activity_at + type: Text + - name: ip_address + type: Text + - name: user_agent + type: Text + - name: is_revoked + type: Boolean + defaultValue: 0 + indexes: + - name: idx_session_user + columns: + - user_id + - name: idx_session_expires + columns: + - expires_at + foreignKeys: + - name: FK_gk_session_user_id + columns: + - user_id + referencedTable: gk_user + referencedColumns: + - id + onDelete: Cascade + - name: FK_gk_session_credential_id + columns: + - credential_id + referencedTable: gk_credential + referencedColumns: + - id + primaryKey: + name: PK_gk_session + columns: + - id +- name: gk_challenge + columns: + - name: id + type: Text + - name: user_id + type: Text + - name: challenge + type: Blob + - name: type + type: Text + - name: created_at + type: Text + - name: expires_at + type: Text + primaryKey: + name: PK_gk_challenge + columns: + - id +- name: gk_role + columns: + - name: id + type: Text + - name: name + type: Text + - name: description + type: Text + - name: is_system + type: Boolean + defaultValue: 0 + - name: created_at + type: Text + - name: parent_role_id + type: Text + indexes: + - name: idx_role_name + columns: + - name + isUnique: true + foreignKeys: + - name: FK_gk_role_parent_role_id + columns: + - parent_role_id + referencedTable: gk_role + referencedColumns: + - id + primaryKey: + name: PK_gk_role + columns: + - id +- name: gk_user_role + columns: + - name: user_id + type: Text + - name: role_id + type: Text + - name: granted_at + type: Text + - name: granted_by + type: Text + - name: expires_at + type: Text + foreignKeys: + - name: FK_gk_user_role_user_id + columns: + - user_id + referencedTable: gk_user + referencedColumns: + - id + onDelete: Cascade + - name: FK_gk_user_role_role_id + columns: + - role_id + referencedTable: gk_role + referencedColumns: + - id + onDelete: Cascade + - name: FK_gk_user_role_granted_by + columns: + - granted_by + referencedTable: gk_user + referencedColumns: + - id + primaryKey: + name: PK_gk_user_role + columns: + - user_id + - role_id +- name: gk_permission + columns: + - name: id + type: Text + - name: code + type: Text + - name: resource_type + type: Text + - name: action + type: Text + - name: description + type: Text + - name: created_at + type: Text + indexes: + - name: idx_permission_code + columns: + - code + isUnique: true + - name: idx_permission_resource + columns: + - resource_type + primaryKey: + name: PK_gk_permission + columns: + - id +- name: gk_role_permission + columns: + - name: role_id + type: Text + - name: permission_id + type: Text + - name: granted_at + type: Text + foreignKeys: + - name: FK_gk_role_permission_role_id + columns: + - role_id + referencedTable: gk_role + referencedColumns: + - id + onDelete: Cascade + - name: FK_gk_role_permission_permission_id + columns: + - permission_id + referencedTable: gk_permission + referencedColumns: + - id + onDelete: Cascade + primaryKey: + name: PK_gk_role_permission + columns: + - role_id + - permission_id +- name: gk_user_permission + columns: + - name: user_id + type: Text + - name: permission_id + type: Text + - name: scope_type + type: Text + - name: scope_value + type: Text + - name: granted_at + type: Text + - name: granted_by + type: Text + - name: expires_at + type: Text + - name: reason + type: Text + indexes: + - name: idx_user_permission + columns: + - user_id + - permission_id + - scope_value + isUnique: true + foreignKeys: + - name: FK_gk_user_permission_user_id + columns: + - user_id + referencedTable: gk_user + referencedColumns: + - id + onDelete: Cascade + - name: FK_gk_user_permission_permission_id + columns: + - permission_id + referencedTable: gk_permission + referencedColumns: + - id + onDelete: Cascade + - name: FK_gk_user_permission_granted_by + columns: + - granted_by + referencedTable: gk_user + referencedColumns: + - id +- name: gk_resource_grant + columns: + - name: id + type: Text + - name: user_id + type: Text + - name: resource_type + type: Text + - name: resource_id + type: Text + - name: permission_id + type: Text + - name: granted_at + type: Text + - name: granted_by + type: Text + - name: expires_at + type: Text + indexes: + - name: idx_resource_grant_user + columns: + - user_id + - name: idx_resource_grant_resource + columns: + - resource_type + - resource_id + foreignKeys: + - name: FK_gk_resource_grant_user_id + columns: + - user_id + referencedTable: gk_user + referencedColumns: + - id + onDelete: Cascade + - name: FK_gk_resource_grant_permission_id + columns: + - permission_id + referencedTable: gk_permission + referencedColumns: + - id + - name: FK_gk_resource_grant_granted_by + columns: + - granted_by + referencedTable: gk_user + referencedColumns: + - id + primaryKey: + name: PK_gk_resource_grant + columns: + - id + uniqueConstraints: + - name: uq_resource_grant + columns: + - user_id + - resource_type + - resource_id + - permission_id +- name: gk_policy + columns: + - name: id + type: Text + - name: name + type: Text + - name: description + type: Text + - name: resource_type + type: Text + - name: action + type: Text + - name: condition + type: Json + - name: effect + type: Text + defaultValue: "'allow'" + - name: priority + type: Int + defaultValue: 0 + - name: is_active + type: Boolean + defaultValue: 1 + - name: created_at + type: Text + indexes: + - name: idx_policy_name + columns: + - name + isUnique: true + primaryKey: + name: PK_gk_policy + columns: + - id diff --git a/Gatekeeper/Gatekeeper.Migration/Gatekeeper.Migration.csproj b/Gatekeeper/Gatekeeper.Migration/Gatekeeper.Migration.csproj deleted file mode 100644 index ad8c6900..00000000 --- a/Gatekeeper/Gatekeeper.Migration/Gatekeeper.Migration.csproj +++ /dev/null @@ -1,13 +0,0 @@ - - - - Library - Gatekeeper.Migration - $(NoWarn);CA1848;CA2254 - - - - - - - diff --git a/Gatekeeper/Gatekeeper.Migration/GatekeeperSchema.cs b/Gatekeeper/Gatekeeper.Migration/GatekeeperSchema.cs deleted file mode 100644 index 66338a29..00000000 --- a/Gatekeeper/Gatekeeper.Migration/GatekeeperSchema.cs +++ /dev/null @@ -1,215 +0,0 @@ -namespace Gatekeeper.Migration; - -using global::Migration; -using static global::Migration.PortableTypes; - -/// -/// Database schema for Gatekeeper authentication and authorization service. -/// -public static class GatekeeperSchema -{ - /// - /// Builds the complete Gatekeeper schema definition. - /// - public static SchemaDefinition Build() => - Schema - .Define("gatekeeper") - // ═══════════════════════════════════════════════════════════════ - // CORE AUTHENTICATION TABLES - // ═══════════════════════════════════════════════════════════════ - - // Users (minimal - no password!) - .Table( - "gk_user", - t => - t.Column("id", Text, c => c.PrimaryKey()) - .Column("display_name", Text, c => c.NotNull()) - .Column("email", Text) - .Column("created_at", Text, c => c.NotNull()) - .Column("last_login_at", Text) - .Column("is_active", Boolean, c => c.NotNull().Default("1")) - .Column("metadata", Json) - .Index("idx_user_email", "email", unique: true) - ) - // WebAuthn Credentials (passkeys) - .Table( - "gk_credential", - t => - t.Column("id", Text, c => c.PrimaryKey()) - .Column("user_id", Text, c => c.NotNull()) - .Column("public_key", Blob, c => c.NotNull()) - .Column("sign_count", Int, c => c.NotNull().Default("0")) - .Column("aaguid", Text) - .Column("credential_type", Text, c => c.NotNull()) - .Column("transports", Json) - .Column("attestation_format", Text) - .Column("created_at", Text, c => c.NotNull()) - .Column("last_used_at", Text) - .Column("device_name", Text) - .Column("is_backup_eligible", Boolean) - .Column("is_backed_up", Boolean) - .ForeignKey("user_id", "gk_user", "id", onDelete: ForeignKeyAction.Cascade) - .Index("idx_credential_user", "user_id") - ) - // Sessions (stateful for revocation support) - .Table( - "gk_session", - t => - t.Column("id", Text, c => c.PrimaryKey()) - .Column("user_id", Text, c => c.NotNull()) - .Column("credential_id", Text) - .Column("created_at", Text, c => c.NotNull()) - .Column("expires_at", Text, c => c.NotNull()) - .Column("last_activity_at", Text, c => c.NotNull()) - .Column("ip_address", Text) - .Column("user_agent", Text) - .Column("is_revoked", Boolean, c => c.NotNull().Default("0")) - .ForeignKey("user_id", "gk_user", "id", onDelete: ForeignKeyAction.Cascade) - .ForeignKey("credential_id", "gk_credential", "id") - .Index("idx_session_user", "user_id") - .Index("idx_session_expires", "expires_at") - ) - // WebAuthn Challenge Store (temporary, for registration/login) - .Table( - "gk_challenge", - t => - t.Column("id", Text, c => c.PrimaryKey()) - .Column("user_id", Text) - .Column("challenge", Blob, c => c.NotNull()) - .Column("type", Text, c => c.NotNull()) - .Column("created_at", Text, c => c.NotNull()) - .Column("expires_at", Text, c => c.NotNull()) - ) - // ═══════════════════════════════════════════════════════════════ - // RBAC TABLES - // ═══════════════════════════════════════════════════════════════ - - // Roles - .Table( - "gk_role", - t => - t.Column("id", Text, c => c.PrimaryKey()) - .Column("name", Text, c => c.NotNull()) - .Column("description", Text) - .Column("is_system", Boolean, c => c.NotNull().Default("0")) - .Column("created_at", Text, c => c.NotNull()) - .Column("parent_role_id", Text) - .ForeignKey("parent_role_id", "gk_role", "id") - .Index("idx_role_name", "name", unique: true) - ) - // User-Role assignments - .Table( - "gk_user_role", - t => - t.Column("user_id", Text, c => c.NotNull()) - .Column("role_id", Text, c => c.NotNull()) - .Column("granted_at", Text, c => c.NotNull()) - .Column("granted_by", Text) - .Column("expires_at", Text) - .CompositePrimaryKey("user_id", "role_id") - .ForeignKey("user_id", "gk_user", "id", onDelete: ForeignKeyAction.Cascade) - .ForeignKey("role_id", "gk_role", "id", onDelete: ForeignKeyAction.Cascade) - .ForeignKey("granted_by", "gk_user", "id") - ) - // Permissions (the actual capabilities) - .Table( - "gk_permission", - t => - t.Column("id", Text, c => c.PrimaryKey()) - .Column("code", Text, c => c.NotNull()) - .Column("resource_type", Text, c => c.NotNull()) - .Column("action", Text, c => c.NotNull()) - .Column("description", Text) - .Column("created_at", Text, c => c.NotNull()) - .Index("idx_permission_code", "code", unique: true) - .Index("idx_permission_resource", "resource_type") - ) - // Role-Permission assignments - .Table( - "gk_role_permission", - t => - t.Column("role_id", Text, c => c.NotNull()) - .Column("permission_id", Text, c => c.NotNull()) - .Column("granted_at", Text, c => c.NotNull()) - .CompositePrimaryKey("role_id", "permission_id") - .ForeignKey("role_id", "gk_role", "id", onDelete: ForeignKeyAction.Cascade) - .ForeignKey( - "permission_id", - "gk_permission", - "id", - onDelete: ForeignKeyAction.Cascade - ) - ) - // Direct user-permission grants (bypass roles for exceptions) - .Table( - "gk_user_permission", - t => - t.Column("user_id", Text, c => c.NotNull()) - .Column("permission_id", Text, c => c.NotNull()) - .Column("scope_type", Text) - .Column("scope_value", Text) - .Column("granted_at", Text, c => c.NotNull()) - .Column("granted_by", Text) - .Column("expires_at", Text) - .Column("reason", Text) - .ForeignKey("user_id", "gk_user", "id", onDelete: ForeignKeyAction.Cascade) - .ForeignKey( - "permission_id", - "gk_permission", - "id", - onDelete: ForeignKeyAction.Cascade - ) - .ForeignKey("granted_by", "gk_user", "id") - .Index( - "idx_user_permission", - ["user_id", "permission_id", "scope_value"], - unique: true - ) - ) - // ═══════════════════════════════════════════════════════════════ - // FINE-GRAINED ACCESS CONTROL - // ═══════════════════════════════════════════════════════════════ - - // Resource-level permissions (record-level access) - .Table( - "gk_resource_grant", - t => - t.Column("id", Text, c => c.PrimaryKey()) - .Column("user_id", Text, c => c.NotNull()) - .Column("resource_type", Text, c => c.NotNull()) - .Column("resource_id", Text, c => c.NotNull()) - .Column("permission_id", Text, c => c.NotNull()) - .Column("granted_at", Text, c => c.NotNull()) - .Column("granted_by", Text) - .Column("expires_at", Text) - .ForeignKey("user_id", "gk_user", "id", onDelete: ForeignKeyAction.Cascade) - .ForeignKey("permission_id", "gk_permission", "id") - .ForeignKey("granted_by", "gk_user", "id") - .Index("idx_resource_grant_user", "user_id") - .Index("idx_resource_grant_resource", ["resource_type", "resource_id"]) - .Unique( - "uq_resource_grant", - "user_id", - "resource_type", - "resource_id", - "permission_id" - ) - ) - // Policies (conditional access rules) - .Table( - "gk_policy", - t => - t.Column("id", Text, c => c.PrimaryKey()) - .Column("name", Text, c => c.NotNull()) - .Column("description", Text) - .Column("resource_type", Text, c => c.NotNull()) - .Column("action", Text, c => c.NotNull()) - .Column("condition", Json, c => c.NotNull()) - .Column("effect", Text, c => c.NotNull().Default("'allow'")) - .Column("priority", Int, c => c.NotNull().Default("0")) - .Column("is_active", Boolean, c => c.NotNull().Default("1")) - .Column("created_at", Text, c => c.NotNull()) - .Index("idx_policy_name", "name", unique: true) - ) - .Build(); -} diff --git a/Lql/Lql/Lql.csproj b/Lql/Lql/Lql.csproj index e9517db8..e7f31b61 100644 --- a/Lql/Lql/Lql.csproj +++ b/Lql/Lql/Lql.csproj @@ -2,24 +2,13 @@ Library - CA1515;CA1866;CA1310;CA1834; + + CA1515;CA1866;CA1310;CA1834;CS3021 + - - all - runtime; build; native; contentfiles; analyzers - - - - - - MSBuild:Compile - LqlTranspiler.Generated - false - true - diff --git a/Lql/Lql/Parsing/Lql.interp b/Lql/Lql/Parsing/Lql.interp new file mode 100644 index 00000000..0d9c6276 --- /dev/null +++ b/Lql/Lql/Parsing/Lql.interp @@ -0,0 +1,153 @@ +token literal names: +null +'let' +'=' +'|>' +'(' +')' +'fn' +',' +'=>' +'.' +'+' +'-' +'||' +'/' +'%' +'!=' +'<>' +'<' +'>' +'<=' +'>=' +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +'*' + +token symbolic names: +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +ASC +DESC +AND +OR +DISTINCT +EXISTS +NULL +IS +NOT +IN +AS +CASE +WHEN +THEN +ELSE +END +WITH +OVER +PARTITION +ORDER +BY +COALESCE +EXTRACT +FROM +INTERVAL +CURRENT_DATE +DATE_TRUNC +ON +PARAMETER +IDENT +INT +DECIMAL +STRING +COMMENT +WS +ASTERISK + +rule names: +program +statement +letStmt +pipeExpr +expr +windowSpec +partitionClause +orderClause +lambdaExpr +qualifiedIdent +argList +arg +columnAlias +arithmeticExpr +arithmeticTerm +arithmeticFactor +functionCall +namedArg +logicalExpr +andExpr +atomicExpr +comparison +existsExpr +nullCheckExpr +inExpr +caseExpr +whenClause +caseResult +orderDirection +comparisonOp + + +atn: +[4, 1, 56, 370, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 1, 0, 5, 0, 62, 8, 0, 10, 0, 12, 0, 65, 9, 0, 1, 0, 1, 0, 1, 1, 1, 1, 3, 1, 71, 8, 1, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 3, 5, 3, 81, 8, 3, 10, 3, 12, 3, 84, 9, 3, 1, 4, 1, 4, 1, 4, 3, 4, 89, 8, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 3, 4, 100, 8, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 3, 4, 116, 8, 4, 1, 5, 3, 5, 119, 8, 5, 1, 5, 3, 5, 122, 8, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 5, 8, 137, 8, 8, 10, 8, 12, 8, 140, 9, 8, 1, 8, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 9, 4, 9, 149, 8, 9, 11, 9, 12, 9, 150, 1, 10, 1, 10, 1, 10, 5, 10, 156, 8, 10, 10, 10, 12, 10, 159, 9, 10, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 1, 11, 3, 11, 174, 8, 11, 1, 12, 1, 12, 1, 12, 1, 12, 3, 12, 180, 8, 12, 1, 12, 1, 12, 3, 12, 184, 8, 12, 1, 13, 1, 13, 1, 13, 5, 13, 189, 8, 13, 10, 13, 12, 13, 192, 9, 13, 1, 14, 1, 14, 1, 14, 5, 14, 197, 8, 14, 10, 14, 12, 14, 200, 9, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 3, 15, 214, 8, 15, 1, 16, 1, 16, 1, 16, 3, 16, 219, 8, 16, 1, 16, 3, 16, 222, 8, 16, 1, 16, 1, 16, 1, 17, 1, 17, 1, 17, 1, 17, 3, 17, 230, 8, 17, 1, 18, 1, 18, 1, 18, 5, 18, 235, 8, 18, 10, 18, 12, 18, 238, 9, 18, 1, 19, 1, 19, 1, 19, 5, 19, 243, 8, 19, 10, 19, 12, 19, 246, 9, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 20, 3, 20, 253, 8, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 3, 21, 267, 8, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 3, 21, 277, 8, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 3, 21, 287, 8, 21, 1, 21, 1, 21, 3, 21, 291, 8, 21, 1, 21, 1, 21, 3, 21, 295, 8, 21, 1, 21, 1, 21, 3, 21, 299, 8, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 3, 21, 308, 8, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 3, 23, 318, 8, 23, 1, 23, 1, 23, 3, 23, 322, 8, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 3, 24, 329, 8, 24, 1, 24, 1, 24, 1, 24, 1, 24, 3, 24, 335, 8, 24, 1, 24, 1, 24, 1, 25, 1, 25, 4, 25, 341, 8, 25, 11, 25, 12, 25, 342, 1, 25, 1, 25, 3, 25, 347, 8, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 3, 27, 364, 8, 27, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 0, 0, 30, 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 0, 5, 1, 0, 10, 12, 2, 0, 13, 14, 56, 56, 2, 0, 48, 48, 50, 50, 1, 0, 21, 22, 2, 0, 2, 2, 15, 20, 435, 0, 63, 1, 0, 0, 0, 2, 70, 1, 0, 0, 0, 4, 72, 1, 0, 0, 0, 6, 77, 1, 0, 0, 0, 8, 115, 1, 0, 0, 0, 10, 118, 1, 0, 0, 0, 12, 123, 1, 0, 0, 0, 14, 127, 1, 0, 0, 0, 16, 131, 1, 0, 0, 0, 18, 145, 1, 0, 0, 0, 20, 152, 1, 0, 0, 0, 22, 173, 1, 0, 0, 0, 24, 179, 1, 0, 0, 0, 26, 185, 1, 0, 0, 0, 28, 193, 1, 0, 0, 0, 30, 213, 1, 0, 0, 0, 32, 215, 1, 0, 0, 0, 34, 225, 1, 0, 0, 0, 36, 231, 1, 0, 0, 0, 38, 239, 1, 0, 0, 0, 40, 252, 1, 0, 0, 0, 42, 307, 1, 0, 0, 0, 44, 309, 1, 0, 0, 0, 46, 317, 1, 0, 0, 0, 48, 328, 1, 0, 0, 0, 50, 338, 1, 0, 0, 0, 52, 350, 1, 0, 0, 0, 54, 363, 1, 0, 0, 0, 56, 365, 1, 0, 0, 0, 58, 367, 1, 0, 0, 0, 60, 62, 3, 2, 1, 0, 61, 60, 1, 0, 0, 0, 62, 65, 1, 0, 0, 0, 63, 61, 1, 0, 0, 0, 63, 64, 1, 0, 0, 0, 64, 66, 1, 0, 0, 0, 65, 63, 1, 0, 0, 0, 66, 67, 5, 0, 0, 1, 67, 1, 1, 0, 0, 0, 68, 71, 3, 4, 2, 0, 69, 71, 3, 6, 3, 0, 70, 68, 1, 0, 0, 0, 70, 69, 1, 0, 0, 0, 71, 3, 1, 0, 0, 0, 72, 73, 5, 1, 0, 0, 73, 74, 5, 50, 0, 0, 74, 75, 5, 2, 0, 0, 75, 76, 3, 6, 3, 0, 76, 5, 1, 0, 0, 0, 77, 82, 3, 8, 4, 0, 78, 79, 5, 3, 0, 0, 79, 81, 3, 8, 4, 0, 80, 78, 1, 0, 0, 0, 81, 84, 1, 0, 0, 0, 82, 80, 1, 0, 0, 0, 82, 83, 1, 0, 0, 0, 83, 7, 1, 0, 0, 0, 84, 82, 1, 0, 0, 0, 85, 86, 5, 50, 0, 0, 86, 88, 5, 4, 0, 0, 87, 89, 3, 20, 10, 0, 88, 87, 1, 0, 0, 0, 88, 89, 1, 0, 0, 0, 89, 90, 1, 0, 0, 0, 90, 91, 5, 5, 0, 0, 91, 92, 5, 38, 0, 0, 92, 93, 5, 4, 0, 0, 93, 94, 3, 10, 5, 0, 94, 95, 5, 5, 0, 0, 95, 116, 1, 0, 0, 0, 96, 97, 5, 50, 0, 0, 97, 99, 5, 4, 0, 0, 98, 100, 3, 20, 10, 0, 99, 98, 1, 0, 0, 0, 99, 100, 1, 0, 0, 0, 100, 101, 1, 0, 0, 0, 101, 116, 5, 5, 0, 0, 102, 116, 5, 50, 0, 0, 103, 104, 5, 4, 0, 0, 104, 105, 3, 6, 3, 0, 105, 106, 5, 5, 0, 0, 106, 116, 1, 0, 0, 0, 107, 116, 3, 18, 9, 0, 108, 116, 3, 16, 8, 0, 109, 116, 3, 50, 25, 0, 110, 116, 5, 51, 0, 0, 111, 116, 5, 52, 0, 0, 112, 116, 5, 56, 0, 0, 113, 116, 5, 53, 0, 0, 114, 116, 5, 49, 0, 0, 115, 85, 1, 0, 0, 0, 115, 96, 1, 0, 0, 0, 115, 102, 1, 0, 0, 0, 115, 103, 1, 0, 0, 0, 115, 107, 1, 0, 0, 0, 115, 108, 1, 0, 0, 0, 115, 109, 1, 0, 0, 0, 115, 110, 1, 0, 0, 0, 115, 111, 1, 0, 0, 0, 115, 112, 1, 0, 0, 0, 115, 113, 1, 0, 0, 0, 115, 114, 1, 0, 0, 0, 116, 9, 1, 0, 0, 0, 117, 119, 3, 12, 6, 0, 118, 117, 1, 0, 0, 0, 118, 119, 1, 0, 0, 0, 119, 121, 1, 0, 0, 0, 120, 122, 3, 14, 7, 0, 121, 120, 1, 0, 0, 0, 121, 122, 1, 0, 0, 0, 122, 11, 1, 0, 0, 0, 123, 124, 5, 39, 0, 0, 124, 125, 5, 41, 0, 0, 125, 126, 3, 20, 10, 0, 126, 13, 1, 0, 0, 0, 127, 128, 5, 40, 0, 0, 128, 129, 5, 41, 0, 0, 129, 130, 3, 20, 10, 0, 130, 15, 1, 0, 0, 0, 131, 132, 5, 6, 0, 0, 132, 133, 5, 4, 0, 0, 133, 138, 5, 50, 0, 0, 134, 135, 5, 7, 0, 0, 135, 137, 5, 50, 0, 0, 136, 134, 1, 0, 0, 0, 137, 140, 1, 0, 0, 0, 138, 136, 1, 0, 0, 0, 138, 139, 1, 0, 0, 0, 139, 141, 1, 0, 0, 0, 140, 138, 1, 0, 0, 0, 141, 142, 5, 5, 0, 0, 142, 143, 5, 8, 0, 0, 143, 144, 3, 36, 18, 0, 144, 17, 1, 0, 0, 0, 145, 148, 5, 50, 0, 0, 146, 147, 5, 9, 0, 0, 147, 149, 5, 50, 0, 0, 148, 146, 1, 0, 0, 0, 149, 150, 1, 0, 0, 0, 150, 148, 1, 0, 0, 0, 150, 151, 1, 0, 0, 0, 151, 19, 1, 0, 0, 0, 152, 157, 3, 22, 11, 0, 153, 154, 5, 7, 0, 0, 154, 156, 3, 22, 11, 0, 155, 153, 1, 0, 0, 0, 156, 159, 1, 0, 0, 0, 157, 155, 1, 0, 0, 0, 157, 158, 1, 0, 0, 0, 158, 21, 1, 0, 0, 0, 159, 157, 1, 0, 0, 0, 160, 174, 3, 24, 12, 0, 161, 174, 3, 26, 13, 0, 162, 174, 3, 32, 16, 0, 163, 174, 3, 50, 25, 0, 164, 174, 3, 8, 4, 0, 165, 174, 3, 34, 17, 0, 166, 174, 3, 42, 21, 0, 167, 174, 3, 6, 3, 0, 168, 174, 3, 16, 8, 0, 169, 170, 5, 4, 0, 0, 170, 171, 3, 6, 3, 0, 171, 172, 5, 5, 0, 0, 172, 174, 1, 0, 0, 0, 173, 160, 1, 0, 0, 0, 173, 161, 1, 0, 0, 0, 173, 162, 1, 0, 0, 0, 173, 163, 1, 0, 0, 0, 173, 164, 1, 0, 0, 0, 173, 165, 1, 0, 0, 0, 173, 166, 1, 0, 0, 0, 173, 167, 1, 0, 0, 0, 173, 168, 1, 0, 0, 0, 173, 169, 1, 0, 0, 0, 174, 23, 1, 0, 0, 0, 175, 180, 3, 26, 13, 0, 176, 180, 3, 32, 16, 0, 177, 180, 3, 18, 9, 0, 178, 180, 5, 50, 0, 0, 179, 175, 1, 0, 0, 0, 179, 176, 1, 0, 0, 0, 179, 177, 1, 0, 0, 0, 179, 178, 1, 0, 0, 0, 180, 183, 1, 0, 0, 0, 181, 182, 5, 31, 0, 0, 182, 184, 5, 50, 0, 0, 183, 181, 1, 0, 0, 0, 183, 184, 1, 0, 0, 0, 184, 25, 1, 0, 0, 0, 185, 190, 3, 28, 14, 0, 186, 187, 7, 0, 0, 0, 187, 189, 3, 28, 14, 0, 188, 186, 1, 0, 0, 0, 189, 192, 1, 0, 0, 0, 190, 188, 1, 0, 0, 0, 190, 191, 1, 0, 0, 0, 191, 27, 1, 0, 0, 0, 192, 190, 1, 0, 0, 0, 193, 198, 3, 30, 15, 0, 194, 195, 7, 1, 0, 0, 195, 197, 3, 30, 15, 0, 196, 194, 1, 0, 0, 0, 197, 200, 1, 0, 0, 0, 198, 196, 1, 0, 0, 0, 198, 199, 1, 0, 0, 0, 199, 29, 1, 0, 0, 0, 200, 198, 1, 0, 0, 0, 201, 214, 3, 18, 9, 0, 202, 214, 5, 50, 0, 0, 203, 214, 5, 51, 0, 0, 204, 214, 5, 52, 0, 0, 205, 214, 5, 53, 0, 0, 206, 214, 3, 32, 16, 0, 207, 214, 3, 50, 25, 0, 208, 214, 5, 49, 0, 0, 209, 210, 5, 4, 0, 0, 210, 211, 3, 26, 13, 0, 211, 212, 5, 5, 0, 0, 212, 214, 1, 0, 0, 0, 213, 201, 1, 0, 0, 0, 213, 202, 1, 0, 0, 0, 213, 203, 1, 0, 0, 0, 213, 204, 1, 0, 0, 0, 213, 205, 1, 0, 0, 0, 213, 206, 1, 0, 0, 0, 213, 207, 1, 0, 0, 0, 213, 208, 1, 0, 0, 0, 213, 209, 1, 0, 0, 0, 214, 31, 1, 0, 0, 0, 215, 216, 5, 50, 0, 0, 216, 221, 5, 4, 0, 0, 217, 219, 5, 25, 0, 0, 218, 217, 1, 0, 0, 0, 218, 219, 1, 0, 0, 0, 219, 220, 1, 0, 0, 0, 220, 222, 3, 20, 10, 0, 221, 218, 1, 0, 0, 0, 221, 222, 1, 0, 0, 0, 222, 223, 1, 0, 0, 0, 223, 224, 5, 5, 0, 0, 224, 33, 1, 0, 0, 0, 225, 226, 7, 2, 0, 0, 226, 229, 5, 2, 0, 0, 227, 230, 3, 42, 21, 0, 228, 230, 3, 36, 18, 0, 229, 227, 1, 0, 0, 0, 229, 228, 1, 0, 0, 0, 230, 35, 1, 0, 0, 0, 231, 236, 3, 38, 19, 0, 232, 233, 5, 24, 0, 0, 233, 235, 3, 38, 19, 0, 234, 232, 1, 0, 0, 0, 235, 238, 1, 0, 0, 0, 236, 234, 1, 0, 0, 0, 236, 237, 1, 0, 0, 0, 237, 37, 1, 0, 0, 0, 238, 236, 1, 0, 0, 0, 239, 244, 3, 40, 20, 0, 240, 241, 5, 23, 0, 0, 241, 243, 3, 40, 20, 0, 242, 240, 1, 0, 0, 0, 243, 246, 1, 0, 0, 0, 244, 242, 1, 0, 0, 0, 244, 245, 1, 0, 0, 0, 245, 39, 1, 0, 0, 0, 246, 244, 1, 0, 0, 0, 247, 253, 3, 42, 21, 0, 248, 249, 5, 4, 0, 0, 249, 250, 3, 36, 18, 0, 250, 251, 5, 5, 0, 0, 251, 253, 1, 0, 0, 0, 252, 247, 1, 0, 0, 0, 252, 248, 1, 0, 0, 0, 253, 41, 1, 0, 0, 0, 254, 255, 3, 26, 13, 0, 255, 256, 3, 58, 29, 0, 256, 257, 3, 26, 13, 0, 257, 308, 1, 0, 0, 0, 258, 259, 3, 18, 9, 0, 259, 266, 3, 58, 29, 0, 260, 267, 3, 18, 9, 0, 261, 267, 5, 53, 0, 0, 262, 267, 5, 50, 0, 0, 263, 267, 5, 51, 0, 0, 264, 267, 5, 52, 0, 0, 265, 267, 5, 49, 0, 0, 266, 260, 1, 0, 0, 0, 266, 261, 1, 0, 0, 0, 266, 262, 1, 0, 0, 0, 266, 263, 1, 0, 0, 0, 266, 264, 1, 0, 0, 0, 266, 265, 1, 0, 0, 0, 267, 308, 1, 0, 0, 0, 268, 269, 5, 50, 0, 0, 269, 276, 3, 58, 29, 0, 270, 277, 3, 18, 9, 0, 271, 277, 5, 53, 0, 0, 272, 277, 5, 50, 0, 0, 273, 277, 5, 51, 0, 0, 274, 277, 5, 52, 0, 0, 275, 277, 5, 49, 0, 0, 276, 270, 1, 0, 0, 0, 276, 271, 1, 0, 0, 0, 276, 272, 1, 0, 0, 0, 276, 273, 1, 0, 0, 0, 276, 274, 1, 0, 0, 0, 276, 275, 1, 0, 0, 0, 277, 308, 1, 0, 0, 0, 278, 279, 5, 49, 0, 0, 279, 286, 3, 58, 29, 0, 280, 287, 3, 18, 9, 0, 281, 287, 5, 53, 0, 0, 282, 287, 5, 50, 0, 0, 283, 287, 5, 51, 0, 0, 284, 287, 5, 52, 0, 0, 285, 287, 5, 49, 0, 0, 286, 280, 1, 0, 0, 0, 286, 281, 1, 0, 0, 0, 286, 282, 1, 0, 0, 0, 286, 283, 1, 0, 0, 0, 286, 284, 1, 0, 0, 0, 286, 285, 1, 0, 0, 0, 287, 308, 1, 0, 0, 0, 288, 290, 3, 18, 9, 0, 289, 291, 3, 56, 28, 0, 290, 289, 1, 0, 0, 0, 290, 291, 1, 0, 0, 0, 291, 308, 1, 0, 0, 0, 292, 294, 5, 50, 0, 0, 293, 295, 3, 56, 28, 0, 294, 293, 1, 0, 0, 0, 294, 295, 1, 0, 0, 0, 295, 308, 1, 0, 0, 0, 296, 298, 5, 49, 0, 0, 297, 299, 3, 56, 28, 0, 298, 297, 1, 0, 0, 0, 298, 299, 1, 0, 0, 0, 299, 308, 1, 0, 0, 0, 300, 308, 5, 53, 0, 0, 301, 308, 5, 51, 0, 0, 302, 308, 5, 52, 0, 0, 303, 308, 3, 8, 4, 0, 304, 308, 3, 44, 22, 0, 305, 308, 3, 46, 23, 0, 306, 308, 3, 48, 24, 0, 307, 254, 1, 0, 0, 0, 307, 258, 1, 0, 0, 0, 307, 268, 1, 0, 0, 0, 307, 278, 1, 0, 0, 0, 307, 288, 1, 0, 0, 0, 307, 292, 1, 0, 0, 0, 307, 296, 1, 0, 0, 0, 307, 300, 1, 0, 0, 0, 307, 301, 1, 0, 0, 0, 307, 302, 1, 0, 0, 0, 307, 303, 1, 0, 0, 0, 307, 304, 1, 0, 0, 0, 307, 305, 1, 0, 0, 0, 307, 306, 1, 0, 0, 0, 308, 43, 1, 0, 0, 0, 309, 310, 5, 26, 0, 0, 310, 311, 5, 4, 0, 0, 311, 312, 3, 6, 3, 0, 312, 313, 5, 5, 0, 0, 313, 45, 1, 0, 0, 0, 314, 318, 3, 18, 9, 0, 315, 318, 5, 50, 0, 0, 316, 318, 5, 49, 0, 0, 317, 314, 1, 0, 0, 0, 317, 315, 1, 0, 0, 0, 317, 316, 1, 0, 0, 0, 318, 319, 1, 0, 0, 0, 319, 321, 5, 28, 0, 0, 320, 322, 5, 29, 0, 0, 321, 320, 1, 0, 0, 0, 321, 322, 1, 0, 0, 0, 322, 323, 1, 0, 0, 0, 323, 324, 5, 27, 0, 0, 324, 47, 1, 0, 0, 0, 325, 329, 3, 18, 9, 0, 326, 329, 5, 50, 0, 0, 327, 329, 5, 49, 0, 0, 328, 325, 1, 0, 0, 0, 328, 326, 1, 0, 0, 0, 328, 327, 1, 0, 0, 0, 329, 330, 1, 0, 0, 0, 330, 331, 5, 30, 0, 0, 331, 334, 5, 4, 0, 0, 332, 335, 3, 6, 3, 0, 333, 335, 3, 20, 10, 0, 334, 332, 1, 0, 0, 0, 334, 333, 1, 0, 0, 0, 335, 336, 1, 0, 0, 0, 336, 337, 5, 5, 0, 0, 337, 49, 1, 0, 0, 0, 338, 340, 5, 32, 0, 0, 339, 341, 3, 52, 26, 0, 340, 339, 1, 0, 0, 0, 341, 342, 1, 0, 0, 0, 342, 340, 1, 0, 0, 0, 342, 343, 1, 0, 0, 0, 343, 346, 1, 0, 0, 0, 344, 345, 5, 35, 0, 0, 345, 347, 3, 54, 27, 0, 346, 344, 1, 0, 0, 0, 346, 347, 1, 0, 0, 0, 347, 348, 1, 0, 0, 0, 348, 349, 5, 36, 0, 0, 349, 51, 1, 0, 0, 0, 350, 351, 5, 33, 0, 0, 351, 352, 3, 42, 21, 0, 352, 353, 5, 34, 0, 0, 353, 354, 3, 54, 27, 0, 354, 53, 1, 0, 0, 0, 355, 364, 3, 26, 13, 0, 356, 364, 3, 42, 21, 0, 357, 364, 3, 18, 9, 0, 358, 364, 5, 50, 0, 0, 359, 364, 5, 51, 0, 0, 360, 364, 5, 52, 0, 0, 361, 364, 5, 53, 0, 0, 362, 364, 5, 49, 0, 0, 363, 355, 1, 0, 0, 0, 363, 356, 1, 0, 0, 0, 363, 357, 1, 0, 0, 0, 363, 358, 1, 0, 0, 0, 363, 359, 1, 0, 0, 0, 363, 360, 1, 0, 0, 0, 363, 361, 1, 0, 0, 0, 363, 362, 1, 0, 0, 0, 364, 55, 1, 0, 0, 0, 365, 366, 7, 3, 0, 0, 366, 57, 1, 0, 0, 0, 367, 368, 7, 4, 0, 0, 368, 59, 1, 0, 0, 0, 37, 63, 70, 82, 88, 99, 115, 118, 121, 138, 150, 157, 173, 179, 183, 190, 198, 213, 218, 221, 229, 236, 244, 252, 266, 276, 286, 290, 294, 298, 307, 317, 321, 328, 334, 342, 346, 363] \ No newline at end of file diff --git a/Lql/Lql/Parsing/Lql.tokens b/Lql/Lql/Parsing/Lql.tokens new file mode 100644 index 00000000..07fbd74d --- /dev/null +++ b/Lql/Lql/Parsing/Lql.tokens @@ -0,0 +1,77 @@ +T__0=1 +T__1=2 +T__2=3 +T__3=4 +T__4=5 +T__5=6 +T__6=7 +T__7=8 +T__8=9 +T__9=10 +T__10=11 +T__11=12 +T__12=13 +T__13=14 +T__14=15 +T__15=16 +T__16=17 +T__17=18 +T__18=19 +T__19=20 +ASC=21 +DESC=22 +AND=23 +OR=24 +DISTINCT=25 +EXISTS=26 +NULL=27 +IS=28 +NOT=29 +IN=30 +AS=31 +CASE=32 +WHEN=33 +THEN=34 +ELSE=35 +END=36 +WITH=37 +OVER=38 +PARTITION=39 +ORDER=40 +BY=41 +COALESCE=42 +EXTRACT=43 +FROM=44 +INTERVAL=45 +CURRENT_DATE=46 +DATE_TRUNC=47 +ON=48 +PARAMETER=49 +IDENT=50 +INT=51 +DECIMAL=52 +STRING=53 +COMMENT=54 +WS=55 +ASTERISK=56 +'let'=1 +'='=2 +'|>'=3 +'('=4 +')'=5 +'fn'=6 +','=7 +'=>'=8 +'.'=9 +'+'=10 +'-'=11 +'||'=12 +'/'=13 +'%'=14 +'!='=15 +'<>'=16 +'<'=17 +'>'=18 +'<='=19 +'>='=20 +'*'=56 diff --git a/Lql/Lql/Parsing/LqlBaseListener.cs b/Lql/Lql/Parsing/LqlBaseListener.cs new file mode 100644 index 00000000..a3f55897 --- /dev/null +++ b/Lql/Lql/Parsing/LqlBaseListener.cs @@ -0,0 +1,414 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// ANTLR Version: 4.13.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// Generated from Lql.g4 by ANTLR 4.13.1 + +// Unreachable code detected +#pragma warning disable 0162 +// The variable '...' is assigned but its value is never used +#pragma warning disable 0219 +// Missing XML comment for publicly visible type or member '...' +#pragma warning disable 1591 +// Ambiguous reference in cref attribute +#pragma warning disable 419 + +namespace Lql.Parsing { + + +using Antlr4.Runtime.Misc; +using IErrorNode = Antlr4.Runtime.Tree.IErrorNode; +using ITerminalNode = Antlr4.Runtime.Tree.ITerminalNode; +using IToken = Antlr4.Runtime.IToken; +using ParserRuleContext = Antlr4.Runtime.ParserRuleContext; + +/// +/// This class provides an empty implementation of , +/// which can be extended to create a listener which only needs to handle a subset +/// of the available methods. +/// +[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.13.1")] +[System.Diagnostics.DebuggerNonUserCode] +[System.CLSCompliant(false)] +public partial class LqlBaseListener : ILqlListener { + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterProgram([NotNull] LqlParser.ProgramContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitProgram([NotNull] LqlParser.ProgramContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterStatement([NotNull] LqlParser.StatementContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitStatement([NotNull] LqlParser.StatementContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterLetStmt([NotNull] LqlParser.LetStmtContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitLetStmt([NotNull] LqlParser.LetStmtContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterPipeExpr([NotNull] LqlParser.PipeExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitPipeExpr([NotNull] LqlParser.PipeExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExpr([NotNull] LqlParser.ExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExpr([NotNull] LqlParser.ExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterWindowSpec([NotNull] LqlParser.WindowSpecContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitWindowSpec([NotNull] LqlParser.WindowSpecContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterPartitionClause([NotNull] LqlParser.PartitionClauseContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitPartitionClause([NotNull] LqlParser.PartitionClauseContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOrderClause([NotNull] LqlParser.OrderClauseContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOrderClause([NotNull] LqlParser.OrderClauseContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterLambdaExpr([NotNull] LqlParser.LambdaExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitLambdaExpr([NotNull] LqlParser.LambdaExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterQualifiedIdent([NotNull] LqlParser.QualifiedIdentContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitQualifiedIdent([NotNull] LqlParser.QualifiedIdentContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterArgList([NotNull] LqlParser.ArgListContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitArgList([NotNull] LqlParser.ArgListContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterArg([NotNull] LqlParser.ArgContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitArg([NotNull] LqlParser.ArgContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterColumnAlias([NotNull] LqlParser.ColumnAliasContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitColumnAlias([NotNull] LqlParser.ColumnAliasContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterArithmeticExpr([NotNull] LqlParser.ArithmeticExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitArithmeticExpr([NotNull] LqlParser.ArithmeticExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterArithmeticTerm([NotNull] LqlParser.ArithmeticTermContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitArithmeticTerm([NotNull] LqlParser.ArithmeticTermContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterArithmeticFactor([NotNull] LqlParser.ArithmeticFactorContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitArithmeticFactor([NotNull] LqlParser.ArithmeticFactorContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterFunctionCall([NotNull] LqlParser.FunctionCallContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitFunctionCall([NotNull] LqlParser.FunctionCallContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterNamedArg([NotNull] LqlParser.NamedArgContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitNamedArg([NotNull] LqlParser.NamedArgContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterLogicalExpr([NotNull] LqlParser.LogicalExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitLogicalExpr([NotNull] LqlParser.LogicalExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterAndExpr([NotNull] LqlParser.AndExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitAndExpr([NotNull] LqlParser.AndExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterAtomicExpr([NotNull] LqlParser.AtomicExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitAtomicExpr([NotNull] LqlParser.AtomicExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterComparison([NotNull] LqlParser.ComparisonContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitComparison([NotNull] LqlParser.ComparisonContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterExistsExpr([NotNull] LqlParser.ExistsExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitExistsExpr([NotNull] LqlParser.ExistsExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterNullCheckExpr([NotNull] LqlParser.NullCheckExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitNullCheckExpr([NotNull] LqlParser.NullCheckExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterInExpr([NotNull] LqlParser.InExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitInExpr([NotNull] LqlParser.InExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCaseExpr([NotNull] LqlParser.CaseExprContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCaseExpr([NotNull] LqlParser.CaseExprContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterWhenClause([NotNull] LqlParser.WhenClauseContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitWhenClause([NotNull] LqlParser.WhenClauseContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterCaseResult([NotNull] LqlParser.CaseResultContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitCaseResult([NotNull] LqlParser.CaseResultContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterOrderDirection([NotNull] LqlParser.OrderDirectionContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitOrderDirection([NotNull] LqlParser.OrderDirectionContext context) { } + /// + /// Enter a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void EnterComparisonOp([NotNull] LqlParser.ComparisonOpContext context) { } + /// + /// Exit a parse tree produced by . + /// The default implementation does nothing. + /// + /// The parse tree. + public virtual void ExitComparisonOp([NotNull] LqlParser.ComparisonOpContext context) { } + + /// + /// The default implementation does nothing. + public virtual void EnterEveryRule([NotNull] ParserRuleContext context) { } + /// + /// The default implementation does nothing. + public virtual void ExitEveryRule([NotNull] ParserRuleContext context) { } + /// + /// The default implementation does nothing. + public virtual void VisitTerminal([NotNull] ITerminalNode node) { } + /// + /// The default implementation does nothing. + public virtual void VisitErrorNode([NotNull] IErrorNode node) { } +} +} diff --git a/Lql/Lql/Parsing/LqlBaseVisitor.cs b/Lql/Lql/Parsing/LqlBaseVisitor.cs new file mode 100644 index 00000000..1fb3e3d2 --- /dev/null +++ b/Lql/Lql/Parsing/LqlBaseVisitor.cs @@ -0,0 +1,340 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// ANTLR Version: 4.13.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// Generated from Lql.g4 by ANTLR 4.13.1 + +// Unreachable code detected +#pragma warning disable 0162 +// The variable '...' is assigned but its value is never used +#pragma warning disable 0219 +// Missing XML comment for publicly visible type or member '...' +#pragma warning disable 1591 +// Ambiguous reference in cref attribute +#pragma warning disable 419 + +namespace Lql.Parsing { + +using Antlr4.Runtime.Misc; +using Antlr4.Runtime.Tree; +using IToken = Antlr4.Runtime.IToken; +using ParserRuleContext = Antlr4.Runtime.ParserRuleContext; + +/// +/// This class provides an empty implementation of , +/// which can be extended to create a visitor which only needs to handle a subset +/// of the available methods. +/// +/// The return type of the visit operation. +[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.13.1")] +[System.Diagnostics.DebuggerNonUserCode] +[System.CLSCompliant(false)] +public partial class LqlBaseVisitor : AbstractParseTreeVisitor, ILqlVisitor { + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitProgram([NotNull] LqlParser.ProgramContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitStatement([NotNull] LqlParser.StatementContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitLetStmt([NotNull] LqlParser.LetStmtContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitPipeExpr([NotNull] LqlParser.PipeExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExpr([NotNull] LqlParser.ExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitWindowSpec([NotNull] LqlParser.WindowSpecContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitPartitionClause([NotNull] LqlParser.PartitionClauseContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOrderClause([NotNull] LqlParser.OrderClauseContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitLambdaExpr([NotNull] LqlParser.LambdaExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitQualifiedIdent([NotNull] LqlParser.QualifiedIdentContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitArgList([NotNull] LqlParser.ArgListContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitArg([NotNull] LqlParser.ArgContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitColumnAlias([NotNull] LqlParser.ColumnAliasContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitArithmeticExpr([NotNull] LqlParser.ArithmeticExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitArithmeticTerm([NotNull] LqlParser.ArithmeticTermContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitArithmeticFactor([NotNull] LqlParser.ArithmeticFactorContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitFunctionCall([NotNull] LqlParser.FunctionCallContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitNamedArg([NotNull] LqlParser.NamedArgContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitLogicalExpr([NotNull] LqlParser.LogicalExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitAndExpr([NotNull] LqlParser.AndExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitAtomicExpr([NotNull] LqlParser.AtomicExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitComparison([NotNull] LqlParser.ComparisonContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitExistsExpr([NotNull] LqlParser.ExistsExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitNullCheckExpr([NotNull] LqlParser.NullCheckExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitInExpr([NotNull] LqlParser.InExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCaseExpr([NotNull] LqlParser.CaseExprContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitWhenClause([NotNull] LqlParser.WhenClauseContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitCaseResult([NotNull] LqlParser.CaseResultContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitOrderDirection([NotNull] LqlParser.OrderDirectionContext context) { return VisitChildren(context); } + /// + /// Visit a parse tree produced by . + /// + /// The default implementation returns the result of calling + /// on . + /// + /// + /// The parse tree. + /// The visitor result. + public virtual Result VisitComparisonOp([NotNull] LqlParser.ComparisonOpContext context) { return VisitChildren(context); } +} +} diff --git a/Lql/Lql/Parsing/LqlLexer.cs b/Lql/Lql/Parsing/LqlLexer.cs new file mode 100644 index 00000000..1bfa2e5d --- /dev/null +++ b/Lql/Lql/Parsing/LqlLexer.cs @@ -0,0 +1,288 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// ANTLR Version: 4.13.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// Generated from Lql.g4 by ANTLR 4.13.1 + +// Unreachable code detected +#pragma warning disable 0162 +// The variable '...' is assigned but its value is never used +#pragma warning disable 0219 +// Missing XML comment for publicly visible type or member '...' +#pragma warning disable 1591 +// Ambiguous reference in cref attribute +#pragma warning disable 419 + +namespace Lql.Parsing { + +using System; +using System.IO; +using System.Text; +using Antlr4.Runtime; +using Antlr4.Runtime.Atn; +using Antlr4.Runtime.Misc; +using DFA = Antlr4.Runtime.Dfa.DFA; + +[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.13.1")] +[System.CLSCompliant(false)] +public partial class LqlLexer : Lexer { + protected static DFA[] decisionToDFA; + protected static PredictionContextCache sharedContextCache = new PredictionContextCache(); + public const int + T__0=1, T__1=2, T__2=3, T__3=4, T__4=5, T__5=6, T__6=7, T__7=8, T__8=9, + T__9=10, T__10=11, T__11=12, T__12=13, T__13=14, T__14=15, T__15=16, T__16=17, + T__17=18, T__18=19, T__19=20, ASC=21, DESC=22, AND=23, OR=24, DISTINCT=25, + EXISTS=26, NULL=27, IS=28, NOT=29, IN=30, AS=31, CASE=32, WHEN=33, THEN=34, + ELSE=35, END=36, WITH=37, OVER=38, PARTITION=39, ORDER=40, BY=41, COALESCE=42, + EXTRACT=43, FROM=44, INTERVAL=45, CURRENT_DATE=46, DATE_TRUNC=47, ON=48, + PARAMETER=49, IDENT=50, INT=51, DECIMAL=52, STRING=53, COMMENT=54, WS=55, + ASTERISK=56; + public static string[] channelNames = { + "DEFAULT_TOKEN_CHANNEL", "HIDDEN" + }; + + public static string[] modeNames = { + "DEFAULT_MODE" + }; + + public static readonly string[] ruleNames = { + "T__0", "T__1", "T__2", "T__3", "T__4", "T__5", "T__6", "T__7", "T__8", + "T__9", "T__10", "T__11", "T__12", "T__13", "T__14", "T__15", "T__16", + "T__17", "T__18", "T__19", "ASC", "DESC", "AND", "OR", "DISTINCT", "EXISTS", + "NULL", "IS", "NOT", "IN", "AS", "CASE", "WHEN", "THEN", "ELSE", "END", + "WITH", "OVER", "PARTITION", "ORDER", "BY", "COALESCE", "EXTRACT", "FROM", + "INTERVAL", "CURRENT_DATE", "DATE_TRUNC", "ON", "A", "B", "C", "D", "E", + "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", + "T", "U", "V", "W", "X", "Y", "Z", "PARAMETER", "IDENT", "INT", "DECIMAL", + "STRING", "COMMENT", "WS", "ASTERISK" + }; + + + public LqlLexer(ICharStream input) + : this(input, Console.Out, Console.Error) { } + + public LqlLexer(ICharStream input, TextWriter output, TextWriter errorOutput) + : base(input, output, errorOutput) + { + Interpreter = new LexerATNSimulator(this, _ATN, decisionToDFA, sharedContextCache); + } + + private static readonly string[] _LiteralNames = { + null, "'let'", "'='", "'|>'", "'('", "')'", "'fn'", "','", "'=>'", "'.'", + "'+'", "'-'", "'||'", "'/'", "'%'", "'!='", "'<>'", "'<'", "'>'", "'<='", + "'>='", null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, + "'*'" + }; + private static readonly string[] _SymbolicNames = { + null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, "ASC", "DESC", "AND", + "OR", "DISTINCT", "EXISTS", "NULL", "IS", "NOT", "IN", "AS", "CASE", "WHEN", + "THEN", "ELSE", "END", "WITH", "OVER", "PARTITION", "ORDER", "BY", "COALESCE", + "EXTRACT", "FROM", "INTERVAL", "CURRENT_DATE", "DATE_TRUNC", "ON", "PARAMETER", + "IDENT", "INT", "DECIMAL", "STRING", "COMMENT", "WS", "ASTERISK" + }; + public static readonly IVocabulary DefaultVocabulary = new Vocabulary(_LiteralNames, _SymbolicNames); + + [NotNull] + public override IVocabulary Vocabulary + { + get + { + return DefaultVocabulary; + } + } + + public override string GrammarFileName { get { return "Lql.g4"; } } + + public override string[] RuleNames { get { return ruleNames; } } + + public override string[] ChannelNames { get { return channelNames; } } + + public override string[] ModeNames { get { return modeNames; } } + + public override int[] SerializedAtn { get { return _serializedATN; } } + + static LqlLexer() { + decisionToDFA = new DFA[_ATN.NumberOfDecisions]; + for (int i = 0; i < _ATN.NumberOfDecisions; i++) { + decisionToDFA[i] = new DFA(_ATN.GetDecisionState(i), i); + } + } + private static int[] _serializedATN = { + 4,0,56,490,6,-1,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7, + 6,2,7,7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14, + 7,14,2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7,19,2,20,7,20,2,21, + 7,21,2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,2,26,7,26,2,27,7,27,2,28, + 7,28,2,29,7,29,2,30,7,30,2,31,7,31,2,32,7,32,2,33,7,33,2,34,7,34,2,35, + 7,35,2,36,7,36,2,37,7,37,2,38,7,38,2,39,7,39,2,40,7,40,2,41,7,41,2,42, + 7,42,2,43,7,43,2,44,7,44,2,45,7,45,2,46,7,46,2,47,7,47,2,48,7,48,2,49, + 7,49,2,50,7,50,2,51,7,51,2,52,7,52,2,53,7,53,2,54,7,54,2,55,7,55,2,56, + 7,56,2,57,7,57,2,58,7,58,2,59,7,59,2,60,7,60,2,61,7,61,2,62,7,62,2,63, + 7,63,2,64,7,64,2,65,7,65,2,66,7,66,2,67,7,67,2,68,7,68,2,69,7,69,2,70, + 7,70,2,71,7,71,2,72,7,72,2,73,7,73,2,74,7,74,2,75,7,75,2,76,7,76,2,77, + 7,77,2,78,7,78,2,79,7,79,2,80,7,80,2,81,7,81,1,0,1,0,1,0,1,0,1,1,1,1,1, + 2,1,2,1,2,1,3,1,3,1,4,1,4,1,5,1,5,1,5,1,6,1,6,1,7,1,7,1,7,1,8,1,8,1,9, + 1,9,1,10,1,10,1,11,1,11,1,11,1,12,1,12,1,13,1,13,1,14,1,14,1,14,1,15,1, + 15,1,15,1,16,1,16,1,17,1,17,1,18,1,18,1,18,1,19,1,19,1,19,1,20,1,20,1, + 20,1,20,1,21,1,21,1,21,1,21,1,21,1,22,1,22,1,22,1,22,1,23,1,23,1,23,1, + 24,1,24,1,24,1,24,1,24,1,24,1,24,1,24,1,24,1,25,1,25,1,25,1,25,1,25,1, + 25,1,25,1,26,1,26,1,26,1,26,1,26,1,27,1,27,1,27,1,28,1,28,1,28,1,28,1, + 29,1,29,1,29,1,30,1,30,1,30,1,31,1,31,1,31,1,31,1,31,1,32,1,32,1,32,1, + 32,1,32,1,33,1,33,1,33,1,33,1,33,1,34,1,34,1,34,1,34,1,34,1,35,1,35,1, + 35,1,35,1,36,1,36,1,36,1,36,1,36,1,37,1,37,1,37,1,37,1,37,1,38,1,38,1, + 38,1,38,1,38,1,38,1,38,1,38,1,38,1,38,1,39,1,39,1,39,1,39,1,39,1,39,1, + 40,1,40,1,40,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,41,1,42,1,42,1, + 42,1,42,1,42,1,42,1,42,1,42,1,43,1,43,1,43,1,43,1,43,1,44,1,44,1,44,1, + 44,1,44,1,44,1,44,1,44,1,44,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1,45,1, + 45,1,45,1,45,1,45,1,45,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1,46,1, + 46,1,46,1,47,1,47,1,47,1,48,1,48,1,49,1,49,1,50,1,50,1,51,1,51,1,52,1, + 52,1,53,1,53,1,54,1,54,1,55,1,55,1,56,1,56,1,57,1,57,1,58,1,58,1,59,1, + 59,1,60,1,60,1,61,1,61,1,62,1,62,1,63,1,63,1,64,1,64,1,65,1,65,1,66,1, + 66,1,67,1,67,1,68,1,68,1,69,1,69,1,70,1,70,1,71,1,71,1,72,1,72,1,73,1, + 73,1,74,1,74,1,74,5,74,432,8,74,10,74,12,74,435,9,74,1,75,1,75,5,75,439, + 8,75,10,75,12,75,442,9,75,1,76,4,76,445,8,76,11,76,12,76,446,1,77,4,77, + 450,8,77,11,77,12,77,451,1,77,1,77,4,77,456,8,77,11,77,12,77,457,1,78, + 1,78,1,78,1,78,5,78,464,8,78,10,78,12,78,467,9,78,1,78,1,78,1,79,1,79, + 1,79,1,79,5,79,475,8,79,10,79,12,79,478,9,79,1,79,1,79,1,80,4,80,483,8, + 80,11,80,12,80,484,1,80,1,80,1,81,1,81,0,0,82,1,1,3,2,5,3,7,4,9,5,11,6, + 13,7,15,8,17,9,19,10,21,11,23,12,25,13,27,14,29,15,31,16,33,17,35,18,37, + 19,39,20,41,21,43,22,45,23,47,24,49,25,51,26,53,27,55,28,57,29,59,30,61, + 31,63,32,65,33,67,34,69,35,71,36,73,37,75,38,77,39,79,40,81,41,83,42,85, + 43,87,44,89,45,91,46,93,47,95,48,97,0,99,0,101,0,103,0,105,0,107,0,109, + 0,111,0,113,0,115,0,117,0,119,0,121,0,123,0,125,0,127,0,129,0,131,0,133, + 0,135,0,137,0,139,0,141,0,143,0,145,0,147,0,149,49,151,50,153,51,155,52, + 157,53,159,54,161,55,163,56,1,0,32,2,0,65,65,97,97,2,0,66,66,98,98,2,0, + 67,67,99,99,2,0,68,68,100,100,2,0,69,69,101,101,2,0,70,70,102,102,2,0, + 71,71,103,103,2,0,72,72,104,104,2,0,73,73,105,105,2,0,74,74,106,106,2, + 0,75,75,107,107,2,0,76,76,108,108,2,0,77,77,109,109,2,0,78,78,110,110, + 2,0,79,79,111,111,2,0,80,80,112,112,2,0,81,81,113,113,2,0,82,82,114,114, + 2,0,83,83,115,115,2,0,84,84,116,116,2,0,85,85,117,117,2,0,86,86,118,118, + 2,0,87,87,119,119,2,0,88,88,120,120,2,0,89,89,121,121,2,0,90,90,122,122, + 3,0,65,90,95,95,97,122,4,0,48,57,65,90,95,95,97,122,1,0,48,57,2,0,39,39, + 92,92,2,0,10,10,13,13,3,0,9,10,13,13,32,32,472,0,1,1,0,0,0,0,3,1,0,0,0, + 0,5,1,0,0,0,0,7,1,0,0,0,0,9,1,0,0,0,0,11,1,0,0,0,0,13,1,0,0,0,0,15,1,0, + 0,0,0,17,1,0,0,0,0,19,1,0,0,0,0,21,1,0,0,0,0,23,1,0,0,0,0,25,1,0,0,0,0, + 27,1,0,0,0,0,29,1,0,0,0,0,31,1,0,0,0,0,33,1,0,0,0,0,35,1,0,0,0,0,37,1, + 0,0,0,0,39,1,0,0,0,0,41,1,0,0,0,0,43,1,0,0,0,0,45,1,0,0,0,0,47,1,0,0,0, + 0,49,1,0,0,0,0,51,1,0,0,0,0,53,1,0,0,0,0,55,1,0,0,0,0,57,1,0,0,0,0,59, + 1,0,0,0,0,61,1,0,0,0,0,63,1,0,0,0,0,65,1,0,0,0,0,67,1,0,0,0,0,69,1,0,0, + 0,0,71,1,0,0,0,0,73,1,0,0,0,0,75,1,0,0,0,0,77,1,0,0,0,0,79,1,0,0,0,0,81, + 1,0,0,0,0,83,1,0,0,0,0,85,1,0,0,0,0,87,1,0,0,0,0,89,1,0,0,0,0,91,1,0,0, + 0,0,93,1,0,0,0,0,95,1,0,0,0,0,149,1,0,0,0,0,151,1,0,0,0,0,153,1,0,0,0, + 0,155,1,0,0,0,0,157,1,0,0,0,0,159,1,0,0,0,0,161,1,0,0,0,0,163,1,0,0,0, + 1,165,1,0,0,0,3,169,1,0,0,0,5,171,1,0,0,0,7,174,1,0,0,0,9,176,1,0,0,0, + 11,178,1,0,0,0,13,181,1,0,0,0,15,183,1,0,0,0,17,186,1,0,0,0,19,188,1,0, + 0,0,21,190,1,0,0,0,23,192,1,0,0,0,25,195,1,0,0,0,27,197,1,0,0,0,29,199, + 1,0,0,0,31,202,1,0,0,0,33,205,1,0,0,0,35,207,1,0,0,0,37,209,1,0,0,0,39, + 212,1,0,0,0,41,215,1,0,0,0,43,219,1,0,0,0,45,224,1,0,0,0,47,228,1,0,0, + 0,49,231,1,0,0,0,51,240,1,0,0,0,53,247,1,0,0,0,55,252,1,0,0,0,57,255,1, + 0,0,0,59,259,1,0,0,0,61,262,1,0,0,0,63,265,1,0,0,0,65,270,1,0,0,0,67,275, + 1,0,0,0,69,280,1,0,0,0,71,285,1,0,0,0,73,289,1,0,0,0,75,294,1,0,0,0,77, + 299,1,0,0,0,79,309,1,0,0,0,81,315,1,0,0,0,83,318,1,0,0,0,85,327,1,0,0, + 0,87,335,1,0,0,0,89,340,1,0,0,0,91,349,1,0,0,0,93,362,1,0,0,0,95,373,1, + 0,0,0,97,376,1,0,0,0,99,378,1,0,0,0,101,380,1,0,0,0,103,382,1,0,0,0,105, + 384,1,0,0,0,107,386,1,0,0,0,109,388,1,0,0,0,111,390,1,0,0,0,113,392,1, + 0,0,0,115,394,1,0,0,0,117,396,1,0,0,0,119,398,1,0,0,0,121,400,1,0,0,0, + 123,402,1,0,0,0,125,404,1,0,0,0,127,406,1,0,0,0,129,408,1,0,0,0,131,410, + 1,0,0,0,133,412,1,0,0,0,135,414,1,0,0,0,137,416,1,0,0,0,139,418,1,0,0, + 0,141,420,1,0,0,0,143,422,1,0,0,0,145,424,1,0,0,0,147,426,1,0,0,0,149, + 428,1,0,0,0,151,436,1,0,0,0,153,444,1,0,0,0,155,449,1,0,0,0,157,459,1, + 0,0,0,159,470,1,0,0,0,161,482,1,0,0,0,163,488,1,0,0,0,165,166,5,108,0, + 0,166,167,5,101,0,0,167,168,5,116,0,0,168,2,1,0,0,0,169,170,5,61,0,0,170, + 4,1,0,0,0,171,172,5,124,0,0,172,173,5,62,0,0,173,6,1,0,0,0,174,175,5,40, + 0,0,175,8,1,0,0,0,176,177,5,41,0,0,177,10,1,0,0,0,178,179,5,102,0,0,179, + 180,5,110,0,0,180,12,1,0,0,0,181,182,5,44,0,0,182,14,1,0,0,0,183,184,5, + 61,0,0,184,185,5,62,0,0,185,16,1,0,0,0,186,187,5,46,0,0,187,18,1,0,0,0, + 188,189,5,43,0,0,189,20,1,0,0,0,190,191,5,45,0,0,191,22,1,0,0,0,192,193, + 5,124,0,0,193,194,5,124,0,0,194,24,1,0,0,0,195,196,5,47,0,0,196,26,1,0, + 0,0,197,198,5,37,0,0,198,28,1,0,0,0,199,200,5,33,0,0,200,201,5,61,0,0, + 201,30,1,0,0,0,202,203,5,60,0,0,203,204,5,62,0,0,204,32,1,0,0,0,205,206, + 5,60,0,0,206,34,1,0,0,0,207,208,5,62,0,0,208,36,1,0,0,0,209,210,5,60,0, + 0,210,211,5,61,0,0,211,38,1,0,0,0,212,213,5,62,0,0,213,214,5,61,0,0,214, + 40,1,0,0,0,215,216,3,97,48,0,216,217,3,133,66,0,217,218,3,101,50,0,218, + 42,1,0,0,0,219,220,3,103,51,0,220,221,3,105,52,0,221,222,3,133,66,0,222, + 223,3,101,50,0,223,44,1,0,0,0,224,225,3,97,48,0,225,226,3,123,61,0,226, + 227,3,103,51,0,227,46,1,0,0,0,228,229,3,125,62,0,229,230,3,131,65,0,230, + 48,1,0,0,0,231,232,3,103,51,0,232,233,3,113,56,0,233,234,3,133,66,0,234, + 235,3,135,67,0,235,236,3,113,56,0,236,237,3,123,61,0,237,238,3,101,50, + 0,238,239,3,135,67,0,239,50,1,0,0,0,240,241,3,105,52,0,241,242,3,143,71, + 0,242,243,3,113,56,0,243,244,3,133,66,0,244,245,3,135,67,0,245,246,3,133, + 66,0,246,52,1,0,0,0,247,248,3,123,61,0,248,249,3,137,68,0,249,250,3,119, + 59,0,250,251,3,119,59,0,251,54,1,0,0,0,252,253,3,113,56,0,253,254,3,133, + 66,0,254,56,1,0,0,0,255,256,3,123,61,0,256,257,3,125,62,0,257,258,3,135, + 67,0,258,58,1,0,0,0,259,260,3,113,56,0,260,261,3,123,61,0,261,60,1,0,0, + 0,262,263,3,97,48,0,263,264,3,133,66,0,264,62,1,0,0,0,265,266,3,101,50, + 0,266,267,3,97,48,0,267,268,3,133,66,0,268,269,3,105,52,0,269,64,1,0,0, + 0,270,271,3,141,70,0,271,272,3,111,55,0,272,273,3,105,52,0,273,274,3,123, + 61,0,274,66,1,0,0,0,275,276,3,135,67,0,276,277,3,111,55,0,277,278,3,105, + 52,0,278,279,3,123,61,0,279,68,1,0,0,0,280,281,3,105,52,0,281,282,3,119, + 59,0,282,283,3,133,66,0,283,284,3,105,52,0,284,70,1,0,0,0,285,286,3,105, + 52,0,286,287,3,123,61,0,287,288,3,103,51,0,288,72,1,0,0,0,289,290,3,141, + 70,0,290,291,3,113,56,0,291,292,3,135,67,0,292,293,3,111,55,0,293,74,1, + 0,0,0,294,295,3,125,62,0,295,296,3,139,69,0,296,297,3,105,52,0,297,298, + 3,131,65,0,298,76,1,0,0,0,299,300,3,127,63,0,300,301,3,97,48,0,301,302, + 3,131,65,0,302,303,3,135,67,0,303,304,3,113,56,0,304,305,3,135,67,0,305, + 306,3,113,56,0,306,307,3,125,62,0,307,308,3,123,61,0,308,78,1,0,0,0,309, + 310,3,125,62,0,310,311,3,131,65,0,311,312,3,103,51,0,312,313,3,105,52, + 0,313,314,3,131,65,0,314,80,1,0,0,0,315,316,3,99,49,0,316,317,3,145,72, + 0,317,82,1,0,0,0,318,319,3,101,50,0,319,320,3,125,62,0,320,321,3,97,48, + 0,321,322,3,119,59,0,322,323,3,105,52,0,323,324,3,133,66,0,324,325,3,101, + 50,0,325,326,3,105,52,0,326,84,1,0,0,0,327,328,3,105,52,0,328,329,3,143, + 71,0,329,330,3,135,67,0,330,331,3,131,65,0,331,332,3,97,48,0,332,333,3, + 101,50,0,333,334,3,135,67,0,334,86,1,0,0,0,335,336,3,107,53,0,336,337, + 3,131,65,0,337,338,3,125,62,0,338,339,3,121,60,0,339,88,1,0,0,0,340,341, + 3,113,56,0,341,342,3,123,61,0,342,343,3,135,67,0,343,344,3,105,52,0,344, + 345,3,131,65,0,345,346,3,139,69,0,346,347,3,97,48,0,347,348,3,119,59,0, + 348,90,1,0,0,0,349,350,3,101,50,0,350,351,3,137,68,0,351,352,3,131,65, + 0,352,353,3,131,65,0,353,354,3,105,52,0,354,355,3,123,61,0,355,356,3,135, + 67,0,356,357,5,95,0,0,357,358,3,103,51,0,358,359,3,97,48,0,359,360,3,135, + 67,0,360,361,3,105,52,0,361,92,1,0,0,0,362,363,3,103,51,0,363,364,3,97, + 48,0,364,365,3,135,67,0,365,366,3,105,52,0,366,367,5,95,0,0,367,368,3, + 135,67,0,368,369,3,131,65,0,369,370,3,137,68,0,370,371,3,123,61,0,371, + 372,3,101,50,0,372,94,1,0,0,0,373,374,3,125,62,0,374,375,3,123,61,0,375, + 96,1,0,0,0,376,377,7,0,0,0,377,98,1,0,0,0,378,379,7,1,0,0,379,100,1,0, + 0,0,380,381,7,2,0,0,381,102,1,0,0,0,382,383,7,3,0,0,383,104,1,0,0,0,384, + 385,7,4,0,0,385,106,1,0,0,0,386,387,7,5,0,0,387,108,1,0,0,0,388,389,7, + 6,0,0,389,110,1,0,0,0,390,391,7,7,0,0,391,112,1,0,0,0,392,393,7,8,0,0, + 393,114,1,0,0,0,394,395,7,9,0,0,395,116,1,0,0,0,396,397,7,10,0,0,397,118, + 1,0,0,0,398,399,7,11,0,0,399,120,1,0,0,0,400,401,7,12,0,0,401,122,1,0, + 0,0,402,403,7,13,0,0,403,124,1,0,0,0,404,405,7,14,0,0,405,126,1,0,0,0, + 406,407,7,15,0,0,407,128,1,0,0,0,408,409,7,16,0,0,409,130,1,0,0,0,410, + 411,7,17,0,0,411,132,1,0,0,0,412,413,7,18,0,0,413,134,1,0,0,0,414,415, + 7,19,0,0,415,136,1,0,0,0,416,417,7,20,0,0,417,138,1,0,0,0,418,419,7,21, + 0,0,419,140,1,0,0,0,420,421,7,22,0,0,421,142,1,0,0,0,422,423,7,23,0,0, + 423,144,1,0,0,0,424,425,7,24,0,0,425,146,1,0,0,0,426,427,7,25,0,0,427, + 148,1,0,0,0,428,429,5,64,0,0,429,433,7,26,0,0,430,432,7,27,0,0,431,430, + 1,0,0,0,432,435,1,0,0,0,433,431,1,0,0,0,433,434,1,0,0,0,434,150,1,0,0, + 0,435,433,1,0,0,0,436,440,7,26,0,0,437,439,7,27,0,0,438,437,1,0,0,0,439, + 442,1,0,0,0,440,438,1,0,0,0,440,441,1,0,0,0,441,152,1,0,0,0,442,440,1, + 0,0,0,443,445,7,28,0,0,444,443,1,0,0,0,445,446,1,0,0,0,446,444,1,0,0,0, + 446,447,1,0,0,0,447,154,1,0,0,0,448,450,7,28,0,0,449,448,1,0,0,0,450,451, + 1,0,0,0,451,449,1,0,0,0,451,452,1,0,0,0,452,453,1,0,0,0,453,455,5,46,0, + 0,454,456,7,28,0,0,455,454,1,0,0,0,456,457,1,0,0,0,457,455,1,0,0,0,457, + 458,1,0,0,0,458,156,1,0,0,0,459,465,5,39,0,0,460,464,8,29,0,0,461,462, + 5,92,0,0,462,464,9,0,0,0,463,460,1,0,0,0,463,461,1,0,0,0,464,467,1,0,0, + 0,465,463,1,0,0,0,465,466,1,0,0,0,466,468,1,0,0,0,467,465,1,0,0,0,468, + 469,5,39,0,0,469,158,1,0,0,0,470,471,5,45,0,0,471,472,5,45,0,0,472,476, + 1,0,0,0,473,475,8,30,0,0,474,473,1,0,0,0,475,478,1,0,0,0,476,474,1,0,0, + 0,476,477,1,0,0,0,477,479,1,0,0,0,478,476,1,0,0,0,479,480,6,79,0,0,480, + 160,1,0,0,0,481,483,7,31,0,0,482,481,1,0,0,0,483,484,1,0,0,0,484,482,1, + 0,0,0,484,485,1,0,0,0,485,486,1,0,0,0,486,487,6,80,0,0,487,162,1,0,0,0, + 488,489,5,42,0,0,489,164,1,0,0,0,10,0,433,440,446,451,457,463,465,476, + 484,1,6,0,0 + }; + + public static readonly ATN _ATN = + new ATNDeserializer().Deserialize(_serializedATN); + + +} +} diff --git a/Lql/Lql/Parsing/LqlLexer.interp b/Lql/Lql/Parsing/LqlLexer.interp new file mode 100644 index 00000000..8b282f05 --- /dev/null +++ b/Lql/Lql/Parsing/LqlLexer.interp @@ -0,0 +1,211 @@ +token literal names: +null +'let' +'=' +'|>' +'(' +')' +'fn' +',' +'=>' +'.' +'+' +'-' +'||' +'/' +'%' +'!=' +'<>' +'<' +'>' +'<=' +'>=' +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +'*' + +token symbolic names: +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +null +ASC +DESC +AND +OR +DISTINCT +EXISTS +NULL +IS +NOT +IN +AS +CASE +WHEN +THEN +ELSE +END +WITH +OVER +PARTITION +ORDER +BY +COALESCE +EXTRACT +FROM +INTERVAL +CURRENT_DATE +DATE_TRUNC +ON +PARAMETER +IDENT +INT +DECIMAL +STRING +COMMENT +WS +ASTERISK + +rule names: +T__0 +T__1 +T__2 +T__3 +T__4 +T__5 +T__6 +T__7 +T__8 +T__9 +T__10 +T__11 +T__12 +T__13 +T__14 +T__15 +T__16 +T__17 +T__18 +T__19 +ASC +DESC +AND +OR +DISTINCT +EXISTS +NULL +IS +NOT +IN +AS +CASE +WHEN +THEN +ELSE +END +WITH +OVER +PARTITION +ORDER +BY +COALESCE +EXTRACT +FROM +INTERVAL +CURRENT_DATE +DATE_TRUNC +ON +A +B +C +D +E +F +G +H +I +J +K +L +M +N +O +P +Q +R +S +T +U +V +W +X +Y +Z +PARAMETER +IDENT +INT +DECIMAL +STRING +COMMENT +WS +ASTERISK + +channel names: +DEFAULT_TOKEN_CHANNEL +HIDDEN + +mode names: +DEFAULT_MODE + +atn: +[4, 0, 56, 490, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2, 4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2, 10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15, 7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7, 20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25, 2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2, 31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36, 7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7, 41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46, 2, 47, 7, 47, 2, 48, 7, 48, 2, 49, 7, 49, 2, 50, 7, 50, 2, 51, 7, 51, 2, 52, 7, 52, 2, 53, 7, 53, 2, 54, 7, 54, 2, 55, 7, 55, 2, 56, 7, 56, 2, 57, 7, 57, 2, 58, 7, 58, 2, 59, 7, 59, 2, 60, 7, 60, 2, 61, 7, 61, 2, 62, 7, 62, 2, 63, 7, 63, 2, 64, 7, 64, 2, 65, 7, 65, 2, 66, 7, 66, 2, 67, 7, 67, 2, 68, 7, 68, 2, 69, 7, 69, 2, 70, 7, 70, 2, 71, 7, 71, 2, 72, 7, 72, 2, 73, 7, 73, 2, 74, 7, 74, 2, 75, 7, 75, 2, 76, 7, 76, 2, 77, 7, 77, 2, 78, 7, 78, 2, 79, 7, 79, 2, 80, 7, 80, 2, 81, 7, 81, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 17, 1, 17, 1, 18, 1, 18, 1, 18, 1, 19, 1, 19, 1, 19, 1, 20, 1, 20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 21, 1, 21, 1, 21, 1, 22, 1, 22, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 28, 1, 28, 1, 29, 1, 29, 1, 29, 1, 30, 1, 30, 1, 30, 1, 31, 1, 31, 1, 31, 1, 31, 1, 31, 1, 32, 1, 32, 1, 32, 1, 32, 1, 32, 1, 33, 1, 33, 1, 33, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1, 34, 1, 34, 1, 35, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 41, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 42, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 45, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 46, 1, 47, 1, 47, 1, 47, 1, 48, 1, 48, 1, 49, 1, 49, 1, 50, 1, 50, 1, 51, 1, 51, 1, 52, 1, 52, 1, 53, 1, 53, 1, 54, 1, 54, 1, 55, 1, 55, 1, 56, 1, 56, 1, 57, 1, 57, 1, 58, 1, 58, 1, 59, 1, 59, 1, 60, 1, 60, 1, 61, 1, 61, 1, 62, 1, 62, 1, 63, 1, 63, 1, 64, 1, 64, 1, 65, 1, 65, 1, 66, 1, 66, 1, 67, 1, 67, 1, 68, 1, 68, 1, 69, 1, 69, 1, 70, 1, 70, 1, 71, 1, 71, 1, 72, 1, 72, 1, 73, 1, 73, 1, 74, 1, 74, 1, 74, 5, 74, 432, 8, 74, 10, 74, 12, 74, 435, 9, 74, 1, 75, 1, 75, 5, 75, 439, 8, 75, 10, 75, 12, 75, 442, 9, 75, 1, 76, 4, 76, 445, 8, 76, 11, 76, 12, 76, 446, 1, 77, 4, 77, 450, 8, 77, 11, 77, 12, 77, 451, 1, 77, 1, 77, 4, 77, 456, 8, 77, 11, 77, 12, 77, 457, 1, 78, 1, 78, 1, 78, 1, 78, 5, 78, 464, 8, 78, 10, 78, 12, 78, 467, 9, 78, 1, 78, 1, 78, 1, 79, 1, 79, 1, 79, 1, 79, 5, 79, 475, 8, 79, 10, 79, 12, 79, 478, 9, 79, 1, 79, 1, 79, 1, 80, 4, 80, 483, 8, 80, 11, 80, 12, 80, 484, 1, 80, 1, 80, 1, 81, 1, 81, 0, 0, 82, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 29, 59, 30, 61, 31, 63, 32, 65, 33, 67, 34, 69, 35, 71, 36, 73, 37, 75, 38, 77, 39, 79, 40, 81, 41, 83, 42, 85, 43, 87, 44, 89, 45, 91, 46, 93, 47, 95, 48, 97, 0, 99, 0, 101, 0, 103, 0, 105, 0, 107, 0, 109, 0, 111, 0, 113, 0, 115, 0, 117, 0, 119, 0, 121, 0, 123, 0, 125, 0, 127, 0, 129, 0, 131, 0, 133, 0, 135, 0, 137, 0, 139, 0, 141, 0, 143, 0, 145, 0, 147, 0, 149, 49, 151, 50, 153, 51, 155, 52, 157, 53, 159, 54, 161, 55, 163, 56, 1, 0, 32, 2, 0, 65, 65, 97, 97, 2, 0, 66, 66, 98, 98, 2, 0, 67, 67, 99, 99, 2, 0, 68, 68, 100, 100, 2, 0, 69, 69, 101, 101, 2, 0, 70, 70, 102, 102, 2, 0, 71, 71, 103, 103, 2, 0, 72, 72, 104, 104, 2, 0, 73, 73, 105, 105, 2, 0, 74, 74, 106, 106, 2, 0, 75, 75, 107, 107, 2, 0, 76, 76, 108, 108, 2, 0, 77, 77, 109, 109, 2, 0, 78, 78, 110, 110, 2, 0, 79, 79, 111, 111, 2, 0, 80, 80, 112, 112, 2, 0, 81, 81, 113, 113, 2, 0, 82, 82, 114, 114, 2, 0, 83, 83, 115, 115, 2, 0, 84, 84, 116, 116, 2, 0, 85, 85, 117, 117, 2, 0, 86, 86, 118, 118, 2, 0, 87, 87, 119, 119, 2, 0, 88, 88, 120, 120, 2, 0, 89, 89, 121, 121, 2, 0, 90, 90, 122, 122, 3, 0, 65, 90, 95, 95, 97, 122, 4, 0, 48, 57, 65, 90, 95, 95, 97, 122, 1, 0, 48, 57, 2, 0, 39, 39, 92, 92, 2, 0, 10, 10, 13, 13, 3, 0, 9, 10, 13, 13, 32, 32, 472, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 57, 1, 0, 0, 0, 0, 59, 1, 0, 0, 0, 0, 61, 1, 0, 0, 0, 0, 63, 1, 0, 0, 0, 0, 65, 1, 0, 0, 0, 0, 67, 1, 0, 0, 0, 0, 69, 1, 0, 0, 0, 0, 71, 1, 0, 0, 0, 0, 73, 1, 0, 0, 0, 0, 75, 1, 0, 0, 0, 0, 77, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89, 1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0, 0, 0, 95, 1, 0, 0, 0, 0, 149, 1, 0, 0, 0, 0, 151, 1, 0, 0, 0, 0, 153, 1, 0, 0, 0, 0, 155, 1, 0, 0, 0, 0, 157, 1, 0, 0, 0, 0, 159, 1, 0, 0, 0, 0, 161, 1, 0, 0, 0, 0, 163, 1, 0, 0, 0, 1, 165, 1, 0, 0, 0, 3, 169, 1, 0, 0, 0, 5, 171, 1, 0, 0, 0, 7, 174, 1, 0, 0, 0, 9, 176, 1, 0, 0, 0, 11, 178, 1, 0, 0, 0, 13, 181, 1, 0, 0, 0, 15, 183, 1, 0, 0, 0, 17, 186, 1, 0, 0, 0, 19, 188, 1, 0, 0, 0, 21, 190, 1, 0, 0, 0, 23, 192, 1, 0, 0, 0, 25, 195, 1, 0, 0, 0, 27, 197, 1, 0, 0, 0, 29, 199, 1, 0, 0, 0, 31, 202, 1, 0, 0, 0, 33, 205, 1, 0, 0, 0, 35, 207, 1, 0, 0, 0, 37, 209, 1, 0, 0, 0, 39, 212, 1, 0, 0, 0, 41, 215, 1, 0, 0, 0, 43, 219, 1, 0, 0, 0, 45, 224, 1, 0, 0, 0, 47, 228, 1, 0, 0, 0, 49, 231, 1, 0, 0, 0, 51, 240, 1, 0, 0, 0, 53, 247, 1, 0, 0, 0, 55, 252, 1, 0, 0, 0, 57, 255, 1, 0, 0, 0, 59, 259, 1, 0, 0, 0, 61, 262, 1, 0, 0, 0, 63, 265, 1, 0, 0, 0, 65, 270, 1, 0, 0, 0, 67, 275, 1, 0, 0, 0, 69, 280, 1, 0, 0, 0, 71, 285, 1, 0, 0, 0, 73, 289, 1, 0, 0, 0, 75, 294, 1, 0, 0, 0, 77, 299, 1, 0, 0, 0, 79, 309, 1, 0, 0, 0, 81, 315, 1, 0, 0, 0, 83, 318, 1, 0, 0, 0, 85, 327, 1, 0, 0, 0, 87, 335, 1, 0, 0, 0, 89, 340, 1, 0, 0, 0, 91, 349, 1, 0, 0, 0, 93, 362, 1, 0, 0, 0, 95, 373, 1, 0, 0, 0, 97, 376, 1, 0, 0, 0, 99, 378, 1, 0, 0, 0, 101, 380, 1, 0, 0, 0, 103, 382, 1, 0, 0, 0, 105, 384, 1, 0, 0, 0, 107, 386, 1, 0, 0, 0, 109, 388, 1, 0, 0, 0, 111, 390, 1, 0, 0, 0, 113, 392, 1, 0, 0, 0, 115, 394, 1, 0, 0, 0, 117, 396, 1, 0, 0, 0, 119, 398, 1, 0, 0, 0, 121, 400, 1, 0, 0, 0, 123, 402, 1, 0, 0, 0, 125, 404, 1, 0, 0, 0, 127, 406, 1, 0, 0, 0, 129, 408, 1, 0, 0, 0, 131, 410, 1, 0, 0, 0, 133, 412, 1, 0, 0, 0, 135, 414, 1, 0, 0, 0, 137, 416, 1, 0, 0, 0, 139, 418, 1, 0, 0, 0, 141, 420, 1, 0, 0, 0, 143, 422, 1, 0, 0, 0, 145, 424, 1, 0, 0, 0, 147, 426, 1, 0, 0, 0, 149, 428, 1, 0, 0, 0, 151, 436, 1, 0, 0, 0, 153, 444, 1, 0, 0, 0, 155, 449, 1, 0, 0, 0, 157, 459, 1, 0, 0, 0, 159, 470, 1, 0, 0, 0, 161, 482, 1, 0, 0, 0, 163, 488, 1, 0, 0, 0, 165, 166, 5, 108, 0, 0, 166, 167, 5, 101, 0, 0, 167, 168, 5, 116, 0, 0, 168, 2, 1, 0, 0, 0, 169, 170, 5, 61, 0, 0, 170, 4, 1, 0, 0, 0, 171, 172, 5, 124, 0, 0, 172, 173, 5, 62, 0, 0, 173, 6, 1, 0, 0, 0, 174, 175, 5, 40, 0, 0, 175, 8, 1, 0, 0, 0, 176, 177, 5, 41, 0, 0, 177, 10, 1, 0, 0, 0, 178, 179, 5, 102, 0, 0, 179, 180, 5, 110, 0, 0, 180, 12, 1, 0, 0, 0, 181, 182, 5, 44, 0, 0, 182, 14, 1, 0, 0, 0, 183, 184, 5, 61, 0, 0, 184, 185, 5, 62, 0, 0, 185, 16, 1, 0, 0, 0, 186, 187, 5, 46, 0, 0, 187, 18, 1, 0, 0, 0, 188, 189, 5, 43, 0, 0, 189, 20, 1, 0, 0, 0, 190, 191, 5, 45, 0, 0, 191, 22, 1, 0, 0, 0, 192, 193, 5, 124, 0, 0, 193, 194, 5, 124, 0, 0, 194, 24, 1, 0, 0, 0, 195, 196, 5, 47, 0, 0, 196, 26, 1, 0, 0, 0, 197, 198, 5, 37, 0, 0, 198, 28, 1, 0, 0, 0, 199, 200, 5, 33, 0, 0, 200, 201, 5, 61, 0, 0, 201, 30, 1, 0, 0, 0, 202, 203, 5, 60, 0, 0, 203, 204, 5, 62, 0, 0, 204, 32, 1, 0, 0, 0, 205, 206, 5, 60, 0, 0, 206, 34, 1, 0, 0, 0, 207, 208, 5, 62, 0, 0, 208, 36, 1, 0, 0, 0, 209, 210, 5, 60, 0, 0, 210, 211, 5, 61, 0, 0, 211, 38, 1, 0, 0, 0, 212, 213, 5, 62, 0, 0, 213, 214, 5, 61, 0, 0, 214, 40, 1, 0, 0, 0, 215, 216, 3, 97, 48, 0, 216, 217, 3, 133, 66, 0, 217, 218, 3, 101, 50, 0, 218, 42, 1, 0, 0, 0, 219, 220, 3, 103, 51, 0, 220, 221, 3, 105, 52, 0, 221, 222, 3, 133, 66, 0, 222, 223, 3, 101, 50, 0, 223, 44, 1, 0, 0, 0, 224, 225, 3, 97, 48, 0, 225, 226, 3, 123, 61, 0, 226, 227, 3, 103, 51, 0, 227, 46, 1, 0, 0, 0, 228, 229, 3, 125, 62, 0, 229, 230, 3, 131, 65, 0, 230, 48, 1, 0, 0, 0, 231, 232, 3, 103, 51, 0, 232, 233, 3, 113, 56, 0, 233, 234, 3, 133, 66, 0, 234, 235, 3, 135, 67, 0, 235, 236, 3, 113, 56, 0, 236, 237, 3, 123, 61, 0, 237, 238, 3, 101, 50, 0, 238, 239, 3, 135, 67, 0, 239, 50, 1, 0, 0, 0, 240, 241, 3, 105, 52, 0, 241, 242, 3, 143, 71, 0, 242, 243, 3, 113, 56, 0, 243, 244, 3, 133, 66, 0, 244, 245, 3, 135, 67, 0, 245, 246, 3, 133, 66, 0, 246, 52, 1, 0, 0, 0, 247, 248, 3, 123, 61, 0, 248, 249, 3, 137, 68, 0, 249, 250, 3, 119, 59, 0, 250, 251, 3, 119, 59, 0, 251, 54, 1, 0, 0, 0, 252, 253, 3, 113, 56, 0, 253, 254, 3, 133, 66, 0, 254, 56, 1, 0, 0, 0, 255, 256, 3, 123, 61, 0, 256, 257, 3, 125, 62, 0, 257, 258, 3, 135, 67, 0, 258, 58, 1, 0, 0, 0, 259, 260, 3, 113, 56, 0, 260, 261, 3, 123, 61, 0, 261, 60, 1, 0, 0, 0, 262, 263, 3, 97, 48, 0, 263, 264, 3, 133, 66, 0, 264, 62, 1, 0, 0, 0, 265, 266, 3, 101, 50, 0, 266, 267, 3, 97, 48, 0, 267, 268, 3, 133, 66, 0, 268, 269, 3, 105, 52, 0, 269, 64, 1, 0, 0, 0, 270, 271, 3, 141, 70, 0, 271, 272, 3, 111, 55, 0, 272, 273, 3, 105, 52, 0, 273, 274, 3, 123, 61, 0, 274, 66, 1, 0, 0, 0, 275, 276, 3, 135, 67, 0, 276, 277, 3, 111, 55, 0, 277, 278, 3, 105, 52, 0, 278, 279, 3, 123, 61, 0, 279, 68, 1, 0, 0, 0, 280, 281, 3, 105, 52, 0, 281, 282, 3, 119, 59, 0, 282, 283, 3, 133, 66, 0, 283, 284, 3, 105, 52, 0, 284, 70, 1, 0, 0, 0, 285, 286, 3, 105, 52, 0, 286, 287, 3, 123, 61, 0, 287, 288, 3, 103, 51, 0, 288, 72, 1, 0, 0, 0, 289, 290, 3, 141, 70, 0, 290, 291, 3, 113, 56, 0, 291, 292, 3, 135, 67, 0, 292, 293, 3, 111, 55, 0, 293, 74, 1, 0, 0, 0, 294, 295, 3, 125, 62, 0, 295, 296, 3, 139, 69, 0, 296, 297, 3, 105, 52, 0, 297, 298, 3, 131, 65, 0, 298, 76, 1, 0, 0, 0, 299, 300, 3, 127, 63, 0, 300, 301, 3, 97, 48, 0, 301, 302, 3, 131, 65, 0, 302, 303, 3, 135, 67, 0, 303, 304, 3, 113, 56, 0, 304, 305, 3, 135, 67, 0, 305, 306, 3, 113, 56, 0, 306, 307, 3, 125, 62, 0, 307, 308, 3, 123, 61, 0, 308, 78, 1, 0, 0, 0, 309, 310, 3, 125, 62, 0, 310, 311, 3, 131, 65, 0, 311, 312, 3, 103, 51, 0, 312, 313, 3, 105, 52, 0, 313, 314, 3, 131, 65, 0, 314, 80, 1, 0, 0, 0, 315, 316, 3, 99, 49, 0, 316, 317, 3, 145, 72, 0, 317, 82, 1, 0, 0, 0, 318, 319, 3, 101, 50, 0, 319, 320, 3, 125, 62, 0, 320, 321, 3, 97, 48, 0, 321, 322, 3, 119, 59, 0, 322, 323, 3, 105, 52, 0, 323, 324, 3, 133, 66, 0, 324, 325, 3, 101, 50, 0, 325, 326, 3, 105, 52, 0, 326, 84, 1, 0, 0, 0, 327, 328, 3, 105, 52, 0, 328, 329, 3, 143, 71, 0, 329, 330, 3, 135, 67, 0, 330, 331, 3, 131, 65, 0, 331, 332, 3, 97, 48, 0, 332, 333, 3, 101, 50, 0, 333, 334, 3, 135, 67, 0, 334, 86, 1, 0, 0, 0, 335, 336, 3, 107, 53, 0, 336, 337, 3, 131, 65, 0, 337, 338, 3, 125, 62, 0, 338, 339, 3, 121, 60, 0, 339, 88, 1, 0, 0, 0, 340, 341, 3, 113, 56, 0, 341, 342, 3, 123, 61, 0, 342, 343, 3, 135, 67, 0, 343, 344, 3, 105, 52, 0, 344, 345, 3, 131, 65, 0, 345, 346, 3, 139, 69, 0, 346, 347, 3, 97, 48, 0, 347, 348, 3, 119, 59, 0, 348, 90, 1, 0, 0, 0, 349, 350, 3, 101, 50, 0, 350, 351, 3, 137, 68, 0, 351, 352, 3, 131, 65, 0, 352, 353, 3, 131, 65, 0, 353, 354, 3, 105, 52, 0, 354, 355, 3, 123, 61, 0, 355, 356, 3, 135, 67, 0, 356, 357, 5, 95, 0, 0, 357, 358, 3, 103, 51, 0, 358, 359, 3, 97, 48, 0, 359, 360, 3, 135, 67, 0, 360, 361, 3, 105, 52, 0, 361, 92, 1, 0, 0, 0, 362, 363, 3, 103, 51, 0, 363, 364, 3, 97, 48, 0, 364, 365, 3, 135, 67, 0, 365, 366, 3, 105, 52, 0, 366, 367, 5, 95, 0, 0, 367, 368, 3, 135, 67, 0, 368, 369, 3, 131, 65, 0, 369, 370, 3, 137, 68, 0, 370, 371, 3, 123, 61, 0, 371, 372, 3, 101, 50, 0, 372, 94, 1, 0, 0, 0, 373, 374, 3, 125, 62, 0, 374, 375, 3, 123, 61, 0, 375, 96, 1, 0, 0, 0, 376, 377, 7, 0, 0, 0, 377, 98, 1, 0, 0, 0, 378, 379, 7, 1, 0, 0, 379, 100, 1, 0, 0, 0, 380, 381, 7, 2, 0, 0, 381, 102, 1, 0, 0, 0, 382, 383, 7, 3, 0, 0, 383, 104, 1, 0, 0, 0, 384, 385, 7, 4, 0, 0, 385, 106, 1, 0, 0, 0, 386, 387, 7, 5, 0, 0, 387, 108, 1, 0, 0, 0, 388, 389, 7, 6, 0, 0, 389, 110, 1, 0, 0, 0, 390, 391, 7, 7, 0, 0, 391, 112, 1, 0, 0, 0, 392, 393, 7, 8, 0, 0, 393, 114, 1, 0, 0, 0, 394, 395, 7, 9, 0, 0, 395, 116, 1, 0, 0, 0, 396, 397, 7, 10, 0, 0, 397, 118, 1, 0, 0, 0, 398, 399, 7, 11, 0, 0, 399, 120, 1, 0, 0, 0, 400, 401, 7, 12, 0, 0, 401, 122, 1, 0, 0, 0, 402, 403, 7, 13, 0, 0, 403, 124, 1, 0, 0, 0, 404, 405, 7, 14, 0, 0, 405, 126, 1, 0, 0, 0, 406, 407, 7, 15, 0, 0, 407, 128, 1, 0, 0, 0, 408, 409, 7, 16, 0, 0, 409, 130, 1, 0, 0, 0, 410, 411, 7, 17, 0, 0, 411, 132, 1, 0, 0, 0, 412, 413, 7, 18, 0, 0, 413, 134, 1, 0, 0, 0, 414, 415, 7, 19, 0, 0, 415, 136, 1, 0, 0, 0, 416, 417, 7, 20, 0, 0, 417, 138, 1, 0, 0, 0, 418, 419, 7, 21, 0, 0, 419, 140, 1, 0, 0, 0, 420, 421, 7, 22, 0, 0, 421, 142, 1, 0, 0, 0, 422, 423, 7, 23, 0, 0, 423, 144, 1, 0, 0, 0, 424, 425, 7, 24, 0, 0, 425, 146, 1, 0, 0, 0, 426, 427, 7, 25, 0, 0, 427, 148, 1, 0, 0, 0, 428, 429, 5, 64, 0, 0, 429, 433, 7, 26, 0, 0, 430, 432, 7, 27, 0, 0, 431, 430, 1, 0, 0, 0, 432, 435, 1, 0, 0, 0, 433, 431, 1, 0, 0, 0, 433, 434, 1, 0, 0, 0, 434, 150, 1, 0, 0, 0, 435, 433, 1, 0, 0, 0, 436, 440, 7, 26, 0, 0, 437, 439, 7, 27, 0, 0, 438, 437, 1, 0, 0, 0, 439, 442, 1, 0, 0, 0, 440, 438, 1, 0, 0, 0, 440, 441, 1, 0, 0, 0, 441, 152, 1, 0, 0, 0, 442, 440, 1, 0, 0, 0, 443, 445, 7, 28, 0, 0, 444, 443, 1, 0, 0, 0, 445, 446, 1, 0, 0, 0, 446, 444, 1, 0, 0, 0, 446, 447, 1, 0, 0, 0, 447, 154, 1, 0, 0, 0, 448, 450, 7, 28, 0, 0, 449, 448, 1, 0, 0, 0, 450, 451, 1, 0, 0, 0, 451, 449, 1, 0, 0, 0, 451, 452, 1, 0, 0, 0, 452, 453, 1, 0, 0, 0, 453, 455, 5, 46, 0, 0, 454, 456, 7, 28, 0, 0, 455, 454, 1, 0, 0, 0, 456, 457, 1, 0, 0, 0, 457, 455, 1, 0, 0, 0, 457, 458, 1, 0, 0, 0, 458, 156, 1, 0, 0, 0, 459, 465, 5, 39, 0, 0, 460, 464, 8, 29, 0, 0, 461, 462, 5, 92, 0, 0, 462, 464, 9, 0, 0, 0, 463, 460, 1, 0, 0, 0, 463, 461, 1, 0, 0, 0, 464, 467, 1, 0, 0, 0, 465, 463, 1, 0, 0, 0, 465, 466, 1, 0, 0, 0, 466, 468, 1, 0, 0, 0, 467, 465, 1, 0, 0, 0, 468, 469, 5, 39, 0, 0, 469, 158, 1, 0, 0, 0, 470, 471, 5, 45, 0, 0, 471, 472, 5, 45, 0, 0, 472, 476, 1, 0, 0, 0, 473, 475, 8, 30, 0, 0, 474, 473, 1, 0, 0, 0, 475, 478, 1, 0, 0, 0, 476, 474, 1, 0, 0, 0, 476, 477, 1, 0, 0, 0, 477, 479, 1, 0, 0, 0, 478, 476, 1, 0, 0, 0, 479, 480, 6, 79, 0, 0, 480, 160, 1, 0, 0, 0, 481, 483, 7, 31, 0, 0, 482, 481, 1, 0, 0, 0, 483, 484, 1, 0, 0, 0, 484, 482, 1, 0, 0, 0, 484, 485, 1, 0, 0, 0, 485, 486, 1, 0, 0, 0, 486, 487, 6, 80, 0, 0, 487, 162, 1, 0, 0, 0, 488, 489, 5, 42, 0, 0, 489, 164, 1, 0, 0, 0, 10, 0, 433, 440, 446, 451, 457, 463, 465, 476, 484, 1, 6, 0, 0] \ No newline at end of file diff --git a/Lql/Lql/Parsing/LqlLexer.tokens b/Lql/Lql/Parsing/LqlLexer.tokens new file mode 100644 index 00000000..07fbd74d --- /dev/null +++ b/Lql/Lql/Parsing/LqlLexer.tokens @@ -0,0 +1,77 @@ +T__0=1 +T__1=2 +T__2=3 +T__3=4 +T__4=5 +T__5=6 +T__6=7 +T__7=8 +T__8=9 +T__9=10 +T__10=11 +T__11=12 +T__12=13 +T__13=14 +T__14=15 +T__15=16 +T__16=17 +T__17=18 +T__18=19 +T__19=20 +ASC=21 +DESC=22 +AND=23 +OR=24 +DISTINCT=25 +EXISTS=26 +NULL=27 +IS=28 +NOT=29 +IN=30 +AS=31 +CASE=32 +WHEN=33 +THEN=34 +ELSE=35 +END=36 +WITH=37 +OVER=38 +PARTITION=39 +ORDER=40 +BY=41 +COALESCE=42 +EXTRACT=43 +FROM=44 +INTERVAL=45 +CURRENT_DATE=46 +DATE_TRUNC=47 +ON=48 +PARAMETER=49 +IDENT=50 +INT=51 +DECIMAL=52 +STRING=53 +COMMENT=54 +WS=55 +ASTERISK=56 +'let'=1 +'='=2 +'|>'=3 +'('=4 +')'=5 +'fn'=6 +','=7 +'=>'=8 +'.'=9 +'+'=10 +'-'=11 +'||'=12 +'/'=13 +'%'=14 +'!='=15 +'<>'=16 +'<'=17 +'>'=18 +'<='=19 +'>='=20 +'*'=56 diff --git a/Lql/Lql/Parsing/LqlListener.cs b/Lql/Lql/Parsing/LqlListener.cs new file mode 100644 index 00000000..918aac4a --- /dev/null +++ b/Lql/Lql/Parsing/LqlListener.cs @@ -0,0 +1,336 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// ANTLR Version: 4.13.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// Generated from Lql.g4 by ANTLR 4.13.1 + +// Unreachable code detected +#pragma warning disable 0162 +// The variable '...' is assigned but its value is never used +#pragma warning disable 0219 +// Missing XML comment for publicly visible type or member '...' +#pragma warning disable 1591 +// Ambiguous reference in cref attribute +#pragma warning disable 419 + +namespace Lql.Parsing { + +using Antlr4.Runtime.Misc; +using IParseTreeListener = Antlr4.Runtime.Tree.IParseTreeListener; +using IToken = Antlr4.Runtime.IToken; + +/// +/// This interface defines a complete listener for a parse tree produced by +/// . +/// +[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.13.1")] +[System.CLSCompliant(false)] +public interface ILqlListener : IParseTreeListener { + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterProgram([NotNull] LqlParser.ProgramContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitProgram([NotNull] LqlParser.ProgramContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterStatement([NotNull] LqlParser.StatementContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitStatement([NotNull] LqlParser.StatementContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterLetStmt([NotNull] LqlParser.LetStmtContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitLetStmt([NotNull] LqlParser.LetStmtContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterPipeExpr([NotNull] LqlParser.PipeExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitPipeExpr([NotNull] LqlParser.PipeExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExpr([NotNull] LqlParser.ExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExpr([NotNull] LqlParser.ExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterWindowSpec([NotNull] LqlParser.WindowSpecContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitWindowSpec([NotNull] LqlParser.WindowSpecContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterPartitionClause([NotNull] LqlParser.PartitionClauseContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitPartitionClause([NotNull] LqlParser.PartitionClauseContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOrderClause([NotNull] LqlParser.OrderClauseContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOrderClause([NotNull] LqlParser.OrderClauseContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterLambdaExpr([NotNull] LqlParser.LambdaExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitLambdaExpr([NotNull] LqlParser.LambdaExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterQualifiedIdent([NotNull] LqlParser.QualifiedIdentContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitQualifiedIdent([NotNull] LqlParser.QualifiedIdentContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterArgList([NotNull] LqlParser.ArgListContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitArgList([NotNull] LqlParser.ArgListContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterArg([NotNull] LqlParser.ArgContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitArg([NotNull] LqlParser.ArgContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterColumnAlias([NotNull] LqlParser.ColumnAliasContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitColumnAlias([NotNull] LqlParser.ColumnAliasContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterArithmeticExpr([NotNull] LqlParser.ArithmeticExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitArithmeticExpr([NotNull] LqlParser.ArithmeticExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterArithmeticTerm([NotNull] LqlParser.ArithmeticTermContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitArithmeticTerm([NotNull] LqlParser.ArithmeticTermContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterArithmeticFactor([NotNull] LqlParser.ArithmeticFactorContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitArithmeticFactor([NotNull] LqlParser.ArithmeticFactorContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterFunctionCall([NotNull] LqlParser.FunctionCallContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitFunctionCall([NotNull] LqlParser.FunctionCallContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterNamedArg([NotNull] LqlParser.NamedArgContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitNamedArg([NotNull] LqlParser.NamedArgContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterLogicalExpr([NotNull] LqlParser.LogicalExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitLogicalExpr([NotNull] LqlParser.LogicalExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterAndExpr([NotNull] LqlParser.AndExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitAndExpr([NotNull] LqlParser.AndExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterAtomicExpr([NotNull] LqlParser.AtomicExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitAtomicExpr([NotNull] LqlParser.AtomicExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterComparison([NotNull] LqlParser.ComparisonContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitComparison([NotNull] LqlParser.ComparisonContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterExistsExpr([NotNull] LqlParser.ExistsExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitExistsExpr([NotNull] LqlParser.ExistsExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterNullCheckExpr([NotNull] LqlParser.NullCheckExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitNullCheckExpr([NotNull] LqlParser.NullCheckExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterInExpr([NotNull] LqlParser.InExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitInExpr([NotNull] LqlParser.InExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCaseExpr([NotNull] LqlParser.CaseExprContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCaseExpr([NotNull] LqlParser.CaseExprContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterWhenClause([NotNull] LqlParser.WhenClauseContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitWhenClause([NotNull] LqlParser.WhenClauseContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterCaseResult([NotNull] LqlParser.CaseResultContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitCaseResult([NotNull] LqlParser.CaseResultContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterOrderDirection([NotNull] LqlParser.OrderDirectionContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitOrderDirection([NotNull] LqlParser.OrderDirectionContext context); + /// + /// Enter a parse tree produced by . + /// + /// The parse tree. + void EnterComparisonOp([NotNull] LqlParser.ComparisonOpContext context); + /// + /// Exit a parse tree produced by . + /// + /// The parse tree. + void ExitComparisonOp([NotNull] LqlParser.ComparisonOpContext context); +} +} diff --git a/Lql/Lql/Parsing/LqlParser.cs b/Lql/Lql/Parsing/LqlParser.cs new file mode 100644 index 00000000..414d40b6 --- /dev/null +++ b/Lql/Lql/Parsing/LqlParser.cs @@ -0,0 +1,2987 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// ANTLR Version: 4.13.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// Generated from Lql.g4 by ANTLR 4.13.1 + +// Unreachable code detected +#pragma warning disable 0162 +// The variable '...' is assigned but its value is never used +#pragma warning disable 0219 +// Missing XML comment for publicly visible type or member '...' +#pragma warning disable 1591 +// Ambiguous reference in cref attribute +#pragma warning disable 419 + +namespace Lql.Parsing { + +using System; +using System.IO; +using System.Text; +using System.Diagnostics; +using System.Collections.Generic; +using Antlr4.Runtime; +using Antlr4.Runtime.Atn; +using Antlr4.Runtime.Misc; +using Antlr4.Runtime.Tree; +using DFA = Antlr4.Runtime.Dfa.DFA; + +[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.13.1")] +[System.CLSCompliant(false)] +public partial class LqlParser : Parser { + protected static DFA[] decisionToDFA; + protected static PredictionContextCache sharedContextCache = new PredictionContextCache(); + public const int + T__0=1, T__1=2, T__2=3, T__3=4, T__4=5, T__5=6, T__6=7, T__7=8, T__8=9, + T__9=10, T__10=11, T__11=12, T__12=13, T__13=14, T__14=15, T__15=16, T__16=17, + T__17=18, T__18=19, T__19=20, ASC=21, DESC=22, AND=23, OR=24, DISTINCT=25, + EXISTS=26, NULL=27, IS=28, NOT=29, IN=30, AS=31, CASE=32, WHEN=33, THEN=34, + ELSE=35, END=36, WITH=37, OVER=38, PARTITION=39, ORDER=40, BY=41, COALESCE=42, + EXTRACT=43, FROM=44, INTERVAL=45, CURRENT_DATE=46, DATE_TRUNC=47, ON=48, + PARAMETER=49, IDENT=50, INT=51, DECIMAL=52, STRING=53, COMMENT=54, WS=55, + ASTERISK=56; + public const int + RULE_program = 0, RULE_statement = 1, RULE_letStmt = 2, RULE_pipeExpr = 3, + RULE_expr = 4, RULE_windowSpec = 5, RULE_partitionClause = 6, RULE_orderClause = 7, + RULE_lambdaExpr = 8, RULE_qualifiedIdent = 9, RULE_argList = 10, RULE_arg = 11, + RULE_columnAlias = 12, RULE_arithmeticExpr = 13, RULE_arithmeticTerm = 14, + RULE_arithmeticFactor = 15, RULE_functionCall = 16, RULE_namedArg = 17, + RULE_logicalExpr = 18, RULE_andExpr = 19, RULE_atomicExpr = 20, RULE_comparison = 21, + RULE_existsExpr = 22, RULE_nullCheckExpr = 23, RULE_inExpr = 24, RULE_caseExpr = 25, + RULE_whenClause = 26, RULE_caseResult = 27, RULE_orderDirection = 28, + RULE_comparisonOp = 29; + public static readonly string[] ruleNames = { + "program", "statement", "letStmt", "pipeExpr", "expr", "windowSpec", "partitionClause", + "orderClause", "lambdaExpr", "qualifiedIdent", "argList", "arg", "columnAlias", + "arithmeticExpr", "arithmeticTerm", "arithmeticFactor", "functionCall", + "namedArg", "logicalExpr", "andExpr", "atomicExpr", "comparison", "existsExpr", + "nullCheckExpr", "inExpr", "caseExpr", "whenClause", "caseResult", "orderDirection", + "comparisonOp" + }; + + private static readonly string[] _LiteralNames = { + null, "'let'", "'='", "'|>'", "'('", "')'", "'fn'", "','", "'=>'", "'.'", + "'+'", "'-'", "'||'", "'/'", "'%'", "'!='", "'<>'", "'<'", "'>'", "'<='", + "'>='", null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, null, + "'*'" + }; + private static readonly string[] _SymbolicNames = { + null, null, null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, "ASC", "DESC", "AND", + "OR", "DISTINCT", "EXISTS", "NULL", "IS", "NOT", "IN", "AS", "CASE", "WHEN", + "THEN", "ELSE", "END", "WITH", "OVER", "PARTITION", "ORDER", "BY", "COALESCE", + "EXTRACT", "FROM", "INTERVAL", "CURRENT_DATE", "DATE_TRUNC", "ON", "PARAMETER", + "IDENT", "INT", "DECIMAL", "STRING", "COMMENT", "WS", "ASTERISK" + }; + public static readonly IVocabulary DefaultVocabulary = new Vocabulary(_LiteralNames, _SymbolicNames); + + [NotNull] + public override IVocabulary Vocabulary + { + get + { + return DefaultVocabulary; + } + } + + public override string GrammarFileName { get { return "Lql.g4"; } } + + public override string[] RuleNames { get { return ruleNames; } } + + public override int[] SerializedAtn { get { return _serializedATN; } } + + static LqlParser() { + decisionToDFA = new DFA[_ATN.NumberOfDecisions]; + for (int i = 0; i < _ATN.NumberOfDecisions; i++) { + decisionToDFA[i] = new DFA(_ATN.GetDecisionState(i), i); + } + } + + public LqlParser(ITokenStream input) : this(input, Console.Out, Console.Error) { } + + public LqlParser(ITokenStream input, TextWriter output, TextWriter errorOutput) + : base(input, output, errorOutput) + { + Interpreter = new ParserATNSimulator(this, _ATN, decisionToDFA, sharedContextCache); + } + + public partial class ProgramContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode Eof() { return GetToken(LqlParser.Eof, 0); } + [System.Diagnostics.DebuggerNonUserCode] public StatementContext[] statement() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public StatementContext statement(int i) { + return GetRuleContext(i); + } + public ProgramContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_program; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterProgram(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitProgram(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitProgram(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ProgramContext program() { + ProgramContext _localctx = new ProgramContext(Context, State); + EnterRule(_localctx, 0, RULE_program); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 63; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while ((((_la) & ~0x3f) == 0 && ((1L << _la) & 89509046888955986L) != 0)) { + { + { + State = 60; + statement(); + } + } + State = 65; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 66; + Match(Eof); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class StatementContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public LetStmtContext letStmt() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public PipeExprContext pipeExpr() { + return GetRuleContext(0); + } + public StatementContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_statement; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterStatement(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitStatement(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitStatement(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public StatementContext statement() { + StatementContext _localctx = new StatementContext(Context, State); + EnterRule(_localctx, 2, RULE_statement); + try { + State = 70; + ErrorHandler.Sync(this); + switch (TokenStream.LA(1)) { + case T__0: + EnterOuterAlt(_localctx, 1); + { + State = 68; + letStmt(); + } + break; + case T__3: + case T__5: + case CASE: + case PARAMETER: + case IDENT: + case INT: + case DECIMAL: + case STRING: + case ASTERISK: + EnterOuterAlt(_localctx, 2); + { + State = 69; + pipeExpr(); + } + break; + default: + throw new NoViableAltException(this); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class LetStmtContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IDENT() { return GetToken(LqlParser.IDENT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public PipeExprContext pipeExpr() { + return GetRuleContext(0); + } + public LetStmtContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_letStmt; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterLetStmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitLetStmt(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitLetStmt(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public LetStmtContext letStmt() { + LetStmtContext _localctx = new LetStmtContext(Context, State); + EnterRule(_localctx, 4, RULE_letStmt); + try { + EnterOuterAlt(_localctx, 1); + { + State = 72; + Match(T__0); + State = 73; + Match(IDENT); + State = 74; + Match(T__1); + State = 75; + pipeExpr(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class PipeExprContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ExprContext[] expr() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr(int i) { + return GetRuleContext(i); + } + public PipeExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_pipeExpr; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterPipeExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitPipeExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitPipeExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public PipeExprContext pipeExpr() { + PipeExprContext _localctx = new PipeExprContext(Context, State); + EnterRule(_localctx, 6, RULE_pipeExpr); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 77; + expr(); + State = 82; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==T__2) { + { + { + State = 78; + Match(T__2); + State = 79; + expr(); + } + } + State = 84; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ExprContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IDENT() { return GetToken(LqlParser.IDENT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OVER() { return GetToken(LqlParser.OVER, 0); } + [System.Diagnostics.DebuggerNonUserCode] public WindowSpecContext windowSpec() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ArgListContext argList() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public PipeExprContext pipeExpr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public QualifiedIdentContext qualifiedIdent() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public LambdaExprContext lambdaExpr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public CaseExprContext caseExpr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INT() { return GetToken(LqlParser.INT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DECIMAL() { return GetToken(LqlParser.DECIMAL, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ASTERISK() { return GetToken(LqlParser.ASTERISK, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode STRING() { return GetToken(LqlParser.STRING, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PARAMETER() { return GetToken(LqlParser.PARAMETER, 0); } + public ExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_expr; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ExprContext expr() { + ExprContext _localctx = new ExprContext(Context, State); + EnterRule(_localctx, 8, RULE_expr); + int _la; + try { + State = 115; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,5,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 85; + Match(IDENT); + State = 86; + Match(T__3); + State = 88; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & 89790521932775504L) != 0)) { + { + State = 87; + argList(); + } + } + + State = 90; + Match(T__4); + State = 91; + Match(OVER); + State = 92; + Match(T__3); + State = 93; + windowSpec(); + State = 94; + Match(T__4); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 96; + Match(IDENT); + State = 97; + Match(T__3); + State = 99; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & 89790521932775504L) != 0)) { + { + State = 98; + argList(); + } + } + + State = 101; + Match(T__4); + } + break; + case 3: + EnterOuterAlt(_localctx, 3); + { + State = 102; + Match(IDENT); + } + break; + case 4: + EnterOuterAlt(_localctx, 4); + { + State = 103; + Match(T__3); + State = 104; + pipeExpr(); + State = 105; + Match(T__4); + } + break; + case 5: + EnterOuterAlt(_localctx, 5); + { + State = 107; + qualifiedIdent(); + } + break; + case 6: + EnterOuterAlt(_localctx, 6); + { + State = 108; + lambdaExpr(); + } + break; + case 7: + EnterOuterAlt(_localctx, 7); + { + State = 109; + caseExpr(); + } + break; + case 8: + EnterOuterAlt(_localctx, 8); + { + State = 110; + Match(INT); + } + break; + case 9: + EnterOuterAlt(_localctx, 9); + { + State = 111; + Match(DECIMAL); + } + break; + case 10: + EnterOuterAlt(_localctx, 10); + { + State = 112; + Match(ASTERISK); + } + break; + case 11: + EnterOuterAlt(_localctx, 11); + { + State = 113; + Match(STRING); + } + break; + case 12: + EnterOuterAlt(_localctx, 12); + { + State = 114; + Match(PARAMETER); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class WindowSpecContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public PartitionClauseContext partitionClause() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public OrderClauseContext orderClause() { + return GetRuleContext(0); + } + public WindowSpecContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_windowSpec; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterWindowSpec(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitWindowSpec(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitWindowSpec(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public WindowSpecContext windowSpec() { + WindowSpecContext _localctx = new WindowSpecContext(Context, State); + EnterRule(_localctx, 10, RULE_windowSpec); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 118; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==PARTITION) { + { + State = 117; + partitionClause(); + } + } + + State = 121; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ORDER) { + { + State = 120; + orderClause(); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class PartitionClauseContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PARTITION() { return GetToken(LqlParser.PARTITION, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BY() { return GetToken(LqlParser.BY, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ArgListContext argList() { + return GetRuleContext(0); + } + public PartitionClauseContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_partitionClause; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterPartitionClause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitPartitionClause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitPartitionClause(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public PartitionClauseContext partitionClause() { + PartitionClauseContext _localctx = new PartitionClauseContext(Context, State); + EnterRule(_localctx, 12, RULE_partitionClause); + try { + EnterOuterAlt(_localctx, 1); + { + State = 123; + Match(PARTITION); + State = 124; + Match(BY); + State = 125; + argList(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OrderClauseContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ORDER() { return GetToken(LqlParser.ORDER, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode BY() { return GetToken(LqlParser.BY, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ArgListContext argList() { + return GetRuleContext(0); + } + public OrderClauseContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_orderClause; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterOrderClause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitOrderClause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitOrderClause(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OrderClauseContext orderClause() { + OrderClauseContext _localctx = new OrderClauseContext(Context, State); + EnterRule(_localctx, 14, RULE_orderClause); + try { + EnterOuterAlt(_localctx, 1); + { + State = 127; + Match(ORDER); + State = 128; + Match(BY); + State = 129; + argList(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class LambdaExprContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] IDENT() { return GetTokens(LqlParser.IDENT); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IDENT(int i) { + return GetToken(LqlParser.IDENT, i); + } + [System.Diagnostics.DebuggerNonUserCode] public LogicalExprContext logicalExpr() { + return GetRuleContext(0); + } + public LambdaExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_lambdaExpr; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterLambdaExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitLambdaExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitLambdaExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public LambdaExprContext lambdaExpr() { + LambdaExprContext _localctx = new LambdaExprContext(Context, State); + EnterRule(_localctx, 16, RULE_lambdaExpr); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 131; + Match(T__5); + State = 132; + Match(T__3); + State = 133; + Match(IDENT); + State = 138; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==T__6) { + { + { + State = 134; + Match(T__6); + State = 135; + Match(IDENT); + } + } + State = 140; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + State = 141; + Match(T__4); + State = 142; + Match(T__7); + State = 143; + logicalExpr(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class QualifiedIdentContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] IDENT() { return GetTokens(LqlParser.IDENT); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IDENT(int i) { + return GetToken(LqlParser.IDENT, i); + } + public QualifiedIdentContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_qualifiedIdent; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterQualifiedIdent(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitQualifiedIdent(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitQualifiedIdent(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public QualifiedIdentContext qualifiedIdent() { + QualifiedIdentContext _localctx = new QualifiedIdentContext(Context, State); + EnterRule(_localctx, 18, RULE_qualifiedIdent); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 145; + Match(IDENT); + State = 148; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + do { + { + { + State = 146; + Match(T__8); + State = 147; + Match(IDENT); + } + } + State = 150; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } while ( _la==T__8 ); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ArgListContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ArgContext[] arg() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public ArgContext arg(int i) { + return GetRuleContext(i); + } + public ArgListContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_argList; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterArgList(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitArgList(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitArgList(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ArgListContext argList() { + ArgListContext _localctx = new ArgListContext(Context, State); + EnterRule(_localctx, 20, RULE_argList); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 152; + arg(); + State = 157; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while (_la==T__6) { + { + { + State = 153; + Match(T__6); + State = 154; + arg(); + } + } + State = 159; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ArgContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ColumnAliasContext columnAlias() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ArithmeticExprContext arithmeticExpr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public FunctionCallContext functionCall() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public CaseExprContext caseExpr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public NamedArgContext namedArg() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ComparisonContext comparison() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public PipeExprContext pipeExpr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public LambdaExprContext lambdaExpr() { + return GetRuleContext(0); + } + public ArgContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_arg; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterArg(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitArg(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitArg(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ArgContext arg() { + ArgContext _localctx = new ArgContext(Context, State); + EnterRule(_localctx, 22, RULE_arg); + try { + State = 173; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,11,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 160; + columnAlias(); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 161; + arithmeticExpr(); + } + break; + case 3: + EnterOuterAlt(_localctx, 3); + { + State = 162; + functionCall(); + } + break; + case 4: + EnterOuterAlt(_localctx, 4); + { + State = 163; + caseExpr(); + } + break; + case 5: + EnterOuterAlt(_localctx, 5); + { + State = 164; + expr(); + } + break; + case 6: + EnterOuterAlt(_localctx, 6); + { + State = 165; + namedArg(); + } + break; + case 7: + EnterOuterAlt(_localctx, 7); + { + State = 166; + comparison(); + } + break; + case 8: + EnterOuterAlt(_localctx, 8); + { + State = 167; + pipeExpr(); + } + break; + case 9: + EnterOuterAlt(_localctx, 9); + { + State = 168; + lambdaExpr(); + } + break; + case 10: + EnterOuterAlt(_localctx, 10); + { + State = 169; + Match(T__3); + State = 170; + pipeExpr(); + State = 171; + Match(T__4); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ColumnAliasContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ArithmeticExprContext arithmeticExpr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public FunctionCallContext functionCall() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public QualifiedIdentContext qualifiedIdent() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] IDENT() { return GetTokens(LqlParser.IDENT); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IDENT(int i) { + return GetToken(LqlParser.IDENT, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AS() { return GetToken(LqlParser.AS, 0); } + public ColumnAliasContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_columnAlias; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterColumnAlias(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitColumnAlias(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitColumnAlias(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ColumnAliasContext columnAlias() { + ColumnAliasContext _localctx = new ColumnAliasContext(Context, State); + EnterRule(_localctx, 24, RULE_columnAlias); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 179; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,12,Context) ) { + case 1: + { + State = 175; + arithmeticExpr(); + } + break; + case 2: + { + State = 176; + functionCall(); + } + break; + case 3: + { + State = 177; + qualifiedIdent(); + } + break; + case 4: + { + State = 178; + Match(IDENT); + } + break; + } + State = 183; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==AS) { + { + State = 181; + Match(AS); + State = 182; + Match(IDENT); + } + } + + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ArithmeticExprContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ArithmeticTermContext[] arithmeticTerm() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public ArithmeticTermContext arithmeticTerm(int i) { + return GetRuleContext(i); + } + public ArithmeticExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_arithmeticExpr; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterArithmeticExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitArithmeticExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitArithmeticExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ArithmeticExprContext arithmeticExpr() { + ArithmeticExprContext _localctx = new ArithmeticExprContext(Context, State); + EnterRule(_localctx, 26, RULE_arithmeticExpr); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 185; + arithmeticTerm(); + State = 190; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + while ((((_la) & ~0x3f) == 0 && ((1L << _la) & 7168L) != 0)) { + { + { + State = 186; + _la = TokenStream.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 7168L) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 187; + arithmeticTerm(); + } + } + State = 192; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ArithmeticTermContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ArithmeticFactorContext[] arithmeticFactor() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public ArithmeticFactorContext arithmeticFactor(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] ASTERISK() { return GetTokens(LqlParser.ASTERISK); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ASTERISK(int i) { + return GetToken(LqlParser.ASTERISK, i); + } + public ArithmeticTermContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_arithmeticTerm; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterArithmeticTerm(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitArithmeticTerm(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitArithmeticTerm(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ArithmeticTermContext arithmeticTerm() { + ArithmeticTermContext _localctx = new ArithmeticTermContext(Context, State); + EnterRule(_localctx, 28, RULE_arithmeticTerm); + int _la; + try { + int _alt; + EnterOuterAlt(_localctx, 1); + { + State = 193; + arithmeticFactor(); + State = 198; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,15,Context); + while ( _alt!=2 && _alt!=global::Antlr4.Runtime.Atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + State = 194; + _la = TokenStream.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 72057594037952512L) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 195; + arithmeticFactor(); + } + } + } + State = 200; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,15,Context); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ArithmeticFactorContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public QualifiedIdentContext qualifiedIdent() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IDENT() { return GetToken(LqlParser.IDENT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INT() { return GetToken(LqlParser.INT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DECIMAL() { return GetToken(LqlParser.DECIMAL, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode STRING() { return GetToken(LqlParser.STRING, 0); } + [System.Diagnostics.DebuggerNonUserCode] public FunctionCallContext functionCall() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public CaseExprContext caseExpr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PARAMETER() { return GetToken(LqlParser.PARAMETER, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ArithmeticExprContext arithmeticExpr() { + return GetRuleContext(0); + } + public ArithmeticFactorContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_arithmeticFactor; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterArithmeticFactor(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitArithmeticFactor(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitArithmeticFactor(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ArithmeticFactorContext arithmeticFactor() { + ArithmeticFactorContext _localctx = new ArithmeticFactorContext(Context, State); + EnterRule(_localctx, 30, RULE_arithmeticFactor); + try { + State = 213; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,16,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 201; + qualifiedIdent(); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 202; + Match(IDENT); + } + break; + case 3: + EnterOuterAlt(_localctx, 3); + { + State = 203; + Match(INT); + } + break; + case 4: + EnterOuterAlt(_localctx, 4); + { + State = 204; + Match(DECIMAL); + } + break; + case 5: + EnterOuterAlt(_localctx, 5); + { + State = 205; + Match(STRING); + } + break; + case 6: + EnterOuterAlt(_localctx, 6); + { + State = 206; + functionCall(); + } + break; + case 7: + EnterOuterAlt(_localctx, 7); + { + State = 207; + caseExpr(); + } + break; + case 8: + EnterOuterAlt(_localctx, 8); + { + State = 208; + Match(PARAMETER); + } + break; + case 9: + EnterOuterAlt(_localctx, 9); + { + State = 209; + Match(T__3); + State = 210; + arithmeticExpr(); + State = 211; + Match(T__4); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class FunctionCallContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IDENT() { return GetToken(LqlParser.IDENT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ArgListContext argList() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DISTINCT() { return GetToken(LqlParser.DISTINCT, 0); } + public FunctionCallContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_functionCall; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterFunctionCall(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitFunctionCall(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitFunctionCall(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public FunctionCallContext functionCall() { + FunctionCallContext _localctx = new FunctionCallContext(Context, State); + EnterRule(_localctx, 32, RULE_functionCall); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 215; + Match(IDENT); + State = 216; + Match(T__3); + State = 221; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if ((((_la) & ~0x3f) == 0 && ((1L << _la) & 89790521966329936L) != 0)) { + { + State = 218; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==DISTINCT) { + { + State = 217; + Match(DISTINCT); + } + } + + State = 220; + argList(); + } + } + + State = 223; + Match(T__4); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class NamedArgContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IDENT() { return GetToken(LqlParser.IDENT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ON() { return GetToken(LqlParser.ON, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ComparisonContext comparison() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public LogicalExprContext logicalExpr() { + return GetRuleContext(0); + } + public NamedArgContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_namedArg; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterNamedArg(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitNamedArg(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitNamedArg(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public NamedArgContext namedArg() { + NamedArgContext _localctx = new NamedArgContext(Context, State); + EnterRule(_localctx, 34, RULE_namedArg); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 225; + _la = TokenStream.LA(1); + if ( !(_la==ON || _la==IDENT) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + State = 226; + Match(T__1); + State = 229; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,19,Context) ) { + case 1: + { + State = 227; + comparison(); + } + break; + case 2: + { + State = 228; + logicalExpr(); + } + break; + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class LogicalExprContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public AndExprContext[] andExpr() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public AndExprContext andExpr(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] OR() { return GetTokens(LqlParser.OR); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode OR(int i) { + return GetToken(LqlParser.OR, i); + } + public LogicalExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_logicalExpr; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterLogicalExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitLogicalExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitLogicalExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public LogicalExprContext logicalExpr() { + LogicalExprContext _localctx = new LogicalExprContext(Context, State); + EnterRule(_localctx, 36, RULE_logicalExpr); + try { + int _alt; + EnterOuterAlt(_localctx, 1); + { + State = 231; + andExpr(); + State = 236; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,20,Context); + while ( _alt!=2 && _alt!=global::Antlr4.Runtime.Atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + State = 232; + Match(OR); + State = 233; + andExpr(); + } + } + } + State = 238; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,20,Context); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class AndExprContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public AtomicExprContext[] atomicExpr() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public AtomicExprContext atomicExpr(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] AND() { return GetTokens(LqlParser.AND); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode AND(int i) { + return GetToken(LqlParser.AND, i); + } + public AndExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_andExpr; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterAndExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitAndExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitAndExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public AndExprContext andExpr() { + AndExprContext _localctx = new AndExprContext(Context, State); + EnterRule(_localctx, 38, RULE_andExpr); + try { + int _alt; + EnterOuterAlt(_localctx, 1); + { + State = 239; + atomicExpr(); + State = 244; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,21,Context); + while ( _alt!=2 && _alt!=global::Antlr4.Runtime.Atn.ATN.INVALID_ALT_NUMBER ) { + if ( _alt==1 ) { + { + { + State = 240; + Match(AND); + State = 241; + atomicExpr(); + } + } + } + State = 246; + ErrorHandler.Sync(this); + _alt = Interpreter.AdaptivePredict(TokenStream,21,Context); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class AtomicExprContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ComparisonContext comparison() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public LogicalExprContext logicalExpr() { + return GetRuleContext(0); + } + public AtomicExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_atomicExpr; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterAtomicExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitAtomicExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitAtomicExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public AtomicExprContext atomicExpr() { + AtomicExprContext _localctx = new AtomicExprContext(Context, State); + EnterRule(_localctx, 40, RULE_atomicExpr); + try { + State = 252; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,22,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 247; + comparison(); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 248; + Match(T__3); + State = 249; + logicalExpr(); + State = 250; + Match(T__4); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ComparisonContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ArithmeticExprContext[] arithmeticExpr() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public ArithmeticExprContext arithmeticExpr(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ComparisonOpContext comparisonOp() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public QualifiedIdentContext[] qualifiedIdent() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public QualifiedIdentContext qualifiedIdent(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode STRING() { return GetToken(LqlParser.STRING, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] IDENT() { return GetTokens(LqlParser.IDENT); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IDENT(int i) { + return GetToken(LqlParser.IDENT, i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INT() { return GetToken(LqlParser.INT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DECIMAL() { return GetToken(LqlParser.DECIMAL, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode[] PARAMETER() { return GetTokens(LqlParser.PARAMETER); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PARAMETER(int i) { + return GetToken(LqlParser.PARAMETER, i); + } + [System.Diagnostics.DebuggerNonUserCode] public OrderDirectionContext orderDirection() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ExprContext expr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ExistsExprContext existsExpr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public NullCheckExprContext nullCheckExpr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public InExprContext inExpr() { + return GetRuleContext(0); + } + public ComparisonContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_comparison; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterComparison(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitComparison(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitComparison(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ComparisonContext comparison() { + ComparisonContext _localctx = new ComparisonContext(Context, State); + EnterRule(_localctx, 42, RULE_comparison); + int _la; + try { + State = 307; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,29,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 254; + arithmeticExpr(); + State = 255; + comparisonOp(); + State = 256; + arithmeticExpr(); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 258; + qualifiedIdent(); + State = 259; + comparisonOp(); + State = 266; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,23,Context) ) { + case 1: + { + State = 260; + qualifiedIdent(); + } + break; + case 2: + { + State = 261; + Match(STRING); + } + break; + case 3: + { + State = 262; + Match(IDENT); + } + break; + case 4: + { + State = 263; + Match(INT); + } + break; + case 5: + { + State = 264; + Match(DECIMAL); + } + break; + case 6: + { + State = 265; + Match(PARAMETER); + } + break; + } + } + break; + case 3: + EnterOuterAlt(_localctx, 3); + { + State = 268; + Match(IDENT); + State = 269; + comparisonOp(); + State = 276; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,24,Context) ) { + case 1: + { + State = 270; + qualifiedIdent(); + } + break; + case 2: + { + State = 271; + Match(STRING); + } + break; + case 3: + { + State = 272; + Match(IDENT); + } + break; + case 4: + { + State = 273; + Match(INT); + } + break; + case 5: + { + State = 274; + Match(DECIMAL); + } + break; + case 6: + { + State = 275; + Match(PARAMETER); + } + break; + } + } + break; + case 4: + EnterOuterAlt(_localctx, 4); + { + State = 278; + Match(PARAMETER); + State = 279; + comparisonOp(); + State = 286; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,25,Context) ) { + case 1: + { + State = 280; + qualifiedIdent(); + } + break; + case 2: + { + State = 281; + Match(STRING); + } + break; + case 3: + { + State = 282; + Match(IDENT); + } + break; + case 4: + { + State = 283; + Match(INT); + } + break; + case 5: + { + State = 284; + Match(DECIMAL); + } + break; + case 6: + { + State = 285; + Match(PARAMETER); + } + break; + } + } + break; + case 5: + EnterOuterAlt(_localctx, 5); + { + State = 288; + qualifiedIdent(); + State = 290; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ASC || _la==DESC) { + { + State = 289; + orderDirection(); + } + } + + } + break; + case 6: + EnterOuterAlt(_localctx, 6); + { + State = 292; + Match(IDENT); + State = 294; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ASC || _la==DESC) { + { + State = 293; + orderDirection(); + } + } + + } + break; + case 7: + EnterOuterAlt(_localctx, 7); + { + State = 296; + Match(PARAMETER); + State = 298; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ASC || _la==DESC) { + { + State = 297; + orderDirection(); + } + } + + } + break; + case 8: + EnterOuterAlt(_localctx, 8); + { + State = 300; + Match(STRING); + } + break; + case 9: + EnterOuterAlt(_localctx, 9); + { + State = 301; + Match(INT); + } + break; + case 10: + EnterOuterAlt(_localctx, 10); + { + State = 302; + Match(DECIMAL); + } + break; + case 11: + EnterOuterAlt(_localctx, 11); + { + State = 303; + expr(); + } + break; + case 12: + EnterOuterAlt(_localctx, 12); + { + State = 304; + existsExpr(); + } + break; + case 13: + EnterOuterAlt(_localctx, 13); + { + State = 305; + nullCheckExpr(); + } + break; + case 14: + EnterOuterAlt(_localctx, 14); + { + State = 306; + inExpr(); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ExistsExprContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode EXISTS() { return GetToken(LqlParser.EXISTS, 0); } + [System.Diagnostics.DebuggerNonUserCode] public PipeExprContext pipeExpr() { + return GetRuleContext(0); + } + public ExistsExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_existsExpr; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterExistsExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitExistsExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitExistsExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ExistsExprContext existsExpr() { + ExistsExprContext _localctx = new ExistsExprContext(Context, State); + EnterRule(_localctx, 44, RULE_existsExpr); + try { + EnterOuterAlt(_localctx, 1); + { + State = 309; + Match(EXISTS); + State = 310; + Match(T__3); + State = 311; + pipeExpr(); + State = 312; + Match(T__4); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class NullCheckExprContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public QualifiedIdentContext qualifiedIdent() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IDENT() { return GetToken(LqlParser.IDENT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PARAMETER() { return GetToken(LqlParser.PARAMETER, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IS() { return GetToken(LqlParser.IS, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NULL() { return GetToken(LqlParser.NULL, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode NOT() { return GetToken(LqlParser.NOT, 0); } + public NullCheckExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_nullCheckExpr; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterNullCheckExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitNullCheckExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitNullCheckExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public NullCheckExprContext nullCheckExpr() { + NullCheckExprContext _localctx = new NullCheckExprContext(Context, State); + EnterRule(_localctx, 46, RULE_nullCheckExpr); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 317; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,30,Context) ) { + case 1: + { + State = 314; + qualifiedIdent(); + } + break; + case 2: + { + State = 315; + Match(IDENT); + } + break; + case 3: + { + State = 316; + Match(PARAMETER); + } + break; + } + { + State = 319; + Match(IS); + State = 321; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==NOT) { + { + State = 320; + Match(NOT); + } + } + + State = 323; + Match(NULL); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class InExprContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IN() { return GetToken(LqlParser.IN, 0); } + [System.Diagnostics.DebuggerNonUserCode] public QualifiedIdentContext qualifiedIdent() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IDENT() { return GetToken(LqlParser.IDENT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PARAMETER() { return GetToken(LqlParser.PARAMETER, 0); } + [System.Diagnostics.DebuggerNonUserCode] public PipeExprContext pipeExpr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ArgListContext argList() { + return GetRuleContext(0); + } + public InExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_inExpr; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterInExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitInExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitInExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public InExprContext inExpr() { + InExprContext _localctx = new InExprContext(Context, State); + EnterRule(_localctx, 48, RULE_inExpr); + try { + EnterOuterAlt(_localctx, 1); + { + State = 328; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,32,Context) ) { + case 1: + { + State = 325; + qualifiedIdent(); + } + break; + case 2: + { + State = 326; + Match(IDENT); + } + break; + case 3: + { + State = 327; + Match(PARAMETER); + } + break; + } + State = 330; + Match(IN); + State = 331; + Match(T__3); + State = 334; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,33,Context) ) { + case 1: + { + State = 332; + pipeExpr(); + } + break; + case 2: + { + State = 333; + argList(); + } + break; + } + State = 336; + Match(T__4); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CaseExprContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode CASE() { return GetToken(LqlParser.CASE, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode END() { return GetToken(LqlParser.END, 0); } + [System.Diagnostics.DebuggerNonUserCode] public WhenClauseContext[] whenClause() { + return GetRuleContexts(); + } + [System.Diagnostics.DebuggerNonUserCode] public WhenClauseContext whenClause(int i) { + return GetRuleContext(i); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ELSE() { return GetToken(LqlParser.ELSE, 0); } + [System.Diagnostics.DebuggerNonUserCode] public CaseResultContext caseResult() { + return GetRuleContext(0); + } + public CaseExprContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_caseExpr; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterCaseExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitCaseExpr(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitCaseExpr(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CaseExprContext caseExpr() { + CaseExprContext _localctx = new CaseExprContext(Context, State); + EnterRule(_localctx, 50, RULE_caseExpr); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 338; + Match(CASE); + State = 340; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + do { + { + { + State = 339; + whenClause(); + } + } + State = 342; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + } while ( _la==WHEN ); + State = 346; + ErrorHandler.Sync(this); + _la = TokenStream.LA(1); + if (_la==ELSE) { + { + State = 344; + Match(ELSE); + State = 345; + caseResult(); + } + } + + State = 348; + Match(END); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class WhenClauseContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode WHEN() { return GetToken(LqlParser.WHEN, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ComparisonContext comparison() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode THEN() { return GetToken(LqlParser.THEN, 0); } + [System.Diagnostics.DebuggerNonUserCode] public CaseResultContext caseResult() { + return GetRuleContext(0); + } + public WhenClauseContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_whenClause; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterWhenClause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitWhenClause(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitWhenClause(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public WhenClauseContext whenClause() { + WhenClauseContext _localctx = new WhenClauseContext(Context, State); + EnterRule(_localctx, 52, RULE_whenClause); + try { + EnterOuterAlt(_localctx, 1); + { + State = 350; + Match(WHEN); + State = 351; + comparison(); + State = 352; + Match(THEN); + State = 353; + caseResult(); + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class CaseResultContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ArithmeticExprContext arithmeticExpr() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ComparisonContext comparison() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public QualifiedIdentContext qualifiedIdent() { + return GetRuleContext(0); + } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode IDENT() { return GetToken(LqlParser.IDENT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode INT() { return GetToken(LqlParser.INT, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DECIMAL() { return GetToken(LqlParser.DECIMAL, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode STRING() { return GetToken(LqlParser.STRING, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode PARAMETER() { return GetToken(LqlParser.PARAMETER, 0); } + public CaseResultContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_caseResult; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterCaseResult(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitCaseResult(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitCaseResult(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public CaseResultContext caseResult() { + CaseResultContext _localctx = new CaseResultContext(Context, State); + EnterRule(_localctx, 54, RULE_caseResult); + try { + State = 363; + ErrorHandler.Sync(this); + switch ( Interpreter.AdaptivePredict(TokenStream,36,Context) ) { + case 1: + EnterOuterAlt(_localctx, 1); + { + State = 355; + arithmeticExpr(); + } + break; + case 2: + EnterOuterAlt(_localctx, 2); + { + State = 356; + comparison(); + } + break; + case 3: + EnterOuterAlt(_localctx, 3); + { + State = 357; + qualifiedIdent(); + } + break; + case 4: + EnterOuterAlt(_localctx, 4); + { + State = 358; + Match(IDENT); + } + break; + case 5: + EnterOuterAlt(_localctx, 5); + { + State = 359; + Match(INT); + } + break; + case 6: + EnterOuterAlt(_localctx, 6); + { + State = 360; + Match(DECIMAL); + } + break; + case 7: + EnterOuterAlt(_localctx, 7); + { + State = 361; + Match(STRING); + } + break; + case 8: + EnterOuterAlt(_localctx, 8); + { + State = 362; + Match(PARAMETER); + } + break; + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class OrderDirectionContext : ParserRuleContext { + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode ASC() { return GetToken(LqlParser.ASC, 0); } + [System.Diagnostics.DebuggerNonUserCode] public ITerminalNode DESC() { return GetToken(LqlParser.DESC, 0); } + public OrderDirectionContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_orderDirection; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterOrderDirection(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitOrderDirection(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitOrderDirection(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public OrderDirectionContext orderDirection() { + OrderDirectionContext _localctx = new OrderDirectionContext(Context, State); + EnterRule(_localctx, 56, RULE_orderDirection); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 365; + _la = TokenStream.LA(1); + if ( !(_la==ASC || _la==DESC) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + public partial class ComparisonOpContext : ParserRuleContext { + public ComparisonOpContext(ParserRuleContext parent, int invokingState) + : base(parent, invokingState) + { + } + public override int RuleIndex { get { return RULE_comparisonOp; } } + [System.Diagnostics.DebuggerNonUserCode] + public override void EnterRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.EnterComparisonOp(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override void ExitRule(IParseTreeListener listener) { + ILqlListener typedListener = listener as ILqlListener; + if (typedListener != null) typedListener.ExitComparisonOp(this); + } + [System.Diagnostics.DebuggerNonUserCode] + public override TResult Accept(IParseTreeVisitor visitor) { + ILqlVisitor typedVisitor = visitor as ILqlVisitor; + if (typedVisitor != null) return typedVisitor.VisitComparisonOp(this); + else return visitor.VisitChildren(this); + } + } + + [RuleVersion(0)] + public ComparisonOpContext comparisonOp() { + ComparisonOpContext _localctx = new ComparisonOpContext(Context, State); + EnterRule(_localctx, 58, RULE_comparisonOp); + int _la; + try { + EnterOuterAlt(_localctx, 1); + { + State = 367; + _la = TokenStream.LA(1); + if ( !((((_la) & ~0x3f) == 0 && ((1L << _la) & 2064388L) != 0)) ) { + ErrorHandler.RecoverInline(this); + } + else { + ErrorHandler.ReportMatch(this); + Consume(); + } + } + } + catch (RecognitionException re) { + _localctx.exception = re; + ErrorHandler.ReportError(this, re); + ErrorHandler.Recover(this, re); + } + finally { + ExitRule(); + } + return _localctx; + } + + private static int[] _serializedATN = { + 4,1,56,370,2,0,7,0,2,1,7,1,2,2,7,2,2,3,7,3,2,4,7,4,2,5,7,5,2,6,7,6,2,7, + 7,7,2,8,7,8,2,9,7,9,2,10,7,10,2,11,7,11,2,12,7,12,2,13,7,13,2,14,7,14, + 2,15,7,15,2,16,7,16,2,17,7,17,2,18,7,18,2,19,7,19,2,20,7,20,2,21,7,21, + 2,22,7,22,2,23,7,23,2,24,7,24,2,25,7,25,2,26,7,26,2,27,7,27,2,28,7,28, + 2,29,7,29,1,0,5,0,62,8,0,10,0,12,0,65,9,0,1,0,1,0,1,1,1,1,3,1,71,8,1,1, + 2,1,2,1,2,1,2,1,2,1,3,1,3,1,3,5,3,81,8,3,10,3,12,3,84,9,3,1,4,1,4,1,4, + 3,4,89,8,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,3,4,100,8,4,1,4,1,4,1,4, + 1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,1,4,3,4,116,8,4,1,5,3,5,119,8, + 5,1,5,3,5,122,8,5,1,6,1,6,1,6,1,6,1,7,1,7,1,7,1,7,1,8,1,8,1,8,1,8,1,8, + 5,8,137,8,8,10,8,12,8,140,9,8,1,8,1,8,1,8,1,8,1,9,1,9,1,9,4,9,149,8,9, + 11,9,12,9,150,1,10,1,10,1,10,5,10,156,8,10,10,10,12,10,159,9,10,1,11,1, + 11,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1,11,1,11,3,11,174,8,11, + 1,12,1,12,1,12,1,12,3,12,180,8,12,1,12,1,12,3,12,184,8,12,1,13,1,13,1, + 13,5,13,189,8,13,10,13,12,13,192,9,13,1,14,1,14,1,14,5,14,197,8,14,10, + 14,12,14,200,9,14,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1,15,1, + 15,1,15,3,15,214,8,15,1,16,1,16,1,16,3,16,219,8,16,1,16,3,16,222,8,16, + 1,16,1,16,1,17,1,17,1,17,1,17,3,17,230,8,17,1,18,1,18,1,18,5,18,235,8, + 18,10,18,12,18,238,9,18,1,19,1,19,1,19,5,19,243,8,19,10,19,12,19,246,9, + 19,1,20,1,20,1,20,1,20,1,20,3,20,253,8,20,1,21,1,21,1,21,1,21,1,21,1,21, + 1,21,1,21,1,21,1,21,1,21,1,21,3,21,267,8,21,1,21,1,21,1,21,1,21,1,21,1, + 21,1,21,1,21,3,21,277,8,21,1,21,1,21,1,21,1,21,1,21,1,21,1,21,1,21,3,21, + 287,8,21,1,21,1,21,3,21,291,8,21,1,21,1,21,3,21,295,8,21,1,21,1,21,3,21, + 299,8,21,1,21,1,21,1,21,1,21,1,21,1,21,1,21,3,21,308,8,21,1,22,1,22,1, + 22,1,22,1,22,1,23,1,23,1,23,3,23,318,8,23,1,23,1,23,3,23,322,8,23,1,23, + 1,23,1,24,1,24,1,24,3,24,329,8,24,1,24,1,24,1,24,1,24,3,24,335,8,24,1, + 24,1,24,1,25,1,25,4,25,341,8,25,11,25,12,25,342,1,25,1,25,3,25,347,8,25, + 1,25,1,25,1,26,1,26,1,26,1,26,1,26,1,27,1,27,1,27,1,27,1,27,1,27,1,27, + 1,27,3,27,364,8,27,1,28,1,28,1,29,1,29,1,29,0,0,30,0,2,4,6,8,10,12,14, + 16,18,20,22,24,26,28,30,32,34,36,38,40,42,44,46,48,50,52,54,56,58,0,5, + 1,0,10,12,2,0,13,14,56,56,2,0,48,48,50,50,1,0,21,22,2,0,2,2,15,20,435, + 0,63,1,0,0,0,2,70,1,0,0,0,4,72,1,0,0,0,6,77,1,0,0,0,8,115,1,0,0,0,10,118, + 1,0,0,0,12,123,1,0,0,0,14,127,1,0,0,0,16,131,1,0,0,0,18,145,1,0,0,0,20, + 152,1,0,0,0,22,173,1,0,0,0,24,179,1,0,0,0,26,185,1,0,0,0,28,193,1,0,0, + 0,30,213,1,0,0,0,32,215,1,0,0,0,34,225,1,0,0,0,36,231,1,0,0,0,38,239,1, + 0,0,0,40,252,1,0,0,0,42,307,1,0,0,0,44,309,1,0,0,0,46,317,1,0,0,0,48,328, + 1,0,0,0,50,338,1,0,0,0,52,350,1,0,0,0,54,363,1,0,0,0,56,365,1,0,0,0,58, + 367,1,0,0,0,60,62,3,2,1,0,61,60,1,0,0,0,62,65,1,0,0,0,63,61,1,0,0,0,63, + 64,1,0,0,0,64,66,1,0,0,0,65,63,1,0,0,0,66,67,5,0,0,1,67,1,1,0,0,0,68,71, + 3,4,2,0,69,71,3,6,3,0,70,68,1,0,0,0,70,69,1,0,0,0,71,3,1,0,0,0,72,73,5, + 1,0,0,73,74,5,50,0,0,74,75,5,2,0,0,75,76,3,6,3,0,76,5,1,0,0,0,77,82,3, + 8,4,0,78,79,5,3,0,0,79,81,3,8,4,0,80,78,1,0,0,0,81,84,1,0,0,0,82,80,1, + 0,0,0,82,83,1,0,0,0,83,7,1,0,0,0,84,82,1,0,0,0,85,86,5,50,0,0,86,88,5, + 4,0,0,87,89,3,20,10,0,88,87,1,0,0,0,88,89,1,0,0,0,89,90,1,0,0,0,90,91, + 5,5,0,0,91,92,5,38,0,0,92,93,5,4,0,0,93,94,3,10,5,0,94,95,5,5,0,0,95,116, + 1,0,0,0,96,97,5,50,0,0,97,99,5,4,0,0,98,100,3,20,10,0,99,98,1,0,0,0,99, + 100,1,0,0,0,100,101,1,0,0,0,101,116,5,5,0,0,102,116,5,50,0,0,103,104,5, + 4,0,0,104,105,3,6,3,0,105,106,5,5,0,0,106,116,1,0,0,0,107,116,3,18,9,0, + 108,116,3,16,8,0,109,116,3,50,25,0,110,116,5,51,0,0,111,116,5,52,0,0,112, + 116,5,56,0,0,113,116,5,53,0,0,114,116,5,49,0,0,115,85,1,0,0,0,115,96,1, + 0,0,0,115,102,1,0,0,0,115,103,1,0,0,0,115,107,1,0,0,0,115,108,1,0,0,0, + 115,109,1,0,0,0,115,110,1,0,0,0,115,111,1,0,0,0,115,112,1,0,0,0,115,113, + 1,0,0,0,115,114,1,0,0,0,116,9,1,0,0,0,117,119,3,12,6,0,118,117,1,0,0,0, + 118,119,1,0,0,0,119,121,1,0,0,0,120,122,3,14,7,0,121,120,1,0,0,0,121,122, + 1,0,0,0,122,11,1,0,0,0,123,124,5,39,0,0,124,125,5,41,0,0,125,126,3,20, + 10,0,126,13,1,0,0,0,127,128,5,40,0,0,128,129,5,41,0,0,129,130,3,20,10, + 0,130,15,1,0,0,0,131,132,5,6,0,0,132,133,5,4,0,0,133,138,5,50,0,0,134, + 135,5,7,0,0,135,137,5,50,0,0,136,134,1,0,0,0,137,140,1,0,0,0,138,136,1, + 0,0,0,138,139,1,0,0,0,139,141,1,0,0,0,140,138,1,0,0,0,141,142,5,5,0,0, + 142,143,5,8,0,0,143,144,3,36,18,0,144,17,1,0,0,0,145,148,5,50,0,0,146, + 147,5,9,0,0,147,149,5,50,0,0,148,146,1,0,0,0,149,150,1,0,0,0,150,148,1, + 0,0,0,150,151,1,0,0,0,151,19,1,0,0,0,152,157,3,22,11,0,153,154,5,7,0,0, + 154,156,3,22,11,0,155,153,1,0,0,0,156,159,1,0,0,0,157,155,1,0,0,0,157, + 158,1,0,0,0,158,21,1,0,0,0,159,157,1,0,0,0,160,174,3,24,12,0,161,174,3, + 26,13,0,162,174,3,32,16,0,163,174,3,50,25,0,164,174,3,8,4,0,165,174,3, + 34,17,0,166,174,3,42,21,0,167,174,3,6,3,0,168,174,3,16,8,0,169,170,5,4, + 0,0,170,171,3,6,3,0,171,172,5,5,0,0,172,174,1,0,0,0,173,160,1,0,0,0,173, + 161,1,0,0,0,173,162,1,0,0,0,173,163,1,0,0,0,173,164,1,0,0,0,173,165,1, + 0,0,0,173,166,1,0,0,0,173,167,1,0,0,0,173,168,1,0,0,0,173,169,1,0,0,0, + 174,23,1,0,0,0,175,180,3,26,13,0,176,180,3,32,16,0,177,180,3,18,9,0,178, + 180,5,50,0,0,179,175,1,0,0,0,179,176,1,0,0,0,179,177,1,0,0,0,179,178,1, + 0,0,0,180,183,1,0,0,0,181,182,5,31,0,0,182,184,5,50,0,0,183,181,1,0,0, + 0,183,184,1,0,0,0,184,25,1,0,0,0,185,190,3,28,14,0,186,187,7,0,0,0,187, + 189,3,28,14,0,188,186,1,0,0,0,189,192,1,0,0,0,190,188,1,0,0,0,190,191, + 1,0,0,0,191,27,1,0,0,0,192,190,1,0,0,0,193,198,3,30,15,0,194,195,7,1,0, + 0,195,197,3,30,15,0,196,194,1,0,0,0,197,200,1,0,0,0,198,196,1,0,0,0,198, + 199,1,0,0,0,199,29,1,0,0,0,200,198,1,0,0,0,201,214,3,18,9,0,202,214,5, + 50,0,0,203,214,5,51,0,0,204,214,5,52,0,0,205,214,5,53,0,0,206,214,3,32, + 16,0,207,214,3,50,25,0,208,214,5,49,0,0,209,210,5,4,0,0,210,211,3,26,13, + 0,211,212,5,5,0,0,212,214,1,0,0,0,213,201,1,0,0,0,213,202,1,0,0,0,213, + 203,1,0,0,0,213,204,1,0,0,0,213,205,1,0,0,0,213,206,1,0,0,0,213,207,1, + 0,0,0,213,208,1,0,0,0,213,209,1,0,0,0,214,31,1,0,0,0,215,216,5,50,0,0, + 216,221,5,4,0,0,217,219,5,25,0,0,218,217,1,0,0,0,218,219,1,0,0,0,219,220, + 1,0,0,0,220,222,3,20,10,0,221,218,1,0,0,0,221,222,1,0,0,0,222,223,1,0, + 0,0,223,224,5,5,0,0,224,33,1,0,0,0,225,226,7,2,0,0,226,229,5,2,0,0,227, + 230,3,42,21,0,228,230,3,36,18,0,229,227,1,0,0,0,229,228,1,0,0,0,230,35, + 1,0,0,0,231,236,3,38,19,0,232,233,5,24,0,0,233,235,3,38,19,0,234,232,1, + 0,0,0,235,238,1,0,0,0,236,234,1,0,0,0,236,237,1,0,0,0,237,37,1,0,0,0,238, + 236,1,0,0,0,239,244,3,40,20,0,240,241,5,23,0,0,241,243,3,40,20,0,242,240, + 1,0,0,0,243,246,1,0,0,0,244,242,1,0,0,0,244,245,1,0,0,0,245,39,1,0,0,0, + 246,244,1,0,0,0,247,253,3,42,21,0,248,249,5,4,0,0,249,250,3,36,18,0,250, + 251,5,5,0,0,251,253,1,0,0,0,252,247,1,0,0,0,252,248,1,0,0,0,253,41,1,0, + 0,0,254,255,3,26,13,0,255,256,3,58,29,0,256,257,3,26,13,0,257,308,1,0, + 0,0,258,259,3,18,9,0,259,266,3,58,29,0,260,267,3,18,9,0,261,267,5,53,0, + 0,262,267,5,50,0,0,263,267,5,51,0,0,264,267,5,52,0,0,265,267,5,49,0,0, + 266,260,1,0,0,0,266,261,1,0,0,0,266,262,1,0,0,0,266,263,1,0,0,0,266,264, + 1,0,0,0,266,265,1,0,0,0,267,308,1,0,0,0,268,269,5,50,0,0,269,276,3,58, + 29,0,270,277,3,18,9,0,271,277,5,53,0,0,272,277,5,50,0,0,273,277,5,51,0, + 0,274,277,5,52,0,0,275,277,5,49,0,0,276,270,1,0,0,0,276,271,1,0,0,0,276, + 272,1,0,0,0,276,273,1,0,0,0,276,274,1,0,0,0,276,275,1,0,0,0,277,308,1, + 0,0,0,278,279,5,49,0,0,279,286,3,58,29,0,280,287,3,18,9,0,281,287,5,53, + 0,0,282,287,5,50,0,0,283,287,5,51,0,0,284,287,5,52,0,0,285,287,5,49,0, + 0,286,280,1,0,0,0,286,281,1,0,0,0,286,282,1,0,0,0,286,283,1,0,0,0,286, + 284,1,0,0,0,286,285,1,0,0,0,287,308,1,0,0,0,288,290,3,18,9,0,289,291,3, + 56,28,0,290,289,1,0,0,0,290,291,1,0,0,0,291,308,1,0,0,0,292,294,5,50,0, + 0,293,295,3,56,28,0,294,293,1,0,0,0,294,295,1,0,0,0,295,308,1,0,0,0,296, + 298,5,49,0,0,297,299,3,56,28,0,298,297,1,0,0,0,298,299,1,0,0,0,299,308, + 1,0,0,0,300,308,5,53,0,0,301,308,5,51,0,0,302,308,5,52,0,0,303,308,3,8, + 4,0,304,308,3,44,22,0,305,308,3,46,23,0,306,308,3,48,24,0,307,254,1,0, + 0,0,307,258,1,0,0,0,307,268,1,0,0,0,307,278,1,0,0,0,307,288,1,0,0,0,307, + 292,1,0,0,0,307,296,1,0,0,0,307,300,1,0,0,0,307,301,1,0,0,0,307,302,1, + 0,0,0,307,303,1,0,0,0,307,304,1,0,0,0,307,305,1,0,0,0,307,306,1,0,0,0, + 308,43,1,0,0,0,309,310,5,26,0,0,310,311,5,4,0,0,311,312,3,6,3,0,312,313, + 5,5,0,0,313,45,1,0,0,0,314,318,3,18,9,0,315,318,5,50,0,0,316,318,5,49, + 0,0,317,314,1,0,0,0,317,315,1,0,0,0,317,316,1,0,0,0,318,319,1,0,0,0,319, + 321,5,28,0,0,320,322,5,29,0,0,321,320,1,0,0,0,321,322,1,0,0,0,322,323, + 1,0,0,0,323,324,5,27,0,0,324,47,1,0,0,0,325,329,3,18,9,0,326,329,5,50, + 0,0,327,329,5,49,0,0,328,325,1,0,0,0,328,326,1,0,0,0,328,327,1,0,0,0,329, + 330,1,0,0,0,330,331,5,30,0,0,331,334,5,4,0,0,332,335,3,6,3,0,333,335,3, + 20,10,0,334,332,1,0,0,0,334,333,1,0,0,0,335,336,1,0,0,0,336,337,5,5,0, + 0,337,49,1,0,0,0,338,340,5,32,0,0,339,341,3,52,26,0,340,339,1,0,0,0,341, + 342,1,0,0,0,342,340,1,0,0,0,342,343,1,0,0,0,343,346,1,0,0,0,344,345,5, + 35,0,0,345,347,3,54,27,0,346,344,1,0,0,0,346,347,1,0,0,0,347,348,1,0,0, + 0,348,349,5,36,0,0,349,51,1,0,0,0,350,351,5,33,0,0,351,352,3,42,21,0,352, + 353,5,34,0,0,353,354,3,54,27,0,354,53,1,0,0,0,355,364,3,26,13,0,356,364, + 3,42,21,0,357,364,3,18,9,0,358,364,5,50,0,0,359,364,5,51,0,0,360,364,5, + 52,0,0,361,364,5,53,0,0,362,364,5,49,0,0,363,355,1,0,0,0,363,356,1,0,0, + 0,363,357,1,0,0,0,363,358,1,0,0,0,363,359,1,0,0,0,363,360,1,0,0,0,363, + 361,1,0,0,0,363,362,1,0,0,0,364,55,1,0,0,0,365,366,7,3,0,0,366,57,1,0, + 0,0,367,368,7,4,0,0,368,59,1,0,0,0,37,63,70,82,88,99,115,118,121,138,150, + 157,173,179,183,190,198,213,218,221,229,236,244,252,266,276,286,290,294, + 298,307,317,321,328,334,342,346,363 + }; + + public static readonly ATN _ATN = + new ATNDeserializer().Deserialize(_serializedATN); + + +} +} diff --git a/Lql/Lql/Parsing/LqlVisitor.cs b/Lql/Lql/Parsing/LqlVisitor.cs new file mode 100644 index 00000000..e52821b9 --- /dev/null +++ b/Lql/Lql/Parsing/LqlVisitor.cs @@ -0,0 +1,217 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// ANTLR Version: 4.13.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// Generated from Lql.g4 by ANTLR 4.13.1 + +// Unreachable code detected +#pragma warning disable 0162 +// The variable '...' is assigned but its value is never used +#pragma warning disable 0219 +// Missing XML comment for publicly visible type or member '...' +#pragma warning disable 1591 +// Ambiguous reference in cref attribute +#pragma warning disable 419 + +namespace Lql.Parsing { + +using Antlr4.Runtime.Misc; +using Antlr4.Runtime.Tree; +using IToken = Antlr4.Runtime.IToken; + +/// +/// This interface defines a complete generic visitor for a parse tree produced +/// by . +/// +/// The return type of the visit operation. +[System.CodeDom.Compiler.GeneratedCode("ANTLR", "4.13.1")] +[System.CLSCompliant(false)] +public interface ILqlVisitor : IParseTreeVisitor { + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitProgram([NotNull] LqlParser.ProgramContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitStatement([NotNull] LqlParser.StatementContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitLetStmt([NotNull] LqlParser.LetStmtContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitPipeExpr([NotNull] LqlParser.PipeExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExpr([NotNull] LqlParser.ExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitWindowSpec([NotNull] LqlParser.WindowSpecContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitPartitionClause([NotNull] LqlParser.PartitionClauseContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOrderClause([NotNull] LqlParser.OrderClauseContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitLambdaExpr([NotNull] LqlParser.LambdaExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitQualifiedIdent([NotNull] LqlParser.QualifiedIdentContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitArgList([NotNull] LqlParser.ArgListContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitArg([NotNull] LqlParser.ArgContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitColumnAlias([NotNull] LqlParser.ColumnAliasContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitArithmeticExpr([NotNull] LqlParser.ArithmeticExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitArithmeticTerm([NotNull] LqlParser.ArithmeticTermContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitArithmeticFactor([NotNull] LqlParser.ArithmeticFactorContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitFunctionCall([NotNull] LqlParser.FunctionCallContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitNamedArg([NotNull] LqlParser.NamedArgContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitLogicalExpr([NotNull] LqlParser.LogicalExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitAndExpr([NotNull] LqlParser.AndExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitAtomicExpr([NotNull] LqlParser.AtomicExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitComparison([NotNull] LqlParser.ComparisonContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitExistsExpr([NotNull] LqlParser.ExistsExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitNullCheckExpr([NotNull] LqlParser.NullCheckExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitInExpr([NotNull] LqlParser.InExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCaseExpr([NotNull] LqlParser.CaseExprContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitWhenClause([NotNull] LqlParser.WhenClauseContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitCaseResult([NotNull] LqlParser.CaseResultContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitOrderDirection([NotNull] LqlParser.OrderDirectionContext context); + /// + /// Visit a parse tree produced by . + /// + /// The parse tree. + /// The visitor result. + Result VisitComparisonOp([NotNull] LqlParser.ComparisonOpContext context); +} +} diff --git a/Lql/LqlCli.SQLite.Tests/CliEndToEndTests.cs b/Lql/LqlCli.SQLite.Tests/CliEndToEndTests.cs index 1dd46952..3931af50 100644 --- a/Lql/LqlCli.SQLite.Tests/CliEndToEndTests.cs +++ b/Lql/LqlCli.SQLite.Tests/CliEndToEndTests.cs @@ -23,10 +23,9 @@ public CliEndToEndTests() _tempDirectory = Path.Combine(Path.GetTempPath(), $"lql-cli-tests-{Guid.NewGuid():N}"); Directory.CreateDirectory(_tempDirectory); - // Assuming the CLI is built and available in the output directory - var currentDir = Directory.GetCurrentDirectory(); - var projectRoot = Path.GetFullPath(Path.Combine(currentDir, "..", "..", "..", "..")); - _cliPath = Path.Combine(projectRoot, "LqlCli.SQLite", "LqlCli.csproj"); + // Use the built executable directly from the test's bin directory + // This avoids the slow and potentially hanging 'dotnet run --project' approach + _cliPath = Path.Combine(AppContext.BaseDirectory, "lql-sqlite"); } /// @@ -244,13 +243,10 @@ private string CreateTempFile(string content) private async Task RunCliAsync(params string[] args) { using var process = new Process(); - process.StartInfo.FileName = "dotnet"; - - var arguments = new List { "run", "--project", _cliPath, "--" }; - arguments.AddRange(args); + process.StartInfo.FileName = _cliPath; process.StartInfo.Arguments = string.Join( " ", - arguments.Select(arg => arg.Contains(' ') ? $"\"{arg}\"" : arg) + args.Select(arg => arg.Contains(' ') ? $"\"{arg}\"" : arg) ); process.StartInfo.UseShellExecute = false; diff --git a/Lql/LqlCli.SQLite.Tests/LqlCli.SQLite.Tests.csproj b/Lql/LqlCli.SQLite.Tests/LqlCli.SQLite.Tests.csproj index 3ef3cdf5..345dcfde 100644 --- a/Lql/LqlCli.SQLite.Tests/LqlCli.SQLite.Tests.csproj +++ b/Lql/LqlCli.SQLite.Tests/LqlCli.SQLite.Tests.csproj @@ -16,7 +16,7 @@ - + \ No newline at end of file diff --git a/Lql/LqlCli.SQLite/LqlCli.csproj b/Lql/LqlCli.SQLite/LqlCli.SQLite.csproj similarity index 100% rename from Lql/LqlCli.SQLite/LqlCli.csproj rename to Lql/LqlCli.SQLite/LqlCli.SQLite.csproj diff --git a/Lql/LqlCli.SQLite/Program.cs b/Lql/LqlCli.SQLite/Program.cs index 7f121787..3bbb621b 100644 --- a/Lql/LqlCli.SQLite/Program.cs +++ b/Lql/LqlCli.SQLite/Program.cs @@ -13,7 +13,7 @@ using StringSqlError = Outcome.Result.Error; using StringSqlOk = Outcome.Result.Ok; -namespace LqlCli; +namespace LqlCli.SQLite; /// /// LQL to SQLite CLI transpiler diff --git a/Lql/LqlCli.SQLite/README.md b/Lql/LqlCli.SQLite/README.md index e10e300f..0fc875ec 100644 --- a/Lql/LqlCli.SQLite/README.md +++ b/Lql/LqlCli.SQLite/README.md @@ -7,7 +7,7 @@ A command-line tool that transpiles LQL (Language Query Language) files to SQLit Build the project: ```bash -dotnet build LqlCli.SQLite/LqlCli.csproj +dotnet build LqlCli.SQLite/LqlCli.SQLite.csproj ``` ## Usage @@ -17,7 +17,7 @@ dotnet build LqlCli.SQLite/LqlCli.csproj Transpile an LQL file to SQLite SQL and print to console: ```bash -dotnet run --project LqlCli.SQLite/LqlCli.csproj -- --input input.lql +dotnet run --project LqlCli.SQLite/LqlCli.SQLite.csproj -- --input input.lql ``` ### Output to File @@ -25,7 +25,7 @@ dotnet run --project LqlCli.SQLite/LqlCli.csproj -- --input input.lql Transpile and save to a file: ```bash -dotnet run --project LqlCli.SQLite/LqlCli.csproj -- --input input.lql --output output.sql +dotnet run --project LqlCli.SQLite/LqlCli.SQLite.csproj -- --input input.lql --output output.sql ``` ### Validate Syntax Only @@ -33,7 +33,7 @@ dotnet run --project LqlCli.SQLite/LqlCli.csproj -- --input input.lql --output o Check if the LQL syntax is valid without generating output: ```bash -dotnet run --project LqlCli.SQLite/LqlCli.csproj -- --input input.lql --validate +dotnet run --project LqlCli.SQLite/LqlCli.SQLite.csproj -- --input input.lql --validate ``` ## Options @@ -87,7 +87,7 @@ Exit codes: To create a native AOT binary: ```bash -dotnet publish LqlCli.SQLite/LqlCli.csproj -c Release -r win-x64 --self-contained +dotnet publish LqlCli.SQLite/LqlCli.SQLite.csproj -c Release -r win-x64 --self-contained ``` Replace `win-x64` with your target runtime identifier (`linux-x64`, `osx-x64`, etc.). \ No newline at end of file diff --git a/Lql/Website/Components/App.razor b/Lql/LqlWebsite/Components/App.razor similarity index 100% rename from Lql/Website/Components/App.razor rename to Lql/LqlWebsite/Components/App.razor diff --git a/Lql/Website/Components/Layout/MainLayout.razor b/Lql/LqlWebsite/Components/Layout/MainLayout.razor similarity index 100% rename from Lql/Website/Components/Layout/MainLayout.razor rename to Lql/LqlWebsite/Components/Layout/MainLayout.razor diff --git a/Lql/Website/Components/Pages/Home.razor b/Lql/LqlWebsite/Components/Pages/Home.razor similarity index 100% rename from Lql/Website/Components/Pages/Home.razor rename to Lql/LqlWebsite/Components/Pages/Home.razor diff --git a/Lql/Website/Components/_Imports.razor b/Lql/LqlWebsite/Components/_Imports.razor similarity index 100% rename from Lql/Website/Components/_Imports.razor rename to Lql/LqlWebsite/Components/_Imports.razor diff --git a/Lql/Website/LqlWebsite.csproj b/Lql/LqlWebsite/LqlWebsite.csproj similarity index 100% rename from Lql/Website/LqlWebsite.csproj rename to Lql/LqlWebsite/LqlWebsite.csproj diff --git a/Lql/Website/Program.cs b/Lql/LqlWebsite/Program.cs similarity index 100% rename from Lql/Website/Program.cs rename to Lql/LqlWebsite/Program.cs diff --git a/Lql/Website/Properties/launchSettings.json b/Lql/LqlWebsite/Properties/launchSettings.json similarity index 100% rename from Lql/Website/Properties/launchSettings.json rename to Lql/LqlWebsite/Properties/launchSettings.json diff --git a/Lql/Website/design-system.md b/Lql/LqlWebsite/design-system.md similarity index 100% rename from Lql/Website/design-system.md rename to Lql/LqlWebsite/design-system.md diff --git a/Lql/Website/lql-icon.png b/Lql/LqlWebsite/lql-icon.png similarity index 100% rename from Lql/Website/lql-icon.png rename to Lql/LqlWebsite/lql-icon.png diff --git a/Lql/Website/wwwroot/css/site.css b/Lql/LqlWebsite/wwwroot/css/site.css similarity index 100% rename from Lql/Website/wwwroot/css/site.css rename to Lql/LqlWebsite/wwwroot/css/site.css diff --git a/Lql/Website/wwwroot/index.html b/Lql/LqlWebsite/wwwroot/index.html similarity index 100% rename from Lql/Website/wwwroot/index.html rename to Lql/LqlWebsite/wwwroot/index.html diff --git a/Lql/Website/wwwroot/lql-icon.png b/Lql/LqlWebsite/wwwroot/lql-icon.png similarity index 100% rename from Lql/Website/wwwroot/lql-icon.png rename to Lql/LqlWebsite/wwwroot/lql-icon.png diff --git a/Migration/Migration.Cli/Migration.Cli.csproj b/Migration/Migration.Cli/Migration.Cli.csproj new file mode 100644 index 00000000..f91a0288 --- /dev/null +++ b/Migration/Migration.Cli/Migration.Cli.csproj @@ -0,0 +1,20 @@ + + + + Exe + Migration.Cli + $(NoWarn);CA2254;CA1515;RS1035;CA2100 + + + + + + + + + + + + + + diff --git a/Migration/Migration.Cli/Program.cs b/Migration/Migration.Cli/Program.cs new file mode 100644 index 00000000..3e336a4c --- /dev/null +++ b/Migration/Migration.Cli/Program.cs @@ -0,0 +1,303 @@ +using Microsoft.Data.Sqlite; +using Migration.Postgres; +using Migration.SQLite; +using Npgsql; + +namespace Migration.Cli; + +/// +/// CLI tool to create databases from YAML schema definitions. +/// This is the ONLY canonical tool for database creation - all projects MUST use this. +/// +public static class Program +{ + /// + /// Entry point - creates database from YAML schema file. + /// Usage: dotnet run -- --schema path/to/schema.yaml --output path/to/database.db --provider [sqlite|postgres] + /// + public static int Main(string[] args) + { + var parseResult = ParseArguments(args); + + return parseResult switch + { + ParseResult.Success success => ExecuteMigration(success), + ParseResult.Failure failure => ShowError(failure), + ParseResult.HelpRequested => ShowUsage(), + }; + } + + private static int ExecuteMigration(ParseResult.Success args) + { + Console.WriteLine("Migration.Cli - Database Schema Tool"); + Console.WriteLine($" Schema: {args.SchemaPath}"); + Console.WriteLine($" Output: {args.OutputPath}"); + Console.WriteLine($" Provider: {args.Provider}"); + Console.WriteLine(); + + if (!File.Exists(args.SchemaPath)) + { + Console.WriteLine($"Error: Schema file not found: {args.SchemaPath}"); + return 1; + } + + SchemaDefinition schema; + try + { + var yamlContent = File.ReadAllText(args.SchemaPath); + schema = SchemaYamlSerializer.FromYaml(yamlContent); + Console.WriteLine($"Loaded schema '{schema.Name}' with {schema.Tables.Count} tables"); + } + catch (Exception ex) + { + Console.WriteLine($"Error: Failed to parse YAML schema: {ex}"); + return 1; + } + + return args.Provider.ToLowerInvariant() switch + { + "sqlite" => CreateSqliteDatabase(schema, args.OutputPath), + "postgres" => CreatePostgresDatabase(schema, args.OutputPath), + _ => ShowProviderError(args.Provider), + }; + } + + private static int CreateSqliteDatabase(SchemaDefinition schema, string outputPath) + { + try + { + // Delete existing file to start fresh + if (File.Exists(outputPath)) + { + File.Delete(outputPath); + Console.WriteLine($"Deleted existing database: {outputPath}"); + } + + // Ensure directory exists + var directory = Path.GetDirectoryName(outputPath); + if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + var connectionString = $"Data Source={outputPath}"; + using var connection = new SqliteConnection(connectionString); + connection.Open(); + + var tablesCreated = 0; + foreach (var table in schema.Tables) + { + var ddl = SqliteDdlGenerator.Generate(new CreateTableOperation(table)); + using var cmd = connection.CreateCommand(); + cmd.CommandText = ddl; + cmd.ExecuteNonQuery(); + Console.WriteLine($" Created table: {table.Name}"); + tablesCreated++; + } + + Console.WriteLine(); + Console.WriteLine($"Successfully created SQLite database with {tablesCreated} tables"); + Console.WriteLine($" Output: {outputPath}"); + return 0; + } + catch (Exception ex) + { + Console.WriteLine($"Error: SQLite migration failed: {ex}"); + return 1; + } + } + + private static int CreatePostgresDatabase(SchemaDefinition schema, string connectionString) + { + try + { + using var connection = new NpgsqlConnection(connectionString); + connection.Open(); + + Console.WriteLine("Connected to PostgreSQL database"); + + var result = PostgresDdlGenerator.MigrateSchema( + connection: connection, + schema: schema, + onTableCreated: table => Console.WriteLine($" Created table: {table}"), + onTableFailed: (table, ex) => Console.WriteLine($" Failed table: {table} - {ex}") + ); + + Console.WriteLine(); + + if (result.Success) + { + Console.WriteLine( + $"Successfully created PostgreSQL database with {result.TablesCreated} tables" + ); + return 0; + } + else + { + Console.WriteLine("PostgreSQL migration completed with errors:"); + foreach (var error in result.Errors) + { + Console.WriteLine($" {error}"); + } + + return result.TablesCreated > 0 ? 0 : 1; + } + } + catch (Exception ex) + { + Console.WriteLine($"Error: PostgreSQL connection/migration failed: {ex}"); + return 1; + } + } + + private static int ShowProviderError(string provider) + { + Console.WriteLine($"Error: Unknown provider '{provider}'"); + Console.WriteLine("Valid providers: sqlite, postgres"); + return 1; + } + + private static int ShowError(ParseResult.Failure failure) + { + Console.WriteLine($"Error: {failure.Message}"); + Console.WriteLine(); + return ShowUsage(); + } + + private static int ShowUsage() + { + Console.WriteLine("Migration.Cli - Database Schema Tool"); + Console.WriteLine(); + Console.WriteLine("Usage:"); + Console.WriteLine( + " dotnet run --project Migration/Migration.Cli/Migration.Cli.csproj -- \\" + ); + Console.WriteLine(" --schema path/to/schema.yaml \\"); + Console.WriteLine(" --output path/to/database.db \\"); + Console.WriteLine(" --provider [sqlite|postgres]"); + Console.WriteLine(); + Console.WriteLine("Options:"); + Console.WriteLine(" --schema Path to YAML schema definition file (required)"); + Console.WriteLine( + " --output Path to output database file (SQLite) or connection string (Postgres)" + ); + Console.WriteLine(" --provider Database provider: sqlite or postgres (default: sqlite)"); + Console.WriteLine(); + Console.WriteLine("Examples:"); + Console.WriteLine(" # SQLite (file path)"); + Console.WriteLine( + " dotnet run -- --schema my-schema.yaml --output ./build.db --provider sqlite" + ); + Console.WriteLine(); + Console.WriteLine(" # PostgreSQL (connection string)"); + Console.WriteLine(" dotnet run -- --schema my-schema.yaml \\"); + Console.WriteLine( + " --output \"Host=localhost;Database=mydb;Username=user;Password=pass\" \\" + ); + Console.WriteLine(" --provider postgres"); + Console.WriteLine(); + Console.WriteLine("YAML Schema Format:"); + Console.WriteLine(" name: my_schema"); + Console.WriteLine(" tables:"); + Console.WriteLine(" - name: Users"); + Console.WriteLine(" columns:"); + Console.WriteLine(" - name: Id"); + Console.WriteLine(" type: Uuid"); + Console.WriteLine(" isNullable: false"); + Console.WriteLine(" - name: Email"); + Console.WriteLine(" type: VarChar(255)"); + Console.WriteLine(" isNullable: false"); + Console.WriteLine(" primaryKey:"); + Console.WriteLine(" columns: [Id]"); + return 1; + } + + private static ParseResult ParseArguments(string[] args) + { + string? schemaPath = null; + string? outputPath = null; + var provider = "sqlite"; + + for (var i = 0; i < args.Length; i++) + { + var arg = args[i]; + + switch (arg) + { + case "--schema" + or "-s": + if (i + 1 >= args.Length) + { + return new ParseResult.Failure("--schema requires a path argument"); + } + + schemaPath = args[++i]; + break; + + case "--output" + or "-o": + if (i + 1 >= args.Length) + { + return new ParseResult.Failure("--output requires a path argument"); + } + + outputPath = args[++i]; + break; + + case "--provider" + or "-p": + if (i + 1 >= args.Length) + { + return new ParseResult.Failure( + "--provider requires an argument (sqlite or postgres)" + ); + } + + provider = args[++i]; + break; + + case "--help" + or "-h": + return new ParseResult.HelpRequested(); + + default: + if (arg.StartsWith('-')) + { + return new ParseResult.Failure($"Unknown option: {arg}"); + } + + break; + } + } + + if (string.IsNullOrEmpty(schemaPath)) + { + return new ParseResult.Failure("--schema is required"); + } + + if (string.IsNullOrEmpty(outputPath)) + { + return new ParseResult.Failure("--output is required"); + } + + return new ParseResult.Success(schemaPath, outputPath, provider); + } +} + +/// +/// Argument parsing result - closed type hierarchy. +/// +public abstract record ParseResult +{ + private ParseResult() { } + + /// Successfully parsed arguments. + public sealed record Success(string SchemaPath, string OutputPath, string Provider) + : ParseResult; + + /// Parse error. + public sealed record Failure(string Message) : ParseResult; + + /// Help requested. + public sealed record HelpRequested : ParseResult; +} diff --git a/Migration/Migration.Cli/example-schema.yaml b/Migration/Migration.Cli/example-schema.yaml new file mode 100644 index 00000000..8da71057 --- /dev/null +++ b/Migration/Migration.Cli/example-schema.yaml @@ -0,0 +1,199 @@ +name: example +tables: + - name: Invoice + schema: public + columns: + - name: Id + type: Text + isNullable: false + - name: InvoiceNumber + type: Text + isNullable: false + - name: InvoiceDate + type: Text + isNullable: false + - name: CustomerName + type: Text + isNullable: false + - name: CustomerEmail + type: Text + isNullable: true + - name: TotalAmount + type: Double + isNullable: false + - name: DiscountAmount + type: Double + isNullable: true + - name: Notes + type: Text + isNullable: true + primaryKey: + columns: + - Id + + - name: InvoiceLine + schema: public + columns: + - name: Id + type: Text + isNullable: false + - name: InvoiceId + type: Text + isNullable: false + - name: Description + type: Text + isNullable: false + - name: Quantity + type: Double + isNullable: false + - name: UnitPrice + type: Double + isNullable: false + - name: Amount + type: Double + isNullable: false + - name: DiscountPercentage + type: Double + isNullable: true + - name: Notes + type: Text + isNullable: true + primaryKey: + columns: + - Id + foreignKeys: + - columns: + - InvoiceId + referencedTable: Invoice + referencedSchema: public + referencedColumns: + - Id + onDelete: NoAction + onUpdate: NoAction + + - name: Customer + schema: public + columns: + - name: Id + type: Text + isNullable: false + - name: CustomerName + type: Text + isNullable: false + - name: Email + type: Text + isNullable: true + - name: Phone + type: Text + isNullable: true + - name: CreatedDate + type: Text + isNullable: false + primaryKey: + columns: + - Id + + - name: Address + schema: public + columns: + - name: Id + type: Text + isNullable: false + - name: CustomerId + type: Text + isNullable: false + - name: Street + type: Text + isNullable: false + - name: City + type: Text + isNullable: false + - name: State + type: Text + isNullable: false + - name: ZipCode + type: Text + isNullable: false + - name: Country + type: Text + isNullable: false + primaryKey: + columns: + - Id + foreignKeys: + - columns: + - CustomerId + referencedTable: Customer + referencedSchema: public + referencedColumns: + - Id + onDelete: NoAction + onUpdate: NoAction + + - name: Orders + schema: public + columns: + - name: Id + type: Text + isNullable: false + - name: OrderNumber + type: Text + isNullable: false + - name: OrderDate + type: Text + isNullable: false + - name: CustomerId + type: Text + isNullable: false + - name: TotalAmount + type: Double + isNullable: false + - name: Status + type: Text + isNullable: false + primaryKey: + columns: + - Id + foreignKeys: + - columns: + - CustomerId + referencedTable: Customer + referencedSchema: public + referencedColumns: + - Id + onDelete: NoAction + onUpdate: NoAction + + - name: OrderItem + schema: public + columns: + - name: Id + type: Text + isNullable: false + - name: OrderId + type: Text + isNullable: false + - name: ProductName + type: Text + isNullable: false + - name: Quantity + type: Double + isNullable: false + - name: Price + type: Double + isNullable: false + - name: Subtotal + type: Double + isNullable: false + primaryKey: + columns: + - Id + foreignKeys: + - columns: + - OrderId + referencedTable: Orders + referencedSchema: public + referencedColumns: + - Id + onDelete: NoAction + onUpdate: NoAction diff --git a/Migration/Migration.Postgres/Migration.Postgres.csproj b/Migration/Migration.Postgres/Migration.Postgres.csproj index 9b17c9c4..022fea97 100644 --- a/Migration/Migration.Postgres/Migration.Postgres.csproj +++ b/Migration/Migration.Postgres/Migration.Postgres.csproj @@ -3,7 +3,7 @@ Library Migration.Postgres - $(NoWarn);CA1848;CA2254;CA1305;CA2100 + $(NoWarn);CA2254;CA2100 diff --git a/Migration/Migration.Postgres/PostgresDdlGenerator.cs b/Migration/Migration.Postgres/PostgresDdlGenerator.cs index 63ac2a00..925aa8db 100644 --- a/Migration/Migration.Postgres/PostgresDdlGenerator.cs +++ b/Migration/Migration.Postgres/PostgresDdlGenerator.cs @@ -2,11 +2,64 @@ namespace Migration.Postgres; +/// +/// Result of a schema migration operation. +/// +/// Whether the migration completed without errors. +/// Number of tables successfully created or already existing. +/// List of table names and error messages for any failures. +public sealed record MigrationResult(bool Success, int TablesCreated, IReadOnlyList Errors); + /// /// PostgreSQL DDL generator for schema operations. /// public static class PostgresDdlGenerator { + /// + /// Migrate a schema definition to PostgreSQL, creating all tables. + /// Each table is created independently - failures on one table don't block others. + /// Uses CREATE TABLE IF NOT EXISTS for idempotency. + /// + /// Open database connection. + /// Schema definition to migrate. + /// Optional callback for each table created (table name). + /// Optional callback for each table that failed (table name, exception). + /// Migration result with success status and any errors. + public static MigrationResult MigrateSchema( + IDbConnection connection, + SchemaDefinition schema, + Action? onTableCreated = null, + Action? onTableFailed = null + ) + { + var errors = new List(); + var tablesCreated = 0; + + foreach (var table in schema.Tables) + { + try + { + var ddl = Generate(new CreateTableOperation(table)); + using var cmd = connection.CreateCommand(); + cmd.CommandText = ddl; + cmd.ExecuteNonQuery(); + tablesCreated++; + onTableCreated?.Invoke(table.Name); + } + catch (Exception ex) + { + errors.Add($"{table.Name}: {ex.Message}"); + onTableFailed?.Invoke(table.Name, ex); + } + } + + return new MigrationResult( + Success: errors.Count == 0, + TablesCreated: tablesCreated, + Errors: errors.AsReadOnly() + ); + } + /// /// Generate PostgreSQL DDL for a schema operation. /// @@ -90,11 +143,15 @@ private static string GenerateCreateTable(TableDefinition table) { sb.AppendLine(";"); var unique = index.IsUnique ? "UNIQUE " : ""; - var cols = string.Join(", ", index.Columns.Select(c => $"\"{c}\"")); + // Expression indexes use Expressions verbatim, column indexes quote column names + var indexItems = + index.Expressions.Count > 0 + ? string.Join(", ", index.Expressions) + : string.Join(", ", index.Columns.Select(c => $"\"{c}\"")); var filter = index.Filter is not null ? $" WHERE {index.Filter}" : ""; sb.Append( CultureInfo.InvariantCulture, - $"CREATE {unique}INDEX IF NOT EXISTS \"{index.Name}\" ON \"{table.Schema}\".\"{table.Name}\" ({cols}){filter}" + $"CREATE {unique}INDEX IF NOT EXISTS \"{index.Name}\" ON \"{table.Schema}\".\"{table.Name}\" ({indexItems}){filter}" ); } @@ -129,7 +186,13 @@ private static string GenerateColumnDef(ColumnDefinition column) sb.Append(" NOT NULL"); } - if (column.DefaultValue is not null) + // LQL expression takes precedence over raw SQL default + if (column.DefaultLqlExpression is not null) + { + var translated = LqlDefaultTranslator.ToPostgres(column.DefaultLqlExpression); + sb.Append(CultureInfo.InvariantCulture, $" DEFAULT {translated}"); + } + else if (column.DefaultValue is not null) { sb.Append(CultureInfo.InvariantCulture, $" DEFAULT {column.DefaultValue}"); } @@ -151,10 +214,14 @@ private static string GenerateAddColumn(AddColumnOperation op) private static string GenerateCreateIndex(CreateIndexOperation op) { var unique = op.Index.IsUnique ? "UNIQUE " : ""; - var cols = string.Join(", ", op.Index.Columns.Select(c => $"\"{c}\"")); + // Expression indexes use Expressions verbatim, column indexes quote column names + var indexItems = + op.Index.Expressions.Count > 0 + ? string.Join(", ", op.Index.Expressions) + : string.Join(", ", op.Index.Columns.Select(c => $"\"{c}\"")); var filter = op.Index.Filter is not null ? $" WHERE {op.Index.Filter}" : ""; - return $"CREATE {unique}INDEX IF NOT EXISTS \"{op.Index.Name}\" ON \"{op.Schema}\".\"{op.TableName}\" ({cols}){filter}"; + return $"CREATE {unique}INDEX IF NOT EXISTS \"{op.Index.Name}\" ON \"{op.Schema}\".\"{op.TableName}\" ({indexItems}){filter}"; } private static string GenerateAddForeignKey(AddForeignKeyOperation op) diff --git a/Migration/Migration.Postgres/PostgresSchemaInspector.cs b/Migration/Migration.Postgres/PostgresSchemaInspector.cs index 44dce053..aa58c9cd 100644 --- a/Migration/Migration.Postgres/PostgresSchemaInspector.cs +++ b/Migration/Migration.Postgres/PostgresSchemaInspector.cs @@ -177,54 +177,68 @@ ORDER BY kcu.ordinal_position }; } - // Get indexes + // Get indexes (both column-based and expression indexes) using var idxCmd = connection.CreateCommand(); idxCmd.CommandText = """ - SELECT + SELECT i.relname AS index_name, - a.attname AS column_name, - ix.indisunique AS is_unique + ix.indisunique AS is_unique, + pg_get_indexdef(ix.indexrelid) AS index_def, + (SELECT array_agg(a.attname ORDER BY ord.n) + FROM unnest(ix.indkey) WITH ORDINALITY AS ord(attnum, n) + LEFT JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ord.attnum + WHERE ord.attnum > 0) AS column_names, + (SELECT bool_or(ord.attnum = 0) + FROM unnest(ix.indkey) WITH ORDINALITY AS ord(attnum, n)) AS has_expressions FROM pg_class t JOIN pg_index ix ON t.oid = ix.indrelid JOIN pg_class i ON i.oid = ix.indexrelid - JOIN pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(ix.indkey) JOIN pg_namespace n ON n.oid = t.relnamespace - WHERE n.nspname = @schema + WHERE n.nspname = @schema AND t.relname = @table AND NOT ix.indisprimary - ORDER BY i.relname, a.attnum + ORDER BY i.relname """; idxCmd.Parameters.AddWithValue("@schema", schemaName); idxCmd.Parameters.AddWithValue("@table", tableName); - var indexData = new Dictionary Columns)>(); using (var reader = idxCmd.ExecuteReader()) { while (reader.Read()) { var indexName = reader.GetString(0); - var columnName = reader.GetString(1); - var isUnique = reader.GetBoolean(2); + var isUnique = reader.GetBoolean(1); + var indexDef = reader.GetString(2); + var columnNames = reader.IsDBNull(3) ? [] : (string[])reader.GetValue(3); + var hasExpressions = reader.IsDBNull(4) ? false : reader.GetBoolean(4); - if (!indexData.TryGetValue(indexName, out var data)) + if (hasExpressions) { - data = (isUnique, []); - indexData[indexName] = data; + // Expression index - parse expressions from the index definition + // Format: CREATE [UNIQUE] INDEX name ON table (expr1, expr2, ...) + var expressions = ParseIndexExpressions(indexDef); + indexes.Add( + new IndexDefinition + { + Name = indexName, + Expressions = expressions.AsReadOnly(), + IsUnique = isUnique, + } + ); } - data.Columns.Add(columnName); - } - } - - foreach (var (indexName, (isUnique, indexColumns)) in indexData) - { - indexes.Add( - new IndexDefinition + else { - Name = indexName, - Columns = indexColumns.AsReadOnly(), - IsUnique = isUnique, + // Simple column index + indexes.Add( + new IndexDefinition + { + Name = indexName, + Columns = columnNames.ToList().AsReadOnly(), + IsUnique = isUnique, + } + ); } - ); + } } // Get foreign keys @@ -355,4 +369,64 @@ private static ForeignKeyAction ParseForeignKeyAction(string action) => "RESTRICT" => ForeignKeyAction.Restrict, _ => ForeignKeyAction.NoAction, }; + + /// + /// Parse expressions from a PostgreSQL index definition string. + /// Example: "CREATE UNIQUE INDEX uq_name ON public.table USING btree (lower(name), suburb_id)" + /// Returns: ["lower(name)", "suburb_id"] + /// + private static List ParseIndexExpressions(string indexDef) + { + var expressions = new List(); + + // Find the opening parenthesis after USING btree (or just after table name) + var parenStart = indexDef.LastIndexOf('('); + var parenEnd = indexDef.LastIndexOf(')'); + + if (parenStart < 0 || parenEnd < 0 || parenEnd <= parenStart) + { + return expressions; + } + + var content = indexDef.Substring(parenStart + 1, parenEnd - parenStart - 1); + + // Split by comma, but respect nested parentheses (for function calls) + var current = new StringBuilder(); + var depth = 0; + + foreach (var ch in content) + { + switch (ch) + { + case '(': + depth++; + current.Append(ch); + break; + case ')': + depth--; + current.Append(ch); + break; + case ',' when depth == 0: + var expr = current.ToString().Trim(); + if (!string.IsNullOrEmpty(expr)) + { + expressions.Add(expr); + } + current.Clear(); + break; + default: + current.Append(ch); + break; + } + } + + // Add the last expression + var lastExpr = current.ToString().Trim(); + if (!string.IsNullOrEmpty(lastExpr)) + { + expressions.Add(lastExpr); + } + + return expressions; + } } diff --git a/Migration/Migration.SQLite/Migration.SQLite.csproj b/Migration/Migration.SQLite/Migration.SQLite.csproj index 3a36c241..2afb75c0 100644 --- a/Migration/Migration.SQLite/Migration.SQLite.csproj +++ b/Migration/Migration.SQLite/Migration.SQLite.csproj @@ -3,7 +3,7 @@ Library Migration.SQLite - $(NoWarn);CA1848;CA2254;CA1305;CA2100 + $(NoWarn);CA2254;CA2100 diff --git a/Migration/Migration.SQLite/SqliteDdlGenerator.cs b/Migration/Migration.SQLite/SqliteDdlGenerator.cs index 477eac73..88228829 100644 --- a/Migration/Migration.SQLite/SqliteDdlGenerator.cs +++ b/Migration/Migration.SQLite/SqliteDdlGenerator.cs @@ -1,3 +1,5 @@ +using System.Globalization; + namespace Migration.SQLite; /// @@ -39,7 +41,7 @@ public static string Generate(SchemaOperation operation) => private static string GenerateCreateTable(TableDefinition table) { var sb = new StringBuilder(); - sb.Append($"CREATE TABLE IF NOT EXISTS [{table.Name}] ("); + sb.Append(CultureInfo.InvariantCulture, $"CREATE TABLE IF NOT EXISTS [{table.Name}] ("); var columnDefs = new List(); @@ -89,10 +91,15 @@ private static string GenerateCreateTable(TableDefinition table) { sb.AppendLine(";"); var unique = index.IsUnique ? "UNIQUE " : ""; - var cols = string.Join(", ", index.Columns.Select(c => $"[{c}]")); + // Expression indexes use Expressions verbatim, column indexes quote column names + var indexItems = + index.Expressions.Count > 0 + ? string.Join(", ", index.Expressions) + : string.Join(", ", index.Columns.Select(c => $"[{c}]")); var filter = index.Filter is not null ? $" WHERE {index.Filter}" : ""; sb.Append( - $"CREATE {unique}INDEX IF NOT EXISTS [{index.Name}] ON [{table.Name}] ({cols}){filter}" + CultureInfo.InvariantCulture, + $"CREATE {unique}INDEX IF NOT EXISTS [{index.Name}] ON [{table.Name}] ({indexItems}){filter}" ); } @@ -102,7 +109,7 @@ private static string GenerateCreateTable(TableDefinition table) private static string GenerateColumnDef(ColumnDefinition column) { var sb = new StringBuilder(); - sb.Append($"[{column.Name}] "); + sb.Append(CultureInfo.InvariantCulture, $"[{column.Name}] "); sb.Append(PortableTypeToSqlite(column.Type)); if (!column.IsNullable) @@ -110,19 +117,25 @@ private static string GenerateColumnDef(ColumnDefinition column) sb.Append(" NOT NULL"); } - if (column.DefaultValue is not null) + // LQL expression takes precedence over raw SQL default + if (column.DefaultLqlExpression is not null) + { + var translated = LqlDefaultTranslator.ToSqlite(column.DefaultLqlExpression); + sb.Append(CultureInfo.InvariantCulture, $" DEFAULT {translated}"); + } + else if (column.DefaultValue is not null) { - sb.Append($" DEFAULT {column.DefaultValue}"); + sb.Append(CultureInfo.InvariantCulture, $" DEFAULT {column.DefaultValue}"); } if (column.Collation is not null) { - sb.Append($" COLLATE {column.Collation}"); + sb.Append(CultureInfo.InvariantCulture, $" COLLATE {column.Collation}"); } if (column.CheckConstraint is not null) { - sb.Append($" CHECK ({column.CheckConstraint})"); + sb.Append(CultureInfo.InvariantCulture, $" CHECK ({column.CheckConstraint})"); } return sb.ToString(); @@ -137,10 +150,14 @@ private static string GenerateAddColumn(AddColumnOperation op) private static string GenerateCreateIndex(CreateIndexOperation op) { var unique = op.Index.IsUnique ? "UNIQUE " : ""; - var cols = string.Join(", ", op.Index.Columns.Select(c => $"[{c}]")); + // Expression indexes use Expressions verbatim, column indexes quote column names + var indexItems = + op.Index.Expressions.Count > 0 + ? string.Join(", ", op.Index.Expressions) + : string.Join(", ", op.Index.Columns.Select(c => $"[{c}]")); var filter = op.Index.Filter is not null ? $" WHERE {op.Index.Filter}" : ""; - return $"CREATE {unique}INDEX IF NOT EXISTS [{op.Index.Name}] ON [{op.TableName}] ({cols}){filter}"; + return $"CREATE {unique}INDEX IF NOT EXISTS [{op.Index.Name}] ON [{op.TableName}] ({indexItems}){filter}"; } /// diff --git a/Migration/Migration.SQLite/SqliteSchemaInspector.cs b/Migration/Migration.SQLite/SqliteSchemaInspector.cs index 91bea1d0..e34979e5 100644 --- a/Migration/Migration.SQLite/SqliteSchemaInspector.cs +++ b/Migration/Migration.SQLite/SqliteSchemaInspector.cs @@ -157,19 +157,52 @@ public static TableResult InspectTable( foreach (var (indexName, isUnique) in indexNames) { + // First check if this is an expression index by looking at index_info + // Expression columns return NULL for the column name using var idxColCmd = connection.CreateCommand(); idxColCmd.CommandText = $"PRAGMA index_info([{indexName}])"; var indexColumns = new List(); + var hasExpressions = false; using (var reader = idxColCmd.ExecuteReader()) { while (reader.Read()) { + if (reader.IsDBNull(2)) + { + // NULL column name means expression index + hasExpressions = true; + break; + } indexColumns.Add(reader.GetString(2)); } } - if (indexColumns.Count > 0) + if (hasExpressions) + { + // Expression index - get the full definition from sqlite_master + using var sqlCmd = connection.CreateCommand(); + sqlCmd.CommandText = """ + SELECT sql FROM sqlite_master + WHERE type = 'index' AND name = @name + """; + sqlCmd.Parameters.AddWithValue("@name", indexName); + var sql = sqlCmd.ExecuteScalar() as string; + + if (sql is not null) + { + var expressions = ParseIndexExpressions(sql); + indexes.Add( + new IndexDefinition + { + Name = indexName, + Expressions = expressions.AsReadOnly(), + IsUnique = isUnique, + } + ); + } + } + else if (indexColumns.Count > 0) { indexes.Add( new IndexDefinition @@ -264,4 +297,97 @@ private static ForeignKeyAction ParseForeignKeyAction(string action) => "RESTRICT" => ForeignKeyAction.Restrict, _ => ForeignKeyAction.NoAction, }; + + /// + /// Parse expressions from a SQLite index definition string. + /// Example: "CREATE UNIQUE INDEX uq_name ON table (lower(name), suburb_id)" + /// Returns: ["lower(name)", "suburb_id"] + /// + private static List ParseIndexExpressions(string indexDef) + { + var expressions = new List(); + + // Find "ON tablename" then the first ( after it - that's the index columns/expressions + // We can't use LastIndexOf because expressions like lower(name) contain nested parens + var onIndex = indexDef.IndexOf(" ON ", StringComparison.OrdinalIgnoreCase); + if (onIndex < 0) + { + return expressions; + } + + // Find the first ( after ON - this is the start of the index columns + var parenStart = indexDef.IndexOf('(', onIndex); + if (parenStart < 0) + { + return expressions; + } + + // Find matching closing paren by counting depth + var depth = 0; + var parenEnd = -1; + for (var i = parenStart; i < indexDef.Length; i++) + { + switch (indexDef[i]) + { + case '(': + depth++; + break; + case ')': + depth--; + if (depth == 0) + { + parenEnd = i; + goto found; + } + break; + } + } + found: + + if (parenEnd < 0) + { + return expressions; + } + + var content = indexDef.Substring(parenStart + 1, parenEnd - parenStart - 1); + + // Split by comma, but respect nested parentheses (for function calls) + var current = new StringBuilder(); + depth = 0; + + foreach (var ch in content) + { + switch (ch) + { + case '(': + depth++; + current.Append(ch); + break; + case ')': + depth--; + current.Append(ch); + break; + case ',' when depth == 0: + var expr = current.ToString().Trim(); + if (!string.IsNullOrEmpty(expr)) + { + expressions.Add(expr); + } + current.Clear(); + break; + default: + current.Append(ch); + break; + } + } + + // Add the last expression + var lastExpr = current.ToString().Trim(); + if (!string.IsNullOrEmpty(lastExpr)) + { + expressions.Add(lastExpr); + } + + return expressions; + } } diff --git a/Migration/Migration.Tests/LqlDefaultTranslatorTests.cs b/Migration/Migration.Tests/LqlDefaultTranslatorTests.cs new file mode 100644 index 00000000..3f9f9e67 --- /dev/null +++ b/Migration/Migration.Tests/LqlDefaultTranslatorTests.cs @@ -0,0 +1,530 @@ +namespace Migration.Tests; + +/// +/// Unit tests for LqlDefaultTranslator covering all code paths. +/// Tests both ToPostgres() and ToSqlite() methods for complete coverage. +/// +public sealed class LqlDefaultTranslatorTests +{ + // ========================================================================= + // NULL HANDLING - ArgumentNullException + // ========================================================================= + + [Fact] + public void ToPostgres_NullInput_ThrowsArgumentNullException() => + Assert.Throws(() => LqlDefaultTranslator.ToPostgres(null!)); + + [Fact] + public void ToSqlite_NullInput_ThrowsArgumentNullException() => + Assert.Throws(() => LqlDefaultTranslator.ToSqlite(null!)); + + // ========================================================================= + // TIMESTAMP FUNCTIONS - now(), current_timestamp(), current_date(), current_time() + // ========================================================================= + + [Theory] + [InlineData("now()", "CURRENT_TIMESTAMP")] + [InlineData("NOW()", "CURRENT_TIMESTAMP")] // Case insensitive + [InlineData(" now() ", "CURRENT_TIMESTAMP")] // Whitespace trimmed + [InlineData("current_timestamp()", "CURRENT_TIMESTAMP")] + [InlineData("CURRENT_TIMESTAMP()", "CURRENT_TIMESTAMP")] + [InlineData("current_date()", "CURRENT_DATE")] + [InlineData("CURRENT_DATE()", "CURRENT_DATE")] + [InlineData("current_time()", "CURRENT_TIME")] + [InlineData("CURRENT_TIME()", "CURRENT_TIME")] + public void ToPostgres_TimestampFunctions_TranslatesCorrectly(string input, string expected) + { + var result = LqlDefaultTranslator.ToPostgres(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("now()", "(datetime('now'))")] + [InlineData("NOW()", "(datetime('now'))")] // Case insensitive + [InlineData(" now() ", "(datetime('now'))")] // Whitespace trimmed + [InlineData("current_timestamp()", "CURRENT_TIMESTAMP")] + [InlineData("current_date()", "(date('now'))")] + [InlineData("current_time()", "(time('now'))")] + public void ToSqlite_TimestampFunctions_TranslatesCorrectly(string input, string expected) + { + var result = LqlDefaultTranslator.ToSqlite(input); + Assert.Equal(expected, result); + } + + // ========================================================================= + // UUID FUNCTIONS - gen_uuid(), uuid() + // ========================================================================= + + [Theory] + [InlineData("gen_uuid()")] + [InlineData("GEN_UUID()")] // Case insensitive + [InlineData("uuid()")] + [InlineData("UUID()")] + public void ToPostgres_UuidFunctions_TranslatesToGenRandomUuid(string input) + { + var result = LqlDefaultTranslator.ToPostgres(input); + Assert.Equal("gen_random_uuid()", result); + } + + [Theory] + [InlineData("gen_uuid()")] + [InlineData("GEN_UUID()")] + [InlineData("uuid()")] + [InlineData("UUID()")] + public void ToSqlite_UuidFunctions_TranslatesToHexExpression(string input) + { + var result = LqlDefaultTranslator.ToSqlite(input); + + // Should contain the SQLite UUID v4 expression parts + Assert.Contains("hex(randomblob", result); + Assert.Contains("'-'", result); + Assert.Contains("'-4'", result); // UUID v4 marker + Assert.Contains("'89ab'", result); // UUID variant bits + } + + // ========================================================================= + // BOOLEAN LITERALS - true, false + // ========================================================================= + + [Theory] + [InlineData("true", "true")] + [InlineData("TRUE", "true")] + [InlineData("True", "true")] + [InlineData(" true ", "true")] + [InlineData("false", "false")] + [InlineData("FALSE", "false")] + [InlineData("False", "false")] + public void ToPostgres_BooleanLiterals_TranslatesCorrectly(string input, string expected) + { + var result = LqlDefaultTranslator.ToPostgres(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("true", "1")] + [InlineData("TRUE", "1")] + [InlineData("True", "1")] + [InlineData("false", "0")] + [InlineData("FALSE", "0")] + [InlineData("False", "0")] + public void ToSqlite_BooleanLiterals_TranslatesToIntegerValues(string input, string expected) + { + var result = LqlDefaultTranslator.ToSqlite(input); + Assert.Equal(expected, result); + } + + // ========================================================================= + // NUMERIC LITERALS - integers and decimals + // ========================================================================= + + [Theory] + [InlineData("0", "0")] + [InlineData("1", "1")] + [InlineData("42", "42")] + [InlineData("-100", "-100")] + [InlineData("2147483647", "2147483647")] // int32 max + [InlineData("-2147483648", "-2147483648")] // int32 min + [InlineData(" 123 ", "123")] // Whitespace trimmed + public void ToPostgres_IntegerLiterals_PassThrough(string input, string expected) + { + var result = LqlDefaultTranslator.ToPostgres(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("0", "0")] + [InlineData("1", "1")] + [InlineData("42", "42")] + [InlineData("-100", "-100")] + [InlineData("2147483647", "2147483647")] + [InlineData("-2147483648", "-2147483648")] + public void ToSqlite_IntegerLiterals_PassThrough(string input, string expected) + { + var result = LqlDefaultTranslator.ToSqlite(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("0.0", "0.0")] + [InlineData("3.14", "3.14")] + [InlineData("-99.99", "-99.99")] + [InlineData("0.00000001", "0.00000001")] + [InlineData("3.1415926535", "3.1415926535")] + [InlineData(" 1.5 ", "1.5")] // Whitespace trimmed + public void ToPostgres_DecimalLiterals_PassThrough(string input, string expected) + { + var result = LqlDefaultTranslator.ToPostgres(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("0.0", "0.0")] + [InlineData("3.14", "3.14")] + [InlineData("-99.99", "-99.99")] + [InlineData("0.00000001", "0.00000001")] + public void ToSqlite_DecimalLiterals_PassThrough(string input, string expected) + { + var result = LqlDefaultTranslator.ToSqlite(input); + Assert.Equal(expected, result); + } + + // ========================================================================= + // STRING LITERALS - single-quoted strings + // ========================================================================= + + [Theory] + [InlineData("'hello'", "'hello'")] + [InlineData("'Hello World'", "'hello world'")] // Gets lowercased during normalization + [InlineData("''", "''")] + [InlineData("'test123'", "'test123'")] + [InlineData("'foo-bar-baz'", "'foo-bar-baz'")] + [InlineData("'snake_case'", "'snake_case'")] + public void ToPostgres_StringLiterals_PassThrough(string input, string expected) + { + var result = LqlDefaultTranslator.ToPostgres(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("'hello'", "'hello'")] + [InlineData("'Hello World'", "'hello world'")] // Gets lowercased during normalization + [InlineData("''", "''")] + [InlineData("'test123'", "'test123'")] + public void ToSqlite_StringLiterals_PassThrough(string input, string expected) + { + var result = LqlDefaultTranslator.ToSqlite(input); + Assert.Equal(expected, result); + } + + // ========================================================================= + // FUNCTION CALLS - lower(), upper(), coalesce(), length(), etc. + // ========================================================================= + + [Theory] + [InlineData("lower(name)", "lower(name)")] + [InlineData("LOWER(name)", "lower(name)")] + [InlineData("lower(column_name)", "lower(column_name)")] + public void ToPostgres_LowerFunction_TranslatesCorrectly(string input, string expected) + { + var result = LqlDefaultTranslator.ToPostgres(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("lower(name)", "lower(name)")] + [InlineData("LOWER(name)", "lower(name)")] + public void ToSqlite_LowerFunction_TranslatesCorrectly(string input, string expected) + { + var result = LqlDefaultTranslator.ToSqlite(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("upper(name)", "upper(name)")] + [InlineData("UPPER(name)", "upper(name)")] + public void ToPostgres_UpperFunction_TranslatesCorrectly(string input, string expected) + { + var result = LqlDefaultTranslator.ToPostgres(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("upper(name)", "upper(name)")] + [InlineData("UPPER(name)", "upper(name)")] + public void ToSqlite_UpperFunction_TranslatesCorrectly(string input, string expected) + { + var result = LqlDefaultTranslator.ToSqlite(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("coalesce(a, b)", "COALESCE(a, b)")] + [InlineData("COALESCE(x, y, z)", "COALESCE(x, y, z)")] + [InlineData("coalesce(name, 'default')", "COALESCE(name, 'default')")] + public void ToPostgres_CoalesceFunction_TranslatesCorrectly(string input, string expected) + { + var result = LqlDefaultTranslator.ToPostgres(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("coalesce(a, b)", "coalesce(a, b)")] + [InlineData("COALESCE(x, y, z)", "coalesce(x, y, z)")] + public void ToSqlite_CoalesceFunction_TranslatesCorrectly(string input, string expected) + { + var result = LqlDefaultTranslator.ToSqlite(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("length(name)", "length(name)")] + [InlineData("LENGTH(text)", "length(text)")] + public void ToPostgres_LengthFunction_TranslatesCorrectly(string input, string expected) + { + var result = LqlDefaultTranslator.ToPostgres(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("length(name)", "length(name)")] + [InlineData("LENGTH(text)", "length(text)")] + public void ToSqlite_LengthFunction_TranslatesCorrectly(string input, string expected) + { + var result = LqlDefaultTranslator.ToSqlite(input); + Assert.Equal(expected, result); + } + + // ========================================================================= + // SUBSTRING FUNCTION - different syntax between Postgres and SQLite + // ========================================================================= + + [Fact] + public void ToPostgres_SubstringWith3Args_UsesFromForSyntax() + { + var result = LqlDefaultTranslator.ToPostgres("substring(text, 1, 5)"); + Assert.Equal("substring(text from 1 for 5)", result); + } + + [Fact] + public void ToPostgres_SubstringWith2Args_UsesCommaSyntax() + { + var result = LqlDefaultTranslator.ToPostgres("substring(text, 1)"); + Assert.Equal("substring(text, 1)", result); + } + + [Fact] + public void ToSqlite_SubstringWith3Args_UsesSubstrFunction() + { + var result = LqlDefaultTranslator.ToSqlite("substring(text, 1, 5)"); + Assert.Equal("substr(text, 1, 5)", result); + } + + [Fact] + public void ToSqlite_SubstringWith2Args_UsesSubstrFunction() + { + var result = LqlDefaultTranslator.ToSqlite("substring(text, 1)"); + Assert.Equal("substr(text, 1)", result); + } + + // ========================================================================= + // TRIM FUNCTION + // ========================================================================= + + [Theory] + [InlineData("trim(name)", "trim(name)")] + [InlineData("TRIM(text)", "trim(text)")] + public void ToPostgres_TrimFunction_TranslatesCorrectly(string input, string expected) + { + var result = LqlDefaultTranslator.ToPostgres(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("trim(name)", "trim(name)")] + [InlineData("TRIM(text)", "trim(text)")] + public void ToSqlite_TrimFunction_TranslatesCorrectly(string input, string expected) + { + var result = LqlDefaultTranslator.ToSqlite(input); + Assert.Equal(expected, result); + } + + // ========================================================================= + // CONCAT FUNCTION - different syntax between Postgres and SQLite + // ========================================================================= + + [Theory] + [InlineData("concat(a, b)", "concat(a, b)")] + [InlineData("concat(first, ' ', last)", "concat(first, ' ', last)")] + public void ToPostgres_ConcatFunction_TranslatesCorrectly(string input, string expected) + { + var result = LqlDefaultTranslator.ToPostgres(input); + Assert.Equal(expected, result); + } + + [Fact] + public void ToSqlite_ConcatWith2Args_UsesConcatOperator() + { + var result = LqlDefaultTranslator.ToSqlite("concat(a, b)"); + Assert.Equal("a || b", result); + } + + [Fact] + public void ToSqlite_ConcatWith3Args_UsesConcatOperator() + { + var result = LqlDefaultTranslator.ToSqlite("concat(first, ' ', last)"); + Assert.Equal("first || ' ' || last", result); + } + + [Fact] + public void ToSqlite_ConcatWithNoArgs_ReturnsEmptyString() + { + var result = LqlDefaultTranslator.ToSqlite("concat()"); + Assert.Equal("''", result); + } + + // ========================================================================= + // ABS FUNCTION + // ========================================================================= + + [Theory] + [InlineData("abs(value)", "abs(value)")] + [InlineData("ABS(-10)", "abs(-10)")] + public void ToPostgres_AbsFunction_TranslatesCorrectly(string input, string expected) + { + var result = LqlDefaultTranslator.ToPostgres(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("abs(value)", "abs(value)")] + [InlineData("ABS(-10)", "abs(-10)")] + public void ToSqlite_AbsFunction_TranslatesCorrectly(string input, string expected) + { + var result = LqlDefaultTranslator.ToSqlite(input); + Assert.Equal(expected, result); + } + + // ========================================================================= + // ROUND FUNCTION + // ========================================================================= + + [Theory] + [InlineData("round(value)", "round(value)")] + [InlineData("round(price, 2)", "round(price, 2)")] + [InlineData("ROUND(amount, 0)", "round(amount, 0)")] + public void ToPostgres_RoundFunction_TranslatesCorrectly(string input, string expected) + { + var result = LqlDefaultTranslator.ToPostgres(input); + Assert.Equal(expected, result); + } + + [Theory] + [InlineData("round(value)", "round(value)")] + [InlineData("round(price, 2)", "round(price, 2)")] + [InlineData("ROUND(amount, 0)", "round(amount, 0)")] + public void ToSqlite_RoundFunction_TranslatesCorrectly(string input, string expected) + { + var result = LqlDefaultTranslator.ToSqlite(input); + Assert.Equal(expected, result); + } + + // ========================================================================= + // UNKNOWN FUNCTIONS - pass through with function name preserved + // ========================================================================= + + [Fact] + public void ToPostgres_UnknownFunction_PassThroughWithArgs() + { + var result = LqlDefaultTranslator.ToPostgres("custom_func(arg1, arg2)"); + Assert.Equal("custom_func(arg1, arg2)", result); + } + + [Fact] + public void ToSqlite_UnknownFunction_PassThroughWithArgs() + { + var result = LqlDefaultTranslator.ToSqlite("custom_func(arg1, arg2)"); + Assert.Equal("custom_func(arg1, arg2)", result); + } + + [Fact] + public void ToPostgres_UnknownFunctionNoArgs_PassThrough() + { + var result = LqlDefaultTranslator.ToPostgres("my_function()"); + Assert.Equal("my_function()", result); + } + + [Fact] + public void ToSqlite_UnknownFunctionNoArgs_PassThrough() + { + var result = LqlDefaultTranslator.ToSqlite("my_function()"); + Assert.Equal("my_function()", result); + } + + // ========================================================================= + // NON-FUNCTION EXPRESSIONS - column references, pass through + // ========================================================================= + + [Fact] + public void ToPostgres_ColumnReference_PassThrough() + { + var result = LqlDefaultTranslator.ToPostgres("column_name"); + Assert.Equal("column_name", result); + } + + [Fact] + public void ToSqlite_ColumnReference_PassThrough() + { + var result = LqlDefaultTranslator.ToSqlite("column_name"); + Assert.Equal("column_name", result); + } + + [Fact] + public void ToPostgres_ComplexExpression_PassThrough() + { + var result = LqlDefaultTranslator.ToPostgres("some_expression + 1"); + Assert.Equal("some_expression + 1", result); + } + + [Fact] + public void ToSqlite_ComplexExpression_PassThrough() + { + var result = LqlDefaultTranslator.ToSqlite("some_expression + 1"); + Assert.Equal("some_expression + 1", result); + } + + // ========================================================================= + // EDGE CASES - whitespace, mixed case, empty args + // ========================================================================= + + [Fact] + public void ToPostgres_FunctionWithWhitespaceInArgs_PreservesWhitespace() + { + var result = LqlDefaultTranslator.ToPostgres("coalesce( a , b , c )"); + Assert.Equal("COALESCE(a, b, c)", result); + } + + [Fact] + public void ToSqlite_FunctionWithWhitespaceInArgs_PreservesWhitespace() + { + var result = LqlDefaultTranslator.ToSqlite("coalesce( a , b , c )"); + Assert.Equal("coalesce(a, b, c)", result); + } + + [Fact] + public void ToPostgres_FunctionWithEmptyArgs_HandlesGracefully() + { + var result = LqlDefaultTranslator.ToPostgres("lower()"); + Assert.Equal("lower()", result); + } + + [Fact] + public void ToSqlite_FunctionWithEmptyArgs_HandlesGracefully() + { + var result = LqlDefaultTranslator.ToSqlite("lower()"); + Assert.Equal("lower()", result); + } + + // ========================================================================= + // PLATFORM EQUIVALENCE - Same LQL = Same semantic result + // ========================================================================= + + [Theory] + [InlineData("42")] + [InlineData("3.14")] + [InlineData("'hello'")] + [InlineData("lower(name)")] + [InlineData("upper(name)")] + [InlineData("length(text)")] + [InlineData("trim(value)")] + [InlineData("abs(-5)")] + [InlineData("round(price, 2)")] + public void BothPlatforms_SameLql_ProduceValidSql(string lql) + { + // These should not throw and should produce non-empty results + var pgResult = LqlDefaultTranslator.ToPostgres(lql); + var sqliteResult = LqlDefaultTranslator.ToSqlite(lql); + + Assert.False(string.IsNullOrEmpty(pgResult)); + Assert.False(string.IsNullOrEmpty(sqliteResult)); + } +} diff --git a/Migration/Migration.Tests/LqlDefaultsTests.cs b/Migration/Migration.Tests/LqlDefaultsTests.cs new file mode 100644 index 00000000..25db1958 --- /dev/null +++ b/Migration/Migration.Tests/LqlDefaultsTests.cs @@ -0,0 +1,1256 @@ +namespace Migration.Tests; + +/// +/// E2E tests proving LQL (Language Query Language) default values are TRULY platform-independent. +/// Same LQL expression produces correct, equivalent behavior on both SQLite AND PostgreSQL. +/// These tests verify that: +/// 1. The same schema definition works on both platforms +/// 2. Default values are properly applied when inserting without explicit values +/// 3. The resulting data is semantically equivalent across platforms +/// +public sealed class LqlDefaultsTests : IAsyncLifetime +{ + private PostgreSqlContainer _postgres = null!; + private NpgsqlConnection _pgConnection = null!; + private SqliteConnection _sqliteConnection = null!; + private readonly ILogger _logger = NullLogger.Instance; + + public async Task InitializeAsync() + { + // Setup PostgreSQL + _postgres = new PostgreSqlBuilder() + .WithImage("postgres:16-alpine") + .WithDatabase("lql_test") + .WithUsername("test") + .WithPassword("test") + .Build(); + + await _postgres.StartAsync().ConfigureAwait(false); + + _pgConnection = new NpgsqlConnection(_postgres.GetConnectionString()); + await _pgConnection.OpenAsync().ConfigureAwait(false); + + // Setup SQLite (in-memory) + _sqliteConnection = new SqliteConnection("Data Source=:memory:"); + await _sqliteConnection.OpenAsync().ConfigureAwait(false); + } + + public async Task DisposeAsync() + { + await _pgConnection.DisposeAsync().ConfigureAwait(false); + await _postgres.DisposeAsync().ConfigureAwait(false); + _sqliteConnection.Dispose(); + } + + // ========================================================================= + // BOOLEAN DEFAULTS - true/false across platforms + // ========================================================================= + + [Fact] + public void LqlBoolean_True_WorksOnBothPlatforms() + { + // Arrange - Same LQL schema for both platforms + var schema = Schema + .Define("Test") + .Table( + "public", + "settings", + t => + t.Column("id", PortableTypes.Int, c => c.PrimaryKey()) + .Column("enabled", PortableTypes.Boolean, c => c.DefaultLql("true")) + ) + .Build(); + + // Act - Apply to both databases + ApplySchema(_pgConnection, schema, PostgresDdlGenerator.Generate); + ApplySchema(_sqliteConnection, schema, SqliteDdlGenerator.Generate); + + // VERIFY DDL: Table structure is correct + var pgDdl = GetPostgresTableDdl(_pgConnection, "settings"); + var sqliteDdl = GetSqliteTableDdl(_sqliteConnection, "settings"); + + // PostgreSQL: boolean column with DEFAULT true + Assert.Contains("enabled", pgDdl); + Assert.Contains("DEFAULT true", pgDdl); + + // SQLite: integer column with DEFAULT 1 (true = 1) + Assert.Contains("[enabled]", sqliteDdl); + Assert.Contains("DEFAULT 1", sqliteDdl); + + // VERIFY COLUMNS: Exactly 2 columns, no extras + var pgColumns = GetPostgresColumns(_pgConnection, "settings"); + Assert.Equal(2, pgColumns.Count); + Assert.Contains("id", pgColumns); + Assert.Contains("enabled", pgColumns); + + var sqliteColumns = GetSqliteColumns(_sqliteConnection, "settings"); + Assert.Equal(2, sqliteColumns.Count); + Assert.Contains("id", sqliteColumns); + Assert.Contains("enabled", sqliteColumns); + + // Insert without specifying 'enabled' + ExecutePg("INSERT INTO settings (id) VALUES (1)"); + ExecuteSqlite("INSERT INTO settings (id) VALUES (1)"); + + // VERIFY RUNTIME: Defaults applied correctly + var pgValue = QueryPg("SELECT enabled FROM settings WHERE id = 1"); + var sqliteValue = QuerySqlite("SELECT enabled FROM settings WHERE id = 1"); + + Assert.True(pgValue); // Postgres: true + Assert.Equal(1, sqliteValue); // SQLite: 1 (represents true) + } + + [Fact] + public void LqlBoolean_False_WorksOnBothPlatforms() + { + // Arrange + var schema = Schema + .Define("Test") + .Table( + "public", + "flags", + t => + t.Column("id", PortableTypes.Int, c => c.PrimaryKey()) + .Column("disabled", PortableTypes.Boolean, c => c.DefaultLql("false")) + ) + .Build(); + + // Act + ApplySchema(_pgConnection, schema, PostgresDdlGenerator.Generate); + ApplySchema(_sqliteConnection, schema, SqliteDdlGenerator.Generate); + + // VERIFY DDL: Table structure is correct + var pgDdl = GetPostgresTableDdl(_pgConnection, "flags"); + var sqliteDdl = GetSqliteTableDdl(_sqliteConnection, "flags"); + + // PostgreSQL: boolean column with DEFAULT false + Assert.Contains("disabled", pgDdl); + Assert.Contains("DEFAULT false", pgDdl); + + // SQLite: integer column with DEFAULT 0 (false = 0) + Assert.Contains("[disabled]", sqliteDdl); + Assert.Contains("DEFAULT 0", sqliteDdl); + + // VERIFY COLUMNS: Exactly 2 columns, no extras + Assert.Equal(2, GetPostgresColumns(_pgConnection, "flags").Count); + Assert.Equal(2, GetSqliteColumns(_sqliteConnection, "flags").Count); + + ExecutePg("INSERT INTO flags (id) VALUES (1)"); + ExecuteSqlite("INSERT INTO flags (id) VALUES (1)"); + + // VERIFY RUNTIME: Defaults applied correctly + var pgValue = QueryPg("SELECT disabled FROM flags WHERE id = 1"); + var sqliteValue = QuerySqlite("SELECT disabled FROM flags WHERE id = 1"); + + Assert.False(pgValue); // Postgres: false + Assert.Equal(0, sqliteValue); // SQLite: 0 (represents false) + } + + // ========================================================================= + // TIMESTAMP DEFAULTS - now() and current_timestamp() + // ========================================================================= + + [Fact] + public void LqlNow_DefaultsToCurrentTime_BothPlatforms() + { + // Arrange + var schema = Schema + .Define("Test") + .Table( + "public", + "events", + t => + t.Column("id", PortableTypes.Int, c => c.PrimaryKey()) + .Column( + "created_at", + PortableTypes.DateTimeOffset, + c => c.DefaultLql("now()") + ) + ) + .Build(); + + var beforeTest = DateTime.UtcNow.AddSeconds(-1); + + // Act + ApplySchema(_pgConnection, schema, PostgresDdlGenerator.Generate); + ApplySchema(_sqliteConnection, schema, SqliteDdlGenerator.Generate); + + // VERIFY DDL: Default expressions are correctly translated + var pgDdl = GetPostgresTableDdl(_pgConnection, "events"); + var sqliteDdl = GetSqliteTableDdl(_sqliteConnection, "events"); + + // PostgreSQL: now() translates to CURRENT_TIMESTAMP + Assert.Contains("created_at", pgDdl); + Assert.Contains("CURRENT_TIMESTAMP", pgDdl.ToUpperInvariant()); + + // SQLite: now() translates to (datetime('now')) + Assert.Contains("[created_at]", sqliteDdl); + Assert.Contains("datetime('now')", sqliteDdl.ToLowerInvariant()); + + // VERIFY COLUMNS: Exactly 2 columns + Assert.Equal(2, GetPostgresColumns(_pgConnection, "events").Count); + Assert.Equal(2, GetSqliteColumns(_sqliteConnection, "events").Count); + + ExecutePg("INSERT INTO events (id) VALUES (1)"); + ExecuteSqlite("INSERT INTO events (id) VALUES (1)"); + + var afterTest = DateTime.UtcNow.AddSeconds(1); + + // VERIFY RUNTIME: Both should have timestamps close to now + var pgValue = QueryPg("SELECT created_at FROM events WHERE id = 1"); + var sqliteValue = QuerySqlite("SELECT created_at FROM events WHERE id = 1"); + + // Postgres: DateTime value + Assert.InRange(pgValue, beforeTest, afterTest); + + // SQLite: String in ISO format (e.g., "2025-01-15 10:30:45") + Assert.True(DateTime.TryParse(sqliteValue, out var sqliteDt)); + // SQLite CURRENT_TIMESTAMP is in UTC + Assert.InRange(sqliteDt, beforeTest.AddHours(-24), afterTest.AddHours(24)); + } + + [Fact] + public void LqlCurrentTimestamp_SameAsNow_BothPlatforms() + { + // Arrange + var schema = Schema + .Define("Test") + .Table( + "public", + "logs", + t => + t.Column("id", PortableTypes.Int, c => c.PrimaryKey()) + .Column( + "timestamp", + PortableTypes.DateTimeOffset, + c => c.DefaultLql("current_timestamp()") + ) + ) + .Build(); + + // Act + ApplySchema(_pgConnection, schema, PostgresDdlGenerator.Generate); + ApplySchema(_sqliteConnection, schema, SqliteDdlGenerator.Generate); + + ExecutePg("INSERT INTO logs (id) VALUES (1)"); + ExecuteSqlite("INSERT INTO logs (id) VALUES (1)"); + + // Assert - Should have valid timestamps + var pgValue = QueryPg("SELECT timestamp FROM logs WHERE id = 1"); + var sqliteValue = QuerySqlite("SELECT timestamp FROM logs WHERE id = 1"); + + Assert.True(pgValue > DateTime.MinValue); + Assert.False(string.IsNullOrEmpty(sqliteValue)); + } + + [Fact] + public void LqlCurrentDate_ReturnsDateOnly_BothPlatforms() + { + // Arrange + var schema = Schema + .Define("Test") + .Table( + "public", + "daily_records", + t => + t.Column("id", PortableTypes.Int, c => c.PrimaryKey()) + .Column( + "record_date", + PortableTypes.Date, + c => c.DefaultLql("current_date()") + ) + ) + .Build(); + + // Act + ApplySchema(_pgConnection, schema, PostgresDdlGenerator.Generate); + ApplySchema(_sqliteConnection, schema, SqliteDdlGenerator.Generate); + + ExecutePg("INSERT INTO daily_records (id) VALUES (1)"); + ExecuteSqlite("INSERT INTO daily_records (id) VALUES (1)"); + + // Assert + var pgValue = QueryPg("SELECT record_date FROM daily_records WHERE id = 1"); + var sqliteValue = QuerySqlite("SELECT record_date FROM daily_records WHERE id = 1"); + + // Postgres: Date value + Assert.Equal(DateTime.UtcNow.Date, pgValue.Date); + + // SQLite: Date string (e.g., "2025-01-15") + Assert.True(DateTime.TryParse(sqliteValue, out var sqliteDt)); + } + + // ========================================================================= + // NUMERIC DEFAULTS - integers and decimals + // ========================================================================= + + [Fact] + public void LqlNumericInteger_DefaultsCorrectly_BothPlatforms() + { + // Arrange + var schema = Schema + .Define("Test") + .Table( + "public", + "counters", + t => + t.Column("id", PortableTypes.Int, c => c.PrimaryKey()) + .Column("count", PortableTypes.Int, c => c.DefaultLql("42")) + .Column("negative", PortableTypes.Int, c => c.DefaultLql("-100")) + .Column("zero", PortableTypes.Int, c => c.DefaultLql("0")) + ) + .Build(); + + // Act + ApplySchema(_pgConnection, schema, PostgresDdlGenerator.Generate); + ApplySchema(_sqliteConnection, schema, SqliteDdlGenerator.Generate); + + ExecutePg("INSERT INTO counters (id) VALUES (1)"); + ExecuteSqlite("INSERT INTO counters (id) VALUES (1)"); + + // Assert - Same integer values on both platforms + Assert.Equal(42, QueryPg("SELECT count FROM counters WHERE id = 1")); + Assert.Equal(42, QuerySqlite("SELECT count FROM counters WHERE id = 1")); + + Assert.Equal(-100, QueryPg("SELECT negative FROM counters WHERE id = 1")); + Assert.Equal(-100, QuerySqlite("SELECT negative FROM counters WHERE id = 1")); + + Assert.Equal(0, QueryPg("SELECT zero FROM counters WHERE id = 1")); + Assert.Equal(0, QuerySqlite("SELECT zero FROM counters WHERE id = 1")); + } + + [Fact] + public void LqlNumericDecimal_DefaultsCorrectly_BothPlatforms() + { + // Arrange + var schema = Schema + .Define("Test") + .Table( + "public", + "prices", + t => + t.Column("id", PortableTypes.Int, c => c.PrimaryKey()) + .Column("amount", PortableTypes.Decimal(10, 2), c => c.DefaultLql("99.99")) + .Column( + "tax_rate", + PortableTypes.Decimal(5, 4), + c => c.DefaultLql("0.0825") + ) + .Column( + "discount", + PortableTypes.Decimal(5, 2), + c => c.DefaultLql("-10.50") + ) + ) + .Build(); + + // Act + ApplySchema(_pgConnection, schema, PostgresDdlGenerator.Generate); + ApplySchema(_sqliteConnection, schema, SqliteDdlGenerator.Generate); + + ExecutePg("INSERT INTO prices (id) VALUES (1)"); + ExecuteSqlite("INSERT INTO prices (id) VALUES (1)"); + + // Assert - Same decimal values (within floating-point tolerance for SQLite) + Assert.Equal(99.99m, QueryPg("SELECT amount FROM prices WHERE id = 1")); + Assert.Equal(99.99, QuerySqlite("SELECT amount FROM prices WHERE id = 1"), 2); + + Assert.Equal(0.0825m, QueryPg("SELECT tax_rate FROM prices WHERE id = 1")); + Assert.Equal(0.0825, QuerySqlite("SELECT tax_rate FROM prices WHERE id = 1"), 4); + + Assert.Equal(-10.50m, QueryPg("SELECT discount FROM prices WHERE id = 1")); + Assert.Equal(-10.50, QuerySqlite("SELECT discount FROM prices WHERE id = 1"), 2); + } + + // ========================================================================= + // STRING DEFAULTS - quoted strings + // ========================================================================= + + [Fact] + public void LqlStringLiteral_DefaultsCorrectly_BothPlatforms() + { + // Arrange - Strings must be quoted with single quotes in LQL + var schema = Schema + .Define("Test") + .Table( + "public", + "statuses", + t => + t.Column("id", PortableTypes.Int, c => c.PrimaryKey()) + .Column("status", PortableTypes.VarChar(50), c => c.DefaultLql("'active'")) + .Column( + "category", + PortableTypes.VarChar(100), + c => c.DefaultLql("'uncategorized'") + ) + .Column("empty", PortableTypes.VarChar(10), c => c.DefaultLql("''")) + ) + .Build(); + + // Act + ApplySchema(_pgConnection, schema, PostgresDdlGenerator.Generate); + ApplySchema(_sqliteConnection, schema, SqliteDdlGenerator.Generate); + + ExecutePg("INSERT INTO statuses (id) VALUES (1)"); + ExecuteSqlite("INSERT INTO statuses (id) VALUES (1)"); + + // Assert - Same string values on both platforms + Assert.Equal("active", QueryPg("SELECT status FROM statuses WHERE id = 1")); + Assert.Equal("active", QuerySqlite("SELECT status FROM statuses WHERE id = 1")); + + Assert.Equal( + "uncategorized", + QueryPg("SELECT category FROM statuses WHERE id = 1") + ); + Assert.Equal( + "uncategorized", + QuerySqlite("SELECT category FROM statuses WHERE id = 1") + ); + + Assert.Equal("", QueryPg("SELECT empty FROM statuses WHERE id = 1")); + Assert.Equal("", QuerySqlite("SELECT empty FROM statuses WHERE id = 1")); + } + + // ========================================================================= + // UUID DEFAULTS - gen_uuid() + // ========================================================================= + + [Fact] + public void LqlGenUuid_GeneratesValidUuid_BothPlatforms() + { + // Arrange + var schema = Schema + .Define("Test") + .Table( + "public", + "entities", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey().DefaultLql("gen_uuid()")) + .Column("name", PortableTypes.VarChar(100)) + ) + .Build(); + + // Act + ApplySchema(_pgConnection, schema, PostgresDdlGenerator.Generate); + ApplySchema(_sqliteConnection, schema, SqliteDdlGenerator.Generate); + + // VERIFY DDL: UUID generation is correctly translated + var pgDdl = GetPostgresTableDdl(_pgConnection, "entities"); + var sqliteDdl = GetSqliteTableDdl(_sqliteConnection, "entities"); + + // PostgreSQL: gen_uuid() translates to gen_random_uuid() + Assert.Contains("id", pgDdl); + Assert.Contains("gen_random_uuid()", pgDdl.ToLowerInvariant()); + + // SQLite: gen_uuid() translates to complex hex expression + Assert.Contains("[id]", sqliteDdl); + Assert.Contains("hex(randomblob", sqliteDdl.ToLowerInvariant()); + + // VERIFY COLUMNS: Exactly 2 columns + var pgCols = GetPostgresColumns(_pgConnection, "entities"); + Assert.Equal(2, pgCols.Count); + Assert.Contains("id", pgCols); + Assert.Contains("name", pgCols); + + var sqliteCols = GetSqliteColumns(_sqliteConnection, "entities"); + Assert.Equal(2, sqliteCols.Count); + Assert.Contains("id", sqliteCols); + Assert.Contains("name", sqliteCols); + + // Insert without specifying UUID (let default generate it) + ExecutePg("INSERT INTO entities (name) VALUES ('test1')"); + ExecutePg("INSERT INTO entities (name) VALUES ('test2')"); + ExecuteSqlite("INSERT INTO entities (name) VALUES ('test1')"); + ExecuteSqlite("INSERT INTO entities (name) VALUES ('test2')"); + + // VERIFY RUNTIME: Should generate valid UUIDs + var pgUuid1 = QueryPg("SELECT id FROM entities WHERE name = 'test1'"); + var pgUuid2 = QueryPg("SELECT id FROM entities WHERE name = 'test2'"); + + Assert.NotEqual(Guid.Empty, pgUuid1); + Assert.NotEqual(Guid.Empty, pgUuid2); + Assert.NotEqual(pgUuid1, pgUuid2); // Unique UUIDs + + var sqliteUuid1 = QuerySqlite("SELECT id FROM entities WHERE name = 'test1'"); + var sqliteUuid2 = QuerySqlite("SELECT id FROM entities WHERE name = 'test2'"); + + // SQLite stores UUIDs as text - verify they're valid UUID format + Assert.True(Guid.TryParse(sqliteUuid1, out var parsed1)); + Assert.True(Guid.TryParse(sqliteUuid2, out var parsed2)); + Assert.NotEqual(Guid.Empty, parsed1); + Assert.NotEqual(Guid.Empty, parsed2); + Assert.NotEqual(parsed1, parsed2); // Unique UUIDs + } + + [Fact] + public void LqlUuidAlias_SameAsGenUuid_BothPlatforms() + { + // Arrange - uuid() should work the same as gen_uuid() + var schema = Schema + .Define("Test") + .Table( + "public", + "items", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey().DefaultLql("uuid()")) + .Column("label", PortableTypes.VarChar(50)) + ) + .Build(); + + // Act + ApplySchema(_pgConnection, schema, PostgresDdlGenerator.Generate); + ApplySchema(_sqliteConnection, schema, SqliteDdlGenerator.Generate); + + ExecutePg("INSERT INTO items (label) VALUES ('item1')"); + ExecuteSqlite("INSERT INTO items (label) VALUES ('item1')"); + + // Assert + var pgUuid = QueryPg("SELECT id FROM items WHERE label = 'item1'"); + Assert.NotEqual(Guid.Empty, pgUuid); + + var sqliteUuid = QuerySqlite("SELECT id FROM items WHERE label = 'item1'"); + Assert.True(Guid.TryParse(sqliteUuid, out var parsed)); + Assert.NotEqual(Guid.Empty, parsed); + } + + // ========================================================================= + // COMBINED DEFAULTS - Multiple LQL defaults in one table + // ========================================================================= + + [Fact] + public void LqlMultipleDefaults_AllWorkTogether_BothPlatforms() + { + // Arrange - Real-world scenario with multiple LQL defaults + var schema = Schema + .Define("Test") + .Table( + "public", + "audit_records", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey().DefaultLql("gen_uuid()")) + .Column("action", PortableTypes.VarChar(100), c => c.NotNull()) + .Column("is_success", PortableTypes.Boolean, c => c.DefaultLql("true")) + .Column("retry_count", PortableTypes.Int, c => c.DefaultLql("0")) + .Column("priority", PortableTypes.Decimal(3, 1), c => c.DefaultLql("5.0")) + .Column("status", PortableTypes.VarChar(20), c => c.DefaultLql("'pending'")) + .Column( + "created_at", + PortableTypes.DateTimeOffset, + c => c.DefaultLql("now()") + ) + ) + .Build(); + + // Act + ApplySchema(_pgConnection, schema, PostgresDdlGenerator.Generate); + ApplySchema(_sqliteConnection, schema, SqliteDdlGenerator.Generate); + + // VERIFY DDL: All defaults are correctly translated + var pgDdl = GetPostgresTableDdl(_pgConnection, "audit_records"); + var sqliteDdl = GetSqliteTableDdl(_sqliteConnection, "audit_records"); + + // PostgreSQL DDL verification + Assert.Contains("gen_random_uuid()", pgDdl.ToLowerInvariant()); // UUID + Assert.Contains("DEFAULT true", pgDdl); // boolean true + Assert.Contains("DEFAULT 0", pgDdl); // integer 0 + Assert.Contains("DEFAULT 5.0", pgDdl); // decimal + Assert.Contains("DEFAULT 'pending'", pgDdl); // string + Assert.Contains("CURRENT_TIMESTAMP", pgDdl.ToUpperInvariant()); // now() + + // SQLite DDL verification + Assert.Contains("hex(randomblob", sqliteDdl.ToLowerInvariant()); // UUID + Assert.Contains("DEFAULT 1", sqliteDdl); // boolean true = 1 + Assert.Contains("DEFAULT 0", sqliteDdl); // integer 0 + Assert.Contains("DEFAULT 5.0", sqliteDdl); // decimal + Assert.Contains("DEFAULT 'pending'", sqliteDdl); // string + Assert.Contains("datetime('now')", sqliteDdl.ToLowerInvariant()); // now() + + // VERIFY COLUMNS: Exactly 7 columns with correct names + var pgCols = GetPostgresColumns(_pgConnection, "audit_records"); + Assert.Equal(7, pgCols.Count); + Assert.Contains("id", pgCols); + Assert.Contains("action", pgCols); + Assert.Contains("is_success", pgCols); + Assert.Contains("retry_count", pgCols); + Assert.Contains("priority", pgCols); + Assert.Contains("status", pgCols); + Assert.Contains("created_at", pgCols); + + var sqliteCols = GetSqliteColumns(_sqliteConnection, "audit_records"); + Assert.Equal(7, sqliteCols.Count); + + // Insert with only required fields - all defaults should apply + ExecutePg("INSERT INTO audit_records (action) VALUES ('user_login')"); + ExecuteSqlite("INSERT INTO audit_records (action) VALUES ('user_login')"); + + // VERIFY RUNTIME: All defaults applied on both platforms + // Postgres + var pgId = QueryPg("SELECT id FROM audit_records WHERE action = 'user_login'"); + Assert.NotEqual(Guid.Empty, pgId); + Assert.True( + QueryPg("SELECT is_success FROM audit_records WHERE action = 'user_login'") + ); + Assert.Equal( + 0, + QueryPg("SELECT retry_count FROM audit_records WHERE action = 'user_login'") + ); + Assert.Equal( + 5.0m, + QueryPg("SELECT priority FROM audit_records WHERE action = 'user_login'") + ); + Assert.Equal( + "pending", + QueryPg("SELECT status FROM audit_records WHERE action = 'user_login'") + ); + Assert.True( + QueryPg("SELECT created_at FROM audit_records WHERE action = 'user_login'") + > DateTime.MinValue + ); + + // SQLite + var sqliteId = QuerySqlite( + "SELECT id FROM audit_records WHERE action = 'user_login'" + ); + Assert.True(Guid.TryParse(sqliteId, out _)); + Assert.Equal( + 1, + QuerySqlite("SELECT is_success FROM audit_records WHERE action = 'user_login'") + ); + Assert.Equal( + 0, + QuerySqlite("SELECT retry_count FROM audit_records WHERE action = 'user_login'") + ); + Assert.Equal( + 5.0, + QuerySqlite("SELECT priority FROM audit_records WHERE action = 'user_login'"), + 1 + ); + Assert.Equal( + "pending", + QuerySqlite("SELECT status FROM audit_records WHERE action = 'user_login'") + ); + Assert.False( + string.IsNullOrEmpty( + QuerySqlite( + "SELECT created_at FROM audit_records WHERE action = 'user_login'" + ) + ) + ); + } + + // ========================================================================= + // IDEMPOTENCY - Schema can be applied multiple times with SAME RESULT + // ========================================================================= + + [Fact] + public void LqlDefaults_Idempotent_BothPlatforms() + { + // Arrange + var schema = Schema + .Define("Test") + .Table( + "public", + "rerunnable", + t => + t.Column("id", PortableTypes.Int, c => c.PrimaryKey()) + .Column("active", PortableTypes.Boolean, c => c.DefaultLql("true")) + .Column("count", PortableTypes.Int, c => c.DefaultLql("1")) + ) + .Build(); + + // Act - Apply FIRST time + ApplySchema(_pgConnection, schema, PostgresDdlGenerator.Generate); + ApplySchema(_sqliteConnection, schema, SqliteDdlGenerator.Generate); + + // Capture DDL BEFORE second application + var pgDdlBefore = GetPostgresTableDdl(_pgConnection, "rerunnable"); + var sqliteDdlBefore = GetSqliteTableDdl(_sqliteConnection, "rerunnable"); + + // Apply SECOND time + ApplySchema(_pgConnection, schema, PostgresDdlGenerator.Generate); + ApplySchema(_sqliteConnection, schema, SqliteDdlGenerator.Generate); + + // Capture DDL AFTER second application + var pgDdlAfter = GetPostgresTableDdl(_pgConnection, "rerunnable"); + var sqliteDdlAfter = GetSqliteTableDdl(_sqliteConnection, "rerunnable"); + + // ASSERT 1: DDL is IDENTICAL before and after (proves true idempotency) + Assert.Equal(pgDdlBefore, pgDdlAfter); + Assert.Equal(sqliteDdlBefore, sqliteDdlAfter); + + // ASSERT 2: Table structure is EXACTLY what we defined + // PostgreSQL: Verify table exists with correct columns + Assert.True(PostgresTableExists(_pgConnection, "rerunnable", "public")); + var pgColumns = GetPostgresColumns(_pgConnection, "rerunnable", "public"); + Assert.Equal(3, pgColumns.Count); // id, active, count - NO EXTRA COLUMNS + Assert.Contains("id", pgColumns); + Assert.Contains("active", pgColumns); + Assert.Contains("count", pgColumns); + + // SQLite: Verify table exists with correct columns + Assert.True(SqliteTableExists(_sqliteConnection, "rerunnable")); + var sqliteColumns = GetSqliteColumns(_sqliteConnection, "rerunnable"); + Assert.Equal(3, sqliteColumns.Count); // id, active, count - NO EXTRA COLUMNS + Assert.Contains("id", sqliteColumns); + Assert.Contains("active", sqliteColumns); + Assert.Contains("count", sqliteColumns); + + // ASSERT 3: Defaults are CORRECT in DDL + // PostgreSQL: Check defaults in DDL + Assert.Contains("DEFAULT true", pgDdlAfter); // boolean true + Assert.Contains("DEFAULT 1", pgDdlAfter); // integer 1 + + // SQLite: Check defaults in DDL + Assert.Contains("DEFAULT 1", sqliteDdlAfter); // boolean true = 1, integer 1 = 1 + + // ASSERT 4: Primary key is correctly defined + Assert.Contains("PRIMARY KEY", pgDdlAfter.ToUpperInvariant()); + Assert.Contains("PRIMARY KEY", sqliteDdlAfter.ToUpperInvariant()); + + // ASSERT 5: Runtime defaults work correctly + ExecutePg("INSERT INTO rerunnable (id) VALUES (1)"); + ExecuteSqlite("INSERT INTO rerunnable (id) VALUES (1)"); + + Assert.True(QueryPg("SELECT active FROM rerunnable WHERE id = 1")); + Assert.Equal(1, QueryPg("SELECT count FROM rerunnable WHERE id = 1")); + + Assert.Equal(1, QuerySqlite("SELECT active FROM rerunnable WHERE id = 1")); + Assert.Equal(1, QuerySqlite("SELECT count FROM rerunnable WHERE id = 1")); + + // ASSERT 6: Only ONE row exists (no duplicate inserts from idempotent migrations) + Assert.Equal(1L, QueryPg("SELECT COUNT(*) FROM rerunnable")); + Assert.Equal(1L, QuerySqlite("SELECT COUNT(*) FROM rerunnable")); + } + + // ========================================================================= + // EDGE CASES - Corner cases proving true platform independence + // ========================================================================= + + [Fact] + public void LqlNumeric_LargeInteger_SameValueBothPlatforms() + { + // Arrange - Test large integer values (near int32 boundaries) + var schema = Schema + .Define("Test") + .Table( + "public", + "large_nums", + t => + t.Column("id", PortableTypes.Int, c => c.PrimaryKey()) + .Column("big_pos", PortableTypes.BigInt, c => c.DefaultLql("2147483647")) // int32 max + .Column("big_neg", PortableTypes.BigInt, c => c.DefaultLql("-2147483648")) // int32 min + ) + .Build(); + + // Act + ApplySchema(_pgConnection, schema, PostgresDdlGenerator.Generate); + ApplySchema(_sqliteConnection, schema, SqliteDdlGenerator.Generate); + + ExecutePg("INSERT INTO large_nums (id) VALUES (1)"); + ExecuteSqlite("INSERT INTO large_nums (id) VALUES (1)"); + + // Assert - Same values on both platforms + Assert.Equal(2147483647, QueryPg("SELECT big_pos FROM large_nums WHERE id = 1")); + Assert.Equal(2147483647, QuerySqlite("SELECT big_pos FROM large_nums WHERE id = 1")); + + Assert.Equal(-2147483648, QueryPg("SELECT big_neg FROM large_nums WHERE id = 1")); + Assert.Equal(-2147483648, QuerySqlite("SELECT big_neg FROM large_nums WHERE id = 1")); + } + + [Fact] + public void LqlNumeric_VerySmallDecimal_SameValueBothPlatforms() + { + // Arrange - Test precision with very small decimal values + var schema = Schema + .Define("Test") + .Table( + "public", + "precision_test", + t => + t.Column("id", PortableTypes.Int, c => c.PrimaryKey()) + .Column( + "tiny", + PortableTypes.Decimal(10, 8), + c => c.DefaultLql("0.00000001") + ) + .Column( + "scientific", + PortableTypes.Decimal(15, 10), + c => c.DefaultLql("3.1415926535") + ) + ) + .Build(); + + // Act + ApplySchema(_pgConnection, schema, PostgresDdlGenerator.Generate); + ApplySchema(_sqliteConnection, schema, SqliteDdlGenerator.Generate); + + ExecutePg("INSERT INTO precision_test (id) VALUES (1)"); + ExecuteSqlite("INSERT INTO precision_test (id) VALUES (1)"); + + // Assert + Assert.Equal(0.00000001m, QueryPg("SELECT tiny FROM precision_test WHERE id = 1")); + Assert.Equal( + 0.00000001, + QuerySqlite("SELECT tiny FROM precision_test WHERE id = 1"), + 8 + ); + + Assert.Equal( + 3.1415926535m, + QueryPg("SELECT scientific FROM precision_test WHERE id = 1") + ); + Assert.Equal( + 3.1415926535, + QuerySqlite("SELECT scientific FROM precision_test WHERE id = 1"), + 10 + ); + } + + [Fact] + public void LqlString_SpecialCharacters_SameValueBothPlatforms() + { + // Arrange - Test strings with special characters (escaped in LQL as SQL single quotes) + var schema = Schema + .Define("Test") + .Table( + "public", + "special_strings", + t => + t.Column("id", PortableTypes.Int, c => c.PrimaryKey()) + .Column( + "with_spaces", + PortableTypes.VarChar(50), + c => c.DefaultLql("'hello world'") + ) + .Column( + "with_numbers", + PortableTypes.VarChar(50), + c => c.DefaultLql("'test123'") + ) + .Column( + "with_hyphen", + PortableTypes.VarChar(50), + c => c.DefaultLql("'foo-bar-baz'") + ) + .Column( + "with_underscore", + PortableTypes.VarChar(50), + c => c.DefaultLql("'snake_case_value'") + ) + ) + .Build(); + + // Act + ApplySchema(_pgConnection, schema, PostgresDdlGenerator.Generate); + ApplySchema(_sqliteConnection, schema, SqliteDdlGenerator.Generate); + + ExecutePg("INSERT INTO special_strings (id) VALUES (1)"); + ExecuteSqlite("INSERT INTO special_strings (id) VALUES (1)"); + + // Assert - Same string values on both platforms + Assert.Equal( + "hello world", + QueryPg("SELECT with_spaces FROM special_strings WHERE id = 1") + ); + Assert.Equal( + "hello world", + QuerySqlite("SELECT with_spaces FROM special_strings WHERE id = 1") + ); + + Assert.Equal( + "test123", + QueryPg("SELECT with_numbers FROM special_strings WHERE id = 1") + ); + Assert.Equal( + "test123", + QuerySqlite("SELECT with_numbers FROM special_strings WHERE id = 1") + ); + + Assert.Equal( + "foo-bar-baz", + QueryPg("SELECT with_hyphen FROM special_strings WHERE id = 1") + ); + Assert.Equal( + "foo-bar-baz", + QuerySqlite("SELECT with_hyphen FROM special_strings WHERE id = 1") + ); + + Assert.Equal( + "snake_case_value", + QueryPg("SELECT with_underscore FROM special_strings WHERE id = 1") + ); + Assert.Equal( + "snake_case_value", + QuerySqlite("SELECT with_underscore FROM special_strings WHERE id = 1") + ); + } + + [Fact] + public void LqlBoolean_MultipleColumns_AllDefaultCorrectly() + { + // Arrange - Test multiple boolean defaults in various combinations + var schema = Schema + .Define("Test") + .Table( + "public", + "feature_flags", + t => + t.Column("id", PortableTypes.Int, c => c.PrimaryKey()) + .Column("flag_a", PortableTypes.Boolean, c => c.DefaultLql("true")) + .Column("flag_b", PortableTypes.Boolean, c => c.DefaultLql("false")) + .Column("flag_c", PortableTypes.Boolean, c => c.DefaultLql("true")) + .Column("flag_d", PortableTypes.Boolean, c => c.DefaultLql("false")) + ) + .Build(); + + // Act + ApplySchema(_pgConnection, schema, PostgresDdlGenerator.Generate); + ApplySchema(_sqliteConnection, schema, SqliteDdlGenerator.Generate); + + ExecutePg("INSERT INTO feature_flags (id) VALUES (1)"); + ExecuteSqlite("INSERT INTO feature_flags (id) VALUES (1)"); + + // Assert - All flags match expected values + Assert.True(QueryPg("SELECT flag_a FROM feature_flags WHERE id = 1")); + Assert.Equal(1, QuerySqlite("SELECT flag_a FROM feature_flags WHERE id = 1")); + + Assert.False(QueryPg("SELECT flag_b FROM feature_flags WHERE id = 1")); + Assert.Equal(0, QuerySqlite("SELECT flag_b FROM feature_flags WHERE id = 1")); + + Assert.True(QueryPg("SELECT flag_c FROM feature_flags WHERE id = 1")); + Assert.Equal(1, QuerySqlite("SELECT flag_c FROM feature_flags WHERE id = 1")); + + Assert.False(QueryPg("SELECT flag_d FROM feature_flags WHERE id = 1")); + Assert.Equal(0, QuerySqlite("SELECT flag_d FROM feature_flags WHERE id = 1")); + } + + [Fact] + public void LqlUuid_MultipleInserts_AllUnique_BothPlatforms() + { + // Arrange - Verify UUID generation is truly unique across many inserts + var schema = Schema + .Define("Test") + .Table( + "public", + "uuid_test", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey().DefaultLql("gen_uuid()")) + .Column("seq", PortableTypes.Int, c => c.NotNull()) + ) + .Build(); + + // Act + ApplySchema(_pgConnection, schema, PostgresDdlGenerator.Generate); + ApplySchema(_sqliteConnection, schema, SqliteDdlGenerator.Generate); + + // Insert 10 rows in each database + for (var i = 1; i <= 10; i++) + { + ExecutePg($"INSERT INTO uuid_test (seq) VALUES ({i})"); + ExecuteSqlite($"INSERT INTO uuid_test (seq) VALUES ({i})"); + } + + // Assert - All UUIDs are unique in Postgres + using var pgCmd = _pgConnection.CreateCommand(); + pgCmd.CommandText = "SELECT COUNT(DISTINCT id) FROM uuid_test"; + var pgDistinct = (long)pgCmd.ExecuteScalar()!; + Assert.Equal(10, pgDistinct); + + // Assert - All UUIDs are unique in SQLite + using var sqliteCmd = _sqliteConnection.CreateCommand(); + sqliteCmd.CommandText = "SELECT COUNT(DISTINCT id) FROM uuid_test"; + var sqliteDistinct = (long)sqliteCmd.ExecuteScalar()!; + Assert.Equal(10, sqliteDistinct); + } + + [Fact] + public void LqlTimestamp_AllTimeTypes_WorkBothPlatforms() + { + // Arrange - Test all time-related LQL functions + var schema = Schema + .Define("Test") + .Table( + "public", + "time_test", + t => + t.Column("id", PortableTypes.Int, c => c.PrimaryKey()) + .Column("ts1", PortableTypes.DateTimeOffset, c => c.DefaultLql("now()")) + .Column( + "ts2", + PortableTypes.DateTimeOffset, + c => c.DefaultLql("current_timestamp()") + ) + .Column("d", PortableTypes.Date, c => c.DefaultLql("current_date()")) + .Column("t", PortableTypes.Time(), c => c.DefaultLql("current_time()")) + ) + .Build(); + + // Act + ApplySchema(_pgConnection, schema, PostgresDdlGenerator.Generate); + ApplySchema(_sqliteConnection, schema, SqliteDdlGenerator.Generate); + + ExecutePg("INSERT INTO time_test (id) VALUES (1)"); + ExecuteSqlite("INSERT INTO time_test (id) VALUES (1)"); + + // Assert - All time values are populated + Assert.True( + QueryPg("SELECT ts1 FROM time_test WHERE id = 1") > DateTime.MinValue + ); + Assert.True( + QueryPg("SELECT ts2 FROM time_test WHERE id = 1") > DateTime.MinValue + ); + Assert.True(QueryPg("SELECT d FROM time_test WHERE id = 1") > DateTime.MinValue); + var pgTime = QueryPg("SELECT t FROM time_test WHERE id = 1"); + Assert.True(pgTime >= TimeSpan.Zero); + + // SQLite returns strings for all temporal types + Assert.False( + string.IsNullOrEmpty(QuerySqlite("SELECT ts1 FROM time_test WHERE id = 1")) + ); + Assert.False( + string.IsNullOrEmpty(QuerySqlite("SELECT ts2 FROM time_test WHERE id = 1")) + ); + Assert.False( + string.IsNullOrEmpty(QuerySqlite("SELECT d FROM time_test WHERE id = 1")) + ); + Assert.False( + string.IsNullOrEmpty(QuerySqlite("SELECT t FROM time_test WHERE id = 1")) + ); + } + + // ========================================================================= + // HELPER METHODS + // ========================================================================= + + private void ApplySchema( + NpgsqlConnection conn, + SchemaDefinition schema, + Func generator + ) + { + var currentSchema = ( + (SchemaResultOk)PostgresSchemaInspector.Inspect(conn, "public", _logger) + ).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(currentSchema, schema, logger: _logger) + ).Value; + _ = MigrationRunner.Apply(conn, operations, generator, MigrationOptions.Default, _logger); + } + + private void ApplySchema( + SqliteConnection conn, + SchemaDefinition schema, + Func generator + ) + { + var currentSchema = ((SchemaResultOk)SqliteSchemaInspector.Inspect(conn, _logger)).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(currentSchema, schema, logger: _logger) + ).Value; + _ = MigrationRunner.Apply(conn, operations, generator, MigrationOptions.Default, _logger); + } + + private void ExecutePg(string sql) + { + using var cmd = _pgConnection.CreateCommand(); + cmd.CommandText = sql; + cmd.ExecuteNonQuery(); + } + + private void ExecuteSqlite(string sql) + { + using var cmd = _sqliteConnection.CreateCommand(); + cmd.CommandText = sql; + cmd.ExecuteNonQuery(); + } + + private T QueryPg(string sql) + { + using var cmd = _pgConnection.CreateCommand(); + cmd.CommandText = sql; + return (T)cmd.ExecuteScalar()!; + } + + private T QuerySqlite(string sql) + { + using var cmd = _sqliteConnection.CreateCommand(); + cmd.CommandText = sql; + return (T)cmd.ExecuteScalar()!; + } + + // ========================================================================= + // DDL VERIFICATION HELPERS - Simple string-based verification + // ========================================================================= + + /// + /// Get PostgreSQL table DDL for verification via pg_get_tabledef or information_schema. + /// Returns a string representation of the table structure for comparison. + /// + private static string GetPostgresTableDdl( + NpgsqlConnection conn, + string tableName, + string schema = "public" + ) + { + // Query column info from information_schema and build a normalized DDL representation + using var cmd = conn.CreateCommand(); + cmd.CommandText = """ + SELECT + c.column_name, + c.data_type, + c.is_nullable, + c.column_default, + c.character_maximum_length, + c.numeric_precision, + c.numeric_scale + FROM information_schema.columns c + WHERE c.table_schema = @schema AND c.table_name = @table + ORDER BY c.ordinal_position + """; + cmd.Parameters.AddWithValue("@schema", schema); + cmd.Parameters.AddWithValue("@table", tableName); + + var ddlParts = new List { $"CREATE TABLE {schema}.{tableName} (" }; + + using var reader = cmd.ExecuteReader(); + var columns = new List(); + while (reader.Read()) + { + var colName = reader.GetString(0); + var dataType = reader.GetString(1); + var isNullable = reader.GetString(2); + var colDefault = reader.IsDBNull(3) ? null : reader.GetString(3); + + var colDef = $" {colName} {dataType}"; + if (isNullable == "NO") + colDef += " NOT NULL"; + if (colDefault is not null) + colDef += $" DEFAULT {colDefault}"; + columns.Add(colDef); + } + reader.Close(); + + // Get primary key info + using var pkCmd = conn.CreateCommand(); + pkCmd.CommandText = """ + SELECT kcu.column_name + FROM information_schema.table_constraints tc + JOIN information_schema.key_column_usage kcu + ON tc.constraint_name = kcu.constraint_name + AND tc.table_schema = kcu.table_schema + WHERE tc.table_schema = @schema + AND tc.table_name = @table + AND tc.constraint_type = 'PRIMARY KEY' + ORDER BY kcu.ordinal_position + """; + pkCmd.Parameters.AddWithValue("@schema", schema); + pkCmd.Parameters.AddWithValue("@table", tableName); + + var pkColumns = new List(); + using var pkReader = pkCmd.ExecuteReader(); + while (pkReader.Read()) + { + pkColumns.Add(pkReader.GetString(0)); + } + + ddlParts.Add(string.Join(",\n", columns)); + if (pkColumns.Count > 0) + { + ddlParts.Add($" PRIMARY KEY ({string.Join(", ", pkColumns)})"); + } + ddlParts.Add(")"); + + return string.Join("\n", ddlParts); + } + + /// + /// Get SQLite table DDL using sqlite_master. + /// + private static string GetSqliteTableDdl(SqliteConnection conn, string tableName) + { + using var cmd = conn.CreateCommand(); + cmd.CommandText = "SELECT sql FROM sqlite_master WHERE type='table' AND name=@name"; + cmd.Parameters.AddWithValue("@name", tableName); + var result = cmd.ExecuteScalar(); + return result?.ToString() ?? ""; + } + + /// + /// Check if a PostgreSQL table exists. + /// + private static bool PostgresTableExists( + NpgsqlConnection conn, + string tableName, + string schema = "public" + ) + { + using var cmd = conn.CreateCommand(); + cmd.CommandText = """ + SELECT EXISTS ( + SELECT 1 FROM information_schema.tables + WHERE table_schema = @schema AND table_name = @table + ) + """; + cmd.Parameters.AddWithValue("@schema", schema); + cmd.Parameters.AddWithValue("@table", tableName); + return (bool)cmd.ExecuteScalar()!; + } + + /// + /// Check if a SQLite table exists. + /// + private static bool SqliteTableExists(SqliteConnection conn, string tableName) + { + using var cmd = conn.CreateCommand(); + cmd.CommandText = "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=@name"; + cmd.Parameters.AddWithValue("@name", tableName); + return (long)cmd.ExecuteScalar()! > 0; + } + + /// + /// Get list of column names for a PostgreSQL table. + /// + private static List GetPostgresColumns( + NpgsqlConnection conn, + string tableName, + string schema = "public" + ) + { + using var cmd = conn.CreateCommand(); + cmd.CommandText = """ + SELECT column_name + FROM information_schema.columns + WHERE table_schema = @schema AND table_name = @table + ORDER BY ordinal_position + """; + cmd.Parameters.AddWithValue("@schema", schema); + cmd.Parameters.AddWithValue("@table", tableName); + + var columns = new List(); + using var reader = cmd.ExecuteReader(); + while (reader.Read()) + { + columns.Add(reader.GetString(0)); + } + return columns; + } + + /// + /// Get list of column names for a SQLite table. + /// + private static List GetSqliteColumns(SqliteConnection conn, string tableName) + { + using var cmd = conn.CreateCommand(); + cmd.CommandText = $"PRAGMA table_info({tableName})"; + + var columns = new List(); + using var reader = cmd.ExecuteReader(); + while (reader.Read()) + { + // Column name is at index 1 in pragma table_info + columns.Add(reader.GetString(1)); + } + return columns; + } +} diff --git a/Migration/Migration.Tests/MigrateSchemaTests.cs b/Migration/Migration.Tests/MigrateSchemaTests.cs new file mode 100644 index 00000000..944fd528 --- /dev/null +++ b/Migration/Migration.Tests/MigrateSchemaTests.cs @@ -0,0 +1,424 @@ +namespace Migration.Tests; + +/// +/// Tests for PostgresDdlGenerator.MigrateSchema() method. +/// Covers: drop schema, fresh migration, partial upgrade scenarios. +/// +[System.Diagnostics.CodeAnalysis.SuppressMessage( + "Usage", + "CA1001:Types that own disposable fields should be disposable", + Justification = "Disposed via IAsyncLifetime.DisposeAsync" +)] +public sealed class MigrateSchemaTests : IAsyncLifetime +{ + private PostgreSqlContainer _postgres = null!; + private NpgsqlConnection _connection = null!; + + public async Task InitializeAsync() + { + _postgres = new PostgreSqlBuilder() + .WithImage("postgres:16-alpine") + .WithDatabase("migrate_schema_test") + .WithUsername("test") + .WithPassword("test") + .Build(); + + await _postgres.StartAsync().ConfigureAwait(false); + + _connection = new NpgsqlConnection(_postgres.GetConnectionString()); + await _connection.OpenAsync().ConfigureAwait(false); + } + + public async Task DisposeAsync() + { + await _connection.DisposeAsync().ConfigureAwait(false); + await _postgres.DisposeAsync().ConfigureAwait(false); + } + + /// + /// Test schema definition for migration tests. + /// + private static SchemaDefinition CreateTestSchema() => + Schema + .Define("MigrateSchemaTest") + .Table( + "public", + "countries", + t => + t.Column( + "id", + PortableTypes.Uuid, + c => c.PrimaryKey().Default("gen_random_uuid()") + ) + .Column("name", PortableTypes.VarChar(100), c => c.NotNull()) + .Column("code", PortableTypes.VarChar(10), c => c.NotNull()) + .Unique("uq_countries_code", "code") + ) + .Table( + "public", + "regions", + t => + t.Column( + "id", + PortableTypes.Uuid, + c => c.PrimaryKey().Default("gen_random_uuid()") + ) + .Column("name", PortableTypes.VarChar(100), c => c.NotNull()) + .Column("country_id", PortableTypes.Uuid, c => c.NotNull()) + .ForeignKey("country_id", "countries", "id", ForeignKeyAction.Cascade) + .Index("idx_regions_country", "country_id") + ) + .Table( + "public", + "suburbs", + t => + t.Column( + "id", + PortableTypes.Uuid, + c => c.PrimaryKey().Default("gen_random_uuid()") + ) + .Column("name", PortableTypes.VarChar(100), c => c.NotNull()) + .Column("region_id", PortableTypes.Uuid, c => c.NotNull()) + .ForeignKey("region_id", "regions", "id", ForeignKeyAction.Cascade) + ) + .Table( + "public", + "venues", + t => + t.Column( + "id", + PortableTypes.Uuid, + c => c.PrimaryKey().Default("gen_random_uuid()") + ) + .Column("name", PortableTypes.VarChar(200), c => c.NotNull()) + .Column("suburb_id", PortableTypes.Uuid, c => c.NotNull()) + .Column("address", PortableTypes.VarChar(300)) + .ForeignKey("suburb_id", "suburbs", "id", ForeignKeyAction.Cascade) + .Unique("uq_venues_name_suburb", "name", "suburb_id") + ) + .Build(); + + [Fact] + public void MigrateSchema_FreshDatabase_CreatesAllTables() + { + // Arrange + var schema = CreateTestSchema(); + var tablesCreated = new List(); + + // Act + var result = PostgresDdlGenerator.MigrateSchema( + _connection, + schema, + onTableCreated: tablesCreated.Add + ); + + // Assert + Assert.True( + result.Success, + $"Migration failed with errors: {string.Join(", ", result.Errors)}" + ); + Assert.Equal(4, result.TablesCreated); + Assert.Empty(result.Errors); + Assert.Contains("countries", tablesCreated); + Assert.Contains("regions", tablesCreated); + Assert.Contains("suburbs", tablesCreated); + Assert.Contains("venues", tablesCreated); + + // Verify tables exist in database + Assert.True(TableExists("countries")); + Assert.True(TableExists("regions")); + Assert.True(TableExists("suburbs")); + Assert.True(TableExists("venues")); + } + + [Fact] + public void MigrateSchema_AlreadyMigrated_IsIdempotent() + { + // Arrange + var schema = CreateTestSchema(); + + // First migration + var firstResult = PostgresDdlGenerator.MigrateSchema(_connection, schema); + Assert.True(firstResult.Success); + + // Act - Run migration again + var secondResult = PostgresDdlGenerator.MigrateSchema(_connection, schema); + + // Assert - Should succeed without errors (CREATE TABLE IF NOT EXISTS) + Assert.True(secondResult.Success); + Assert.Equal(4, secondResult.TablesCreated); + Assert.Empty(secondResult.Errors); + } + + [Fact] + public void MigrateSchema_PartiallyMigrated_CreatesRemainingTables() + { + // Arrange - Create only the first two tables manually + ExecuteSql( + """ + CREATE TABLE IF NOT EXISTS "public"."countries" ( + "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(), + "name" VARCHAR(100) NOT NULL, + "code" VARCHAR(10) NOT NULL, + CONSTRAINT "uq_countries_code" UNIQUE ("code") + ) + """ + ); + + ExecuteSql( + """ + CREATE TABLE IF NOT EXISTS "public"."regions" ( + "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(), + "name" VARCHAR(100) NOT NULL, + "country_id" UUID NOT NULL, + CONSTRAINT "fk_regions_country" FOREIGN KEY ("country_id") + REFERENCES "public"."countries" ("id") ON DELETE CASCADE + ) + """ + ); + + Assert.True(TableExists("countries")); + Assert.True(TableExists("regions")); + Assert.False(TableExists("suburbs")); + Assert.False(TableExists("venues")); + + var schema = CreateTestSchema(); + var tablesCreated = new List(); + + // Act - Run migration on partially migrated database + var result = PostgresDdlGenerator.MigrateSchema( + _connection, + schema, + onTableCreated: tablesCreated.Add + ); + + // Assert + Assert.True(result.Success, $"Migration failed: {string.Join(", ", result.Errors)}"); + Assert.Equal(4, result.TablesCreated); // All 4 reported (IF NOT EXISTS) + Assert.Empty(result.Errors); + + // All tables should now exist + Assert.True(TableExists("countries")); + Assert.True(TableExists("regions")); + Assert.True(TableExists("suburbs")); + Assert.True(TableExists("venues")); + } + + [Fact] + public void MigrateSchema_TableCreationFails_ContinuesWithOtherTables() + { + // Arrange - Create a conflicting table that will cause FK failure + // Create suburbs without its parent (regions) - this will cause venues to fail FK + ExecuteSql( + """ + CREATE TABLE IF NOT EXISTS "public"."orphan_suburbs" ( + "id" UUID PRIMARY KEY DEFAULT gen_random_uuid(), + "name" VARCHAR(100) NOT NULL + ) + """ + ); + + // Create a schema where one table references a non-existent table + var schemaWithBadFk = Schema + .Define("Test") + .Table( + "public", + "good_table", + t => t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + ) + .Table( + "public", + "bad_table", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("ref_id", PortableTypes.Uuid) + .ForeignKey("ref_id", "nonexistent_table", "id") + ) + .Table( + "public", + "another_good_table", + t => t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + ) + .Build(); + + var failedTables = new List(); + + // Act + var result = PostgresDdlGenerator.MigrateSchema( + _connection, + schemaWithBadFk, + onTableFailed: (name, _) => failedTables.Add(name) + ); + + // Assert - Should have created 2 tables, failed on 1 + Assert.False(result.Success); + Assert.Equal(2, result.TablesCreated); + Assert.Single(result.Errors); + Assert.Contains("bad_table", failedTables); + Assert.True(TableExists("good_table")); + Assert.True(TableExists("another_good_table")); + Assert.False(TableExists("bad_table")); + } + + [Fact] + public void MigrateSchema_CallbacksInvoked_ForSuccessAndFailure() + { + // Arrange + var schemaWithBadTable = Schema + .Define("Test") + .Table( + "public", + "success_table", + t => t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + ) + .Table( + "public", + "fail_table", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .ForeignKey("id", "missing_parent", "id") + ) + .Build(); + + var createdTables = new List(); + var failedTables = new List<(string Name, Exception Ex)>(); + + // Act + var result = PostgresDdlGenerator.MigrateSchema( + _connection, + schemaWithBadTable, + onTableCreated: createdTables.Add, + onTableFailed: (name, ex) => failedTables.Add((name, ex)) + ); + + // Assert + Assert.False(result.Success); + Assert.Single(createdTables); + Assert.Equal("success_table", createdTables[0]); + Assert.Single(failedTables); + Assert.Equal("fail_table", failedTables[0].Name); + Assert.NotNull(failedTables[0].Ex); + } + + [Fact] + public void DropAllTables_RemovesAllTablesInReverseOrder() + { + // Arrange - Create schema first + var schema = CreateTestSchema(); + var migrateResult = PostgresDdlGenerator.MigrateSchema(_connection, schema); + Assert.True(migrateResult.Success); + Assert.True(TableExists("countries")); + Assert.True(TableExists("venues")); + + // Act - Drop all tables in reverse order (respecting FK constraints) + var tables = schema.Tables.Reverse().ToList(); + foreach (var table in tables) + { + ExecuteSql($"DROP TABLE IF EXISTS \"{table.Schema}\".\"{table.Name}\" CASCADE"); + } + + // Assert + Assert.False(TableExists("countries")); + Assert.False(TableExists("regions")); + Assert.False(TableExists("suburbs")); + Assert.False(TableExists("venues")); + } + + [Fact] + public void MigrateSchema_AfterDrop_RecreatesAllTables() + { + // Arrange - Create and drop schema + var schema = CreateTestSchema(); + _ = PostgresDdlGenerator.MigrateSchema(_connection, schema); + + // Drop all tables + var tables = schema.Tables.Reverse().ToList(); + foreach (var table in tables) + { + ExecuteSql($"DROP TABLE IF EXISTS \"{table.Schema}\".\"{table.Name}\" CASCADE"); + } + + Assert.False(TableExists("countries")); + + // Act - Migrate again + var result = PostgresDdlGenerator.MigrateSchema(_connection, schema); + + // Assert + Assert.True(result.Success); + Assert.Equal(4, result.TablesCreated); + Assert.True(TableExists("countries")); + Assert.True(TableExists("regions")); + Assert.True(TableExists("suburbs")); + Assert.True(TableExists("venues")); + } + + [Fact] + public void MigrateSchema_WithIndexes_CreatesIndexes() + { + // Arrange + var schema = CreateTestSchema(); + + // Act + var result = PostgresDdlGenerator.MigrateSchema(_connection, schema); + + // Assert + Assert.True(result.Success); + Assert.True(IndexExists("idx_regions_country")); + } + + [Fact] + public void MigrateSchema_EmptySchema_ReturnsSuccess() + { + // Arrange + var emptySchema = Schema.Define("Empty").Build(); + + // Act + var result = PostgresDdlGenerator.MigrateSchema(_connection, emptySchema); + + // Assert + Assert.True(result.Success); + Assert.Equal(0, result.TablesCreated); + Assert.Empty(result.Errors); + } + + private bool TableExists(string tableName) + { + using var cmd = _connection.CreateCommand(); + cmd.CommandText = """ + SELECT EXISTS ( + SELECT FROM information_schema.tables + WHERE table_schema = 'public' + AND table_name = @tableName + ) + """; + var param = cmd.CreateParameter(); + param.ParameterName = "@tableName"; + param.Value = tableName; + cmd.Parameters.Add(param); + return (bool)cmd.ExecuteScalar()!; + } + + private bool IndexExists(string indexName) + { + using var cmd = _connection.CreateCommand(); + cmd.CommandText = """ + SELECT EXISTS ( + SELECT FROM pg_indexes + WHERE schemaname = 'public' + AND indexname = @indexName + ) + """; + var param = cmd.CreateParameter(); + param.ParameterName = "@indexName"; + param.Value = indexName; + cmd.Parameters.Add(param); + return (bool)cmd.ExecuteScalar()!; + } + + private void ExecuteSql(string sql) + { + using var cmd = _connection.CreateCommand(); + cmd.CommandText = sql; + cmd.ExecuteNonQuery(); + } +} diff --git a/Migration/Migration.Tests/PostgresMigrationTests.cs b/Migration/Migration.Tests/PostgresMigrationTests.cs index 5b5e6700..5be91c5d 100644 --- a/Migration/Migration.Tests/PostgresMigrationTests.cs +++ b/Migration/Migration.Tests/PostgresMigrationTests.cs @@ -479,6 +479,550 @@ FROM information_schema.columns Assert.Contains("timestamp", columns["timestamp_col"]); } + [Fact] + public void ExpressionIndex_CreateWithLowerFunction_Success() + { + // Arrange - Create table with expression index for case-insensitive uniqueness + var schema = Schema + .Define("Test") + .Table( + "public", + "artists", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("name", PortableTypes.VarChar(200), c => c.NotNull()) + .ExpressionIndex("uq_artists_name", "lower(name)", unique: true) + ) + .Build(); + + // Act + var emptySchema = ( + (SchemaResultOk)PostgresSchemaInspector.Inspect(_connection, "public", _logger) + ).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, schema, logger: _logger) + ).Value; + + var result = MigrationRunner.Apply( + _connection, + operations, + PostgresDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + // Assert - Migration succeeded + Assert.True( + result is MigrationApplyResultOk, + $"Migration failed: {(result as MigrationApplyResultError)?.Value}" + ); + + // Verify index exists and is unique + using var cmd = _connection.CreateCommand(); + cmd.CommandText = """ + SELECT indexname, indexdef + FROM pg_indexes + WHERE tablename = 'artists' AND indexname = 'uq_artists_name' + """; + using var reader = cmd.ExecuteReader(); + + Assert.True(reader.Read(), "Expression index should exist"); + var indexDef = reader.GetString(1); + Assert.Contains("UNIQUE", indexDef); + Assert.Contains("lower", indexDef); + } + + [Fact] + public void ExpressionIndex_EnforcesCaseInsensitiveUniqueness() + { + // Arrange - Create table with expression index + var schema = Schema + .Define("Test") + .Table( + "public", + "venues", + t => + t.Column( + "id", + PortableTypes.Uuid, + c => c.PrimaryKey().Default("gen_random_uuid()") + ) + .Column("name", PortableTypes.VarChar(200), c => c.NotNull()) + .ExpressionIndex("uq_venues_name", "lower(name)", unique: true) + ) + .Build(); + + var emptySchema = ( + (SchemaResultOk)PostgresSchemaInspector.Inspect(_connection, "public", _logger) + ).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, schema, logger: _logger) + ).Value; + _ = MigrationRunner.Apply( + _connection, + operations, + PostgresDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + // Act - Insert first venue + using var insertCmd = _connection.CreateCommand(); + insertCmd.CommandText = "INSERT INTO venues (name) VALUES ('The Corner Hotel')"; + insertCmd.ExecuteNonQuery(); + + // Try to insert duplicate with different case - should fail + using var duplicateCmd = _connection.CreateCommand(); + duplicateCmd.CommandText = "INSERT INTO venues (name) VALUES ('THE CORNER HOTEL')"; + + // Assert - Should throw unique constraint violation + var ex = Assert.Throws(() => duplicateCmd.ExecuteNonQuery()); + Assert.Contains("uq_venues_name", ex.Message); + } + + [Fact] + public void ExpressionIndex_MultiExpression_CompositeIndexSuccess() + { + // Arrange - Create table with multi-expression index (like venues with suburb_id) + var schema = Schema + .Define("Test") + .Table( + "public", + "suburbs", + t => + t.Column( + "id", + PortableTypes.Uuid, + c => c.PrimaryKey().Default("gen_random_uuid()") + ) + .Column("name", PortableTypes.VarChar(100), c => c.NotNull()) + ) + .Table( + "public", + "places", + t => + t.Column( + "id", + PortableTypes.Uuid, + c => c.PrimaryKey().Default("gen_random_uuid()") + ) + .Column("name", PortableTypes.VarChar(200), c => c.NotNull()) + .Column("suburb_id", PortableTypes.Uuid, c => c.NotNull()) + .ForeignKey("suburb_id", "suburbs", "id") + .ExpressionIndex( + "uq_places_name_suburb", + ["lower(name)", "suburb_id"], + unique: true + ) + ) + .Build(); + + // Act + var emptySchema = ( + (SchemaResultOk)PostgresSchemaInspector.Inspect(_connection, "public", _logger) + ).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, schema, logger: _logger) + ).Value; + + var result = MigrationRunner.Apply( + _connection, + operations, + PostgresDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + // Assert + Assert.True( + result is MigrationApplyResultOk, + $"Migration failed: {(result as MigrationApplyResultError)?.Value}" + ); + + // Verify composite expression index exists + using var cmd = _connection.CreateCommand(); + cmd.CommandText = """ + SELECT indexdef + FROM pg_indexes + WHERE tablename = 'places' AND indexname = 'uq_places_name_suburb' + """; + var indexDef = (string?)cmd.ExecuteScalar(); + + Assert.NotNull(indexDef); + Assert.Contains("UNIQUE", indexDef); + Assert.Contains("lower", indexDef); + Assert.Contains("suburb_id", indexDef); + } + + [Fact] + public void ExpressionIndex_MultiExpression_AllowsSameNameDifferentSuburb() + { + // Arrange - Create tables with composite expression index + var schema = Schema + .Define("Test") + .Table( + "public", + "regions", + t => + t.Column( + "id", + PortableTypes.Uuid, + c => c.PrimaryKey().Default("gen_random_uuid()") + ) + .Column("name", PortableTypes.VarChar(100), c => c.NotNull()) + ) + .Table( + "public", + "locations", + t => + t.Column( + "id", + PortableTypes.Uuid, + c => c.PrimaryKey().Default("gen_random_uuid()") + ) + .Column("name", PortableTypes.VarChar(200), c => c.NotNull()) + .Column("region_id", PortableTypes.Uuid, c => c.NotNull()) + .ForeignKey("region_id", "regions", "id") + .ExpressionIndex( + "uq_locations_name_region", + ["lower(name)", "region_id"], + unique: true + ) + ) + .Build(); + + var emptySchema = ( + (SchemaResultOk)PostgresSchemaInspector.Inspect(_connection, "public", _logger) + ).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, schema, logger: _logger) + ).Value; + _ = MigrationRunner.Apply( + _connection, + operations, + PostgresDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + // Create two regions + using var regionCmd = _connection.CreateCommand(); + regionCmd.CommandText = """ + INSERT INTO regions (id, name) VALUES + ('11111111-1111-1111-1111-111111111111', 'Melbourne'), + ('22222222-2222-2222-2222-222222222222', 'Sydney') + """; + regionCmd.ExecuteNonQuery(); + + // Act - Insert same name in different regions (should succeed) + using var insertCmd = _connection.CreateCommand(); + insertCmd.CommandText = """ + INSERT INTO locations (name, region_id) VALUES + ('The Corner', '11111111-1111-1111-1111-111111111111'), + ('The Corner', '22222222-2222-2222-2222-222222222222') + """; + var rowsAffected = insertCmd.ExecuteNonQuery(); + + // Assert - Both inserts should succeed + Assert.Equal(2, rowsAffected); + + // Try to insert duplicate in same region - should fail + using var duplicateCmd = _connection.CreateCommand(); + duplicateCmd.CommandText = """ + INSERT INTO locations (name, region_id) VALUES + ('THE CORNER', '11111111-1111-1111-1111-111111111111') + """; + + var ex = Assert.Throws(() => duplicateCmd.ExecuteNonQuery()); + Assert.Contains("uq_locations_name_region", ex.Message); + } + + [Fact] + public void ExpressionIndex_Idempotent_NoErrorOnRerun() + { + // Arrange + var schema = Schema + .Define("Test") + .Table( + "public", + "bands", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("name", PortableTypes.VarChar(200), c => c.NotNull()) + .ExpressionIndex("uq_bands_name", "lower(name)", unique: true) + ) + .Build(); + + // Act - Run migration twice + // Note: Expression indexes aren't read back by schema inspector (no expression index introspection) + // but CREATE INDEX IF NOT EXISTS ensures idempotency at the database level + for (var i = 0; i < 2; i++) + { + var currentSchema = ( + (SchemaResultOk)PostgresSchemaInspector.Inspect(_connection, "public", _logger) + ).Value; + + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(currentSchema, schema, logger: _logger) + ).Value; + + var result = MigrationRunner.Apply( + _connection, + operations, + PostgresDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + // Both runs should succeed - IF NOT EXISTS handles already-existing index + Assert.True( + result is MigrationApplyResultOk, + $"Migration {i + 1} failed: {(result as MigrationApplyResultError)?.Value}" + ); + } + + // Verify expression index exists and is functional + using var cmd = _connection.CreateCommand(); + cmd.CommandText = """ + SELECT indexdef FROM pg_indexes + WHERE tablename = 'bands' AND indexname = 'uq_bands_name' + """; + var indexDef = (string?)cmd.ExecuteScalar(); + Assert.NotNull(indexDef); + Assert.Contains("lower", indexDef); + } + + // ============================================================================= + // Index Conversion Tests (Column <-> Expression) + // ============================================================================= + + [Fact] + public void UpgradeIndex_ColumnToExpression_RequiresDropAndCreate() + { + // Arrange - Create table with regular column index + var v1 = Schema + .Define("Test") + .Table( + "public", + "artists", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("name", PortableTypes.VarChar(200), c => c.NotNull()) + .Index("idx_artists_name", "name", unique: true) + ) + .Build(); + + // Apply v1 + var emptySchema = ( + (SchemaResultOk)PostgresSchemaInspector.Inspect(_connection, "public", _logger) + ).Value; + var v1Ops = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, v1, logger: _logger) + ).Value; + _ = MigrationRunner.Apply( + _connection, + v1Ops, + PostgresDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + // v2 changes to expression index (different name since semantically different) + var v2 = Schema + .Define("Test") + .Table( + "public", + "artists", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("name", PortableTypes.VarChar(200), c => c.NotNull()) + .ExpressionIndex("uq_artists_name_ci", "lower(name)", unique: true) + ) + .Build(); + + // Act - Calculate upgrade operations + var currentSchema = ( + (SchemaResultOk)PostgresSchemaInspector.Inspect(_connection, "public", _logger) + ).Value; + var upgradeOps = ( + (OperationsResultOk) + SchemaDiff.Calculate(currentSchema, v2, allowDestructive: true, logger: _logger) + ).Value; + + // Assert - Should have drop old index + create new expression index + Assert.Equal(2, upgradeOps.Count); + Assert.Contains(upgradeOps, op => op is DropIndexOperation); + Assert.Contains(upgradeOps, op => op is CreateIndexOperation); + + // Apply the upgrade + var result = MigrationRunner.Apply( + _connection, + upgradeOps, + PostgresDdlGenerator.Generate, + MigrationOptions.Destructive, + _logger + ); + + Assert.True( + result is MigrationApplyResultOk, + $"Upgrade failed: {(result as MigrationApplyResultError)?.Value}" + ); + + // Verify new expression index exists + using var cmd = _connection.CreateCommand(); + cmd.CommandText = """ + SELECT indexdef FROM pg_indexes + WHERE tablename = 'artists' AND indexname = 'uq_artists_name_ci' + """; + var indexDef = (string?)cmd.ExecuteScalar(); + Assert.NotNull(indexDef); + Assert.Contains("lower", indexDef); + } + + [Fact] + public void UpgradeIndex_ExpressionToColumn_RequiresDropAndCreate() + { + // Arrange - Create table with expression index + var v1 = Schema + .Define("Test") + .Table( + "public", + "venues", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("name", PortableTypes.VarChar(200), c => c.NotNull()) + .ExpressionIndex("uq_venues_name", "lower(name)", unique: true) + ) + .Build(); + + // Apply v1 + var emptySchema = ( + (SchemaResultOk)PostgresSchemaInspector.Inspect(_connection, "public", _logger) + ).Value; + var v1Ops = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, v1, logger: _logger) + ).Value; + _ = MigrationRunner.Apply( + _connection, + v1Ops, + PostgresDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + // v2 changes back to simple column index (different name) + var v2 = Schema + .Define("Test") + .Table( + "public", + "venues", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("name", PortableTypes.VarChar(200), c => c.NotNull()) + .Index("idx_venues_name", "name", unique: true) + ) + .Build(); + + // Act + var currentSchema = ( + (SchemaResultOk)PostgresSchemaInspector.Inspect(_connection, "public", _logger) + ).Value; + var upgradeOps = ( + (OperationsResultOk) + SchemaDiff.Calculate(currentSchema, v2, allowDestructive: true, logger: _logger) + ).Value; + + // Assert - Should have drop + create + Assert.Equal(2, upgradeOps.Count); + Assert.Contains(upgradeOps, op => op is DropIndexOperation); + Assert.Contains(upgradeOps, op => op is CreateIndexOperation); + + var result = MigrationRunner.Apply( + _connection, + upgradeOps, + PostgresDdlGenerator.Generate, + MigrationOptions.Destructive, + _logger + ); + + Assert.True( + result is MigrationApplyResultOk, + $"Upgrade failed: {(result as MigrationApplyResultError)?.Value}" + ); + + // Verify new column index exists (no lower() function) + using var cmd = _connection.CreateCommand(); + cmd.CommandText = """ + SELECT indexdef FROM pg_indexes + WHERE tablename = 'venues' AND indexname = 'idx_venues_name' + """; + var indexDef = (string?)cmd.ExecuteScalar(); + Assert.NotNull(indexDef); + Assert.DoesNotContain("lower", indexDef); + } + + [Fact] + public void UpgradeIndex_SameNameDifferentType_NotDetectedWithoutDestructive() + { + // This test verifies that WITHOUT allowDestructive, the system doesn't + // automatically detect that an index definition changed (column vs expression). + // Converting an index type is a destructive operation requiring explicit opt-in. + + // Arrange - Create table with regular column index + var v1 = Schema + .Define("Test") + .Table( + "public", + "products", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("name", PortableTypes.VarChar(200), c => c.NotNull()) + .Index("idx_products_name", "name", unique: true) + ) + .Build(); + + var emptySchema = ( + (SchemaResultOk)PostgresSchemaInspector.Inspect(_connection, "public", _logger) + ).Value; + var v1Ops = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, v1, logger: _logger) + ).Value; + _ = MigrationRunner.Apply( + _connection, + v1Ops, + PostgresDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + // v2 wants to change to expression index with SAME name + // This is a semantic change - case-sensitive to case-insensitive + var v2 = Schema + .Define("Test") + .Table( + "public", + "products", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("name", PortableTypes.VarChar(200), c => c.NotNull()) + .ExpressionIndex("idx_products_name", "lower(name)", unique: true) + ) + .Build(); + + // Act - Calculate without destructive flag + var currentSchema = ( + (SchemaResultOk)PostgresSchemaInspector.Inspect(_connection, "public", _logger) + ).Value; + var upgradeOps = ( + (OperationsResultOk) + SchemaDiff.Calculate(currentSchema, v2, allowDestructive: false, logger: _logger) + ).Value; + + // Assert - No operations since index name already exists and we can't drop without destructive + // The SchemaDiff only compares by name, not by definition + Assert.Empty(upgradeOps); + } + [Fact] public void Destructive_DropTable_AllowedWithOption() { @@ -547,4 +1091,369 @@ public void Destructive_DropTable_AllowedWithOption() Assert.DoesNotContain(finalSchema.Tables, t => t.Name == "dropme"); Assert.Contains(finalSchema.Tables, t => t.Name == "keepers"); } + + // ============================================================================= + // LQL Default Value Tests - Platform Independent Defaults + // ============================================================================= + + [Fact] + public void LqlDefault_NowFunction_GeneratesCurrentTimestamp() + { + // Arrange - Create table with LQL now() default + var schema = Schema + .Define("Test") + .Table( + "public", + "events", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column( + "created_at", + PortableTypes.DateTimeOffset, + c => c.NotNull().DefaultLql("now()") + ) + ) + .Build(); + + // Act + var emptySchema = ( + (SchemaResultOk)PostgresSchemaInspector.Inspect(_connection, "public", _logger) + ).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, schema, logger: _logger) + ).Value; + + var result = MigrationRunner.Apply( + _connection, + operations, + PostgresDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + Assert.True(result is MigrationApplyResultOk); + + // Insert a row without specifying created_at - should use default + using var insertCmd = _connection.CreateCommand(); + insertCmd.CommandText = + "INSERT INTO events (id) VALUES ('11111111-1111-1111-1111-111111111111')"; + insertCmd.ExecuteNonQuery(); + + // Verify the default was applied - should be a recent timestamp + using var selectCmd = _connection.CreateCommand(); + selectCmd.CommandText = + "SELECT created_at FROM events WHERE id = '11111111-1111-1111-1111-111111111111'"; + var createdAt = (DateTime)selectCmd.ExecuteScalar()!; + + // Should be within last few seconds + Assert.True((DateTime.UtcNow - createdAt).TotalSeconds < 10); + } + + [Fact] + public void LqlDefault_GenUuidFunction_GeneratesValidUuid() + { + // Arrange - Create table with LQL gen_uuid() default + var schema = Schema + .Define("Test") + .Table( + "public", + "items", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey().DefaultLql("gen_uuid()")) + .Column("name", PortableTypes.VarChar(100), c => c.NotNull()) + ) + .Build(); + + // Act + var emptySchema = ( + (SchemaResultOk)PostgresSchemaInspector.Inspect(_connection, "public", _logger) + ).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, schema, logger: _logger) + ).Value; + + var result = MigrationRunner.Apply( + _connection, + operations, + PostgresDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + Assert.True(result is MigrationApplyResultOk); + + // Insert rows without specifying id - should generate unique UUIDs + using var insertCmd = _connection.CreateCommand(); + insertCmd.CommandText = + @" + INSERT INTO items (name) VALUES ('Item 1'); + INSERT INTO items (name) VALUES ('Item 2'); + INSERT INTO items (name) VALUES ('Item 3'); + "; + insertCmd.ExecuteNonQuery(); + + // Verify UUIDs were generated and are unique + using var selectCmd = _connection.CreateCommand(); + selectCmd.CommandText = "SELECT id FROM items ORDER BY name"; + using var reader = selectCmd.ExecuteReader(); + + var uuids = new List(); + while (reader.Read()) + { + uuids.Add(reader.GetGuid(0)); + } + + Assert.Equal(3, uuids.Count); + Assert.Equal(3, uuids.Distinct().Count()); // All unique + Assert.All(uuids, id => Assert.NotEqual(Guid.Empty, id)); + } + + [Fact] + public void LqlDefault_BooleanTrue_GeneratesCorrectValue() + { + // Arrange + var schema = Schema + .Define("Test") + .Table( + "public", + "flags", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column( + "is_active", + PortableTypes.Boolean, + c => c.NotNull().DefaultLql("true") + ) + .Column( + "is_deleted", + PortableTypes.Boolean, + c => c.NotNull().DefaultLql("false") + ) + ) + .Build(); + + // Act + var emptySchema = ( + (SchemaResultOk)PostgresSchemaInspector.Inspect(_connection, "public", _logger) + ).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, schema, logger: _logger) + ).Value; + + var result = MigrationRunner.Apply( + _connection, + operations, + PostgresDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + Assert.True(result is MigrationApplyResultOk); + + // Insert without specifying booleans + using var insertCmd = _connection.CreateCommand(); + insertCmd.CommandText = + "INSERT INTO flags (id) VALUES ('11111111-1111-1111-1111-111111111111')"; + insertCmd.ExecuteNonQuery(); + + // Verify defaults + using var selectCmd = _connection.CreateCommand(); + selectCmd.CommandText = "SELECT is_active, is_deleted FROM flags"; + using var reader = selectCmd.ExecuteReader(); + Assert.True(reader.Read()); + Assert.True(reader.GetBoolean(0)); // is_active = true + Assert.False(reader.GetBoolean(1)); // is_deleted = false + } + + [Fact] + public void LqlDefault_NumericLiterals_GeneratesCorrectValues() + { + // Arrange - Test integer and decimal defaults + var schema = Schema + .Define("Test") + .Table( + "public", + "counters", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("count", PortableTypes.Int, c => c.NotNull().DefaultLql("0")) + .Column( + "score", + PortableTypes.Decimal(10, 2), + c => c.NotNull().DefaultLql("100") + ) + .Column("rate", PortableTypes.Double, c => c.NotNull().DefaultLql("0.5")) + ) + .Build(); + + // Act + var emptySchema = ( + (SchemaResultOk)PostgresSchemaInspector.Inspect(_connection, "public", _logger) + ).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, schema, logger: _logger) + ).Value; + + var result = MigrationRunner.Apply( + _connection, + operations, + PostgresDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + Assert.True(result is MigrationApplyResultOk); + + // Insert without specifying values + using var insertCmd = _connection.CreateCommand(); + insertCmd.CommandText = + "INSERT INTO counters (id) VALUES ('11111111-1111-1111-1111-111111111111')"; + insertCmd.ExecuteNonQuery(); + + // Verify defaults + using var selectCmd = _connection.CreateCommand(); + selectCmd.CommandText = "SELECT count, score, rate FROM counters"; + using var reader = selectCmd.ExecuteReader(); + Assert.True(reader.Read()); + Assert.Equal(0, reader.GetInt32(0)); + Assert.Equal(100m, reader.GetDecimal(1)); + Assert.Equal(0.5, reader.GetDouble(2), 2); + } + + [Fact] + public void LqlDefault_StringLiteral_GeneratesCorrectValue() + { + // Arrange - Test string literal default with single quotes + var schema = Schema + .Define("Test") + .Table( + "public", + "statuses", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column( + "status", + PortableTypes.VarChar(50), + c => c.NotNull().DefaultLql("'pending'") + ) + .Column( + "category", + PortableTypes.VarChar(50), + c => c.NotNull().DefaultLql("'default'") + ) + ) + .Build(); + + // Act + var emptySchema = ( + (SchemaResultOk)PostgresSchemaInspector.Inspect(_connection, "public", _logger) + ).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, schema, logger: _logger) + ).Value; + + var result = MigrationRunner.Apply( + _connection, + operations, + PostgresDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + Assert.True(result is MigrationApplyResultOk); + + // Insert without specifying strings + using var insertCmd = _connection.CreateCommand(); + insertCmd.CommandText = + "INSERT INTO statuses (id) VALUES ('11111111-1111-1111-1111-111111111111')"; + insertCmd.ExecuteNonQuery(); + + // Verify defaults + using var selectCmd = _connection.CreateCommand(); + selectCmd.CommandText = "SELECT status, category FROM statuses"; + using var reader = selectCmd.ExecuteReader(); + Assert.True(reader.Read()); + Assert.Equal("pending", reader.GetString(0)); + Assert.Equal("default", reader.GetString(1)); + } + + [Fact] + public void LqlDefault_AllTypesInOneTable_WorksTogether() + { + // Arrange - Comprehensive test with all LQL default types + var schema = Schema + .Define("Test") + .Table( + "public", + "comprehensive_defaults", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey().DefaultLql("gen_uuid()")) + .Column("name", PortableTypes.VarChar(100), c => c.NotNull()) + .Column( + "created_at", + PortableTypes.DateTimeOffset, + c => c.NotNull().DefaultLql("now()") + ) + .Column( + "is_active", + PortableTypes.Boolean, + c => c.NotNull().DefaultLql("true") + ) + .Column( + "is_archived", + PortableTypes.Boolean, + c => c.NotNull().DefaultLql("false") + ) + .Column("count", PortableTypes.Int, c => c.NotNull().DefaultLql("0")) + .Column("priority", PortableTypes.Int, c => c.NotNull().DefaultLql("5")) + .Column( + "status", + PortableTypes.VarChar(20), + c => c.NotNull().DefaultLql("'active'") + ) + ) + .Build(); + + // Act + var emptySchema = ( + (SchemaResultOk)PostgresSchemaInspector.Inspect(_connection, "public", _logger) + ).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, schema, logger: _logger) + ).Value; + + var result = MigrationRunner.Apply( + _connection, + operations, + PostgresDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + Assert.True(result is MigrationApplyResultOk); + + // Insert only name - all other columns should use defaults + using var insertCmd = _connection.CreateCommand(); + insertCmd.CommandText = "INSERT INTO comprehensive_defaults (name) VALUES ('Test Item')"; + insertCmd.ExecuteNonQuery(); + + // Verify all defaults + using var selectCmd = _connection.CreateCommand(); + selectCmd.CommandText = + "SELECT id, created_at, is_active, is_archived, count, priority, status FROM comprehensive_defaults"; + using var reader = selectCmd.ExecuteReader(); + Assert.True(reader.Read()); + + var id = reader.GetGuid(0); + Assert.NotEqual(Guid.Empty, id); + + var createdAt = reader.GetDateTime(1); + Assert.True((DateTime.UtcNow - createdAt).TotalSeconds < 10); + + Assert.True(reader.GetBoolean(2)); // is_active + Assert.False(reader.GetBoolean(3)); // is_archived + Assert.Equal(0, reader.GetInt32(4)); // count + Assert.Equal(5, reader.GetInt32(5)); // priority + Assert.Equal("active", reader.GetString(6)); // status + } } diff --git a/Migration/Migration.Tests/SchemaDiffTests.cs b/Migration/Migration.Tests/SchemaDiffTests.cs new file mode 100644 index 00000000..d6141af9 --- /dev/null +++ b/Migration/Migration.Tests/SchemaDiffTests.cs @@ -0,0 +1,635 @@ +namespace Migration.Tests; + +/// +/// Tests for SchemaDiff.Calculate() method. +/// Covers: create tables, add columns, create indexes, add foreign keys, destructive operations. +/// +public sealed class SchemaDiffTests +{ + [Fact] + public void Calculate_EmptyCurrentToNewDesired_CreatesTable() + { + // Arrange + var current = Schema.Define("Current").Build(); + + var desired = Schema + .Define("Desired") + .Table( + "public", + "users", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("name", PortableTypes.VarChar(100), c => c.NotNull()) + ) + .Build(); + + // Act + var result = SchemaDiff.Calculate(current, desired); + + // Assert + Assert.True(result is OperationsResultOk); + var ops = ((OperationsResultOk)result).Value; + + Assert.Single(ops); + Assert.IsType(ops[0]); + var createOp = (CreateTableOperation)ops[0]; + Assert.Equal("users", createOp.Table.Name); + } + + [Fact] + public void Calculate_SameSchema_NoOperations() + { + // Arrange + var schema = Schema + .Define("Test") + .Table( + "public", + "users", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("name", PortableTypes.VarChar(100)) + ) + .Build(); + + // Act + var result = SchemaDiff.Calculate(schema, schema); + + // Assert + Assert.True(result is OperationsResultOk); + var ops = ((OperationsResultOk)result).Value; + + Assert.Empty(ops); + } + + [Fact] + public void Calculate_NewColumn_AddsColumn() + { + // Arrange + var current = Schema + .Define("Current") + .Table("public", "users", t => t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey())) + .Build(); + + var desired = Schema + .Define("Desired") + .Table( + "public", + "users", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("email", PortableTypes.VarChar(255), c => c.NotNull()) + ) + .Build(); + + // Act + var result = SchemaDiff.Calculate(current, desired); + + // Assert + Assert.True(result is OperationsResultOk); + var ops = ((OperationsResultOk)result).Value; + + Assert.Single(ops); + Assert.IsType(ops[0]); + var addColOp = (AddColumnOperation)ops[0]; + Assert.Equal("email", addColOp.Column.Name); + Assert.Equal("users", addColOp.TableName); + } + + [Fact] + public void Calculate_NewIndex_CreatesIndex() + { + // Arrange + var current = Schema + .Define("Current") + .Table( + "public", + "users", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("email", PortableTypes.VarChar(255)) + ) + .Build(); + + var desired = Schema + .Define("Desired") + .Table( + "public", + "users", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("email", PortableTypes.VarChar(255)) + .Index("idx_users_email", "email") + ) + .Build(); + + // Act + var result = SchemaDiff.Calculate(current, desired); + + // Assert + Assert.True(result is OperationsResultOk); + var ops = ((OperationsResultOk)result).Value; + + Assert.Single(ops); + Assert.IsType(ops[0]); + var createIdxOp = (CreateIndexOperation)ops[0]; + Assert.Equal("idx_users_email", createIdxOp.Index.Name); + } + + [Fact] + public void Calculate_NewForeignKey_AddsForeignKey() + { + // Arrange + var current = Schema + .Define("Current") + .Table( + "public", + "departments", + t => t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + ) + .Table( + "public", + "employees", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("dept_id", PortableTypes.Uuid) + ) + .Build(); + + var desired = Schema + .Define("Desired") + .Table( + "public", + "departments", + t => t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + ) + .Table( + "public", + "employees", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("dept_id", PortableTypes.Uuid) + .ForeignKey("dept_id", "departments", "id", ForeignKeyAction.Cascade) + ) + .Build(); + + // Act + var result = SchemaDiff.Calculate(current, desired); + + // Assert + Assert.True(result is OperationsResultOk); + var ops = ((OperationsResultOk)result).Value; + + Assert.Single(ops); + Assert.IsType(ops[0]); + var addFkOp = (AddForeignKeyOperation)ops[0]; + Assert.Equal("employees", addFkOp.TableName); + } + + [Fact] + public void Calculate_RemovedTable_NotDroppedByDefault() + { + // Arrange + var current = Schema + .Define("Current") + .Table("public", "users", t => t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey())) + .Table( + "public", + "obsolete", + t => t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + ) + .Build(); + + var desired = Schema + .Define("Desired") + .Table("public", "users", t => t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey())) + .Build(); + + // Act + var result = SchemaDiff.Calculate(current, desired, allowDestructive: false); + + // Assert + Assert.True(result is OperationsResultOk); + var ops = ((OperationsResultOk)result).Value; + + Assert.Empty(ops); + } + + [Fact] + public void Calculate_RemovedTable_DroppedWhenDestructiveAllowed() + { + // Arrange + var current = Schema + .Define("Current") + .Table("public", "users", t => t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey())) + .Table( + "public", + "obsolete", + t => t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + ) + .Build(); + + var desired = Schema + .Define("Desired") + .Table("public", "users", t => t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey())) + .Build(); + + // Act + var result = SchemaDiff.Calculate(current, desired, allowDestructive: true); + + // Assert + Assert.True(result is OperationsResultOk); + var ops = ((OperationsResultOk)result).Value; + + Assert.Single(ops); + Assert.IsType(ops[0]); + var dropOp = (DropTableOperation)ops[0]; + Assert.Equal("obsolete", dropOp.TableName); + } + + [Fact] + public void Calculate_RemovedColumn_NotDroppedByDefault() + { + // Arrange + var current = Schema + .Define("Current") + .Table( + "public", + "users", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("old_field", PortableTypes.VarChar(100)) + ) + .Build(); + + var desired = Schema + .Define("Desired") + .Table("public", "users", t => t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey())) + .Build(); + + // Act + var result = SchemaDiff.Calculate(current, desired, allowDestructive: false); + + // Assert + Assert.True(result is OperationsResultOk); + var ops = ((OperationsResultOk)result).Value; + + Assert.Empty(ops); + } + + [Fact] + public void Calculate_RemovedColumn_DroppedWhenDestructiveAllowed() + { + // Arrange + var current = Schema + .Define("Current") + .Table( + "public", + "users", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("old_field", PortableTypes.VarChar(100)) + ) + .Build(); + + var desired = Schema + .Define("Desired") + .Table("public", "users", t => t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey())) + .Build(); + + // Act + var result = SchemaDiff.Calculate(current, desired, allowDestructive: true); + + // Assert + Assert.True(result is OperationsResultOk); + var ops = ((OperationsResultOk)result).Value; + + Assert.Single(ops); + Assert.IsType(ops[0]); + var dropColOp = (DropColumnOperation)ops[0]; + Assert.Equal("old_field", dropColOp.ColumnName); + } + + [Fact] + public void Calculate_RemovedIndex_NotDroppedByDefault() + { + // Arrange + var current = Schema + .Define("Current") + .Table( + "public", + "users", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("email", PortableTypes.VarChar(255)) + .Index("idx_users_email", "email") + ) + .Build(); + + var desired = Schema + .Define("Desired") + .Table( + "public", + "users", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("email", PortableTypes.VarChar(255)) + ) + .Build(); + + // Act + var result = SchemaDiff.Calculate(current, desired, allowDestructive: false); + + // Assert + Assert.True(result is OperationsResultOk); + var ops = ((OperationsResultOk)result).Value; + + Assert.Empty(ops); + } + + [Fact] + public void Calculate_RemovedIndex_DroppedWhenDestructiveAllowed() + { + // Arrange + var current = Schema + .Define("Current") + .Table( + "public", + "users", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("email", PortableTypes.VarChar(255)) + .Index("idx_users_email", "email") + ) + .Build(); + + var desired = Schema + .Define("Desired") + .Table( + "public", + "users", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("email", PortableTypes.VarChar(255)) + ) + .Build(); + + // Act + var result = SchemaDiff.Calculate(current, desired, allowDestructive: true); + + // Assert + Assert.True(result is OperationsResultOk); + var ops = ((OperationsResultOk)result).Value; + + Assert.Single(ops); + Assert.IsType(ops[0]); + var dropIdxOp = (DropIndexOperation)ops[0]; + Assert.Equal("idx_users_email", dropIdxOp.IndexName); + } + + [Fact] + public void Calculate_RemovedForeignKey_DroppedWhenDestructiveAllowed() + { + // Arrange + var current = Schema + .Define("Current") + .Table( + "public", + "departments", + t => t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + ) + .Table( + "public", + "employees", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("dept_id", PortableTypes.Uuid) + .ForeignKey("dept_id", "departments", "id", ForeignKeyAction.Cascade) + ) + .Build(); + + var desired = Schema + .Define("Desired") + .Table( + "public", + "departments", + t => t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + ) + .Table( + "public", + "employees", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("dept_id", PortableTypes.Uuid) + ) + .Build(); + + // Act + var result = SchemaDiff.Calculate(current, desired, allowDestructive: true); + + // Assert + Assert.True(result is OperationsResultOk); + var ops = ((OperationsResultOk)result).Value; + + Assert.Single(ops); + Assert.IsType(ops[0]); + } + + [Fact] + public void Calculate_NewTableWithIndex_CreatesTableAndIndex() + { + // Arrange + var current = Schema.Define("Current").Build(); + + var desired = Schema + .Define("Desired") + .Table( + "public", + "users", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("email", PortableTypes.VarChar(255)) + .Index("idx_users_email", "email") + ) + .Build(); + + // Act + var result = SchemaDiff.Calculate(current, desired); + + // Assert + Assert.True(result is OperationsResultOk); + var ops = ((OperationsResultOk)result).Value; + + Assert.Equal(2, ops.Count); + Assert.IsType(ops[0]); + Assert.IsType(ops[1]); + } + + [Fact] + public void Calculate_CaseInsensitiveTableMatching() + { + // Arrange + var current = Schema + .Define("Current") + .Table("public", "USERS", t => t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey())) + .Build(); + + var desired = Schema + .Define("Desired") + .Table( + "public", + "users", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("email", PortableTypes.VarChar(255)) + ) + .Build(); + + // Act + var result = SchemaDiff.Calculate(current, desired); + + // Assert + Assert.True(result is OperationsResultOk); + var ops = ((OperationsResultOk)result).Value; + + // Should recognize USERS and users as the same table, just add the column + Assert.Single(ops); + Assert.IsType(ops[0]); + } + + [Fact] + public void Calculate_CaseInsensitiveColumnMatching() + { + // Arrange + var current = Schema + .Define("Current") + .Table( + "public", + "users", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("EMAIL", PortableTypes.VarChar(255)) + ) + .Build(); + + var desired = Schema + .Define("Desired") + .Table( + "public", + "users", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("email", PortableTypes.VarChar(255)) + ) + .Build(); + + // Act + var result = SchemaDiff.Calculate(current, desired); + + // Assert + Assert.True(result is OperationsResultOk); + var ops = ((OperationsResultOk)result).Value; + + // Should recognize EMAIL and email as the same column + Assert.Empty(ops); + } + + [Fact] + public void Calculate_MultipleNewTables_CreatesAll() + { + // Arrange + var current = Schema.Define("Current").Build(); + + var desired = Schema + .Define("Desired") + .Table( + "public", + "countries", + t => t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + ) + .Table( + "public", + "regions", + t => t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + ) + .Table("public", "cities", t => t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey())) + .Build(); + + // Act + var result = SchemaDiff.Calculate(current, desired); + + // Assert + Assert.True(result is OperationsResultOk); + var ops = ((OperationsResultOk)result).Value; + + Assert.Equal(3, ops.Count); + Assert.All(ops, op => Assert.IsType(op)); + } + + [Fact] + public void Calculate_ComplexMigration_CombinesOperations() + { + // Arrange + var current = Schema + .Define("Current") + .Table( + "public", + "users", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("old_field", PortableTypes.VarChar(100)) + ) + .Table( + "public", + "obsolete_table", + t => t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + ) + .Build(); + + var desired = Schema + .Define("Desired") + .Table( + "public", + "users", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("new_field", PortableTypes.VarChar(200)) + .Index("idx_users_new", "new_field") + ) + .Table( + "public", + "new_table", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("user_id", PortableTypes.Uuid) + .ForeignKey("user_id", "users", "id") + ) + .Build(); + + // Act + var result = SchemaDiff.Calculate(current, desired, allowDestructive: true); + + // Assert + Assert.True(result is OperationsResultOk); + var ops = ((OperationsResultOk)result).Value; + + // Should have: add column (new_field), create index, drop column (old_field), + // create table (new_table), drop table (obsolete_table) + Assert.Contains(ops, op => op is AddColumnOperation add && add.Column.Name == "new_field"); + Assert.Contains( + ops, + op => op is CreateIndexOperation idx && idx.Index.Name == "idx_users_new" + ); + Assert.Contains( + ops, + op => op is DropColumnOperation drop && drop.ColumnName == "old_field" + ); + Assert.Contains( + ops, + op => op is CreateTableOperation create && create.Table.Name == "new_table" + ); + Assert.Contains( + ops, + op => op is DropTableOperation dropTable && dropTable.TableName == "obsolete_table" + ); + } +} diff --git a/Migration/Migration.Tests/SchemaVerifier.cs b/Migration/Migration.Tests/SchemaVerifier.cs new file mode 100644 index 00000000..c4bda605 --- /dev/null +++ b/Migration/Migration.Tests/SchemaVerifier.cs @@ -0,0 +1,636 @@ +namespace Migration.Tests; + +/// +/// Reusable schema verification utilities for comprehensive E2E testing. +/// Proves that schema objects are created CORRECTLY - tables, columns, indexes, FKs, defaults, constraints. +/// +public static class SchemaVerifier +{ + // ========================================================================= + // POSTGRESQL VERIFICATION + // ========================================================================= + + /// + /// Verifies a PostgreSQL schema matches the expected definition EXACTLY. + /// Checks tables, columns, types, nullability, defaults, indexes, FKs, constraints. + /// + public static void VerifyPostgresSchema( + NpgsqlConnection conn, + SchemaDefinition expected, + string schemaName = "public" + ) + { + foreach (var table in expected.Tables) + { + VerifyPostgresTable(conn, table, schemaName); + } + + // Verify NO extra tables exist + var expectedTableNames = expected.Tables.Select(t => t.Name).ToHashSet(); + var actualTables = GetPostgresTables(conn, schemaName); + var extraTables = actualTables.Except(expectedTableNames).ToList(); + Assert.Empty(extraTables); + } + + /// + /// Verifies a single PostgreSQL table matches its definition. + /// + public static void VerifyPostgresTable( + NpgsqlConnection conn, + TableDefinition expected, + string schemaName = "public" + ) + { + // 1. Table exists + Assert.True( + PostgresTableExists(conn, expected.Name, schemaName), + $"Table '{expected.Name}' should exist" + ); + + // 2. All columns exist with correct types, nullability, defaults + foreach (var col in expected.Columns) + { + VerifyPostgresColumn(conn, expected.Name, col, schemaName); + } + + // 3. No extra columns + var expectedColNames = expected.Columns.Select(c => c.Name).ToHashSet(); + var actualCols = GetPostgresColumns(conn, expected.Name, schemaName); + var extraCols = actualCols.Except(expectedColNames).ToList(); + Assert.Empty(extraCols); + + // 4. Primary key + if (expected.PrimaryKey is not null) + { + VerifyPostgresPrimaryKey(conn, expected.Name, expected.PrimaryKey, schemaName); + } + + // 5. Indexes (including expression indexes) + foreach (var idx in expected.Indexes) + { + VerifyPostgresIndex(conn, expected.Name, idx, schemaName); + } + + // 6. Foreign keys + foreach (var fk in expected.ForeignKeys) + { + VerifyPostgresForeignKey(conn, expected.Name, fk, schemaName); + } + + // 7. Unique constraints + foreach (var uc in expected.UniqueConstraints) + { + VerifyPostgresUniqueConstraint(conn, expected.Name, uc, schemaName); + } + } + + /// + /// Verifies a PostgreSQL column matches its definition. + /// + public static void VerifyPostgresColumn( + NpgsqlConnection conn, + string tableName, + ColumnDefinition expected, + string schemaName = "public" + ) + { + using var cmd = conn.CreateCommand(); + cmd.CommandText = """ + SELECT + column_name, + data_type, + is_nullable, + column_default, + character_maximum_length, + numeric_precision, + numeric_scale + FROM information_schema.columns + WHERE table_schema = @schema AND table_name = @table AND column_name = @column + """; + cmd.Parameters.AddWithValue("@schema", schemaName); + cmd.Parameters.AddWithValue("@table", tableName); + cmd.Parameters.AddWithValue("@column", expected.Name); + + using var reader = cmd.ExecuteReader(); + Assert.True(reader.Read(), $"Column '{expected.Name}' should exist in table '{tableName}'"); + + var isNullable = reader.GetString(2) == "YES"; + Assert.Equal(expected.IsNullable, isNullable); + + // Check default if specified + if (expected.DefaultLqlExpression is not null || expected.DefaultValue is not null) + { + var actualDefault = reader.IsDBNull(3) ? null : reader.GetString(3); + Assert.NotNull(actualDefault); + + // Verify the default contains expected pattern + if (expected.DefaultLqlExpression is not null) + { + var translated = LqlDefaultTranslator.ToPostgres(expected.DefaultLqlExpression); + Assert.Contains(translated.ToLowerInvariant(), actualDefault.ToLowerInvariant()); + } + } + } + + /// + /// Verifies a PostgreSQL primary key matches its definition. + /// + public static void VerifyPostgresPrimaryKey( + NpgsqlConnection conn, + string tableName, + PrimaryKeyDefinition expected, + string schemaName = "public" + ) + { + using var cmd = conn.CreateCommand(); + cmd.CommandText = """ + SELECT kcu.column_name + FROM information_schema.table_constraints tc + JOIN information_schema.key_column_usage kcu + ON tc.constraint_name = kcu.constraint_name + AND tc.table_schema = kcu.table_schema + WHERE tc.table_schema = @schema + AND tc.table_name = @table + AND tc.constraint_type = 'PRIMARY KEY' + ORDER BY kcu.ordinal_position + """; + cmd.Parameters.AddWithValue("@schema", schemaName); + cmd.Parameters.AddWithValue("@table", tableName); + + var actualColumns = new List(); + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + actualColumns.Add(reader.GetString(0)); + } + } + + Assert.Equal(expected.Columns.Count, actualColumns.Count); + for (var i = 0; i < expected.Columns.Count; i++) + { + Assert.Equal(expected.Columns[i], actualColumns[i]); + } + } + + /// + /// Verifies a PostgreSQL index matches its definition. + /// + public static void VerifyPostgresIndex( + NpgsqlConnection conn, + string tableName, + IndexDefinition expected, + string schemaName = "public" + ) + { + using var cmd = conn.CreateCommand(); + cmd.CommandText = """ + SELECT + i.relname AS index_name, + ix.indisunique AS is_unique, + pg_get_indexdef(ix.indexrelid) AS index_def + FROM pg_class t + JOIN pg_index ix ON t.oid = ix.indrelid + JOIN pg_class i ON i.oid = ix.indexrelid + JOIN pg_namespace n ON n.oid = t.relnamespace + WHERE n.nspname = @schema + AND t.relname = @table + AND i.relname = @index + """; + cmd.Parameters.AddWithValue("@schema", schemaName); + cmd.Parameters.AddWithValue("@table", tableName); + cmd.Parameters.AddWithValue("@index", expected.Name); + + using var reader = cmd.ExecuteReader(); + Assert.True(reader.Read(), $"Index '{expected.Name}' should exist on table '{tableName}'"); + + var isUnique = reader.GetBoolean(1); + Assert.Equal(expected.IsUnique, isUnique); + + var indexDef = reader.GetString(2).ToLowerInvariant(); + + // Verify expression or column indexes + if (expected.Expressions.Count > 0) + { + foreach (var expr in expected.Expressions) + { + Assert.Contains(expr.ToLowerInvariant(), indexDef); + } + } + else + { + foreach (var col in expected.Columns) + { + Assert.Contains(col.ToLowerInvariant(), indexDef); + } + } + } + + /// + /// Verifies a PostgreSQL foreign key matches its definition. + /// + public static void VerifyPostgresForeignKey( + NpgsqlConnection conn, + string tableName, + ForeignKeyDefinition expected, + string schemaName = "public" + ) + { + using var cmd = conn.CreateCommand(); + cmd.CommandText = """ + SELECT + tc.constraint_name, + kcu.column_name, + ccu.table_name AS referenced_table, + ccu.column_name AS referenced_column, + rc.delete_rule, + rc.update_rule + FROM information_schema.table_constraints tc + JOIN information_schema.key_column_usage kcu + ON tc.constraint_name = kcu.constraint_name + AND tc.table_schema = kcu.table_schema + JOIN information_schema.constraint_column_usage ccu + ON ccu.constraint_name = tc.constraint_name + JOIN information_schema.referential_constraints rc + ON tc.constraint_name = rc.constraint_name + WHERE tc.table_schema = @schema + AND tc.table_name = @table + AND tc.constraint_type = 'FOREIGN KEY' + AND kcu.column_name = @column + """; + cmd.Parameters.AddWithValue("@schema", schemaName); + cmd.Parameters.AddWithValue("@table", tableName); + cmd.Parameters.AddWithValue("@column", expected.Columns[0]); + + using var reader = cmd.ExecuteReader(); + Assert.True( + reader.Read(), + $"Foreign key on '{expected.Columns[0]}' should exist in table '{tableName}'" + ); + + var refTable = reader.GetString(2); + var refColumn = reader.GetString(3); + var deleteRule = reader.GetString(4); + var updateRule = reader.GetString(5); + + Assert.Equal(expected.ReferencedTable, refTable); + Assert.Equal(expected.ReferencedColumns[0], refColumn); + Assert.Equal(ForeignKeyActionToString(expected.OnDelete), deleteRule); + Assert.Equal(ForeignKeyActionToString(expected.OnUpdate), updateRule); + } + + /// + /// Verifies a PostgreSQL unique constraint matches its definition. + /// + public static void VerifyPostgresUniqueConstraint( + NpgsqlConnection conn, + string tableName, + UniqueConstraintDefinition expected, + string schemaName = "public" + ) + { + using var cmd = conn.CreateCommand(); + cmd.CommandText = """ + SELECT kcu.column_name + FROM information_schema.table_constraints tc + JOIN information_schema.key_column_usage kcu + ON tc.constraint_name = kcu.constraint_name + AND tc.table_schema = kcu.table_schema + WHERE tc.table_schema = @schema + AND tc.table_name = @table + AND tc.constraint_type = 'UNIQUE' + ORDER BY kcu.ordinal_position + """; + cmd.Parameters.AddWithValue("@schema", schemaName); + cmd.Parameters.AddWithValue("@table", tableName); + + var actualColumns = new List(); + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + actualColumns.Add(reader.GetString(0)); + } + } + + // Check that expected columns are present + foreach (var col in expected.Columns) + { + Assert.Contains(col, actualColumns); + } + } + + // ========================================================================= + // SQLITE VERIFICATION + // ========================================================================= + + /// + /// Verifies a SQLite schema matches the expected definition EXACTLY. + /// + public static void VerifySqliteSchema(SqliteConnection conn, SchemaDefinition expected) + { + foreach (var table in expected.Tables) + { + VerifySqliteTable(conn, table); + } + + // Verify NO extra tables exist + var expectedTableNames = expected.Tables.Select(t => t.Name).ToHashSet(); + var actualTables = GetSqliteTables(conn); + var extraTables = actualTables.Except(expectedTableNames).ToList(); + Assert.Empty(extraTables); + } + + /// + /// Verifies a single SQLite table matches its definition. + /// + public static void VerifySqliteTable(SqliteConnection conn, TableDefinition expected) + { + // 1. Table exists + Assert.True( + SqliteTableExists(conn, expected.Name), + $"Table '{expected.Name}' should exist" + ); + + // 2. Get table DDL and verify structure + var tableDdl = GetSqliteTableDdl(conn, expected.Name); + Assert.NotNull(tableDdl); + + // 3. Verify all columns + foreach (var col in expected.Columns) + { + VerifySqliteColumn(conn, expected.Name, col); + } + + // 4. Verify indexes + foreach (var idx in expected.Indexes) + { + VerifySqliteIndex(conn, idx); + } + + // 5. Verify foreign keys are in DDL + foreach (var fk in expected.ForeignKeys) + { + Assert.Contains($"REFERENCES [{fk.ReferencedTable}]", tableDdl); + } + } + + /// + /// Verifies a SQLite column matches its definition. + /// + public static void VerifySqliteColumn( + SqliteConnection conn, + string tableName, + ColumnDefinition expected + ) + { + using var cmd = conn.CreateCommand(); + cmd.CommandText = $"PRAGMA table_info([{tableName}])"; + + ColumnInfo? columnInfo = null; + using (var reader = cmd.ExecuteReader()) + { + while (reader.Read()) + { + var name = reader.GetString(1); + if (name == expected.Name) + { + columnInfo = new ColumnInfo( + Name: name, + Type: reader.GetString(2), + NotNull: reader.GetInt32(3) == 1, + DefaultValue: reader.IsDBNull(4) ? null : reader.GetString(4), + IsPrimaryKey: reader.GetInt32(5) == 1 + ); + break; + } + } + } + + Assert.NotNull(columnInfo); + Assert.Equal(!expected.IsNullable, columnInfo.NotNull); + + // Verify default if specified + if (expected.DefaultLqlExpression is not null) + { + Assert.NotNull(columnInfo.DefaultValue); + var translated = LqlDefaultTranslator.ToSqlite(expected.DefaultLqlExpression); + Assert.Equal(translated, columnInfo.DefaultValue); + } + else if (expected.DefaultValue is not null) + { + Assert.NotNull(columnInfo.DefaultValue); + } + } + + /// + /// Verifies a SQLite index matches its definition. + /// + public static void VerifySqliteIndex(SqliteConnection conn, IndexDefinition expected) + { + using var cmd = conn.CreateCommand(); + cmd.CommandText = "SELECT sql FROM sqlite_master WHERE type='index' AND name=@name"; + cmd.Parameters.AddWithValue("@name", expected.Name); + + var indexDdl = cmd.ExecuteScalar() as string; + Assert.NotNull(indexDdl); + + // Verify uniqueness + if (expected.IsUnique) + { + Assert.Contains("UNIQUE", indexDdl.ToUpperInvariant()); + } + + // Verify expression or column indexes + if (expected.Expressions.Count > 0) + { + foreach (var expr in expected.Expressions) + { + Assert.Contains(expr.ToLowerInvariant(), indexDdl.ToLowerInvariant()); + } + } + else + { + foreach (var col in expected.Columns) + { + Assert.Contains(col, indexDdl); + } + } + } + + /// + /// Verifies default values work at runtime by inserting and querying. + /// + public static void VerifyDefaultsWorkAtRuntime( + NpgsqlConnection pgConn, + SqliteConnection sqliteConn, + TableDefinition table, + string schemaName = "public" + ) + { + // Find columns with defaults + var columnsWithDefaults = table + .Columns.Where(c => c.DefaultLqlExpression is not null || c.DefaultValue is not null) + .ToList(); + + if (columnsWithDefaults.Count == 0) + return; + + // Find a non-default column to insert (or use DEFAULT VALUES) + var pkColumn = table.Columns.FirstOrDefault(c => + table.PrimaryKey?.Columns.Contains(c.Name) == true && !c.IsIdentity + ); + + var insertSql = pkColumn is not null + ? $"INSERT INTO \"{schemaName}\".\"{table.Name}\" (\"{pkColumn.Name}\") VALUES (1)" + : $"INSERT INTO \"{schemaName}\".\"{table.Name}\" DEFAULT VALUES"; + + var sqliteInsertSql = pkColumn is not null + ? $"INSERT INTO [{table.Name}] ([{pkColumn.Name}]) VALUES (1)" + : $"INSERT INTO [{table.Name}] DEFAULT VALUES"; + + // Insert in Postgres + using (var cmd = pgConn.CreateCommand()) + { + cmd.CommandText = insertSql; + cmd.ExecuteNonQuery(); + } + + // Insert in SQLite + using (var cmd = sqliteConn.CreateCommand()) + { + cmd.CommandText = sqliteInsertSql; + cmd.ExecuteNonQuery(); + } + + // Verify defaults were applied + foreach (var col in columnsWithDefaults) + { + // Postgres + using (var cmd = pgConn.CreateCommand()) + { + cmd.CommandText = + $"SELECT \"{col.Name}\" FROM \"{schemaName}\".\"{table.Name}\" LIMIT 1"; + var value = cmd.ExecuteScalar(); + Assert.NotNull(value); + } + + // SQLite + using (var cmd = sqliteConn.CreateCommand()) + { + cmd.CommandText = $"SELECT [{col.Name}] FROM [{table.Name}] LIMIT 1"; + var value = cmd.ExecuteScalar(); + Assert.NotNull(value); + } + } + } + + // ========================================================================= + // HELPER METHODS + // ========================================================================= + + private static bool PostgresTableExists(NpgsqlConnection conn, string tableName, string schema) + { + using var cmd = conn.CreateCommand(); + cmd.CommandText = """ + SELECT EXISTS ( + SELECT 1 FROM information_schema.tables + WHERE table_schema = @schema AND table_name = @table + ) + """; + cmd.Parameters.AddWithValue("@schema", schema); + cmd.Parameters.AddWithValue("@table", tableName); + return (bool)cmd.ExecuteScalar()!; + } + + private static List GetPostgresTables(NpgsqlConnection conn, string schema) + { + using var cmd = conn.CreateCommand(); + cmd.CommandText = """ + SELECT table_name FROM information_schema.tables + WHERE table_schema = @schema AND table_type = 'BASE TABLE' + """; + cmd.Parameters.AddWithValue("@schema", schema); + + var tables = new List(); + using var reader = cmd.ExecuteReader(); + while (reader.Read()) + { + tables.Add(reader.GetString(0)); + } + return tables; + } + + private static List GetPostgresColumns( + NpgsqlConnection conn, + string tableName, + string schema + ) + { + using var cmd = conn.CreateCommand(); + cmd.CommandText = """ + SELECT column_name FROM information_schema.columns + WHERE table_schema = @schema AND table_name = @table + """; + cmd.Parameters.AddWithValue("@schema", schema); + cmd.Parameters.AddWithValue("@table", tableName); + + var columns = new List(); + using var reader = cmd.ExecuteReader(); + while (reader.Read()) + { + columns.Add(reader.GetString(0)); + } + return columns; + } + + private static bool SqliteTableExists(SqliteConnection conn, string tableName) + { + using var cmd = conn.CreateCommand(); + cmd.CommandText = "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name=@name"; + cmd.Parameters.AddWithValue("@name", tableName); + return (long)cmd.ExecuteScalar()! > 0; + } + + private static List GetSqliteTables(SqliteConnection conn) + { + using var cmd = conn.CreateCommand(); + cmd.CommandText = + "SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%'"; + + var tables = new List(); + using var reader = cmd.ExecuteReader(); + while (reader.Read()) + { + tables.Add(reader.GetString(0)); + } + return tables; + } + + private static string? GetSqliteTableDdl(SqliteConnection conn, string tableName) + { + using var cmd = conn.CreateCommand(); + cmd.CommandText = "SELECT sql FROM sqlite_master WHERE type='table' AND name=@name"; + cmd.Parameters.AddWithValue("@name", tableName); + return cmd.ExecuteScalar() as string; + } + + private static string ForeignKeyActionToString(ForeignKeyAction action) => + action switch + { + ForeignKeyAction.NoAction => "NO ACTION", + ForeignKeyAction.Cascade => "CASCADE", + ForeignKeyAction.SetNull => "SET NULL", + ForeignKeyAction.SetDefault => "SET DEFAULT", + ForeignKeyAction.Restrict => "RESTRICT", + _ => "NO ACTION", + }; + + private sealed record ColumnInfo( + string Name, + string Type, + bool NotNull, + string? DefaultValue, + bool IsPrimaryKey + ); +} diff --git a/Migration/Migration.Tests/SchemaYamlSerializerTests.cs b/Migration/Migration.Tests/SchemaYamlSerializerTests.cs new file mode 100644 index 00000000..9f02834b --- /dev/null +++ b/Migration/Migration.Tests/SchemaYamlSerializerTests.cs @@ -0,0 +1,628 @@ +using System.Globalization; + +namespace Migration.Tests; + +/// +/// E2E tests for YAML schema serialization and deserialization. +/// +public sealed class SchemaYamlSerializerTests +{ + [Fact] + public void ToYaml_SimpleSchema_ProducesValidYaml() + { + // Arrange + var schema = Schema + .Define("test") + .Table( + "Users", + t => + t.Column("Id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("Email", PortableTypes.VarChar(255), c => c.NotNull()) + .Column("Name", PortableTypes.Text) + ) + .Build(); + + // Act + var yaml = SchemaYamlSerializer.ToYaml(schema); + + // Assert + Assert.NotNull(yaml); + Assert.Contains("name: test", yaml); + Assert.Contains("tables:", yaml); + Assert.Contains("Users", yaml); + Assert.Contains("Uuid", yaml); + Assert.Contains("VarChar(255)", yaml); + } + + [Fact] + public void FromYaml_ValidYaml_DeserializesCorrectly() + { + // Arrange + var yaml = """ + name: test_schema + tables: + - name: Products + schema: public + columns: + - name: Id + type: Uuid + isNullable: false + - name: Name + type: VarChar(200) + isNullable: false + - name: Price + type: Decimal(10,2) + isNullable: true + primaryKey: + columns: + - Id + """; + + // Act + var schema = SchemaYamlSerializer.FromYaml(yaml); + + // Assert + Assert.Equal("test_schema", schema.Name); + Assert.Single(schema.Tables); + + var table = schema.Tables[0]; + Assert.Equal("Products", table.Name); + Assert.Equal(3, table.Columns.Count); + + var idCol = table.Columns.First(c => c.Name == "Id"); + Assert.IsType(idCol.Type); + Assert.False(idCol.IsNullable); + + var nameCol = table.Columns.First(c => c.Name == "Name"); + Assert.IsType(nameCol.Type); + Assert.Equal(200, ((VarCharType)nameCol.Type).MaxLength); + + var priceCol = table.Columns.First(c => c.Name == "Price"); + Assert.IsType(priceCol.Type); + Assert.Equal(10, ((DecimalType)priceCol.Type).Precision); + Assert.Equal(2, ((DecimalType)priceCol.Type).Scale); + } + + [Fact] + public void RoundTrip_ComplexSchema_PreservesAllData() + { + // Arrange + var schema = Schema + .Define("complex_schema") + .Table( + "Users", + t => + t.Column("Id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("Email", PortableTypes.NVarChar(255), c => c.NotNull()) + .Column( + "CreatedAt", + PortableTypes.DateTime(), + c => c.NotNull().DefaultLql("now()") + ) + .Column( + "IsActive", + PortableTypes.Boolean, + c => c.NotNull().DefaultLql("true") + ) + .Index("idx_users_email", "Email", unique: true) + ) + .Table( + "Orders", + t => + t.Column("Id", PortableTypes.BigInt, c => c.PrimaryKey().Identity()) + .Column("UserId", PortableTypes.Uuid, c => c.NotNull()) + .Column("Total", PortableTypes.Decimal(12, 2), c => c.NotNull()) + .ForeignKey("UserId", "Users", "Id", ForeignKeyAction.Cascade) + ) + .Build(); + + // Act + var yaml = SchemaYamlSerializer.ToYaml(schema); + var restored = SchemaYamlSerializer.FromYaml(yaml); + + // Assert + Assert.Equal(schema.Name, restored.Name); + Assert.Equal(schema.Tables.Count, restored.Tables.Count); + + var usersOriginal = schema.Tables.First(t => t.Name == "Users"); + var usersRestored = restored.Tables.First(t => t.Name == "Users"); + Assert.Equal(usersOriginal.Columns.Count, usersRestored.Columns.Count); + Assert.Equal(usersOriginal.Indexes.Count, usersRestored.Indexes.Count); + + var ordersOriginal = schema.Tables.First(t => t.Name == "Orders"); + var ordersRestored = restored.Tables.First(t => t.Name == "Orders"); + Assert.Equal(ordersOriginal.Columns.Count, ordersRestored.Columns.Count); + Assert.Equal(ordersOriginal.ForeignKeys.Count, ordersRestored.ForeignKeys.Count); + } + + [Fact] + public void PortableType_AllTypes_SerializeCorrectly() + { + // Arrange + var schema = Schema + .Define("types_test") + .Table( + "AllTypes", + t => + t.Column("TinyInt", PortableTypes.TinyInt) + .Column("SmallInt", PortableTypes.SmallInt) + .Column("Int", PortableTypes.Int) + .Column("BigInt", PortableTypes.BigInt) + .Column("Decimal", PortableTypes.Decimal(18, 4)) + .Column("Float", PortableTypes.Float) + .Column("Double", PortableTypes.Double) + .Column("Money", PortableTypes.Money) + .Column("Bool", PortableTypes.Boolean) + .Column("Char", PortableTypes.Char(10)) + .Column("VarChar", PortableTypes.VarChar(100)) + .Column("NChar", PortableTypes.NChar(5)) + .Column("NVarChar", PortableTypes.NVarChar(500)) + .Column("NVarCharMax", PortableTypes.NVarCharMax) + .Column("Text", PortableTypes.Text) + .Column("Binary", PortableTypes.Binary(16)) + .Column("VarBinary", PortableTypes.VarBinary(256)) + .Column("VarBinaryMax", PortableTypes.VarBinaryMax) + .Column("Blob", PortableTypes.Blob) + .Column("Date", PortableTypes.Date) + .Column("Time", PortableTypes.Time(3)) + .Column("DateTime", PortableTypes.DateTime(6)) + .Column("DateTimeOffset", PortableTypes.DateTimeOffset) + .Column("Uuid", PortableTypes.Uuid) + .Column("Json", PortableTypes.Json) + .Column("Xml", PortableTypes.Xml) + ) + .Build(); + + // Act + var yaml = SchemaYamlSerializer.ToYaml(schema); + var restored = SchemaYamlSerializer.FromYaml(yaml); + + // Assert - verify each type round-trips correctly + var original = schema.Tables[0].Columns; + var rest = restored.Tables[0].Columns; + + Assert.Equal(original.Count, rest.Count); + + for (var i = 0; i < original.Count; i++) + { + Assert.Equal(original[i].Type.GetType(), rest[i].Type.GetType()); + } + + // Verify parameterized types + var decimalCol = rest.First(c => c.Name == "Decimal"); + Assert.Equal(18, ((DecimalType)decimalCol.Type).Precision); + Assert.Equal(4, ((DecimalType)decimalCol.Type).Scale); + + var charCol = rest.First(c => c.Name == "Char"); + Assert.Equal(10, ((CharType)charCol.Type).Length); + + var varCharCol = rest.First(c => c.Name == "VarChar"); + Assert.Equal(100, ((VarCharType)varCharCol.Type).MaxLength); + + var nvarCharMaxCol = rest.First(c => c.Name == "NVarCharMax"); + Assert.Equal(int.MaxValue, ((NVarCharType)nvarCharMaxCol.Type).MaxLength); + } + + [Fact] + public void ForeignKey_WithActions_SerializesCorrectly() + { + // Arrange + var schema = Schema + .Define("fk_test") + .Table("Parent", t => t.Column("Id", PortableTypes.Uuid, c => c.PrimaryKey())) + .Table( + "Child", + t => + t.Column("Id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("ParentId", PortableTypes.Uuid, c => c.NotNull()) + .ForeignKey( + "ParentId", + "Parent", + "Id", + onDelete: ForeignKeyAction.Cascade, + onUpdate: ForeignKeyAction.SetNull + ) + ) + .Build(); + + // Act + var yaml = SchemaYamlSerializer.ToYaml(schema); + var restored = SchemaYamlSerializer.FromYaml(yaml); + + // Assert + var childTable = restored.Tables.First(t => t.Name == "Child"); + Assert.Single(childTable.ForeignKeys); + + var fk = childTable.ForeignKeys[0]; + Assert.Equal("Parent", fk.ReferencedTable); + Assert.Equal(ForeignKeyAction.Cascade, fk.OnDelete); + Assert.Equal(ForeignKeyAction.SetNull, fk.OnUpdate); + } + + [Fact] + public void Index_WithFilter_SerializesCorrectly() + { + // Arrange + var schema = Schema + .Define("index_test") + .Table( + "Items", + t => + t.Column("Id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("IsActive", PortableTypes.Boolean, c => c.NotNull()) + .Column("Name", PortableTypes.VarChar(100)) + .Index("idx_active_items", "Name", unique: true, filter: "IsActive = 1") + ) + .Build(); + + // Act + var yaml = SchemaYamlSerializer.ToYaml(schema); + var restored = SchemaYamlSerializer.FromYaml(yaml); + + // Assert + var table = restored.Tables[0]; + Assert.Single(table.Indexes); + + var index = table.Indexes[0]; + Assert.Equal("idx_active_items", index.Name); + Assert.True(index.IsUnique); + Assert.Equal("IsActive = 1", index.Filter); + } + + [Fact] + public void ExpressionIndex_SerializesCorrectly() + { + // Arrange + var schema = Schema + .Define("expr_index_test") + .Table( + "Artists", + t => + t.Column("Id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("Name", PortableTypes.VarChar(200), c => c.NotNull()) + .ExpressionIndex("uq_artists_name_ci", "lower(Name)", unique: true) + ) + .Build(); + + // Act + var yaml = SchemaYamlSerializer.ToYaml(schema); + var restored = SchemaYamlSerializer.FromYaml(yaml); + + // Assert + var table = restored.Tables[0]; + Assert.Single(table.Indexes); + + var index = table.Indexes[0]; + Assert.Equal("uq_artists_name_ci", index.Name); + Assert.True(index.IsUnique); + Assert.Single(index.Expressions); + Assert.Contains("lower(Name)", index.Expressions); + } + + [Fact] + public void CheckConstraint_SerializesCorrectly() + { + // Arrange + var schema = Schema + .Define("check_test") + .Table( + "Products", + t => + t.Column("Id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column( + "Price", + PortableTypes.Decimal(10, 2), + c => c.NotNull().Check("Price >= 0") + ) + .Column("Quantity", PortableTypes.Int, c => c.NotNull()) + .Check("CK_Products_Quantity", "Quantity >= 0") + ) + .Build(); + + // Act + var yaml = SchemaYamlSerializer.ToYaml(schema); + var restored = SchemaYamlSerializer.FromYaml(yaml); + + // Assert + var table = restored.Tables[0]; + + var priceCol = table.Columns.First(c => c.Name == "Price"); + Assert.Equal("Price >= 0", priceCol.CheckConstraint); + + Assert.Single(table.CheckConstraints); + Assert.Equal("CK_Products_Quantity", table.CheckConstraints[0].Name); + Assert.Equal("Quantity >= 0", table.CheckConstraints[0].Expression); + } + + [Fact] + public void UniqueConstraint_SerializesCorrectly() + { + // Arrange + var schema = Schema + .Define("unique_test") + .Table( + "Users", + t => + t.Column("Id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("Email", PortableTypes.VarChar(255), c => c.NotNull()) + .Column("TenantId", PortableTypes.Uuid, c => c.NotNull()) + .Unique("UQ_Users_Email_Tenant", "Email", "TenantId") + ) + .Build(); + + // Act + var yaml = SchemaYamlSerializer.ToYaml(schema); + var restored = SchemaYamlSerializer.FromYaml(yaml); + + // Assert + var table = restored.Tables[0]; + Assert.Single(table.UniqueConstraints); + + var uc = table.UniqueConstraints[0]; + Assert.Equal("UQ_Users_Email_Tenant", uc.Name); + Assert.Equal(2, uc.Columns.Count); + Assert.Contains("Email", uc.Columns); + Assert.Contains("TenantId", uc.Columns); + } + + [Fact] + public void FromYamlFile_ValidFile_LoadsSchema() + { + // Arrange + var tempFile = Path.GetTempFileName(); + var yaml = """ + name: file_test + tables: + - name: TestTable + columns: + - name: Id + type: Int + isNullable: false + """; + File.WriteAllText(tempFile, yaml); + + try + { + // Act - read file content and parse + var yamlContent = File.ReadAllText(tempFile); + var schema = SchemaYamlSerializer.FromYaml(yamlContent); + + // Assert + Assert.Equal("file_test", schema.Name); + Assert.Single(schema.Tables); + Assert.Equal("TestTable", schema.Tables[0].Name); + } + finally + { + File.Delete(tempFile); + } + } + + [Fact] + public void ToYamlFile_WritesValidYaml() + { + // Arrange + var tempFile = Path.GetTempFileName(); + var schema = Schema + .Define("file_write_test") + .Table("Table1", t => t.Column("Id", PortableTypes.Uuid, c => c.PrimaryKey())) + .Build(); + + try + { + // Act - serialize to YAML and write to file + var yaml = SchemaYamlSerializer.ToYaml(schema); + File.WriteAllText(tempFile, yaml); + + // Assert + var content = File.ReadAllText(tempFile); + Assert.Contains("file_write_test", content); + Assert.Contains("Table1", content); + + // Verify it can be read back + var restoredYaml = File.ReadAllText(tempFile); + var restored = SchemaYamlSerializer.FromYaml(restoredYaml); + Assert.Equal("file_write_test", restored.Name); + } + finally + { + File.Delete(tempFile); + } + } + + [Fact] + public void FromYaml_EmptySchema_ReturnsEmptyDefinition() + { + // Arrange + var yaml = """ + name: empty + tables: [] + """; + + // Act + var schema = SchemaYamlSerializer.FromYaml(yaml); + + // Assert + Assert.Equal("empty", schema.Name); + Assert.Empty(schema.Tables); + } + + [Fact] + public void PortableType_EnumType_SerializesCorrectly() + { + // Arrange + var schema = Schema + .Define("enum_test") + .Table( + "Items", + t => + t.Column("Id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column( + "Status", + PortableTypes.Enum("item_status", "pending", "active", "archived") + ) + ) + .Build(); + + // Act + var yaml = SchemaYamlSerializer.ToYaml(schema); + var restored = SchemaYamlSerializer.FromYaml(yaml); + + // Assert + var statusCol = restored.Tables[0].Columns.First(c => c.Name == "Status"); + Assert.IsType(statusCol.Type); + + var enumType = (EnumType)statusCol.Type; + Assert.Equal("item_status", enumType.Name); + Assert.Equal(3, enumType.Values.Count); + Assert.Contains("pending", enumType.Values); + Assert.Contains("active", enumType.Values); + Assert.Contains("archived", enumType.Values); + } + + [Fact] + public void DefaultLqlExpression_SerializesCorrectly() + { + // Arrange + var schema = Schema + .Define("lql_test") + .Table( + "Events", + t => + t.Column("Id", PortableTypes.Uuid, c => c.PrimaryKey().DefaultLql("gen_uuid()")) + .Column( + "CreatedAt", + PortableTypes.DateTime(), + c => c.NotNull().DefaultLql("now()") + ) + .Column( + "IsActive", + PortableTypes.Boolean, + c => c.NotNull().DefaultLql("true") + ) + ) + .Build(); + + // Act + var yaml = SchemaYamlSerializer.ToYaml(schema); + var restored = SchemaYamlSerializer.FromYaml(yaml); + + // Assert + var idCol = restored.Tables[0].Columns.First(c => c.Name == "Id"); + Assert.Equal("gen_uuid()", idCol.DefaultLqlExpression); + + var createdCol = restored.Tables[0].Columns.First(c => c.Name == "CreatedAt"); + Assert.Equal("now()", createdCol.DefaultLqlExpression); + + var activeCol = restored.Tables[0].Columns.First(c => c.Name == "IsActive"); + Assert.Equal("true", activeCol.DefaultLqlExpression); + } + + [Fact] + public void IntegrationTest_YamlToSqlite_CreatesDatabaseSuccessfully() + { + // Arrange - Create schema from YAML + var yaml = """ + name: integration_test + tables: + - name: Users + schema: public + columns: + - name: Id + type: Uuid + isNullable: false + - name: Email + type: VarChar(255) + isNullable: false + - name: Name + type: Text + isNullable: true + primaryKey: + columns: + - Id + indexes: + - name: idx_users_email + columns: + - Email + isUnique: true + - name: Orders + schema: public + columns: + - name: Id + type: BigInt + isNullable: false + isIdentity: true + - name: UserId + type: Uuid + isNullable: false + - name: Total + type: Decimal(12,2) + isNullable: false + primaryKey: + columns: + - Id + foreignKeys: + - columns: + - UserId + referencedTable: Users + referencedSchema: public + referencedColumns: + - Id + onDelete: Cascade + onUpdate: NoAction + """; + + var schema = SchemaYamlSerializer.FromYaml(yaml); + + // Act - Apply to SQLite + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + foreach (var table in schema.Tables) + { + var ddl = SqliteDdlGenerator.Generate(new CreateTableOperation(table)); + using var cmd = connection.CreateCommand(); + cmd.CommandText = ddl; + cmd.ExecuteNonQuery(); + } + + // Assert - Verify tables exist + using var verifyCmd = connection.CreateCommand(); + verifyCmd.CommandText = + "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name IN ('Users', 'Orders')"; + var tableCount = Convert.ToInt32(verifyCmd.ExecuteScalar(), CultureInfo.InvariantCulture); + Assert.Equal(2, tableCount); + + // Verify index exists + verifyCmd.CommandText = + "SELECT COUNT(*) FROM sqlite_master WHERE type='index' AND name='idx_users_email'"; + var indexCount = Convert.ToInt32(verifyCmd.ExecuteScalar(), CultureInfo.InvariantCulture); + Assert.Equal(1, indexCount); + } + + [Fact] + public void ToYaml_OmitsSemanticDefaultValues() + { + // Arrange - Schema with all default values + var schema = Schema + .Define("test") + .Table( + "Users", + t => + t.Column("Id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("Name", PortableTypes.Text) // nullable by default + ) + .Build(); + + // Act + var yaml = SchemaYamlSerializer.ToYaml(schema); + + // Assert - These default values should NOT appear in the YAML + Assert.DoesNotContain("isNullable: true", yaml); + Assert.DoesNotContain("identitySeed", yaml); + Assert.DoesNotContain("identityIncrement", yaml); + Assert.DoesNotContain("isIdentity: false", yaml); + Assert.DoesNotContain("isComputedPersisted: false", yaml); + Assert.DoesNotContain("schema: public", yaml); + } +} diff --git a/Migration/Migration.Tests/SqliteMigrationTests.cs b/Migration/Migration.Tests/SqliteMigrationTests.cs index a8d4f3ba..4bb550c7 100644 --- a/Migration/Migration.Tests/SqliteMigrationTests.cs +++ b/Migration/Migration.Tests/SqliteMigrationTests.cs @@ -776,4 +776,838 @@ active INTEGER DEFAULT 1 Assert.Equal(4, restored.Tables[0].Columns.Count); Assert.Single(restored.Tables[0].Indexes); } + + // ============================================================================= + // Expression Index Tests + // ============================================================================= + + [Fact] + public void ExpressionIndex_CreateWithLowerFunction_Success() + { + // Arrange - Create table with expression index for case-insensitive uniqueness + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + var schema = Schema + .Define("Test") + .Table( + "Artists", + t => + t.Column("Id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("Name", PortableTypes.VarChar(200), c => c.NotNull()) + .ExpressionIndex("uq_artists_name", "lower(Name)", unique: true) + ) + .Build(); + + // Act + var emptySchema = ( + (SchemaResultOk)SqliteSchemaInspector.Inspect(connection, _logger) + ).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, schema, logger: _logger) + ).Value; + + var result = MigrationRunner.Apply( + connection, + operations, + SqliteDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + // Assert - Migration succeeded + Assert.True( + result is MigrationApplyResultOk, + $"Migration failed: {(result as MigrationApplyResultError)?.Value}" + ); + + // Verify index exists + using var cmd = connection.CreateCommand(); + cmd.CommandText = + "SELECT sql FROM sqlite_master WHERE type='index' AND name='uq_artists_name'"; + var indexDef = cmd.ExecuteScalar() as string; + + Assert.NotNull(indexDef); + Assert.Contains("UNIQUE", indexDef); + Assert.Contains("lower", indexDef); + } + + [Fact] + public void ExpressionIndex_EnforcesCaseInsensitiveUniqueness() + { + // Arrange - Create table with expression index + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + var schema = Schema + .Define("Test") + .Table( + "Venues", + t => + t.Column("Id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("Name", PortableTypes.VarChar(200), c => c.NotNull()) + .ExpressionIndex("uq_venues_name", "lower(Name)", unique: true) + ) + .Build(); + + var emptySchema = ( + (SchemaResultOk)SqliteSchemaInspector.Inspect(connection, _logger) + ).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, schema, logger: _logger) + ).Value; + _ = MigrationRunner.Apply( + connection, + operations, + SqliteDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + // Act - Insert first venue + using var insertCmd = connection.CreateCommand(); + insertCmd.CommandText = + "INSERT INTO Venues (Id, Name) VALUES ('11111111-1111-1111-1111-111111111111', 'The Corner Hotel')"; + insertCmd.ExecuteNonQuery(); + + // Try to insert duplicate with different case - should fail + using var duplicateCmd = connection.CreateCommand(); + duplicateCmd.CommandText = + "INSERT INTO Venues (Id, Name) VALUES ('22222222-2222-2222-2222-222222222222', 'THE CORNER HOTEL')"; + + // Assert - Should throw unique constraint violation + var ex = Assert.Throws(() => duplicateCmd.ExecuteNonQuery()); + Assert.Contains("UNIQUE", ex.Message); + } + + [Fact] + public void ExpressionIndex_MultiExpression_CompositeIndexSuccess() + { + // Arrange - Create table with multi-expression index + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + var schema = Schema + .Define("Test") + .Table( + "Suburbs", + t => + t.Column("Id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("Name", PortableTypes.VarChar(100), c => c.NotNull()) + ) + .Table( + "Places", + t => + t.Column("Id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("Name", PortableTypes.VarChar(200), c => c.NotNull()) + .Column("SuburbId", PortableTypes.Uuid, c => c.NotNull()) + .ExpressionIndex( + "uq_places_name_suburb", + ["lower(Name)", "SuburbId"], + unique: true + ) + ) + .Build(); + + // Act + var emptySchema = ( + (SchemaResultOk)SqliteSchemaInspector.Inspect(connection, _logger) + ).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, schema, logger: _logger) + ).Value; + + var result = MigrationRunner.Apply( + connection, + operations, + SqliteDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + // Assert + Assert.True( + result is MigrationApplyResultOk, + $"Migration failed: {(result as MigrationApplyResultError)?.Value}" + ); + + // Verify composite expression index exists + using var cmd = connection.CreateCommand(); + cmd.CommandText = + "SELECT sql FROM sqlite_master WHERE type='index' AND name='uq_places_name_suburb'"; + var indexDef = cmd.ExecuteScalar() as string; + + Assert.NotNull(indexDef); + Assert.Contains("UNIQUE", indexDef); + Assert.Contains("lower", indexDef); + Assert.Contains("SuburbId", indexDef); + } + + [Fact] + public void ExpressionIndex_Idempotent_NoErrorOnRerun() + { + // Arrange + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + var schema = Schema + .Define("Test") + .Table( + "Bands", + t => + t.Column("Id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("Name", PortableTypes.VarChar(200), c => c.NotNull()) + .ExpressionIndex("uq_bands_name", "lower(Name)", unique: true) + ) + .Build(); + + // Act - Run migration twice + for (var i = 0; i < 2; i++) + { + var currentSchema = ( + (SchemaResultOk)SqliteSchemaInspector.Inspect(connection, _logger) + ).Value; + + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(currentSchema, schema, logger: _logger) + ).Value; + + var result = MigrationRunner.Apply( + connection, + operations, + SqliteDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + Assert.True( + result is MigrationApplyResultOk, + $"Migration {i + 1} failed: {(result as MigrationApplyResultError)?.Value}" + ); + + // Second run should have 0 operations (schema already matches) + if (i == 1) + { + Assert.Empty(operations); + } + } + } + + [Fact] + public void ExpressionIndex_SchemaInspector_DetectsExpressionIndex() + { + // Arrange - Create expression index via raw SQL + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + using var cmd = connection.CreateCommand(); + cmd.CommandText = """ + CREATE TABLE artists ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL + ); + CREATE UNIQUE INDEX uq_artists_name ON artists(lower(name)); + """; + cmd.ExecuteNonQuery(); + + // Act - Inspect schema + var result = SqliteSchemaInspector.Inspect(connection, _logger); + + // Assert + Assert.True(result is SchemaResultOk); + var schema = ((SchemaResultOk)result).Value; + var artists = schema.Tables.Single(t => t.Name == "artists"); + + Assert.Single(artists.Indexes); + var index = artists.Indexes[0]; + Assert.Equal("uq_artists_name", index.Name); + Assert.True(index.IsUnique); + Assert.NotEmpty(index.Expressions); + Assert.Contains("lower(name)", index.Expressions); + } + + // ============================================================================= + // Index Conversion Tests (Column <-> Expression) + // ============================================================================= + + [Fact] + public void UpgradeIndex_ColumnToExpression_RequiresDropAndCreate() + { + // Arrange - Create table with regular column index + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + var v1 = Schema + .Define("Test") + .Table( + "Artists", + t => + t.Column("Id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("Name", PortableTypes.VarChar(200), c => c.NotNull()) + .Index("idx_artists_name", "Name", unique: true) + ) + .Build(); + + // Apply v1 + var emptySchema = ( + (SchemaResultOk)SqliteSchemaInspector.Inspect(connection, _logger) + ).Value; + var v1Ops = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, v1, logger: _logger) + ).Value; + _ = MigrationRunner.Apply( + connection, + v1Ops, + SqliteDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + // v2 changes to expression index (different name since it's semantically different) + var v2 = Schema + .Define("Test") + .Table( + "Artists", + t => + t.Column("Id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("Name", PortableTypes.VarChar(200), c => c.NotNull()) + .ExpressionIndex("uq_artists_name_ci", "lower(Name)", unique: true) + ) + .Build(); + + // Act - Calculate upgrade operations + var currentSchema = ( + (SchemaResultOk)SqliteSchemaInspector.Inspect(connection, _logger) + ).Value; + var upgradeOps = ( + (OperationsResultOk) + SchemaDiff.Calculate(currentSchema, v2, allowDestructive: true, logger: _logger) + ).Value; + + // Assert - Should have drop old index + create new expression index + Assert.Equal(2, upgradeOps.Count); + Assert.Contains(upgradeOps, op => op is DropIndexOperation); + Assert.Contains(upgradeOps, op => op is CreateIndexOperation); + + // Apply the upgrade + var result = MigrationRunner.Apply( + connection, + upgradeOps, + SqliteDdlGenerator.Generate, + MigrationOptions.Destructive, + _logger + ); + + Assert.True(result is MigrationApplyResultOk); + + // Verify new expression index exists + using var cmd = connection.CreateCommand(); + cmd.CommandText = + "SELECT sql FROM sqlite_master WHERE type='index' AND name='uq_artists_name_ci'"; + var indexDef = cmd.ExecuteScalar() as string; + Assert.NotNull(indexDef); + Assert.Contains("lower", indexDef); + } + + [Fact] + public void UpgradeIndex_ExpressionToColumn_RequiresDropAndCreate() + { + // Arrange - Create table with expression index + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + var v1 = Schema + .Define("Test") + .Table( + "Venues", + t => + t.Column("Id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("Name", PortableTypes.VarChar(200), c => c.NotNull()) + .ExpressionIndex("uq_venues_name", "lower(Name)", unique: true) + ) + .Build(); + + // Apply v1 + var emptySchema = ( + (SchemaResultOk)SqliteSchemaInspector.Inspect(connection, _logger) + ).Value; + var v1Ops = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, v1, logger: _logger) + ).Value; + _ = MigrationRunner.Apply( + connection, + v1Ops, + SqliteDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + // v2 changes back to simple column index (different name) + var v2 = Schema + .Define("Test") + .Table( + "Venues", + t => + t.Column("Id", PortableTypes.Uuid, c => c.PrimaryKey()) + .Column("Name", PortableTypes.VarChar(200), c => c.NotNull()) + .Index("idx_venues_name", "Name", unique: true) + ) + .Build(); + + // Act + var currentSchema = ( + (SchemaResultOk)SqliteSchemaInspector.Inspect(connection, _logger) + ).Value; + var upgradeOps = ( + (OperationsResultOk) + SchemaDiff.Calculate(currentSchema, v2, allowDestructive: true, logger: _logger) + ).Value; + + // Assert - Should have drop + create + Assert.Equal(2, upgradeOps.Count); + Assert.Contains(upgradeOps, op => op is DropIndexOperation); + Assert.Contains(upgradeOps, op => op is CreateIndexOperation); + + var result = MigrationRunner.Apply( + connection, + upgradeOps, + SqliteDdlGenerator.Generate, + MigrationOptions.Destructive, + _logger + ); + + Assert.True(result is MigrationApplyResultOk); + + // Verify new column index exists (no lower() function) + using var cmd = connection.CreateCommand(); + cmd.CommandText = + "SELECT sql FROM sqlite_master WHERE type='index' AND name='idx_venues_name'"; + var indexDef = cmd.ExecuteScalar() as string; + Assert.NotNull(indexDef); + Assert.DoesNotContain("lower", indexDef); + } + + // ============================================================================= + // LQL Default Value Tests - Platform-Independent Defaults + // ============================================================================= + + [Fact] + public void LqlDefault_NowFunction_TranslatesToCurrentTimestamp() + { + // Arrange + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + var schema = Schema + .Define("Test") + .Table( + "events", + t => + t.Column("id", PortableTypes.Int, c => c.PrimaryKey()) + .Column( + "created_at", + PortableTypes.DateTime(), + c => c.NotNull().DefaultLql("now()") + ) + ) + .Build(); + + // Act + var emptySchema = ( + (SchemaResultOk)SqliteSchemaInspector.Inspect(connection, _logger) + ).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, schema, logger: _logger) + ).Value; + var result = MigrationRunner.Apply( + connection, + operations, + SqliteDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + // Assert + Assert.True(result is MigrationApplyResultOk); + + // Verify table DDL contains datetime('now') - the SQLite translation of now() + using var cmd = connection.CreateCommand(); + cmd.CommandText = "SELECT sql FROM sqlite_master WHERE type='table' AND name='events'"; + var tableDef = cmd.ExecuteScalar() as string; + Assert.NotNull(tableDef); + Assert.Contains("(datetime('now'))", tableDef); + + // Insert and verify default works + using var insertCmd = connection.CreateCommand(); + insertCmd.CommandText = "INSERT INTO events (id) VALUES (1)"; + insertCmd.ExecuteNonQuery(); + + using var selectCmd = connection.CreateCommand(); + selectCmd.CommandText = "SELECT created_at FROM events WHERE id = 1"; + var createdAt = selectCmd.ExecuteScalar() as string; + Assert.NotNull(createdAt); + Assert.NotEmpty(createdAt); + } + + [Fact] + public void LqlDefault_BooleanTrue_TranslatesTo1() + { + // Arrange + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + var schema = Schema + .Define("Test") + .Table( + "flags", + t => + t.Column("id", PortableTypes.Int, c => c.PrimaryKey()) + .Column( + "is_active", + PortableTypes.Boolean, + c => c.NotNull().DefaultLql("true") + ) + .Column( + "is_deleted", + PortableTypes.Boolean, + c => c.NotNull().DefaultLql("false") + ) + ) + .Build(); + + // Act + var emptySchema = ( + (SchemaResultOk)SqliteSchemaInspector.Inspect(connection, _logger) + ).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, schema, logger: _logger) + ).Value; + var result = MigrationRunner.Apply( + connection, + operations, + SqliteDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + // Assert + Assert.True(result is MigrationApplyResultOk); + + // Verify table DDL contains 1 and 0 for boolean defaults + using var cmd = connection.CreateCommand(); + cmd.CommandText = "SELECT sql FROM sqlite_master WHERE type='table' AND name='flags'"; + var tableDef = cmd.ExecuteScalar() as string; + Assert.NotNull(tableDef); + Assert.Contains("DEFAULT 1", tableDef); // true -> 1 + Assert.Contains("DEFAULT 0", tableDef); // false -> 0 + + // Insert and verify defaults work + using var insertCmd = connection.CreateCommand(); + insertCmd.CommandText = "INSERT INTO flags (id) VALUES (1)"; + insertCmd.ExecuteNonQuery(); + + using var selectCmd = connection.CreateCommand(); + selectCmd.CommandText = "SELECT is_active, is_deleted FROM flags WHERE id = 1"; + using var reader = selectCmd.ExecuteReader(); + Assert.True(reader.Read()); + Assert.Equal(1, reader.GetInt64(0)); // is_active = true = 1 + Assert.Equal(0, reader.GetInt64(1)); // is_deleted = false = 0 + } + + [Fact] + public void LqlDefault_NumericValues_PassThrough() + { + // Arrange + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + var schema = Schema + .Define("Test") + .Table( + "counters", + t => + t.Column("id", PortableTypes.Int, c => c.PrimaryKey()) + .Column("count", PortableTypes.Int, c => c.NotNull().DefaultLql("0")) + .Column("priority", PortableTypes.Int, c => c.NotNull().DefaultLql("100")) + .Column( + "rate", + PortableTypes.Decimal(5, 2), + c => c.NotNull().DefaultLql("1.5") + ) + ) + .Build(); + + // Act + var emptySchema = ( + (SchemaResultOk)SqliteSchemaInspector.Inspect(connection, _logger) + ).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, schema, logger: _logger) + ).Value; + var result = MigrationRunner.Apply( + connection, + operations, + SqliteDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + // Assert + Assert.True(result is MigrationApplyResultOk); + + // Insert and verify defaults work + using var insertCmd = connection.CreateCommand(); + insertCmd.CommandText = "INSERT INTO counters (id) VALUES (1)"; + insertCmd.ExecuteNonQuery(); + + using var selectCmd = connection.CreateCommand(); + selectCmd.CommandText = "SELECT count, priority, rate FROM counters WHERE id = 1"; + using var reader = selectCmd.ExecuteReader(); + Assert.True(reader.Read()); + Assert.Equal(0, reader.GetInt64(0)); + Assert.Equal(100, reader.GetInt64(1)); + Assert.Equal(1.5, reader.GetDouble(2), 2); + } + + [Fact] + public void LqlDefault_StringLiteral_PassThrough() + { + // Arrange + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + var schema = Schema + .Define("Test") + .Table( + "items", + t => + t.Column("id", PortableTypes.Int, c => c.PrimaryKey()) + .Column( + "status", + PortableTypes.VarChar(20), + c => c.NotNull().DefaultLql("'pending'") + ) + .Column( + "category", + PortableTypes.VarChar(50), + c => c.DefaultLql("'uncategorized'") + ) + ) + .Build(); + + // Act + var emptySchema = ( + (SchemaResultOk)SqliteSchemaInspector.Inspect(connection, _logger) + ).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, schema, logger: _logger) + ).Value; + var result = MigrationRunner.Apply( + connection, + operations, + SqliteDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + // Assert + Assert.True(result is MigrationApplyResultOk); + + // Insert and verify defaults work + using var insertCmd = connection.CreateCommand(); + insertCmd.CommandText = "INSERT INTO items (id) VALUES (1)"; + insertCmd.ExecuteNonQuery(); + + using var selectCmd = connection.CreateCommand(); + selectCmd.CommandText = "SELECT status, category FROM items WHERE id = 1"; + using var reader = selectCmd.ExecuteReader(); + Assert.True(reader.Read()); + Assert.Equal("pending", reader.GetString(0)); + Assert.Equal("uncategorized", reader.GetString(1)); + } + + [Fact] + public void LqlDefault_GenUuid_GeneratesValidUuidFormat() + { + // Arrange + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + var schema = Schema + .Define("Test") + .Table( + "records", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey().DefaultLql("gen_uuid()")) + .Column("name", PortableTypes.VarChar(100)) + ) + .Build(); + + // Act + var emptySchema = ( + (SchemaResultOk)SqliteSchemaInspector.Inspect(connection, _logger) + ).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, schema, logger: _logger) + ).Value; + var result = MigrationRunner.Apply( + connection, + operations, + SqliteDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + // Assert + Assert.True(result is MigrationApplyResultOk); + + // Insert multiple rows and verify UUIDs are generated and unique + using var insertCmd = connection.CreateCommand(); + insertCmd.CommandText = "INSERT INTO records (name) VALUES ('test1'), ('test2'), ('test3')"; + insertCmd.ExecuteNonQuery(); + + using var selectCmd = connection.CreateCommand(); + selectCmd.CommandText = "SELECT id FROM records"; + using var reader = selectCmd.ExecuteReader(); + + var uuids = new List(); + while (reader.Read()) + { + var uuid = reader.GetString(0); + Assert.NotNull(uuid); + Assert.Matches( + @"^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", + uuid + ); + uuids.Add(uuid); + } + + // All UUIDs should be unique + Assert.Equal(3, uuids.Count); + Assert.Equal(3, uuids.Distinct().Count()); + } + + [Fact] + public void LqlDefault_CurrentDate_ReturnsDateOnly() + { + // Arrange + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + var schema = Schema + .Define("Test") + .Table( + "logs", + t => + t.Column("id", PortableTypes.Int, c => c.PrimaryKey()) + .Column( + "log_date", + PortableTypes.Date, + c => c.NotNull().DefaultLql("current_date()") + ) + ) + .Build(); + + // Act + var emptySchema = ( + (SchemaResultOk)SqliteSchemaInspector.Inspect(connection, _logger) + ).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, schema, logger: _logger) + ).Value; + var result = MigrationRunner.Apply( + connection, + operations, + SqliteDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + // Assert + Assert.True(result is MigrationApplyResultOk); + + // Insert and verify default date + using var insertCmd = connection.CreateCommand(); + insertCmd.CommandText = "INSERT INTO logs (id) VALUES (1)"; + insertCmd.ExecuteNonQuery(); + + using var selectCmd = connection.CreateCommand(); + selectCmd.CommandText = "SELECT log_date FROM logs WHERE id = 1"; + var logDate = selectCmd.ExecuteScalar() as string; + Assert.NotNull(logDate); + Assert.Matches(@"^\d{4}-\d{2}-\d{2}$", logDate); // YYYY-MM-DD format + } + + [Fact] + public void LqlDefault_MixedDefaults_AllWorkTogether() + { + // Arrange - A complex table with multiple LQL defaults + using var connection = new SqliteConnection("Data Source=:memory:"); + connection.Open(); + + var schema = Schema + .Define("Test") + .Table( + "orders", + t => + t.Column("id", PortableTypes.Uuid, c => c.PrimaryKey().DefaultLql("gen_uuid()")) + .Column( + "status", + PortableTypes.VarChar(20), + c => c.NotNull().DefaultLql("'pending'") + ) + .Column("quantity", PortableTypes.Int, c => c.NotNull().DefaultLql("1")) + .Column( + "is_urgent", + PortableTypes.Boolean, + c => c.NotNull().DefaultLql("false") + ) + .Column( + "created_at", + PortableTypes.DateTime(), + c => c.NotNull().DefaultLql("now()") + ) + ) + .Build(); + + // Act + var emptySchema = ( + (SchemaResultOk)SqliteSchemaInspector.Inspect(connection, _logger) + ).Value; + var operations = ( + (OperationsResultOk)SchemaDiff.Calculate(emptySchema, schema, logger: _logger) + ).Value; + var result = MigrationRunner.Apply( + connection, + operations, + SqliteDdlGenerator.Generate, + MigrationOptions.Default, + _logger + ); + + // Assert + Assert.True(result is MigrationApplyResultOk); + + // Insert with no columns specified - all defaults should apply + using var insertCmd = connection.CreateCommand(); + insertCmd.CommandText = "INSERT INTO orders DEFAULT VALUES"; + insertCmd.ExecuteNonQuery(); + + using var selectCmd = connection.CreateCommand(); + selectCmd.CommandText = "SELECT id, status, quantity, is_urgent, created_at FROM orders"; + using var reader = selectCmd.ExecuteReader(); + Assert.True(reader.Read()); + + var id = reader.GetString(0); + var status = reader.GetString(1); + var quantity = reader.GetInt64(2); + var isUrgent = reader.GetInt64(3); + var createdAt = reader.GetString(4); + + Assert.Matches(@"^[0-9a-f-]{36}$", id); // UUID format + Assert.Equal("pending", status); // String default + Assert.Equal(1, quantity); // Numeric default + Assert.Equal(0, isUrgent); // Boolean false = 0 + Assert.NotEmpty(createdAt); // Timestamp generated + } } diff --git a/Migration/Migration/LqlDefaultTranslator.cs b/Migration/Migration/LqlDefaultTranslator.cs new file mode 100644 index 00000000..68a85522 --- /dev/null +++ b/Migration/Migration/LqlDefaultTranslator.cs @@ -0,0 +1,159 @@ +using System.Globalization; +using System.Text.RegularExpressions; + +namespace Migration; + +/// +/// Translates LQL default expressions to platform-specific SQL. +/// Provides consistent behavior across PostgreSQL and SQLite. +/// +public static partial class LqlDefaultTranslator +{ + /// + /// Translates an LQL default expression to PostgreSQL SQL. + /// + /// The LQL expression (e.g., "now()", "gen_uuid()"). + /// PostgreSQL-specific SQL expression. + public static string ToPostgres(string lqlExpression) + { + ArgumentNullException.ThrowIfNull(lqlExpression); + + var normalized = lqlExpression.Trim().ToLowerInvariant(); + + // Handle common LQL functions + return normalized switch + { + // Timestamp functions + "now()" => "CURRENT_TIMESTAMP", + "current_timestamp()" => "CURRENT_TIMESTAMP", + "current_date()" => "CURRENT_DATE", + "current_time()" => "CURRENT_TIME", + + // UUID generation + "gen_uuid()" => "gen_random_uuid()", + "uuid()" => "gen_random_uuid()", + + // Boolean literals + "true" => "true", + "false" => "false", + + // Numeric literals (pass through) + var n when int.TryParse(n, out _) => n, + var d when double.TryParse(d, CultureInfo.InvariantCulture, out _) => d, + + // String literals (already quoted with single quotes) + var s when s.StartsWith('\'') && s.EndsWith('\'') => s, + + // Function calls (lower, upper, coalesce, etc.) + _ => TranslateFunctionCall(lqlExpression, ToPostgresFunction), + }; + } + + /// + /// Translates an LQL default expression to SQLite SQL. + /// + /// The LQL expression (e.g., "now()", "gen_uuid()"). + /// SQLite-specific SQL expression. + public static string ToSqlite(string lqlExpression) + { + ArgumentNullException.ThrowIfNull(lqlExpression); + + var normalized = lqlExpression.Trim().ToLowerInvariant(); + + // Handle common LQL functions + return normalized switch + { + // Timestamp functions - SQLite uses datetime/date/time functions + "now()" => "(datetime('now'))", + "current_timestamp()" => "CURRENT_TIMESTAMP", + "current_date()" => "(date('now'))", + "current_time()" => "(time('now'))", + + // UUID generation - SQLite needs manual UUID v4 construction + "gen_uuid()" => UuidV4SqliteExpression, + "uuid()" => UuidV4SqliteExpression, + + // Boolean literals - SQLite uses 0/1 + "true" => "1", + "false" => "0", + + // Numeric literals (pass through) + var n when int.TryParse(n, out _) => n, + var d when double.TryParse(d, CultureInfo.InvariantCulture, out _) => d, + + // String literals (already quoted with single quotes) + var s when s.StartsWith('\'') && s.EndsWith('\'') => s, + + // Function calls + _ => TranslateFunctionCall(lqlExpression, ToSqliteFunction), + }; + } + + // SQLite UUID v4 generation expression + private const string UuidV4SqliteExpression = + "(lower(hex(randomblob(4))) || '-' || lower(hex(randomblob(2))) || '-4' || " + + "substr(lower(hex(randomblob(2))),2) || '-' || " + + "substr('89ab',abs(random()) % 4 + 1, 1) || substr(lower(hex(randomblob(2))),2) || '-' || " + + "lower(hex(randomblob(6))))"; + + private static string TranslateFunctionCall( + string expression, + Func functionTranslator + ) + { + // Match function calls like: lower(name), coalesce(a, b), substring(text, 1, 5) + var match = FunctionCallRegex().Match(expression); + if (!match.Success) + { + // Not a function call, return as-is (could be a literal or column reference) + return expression; + } + + var functionName = match.Groups["func"].Value.ToLowerInvariant(); + var argsString = match.Groups["args"].Value; + + // Parse arguments (simple split, doesn't handle nested functions with commas) + var args = string.IsNullOrWhiteSpace(argsString) + ? [] + : argsString.Split(',').Select(a => a.Trim()).ToArray(); + + return functionTranslator(functionName, args); + } + + private static string ToPostgresFunction(string functionName, string[] args) => + functionName switch + { + "lower" => $"lower({string.Join(", ", args)})", + "upper" => $"upper({string.Join(", ", args)})", + "coalesce" => $"COALESCE({string.Join(", ", args)})", + "length" => $"length({string.Join(", ", args)})", + "substring" => args.Length >= 3 + ? $"substring({args[0]} from {args[1]} for {args[2]})" + : $"substring({string.Join(", ", args)})", + "trim" => $"trim({string.Join(", ", args)})", + "concat" => $"concat({string.Join(", ", args)})", + "abs" => $"abs({string.Join(", ", args)})", + "round" => $"round({string.Join(", ", args)})", + _ => $"{functionName}({string.Join(", ", args)})", + }; + + private static string ToSqliteFunction(string functionName, string[] args) => + functionName switch + { + "lower" => $"lower({string.Join(", ", args)})", + "upper" => $"upper({string.Join(", ", args)})", + "coalesce" => $"coalesce({string.Join(", ", args)})", + "length" => $"length({string.Join(", ", args)})", + "substring" => args.Length >= 3 + ? $"substr({args[0]}, {args[1]}, {args[2]})" + : $"substr({string.Join(", ", args)})", + "trim" => $"trim({string.Join(", ", args)})", + "concat" => args.Length > 0 ? string.Join(" || ", args) : "''", + "abs" => $"abs({string.Join(", ", args)})", + "round" => $"round({string.Join(", ", args)})", + _ => $"{functionName}({string.Join(", ", args)})", + }; + + [GeneratedRegex(@"^(?\w+)\s*\((?.*)\)$", RegexOptions.Singleline)] + private static partial Regex FunctionCallRegex(); +} diff --git a/Migration/Migration/Migration.csproj b/Migration/Migration/Migration.csproj index 3dfe7757..bee03717 100644 --- a/Migration/Migration/Migration.csproj +++ b/Migration/Migration/Migration.csproj @@ -4,12 +4,13 @@ Library Migration - $(NoWarn);CA1848;CA2254;CA1720;CA1724 + $(NoWarn);CA2254;CA1720;CA1724;RS1035 + diff --git a/Migration/Migration/PortableDefaults.cs b/Migration/Migration/PortableDefaults.cs new file mode 100644 index 00000000..b1693cbe --- /dev/null +++ b/Migration/Migration/PortableDefaults.cs @@ -0,0 +1,149 @@ +namespace Migration; + +/// +/// Platform-independent default value expression. +/// These get translated to the correct SQL for each database platform. +/// +public abstract record PortableDefault +{ + /// + /// Prevents external inheritance - this makes the type hierarchy "closed". + /// + private protected PortableDefault() { } +} + +// ═══════════════════════════════════════════════════════════════════ +// LITERAL DEFAULTS - Exact values +// ═══════════════════════════════════════════════════════════════════ + +/// +/// A literal SQL expression to use as-is (no translation). +/// Use this for platform-specific expressions when needed. +/// +/// The raw SQL expression +public sealed record LiteralDefault(string Expression) : PortableDefault; + +/// +/// A string literal value, properly quoted. +/// +/// The string value +public sealed record StringDefault(string Value) : PortableDefault; + +/// +/// An integer literal value. +/// +/// The integer value +public sealed record IntDefault(long Value) : PortableDefault; + +/// +/// A decimal/floating point literal value. +/// +/// The decimal value +public sealed record DecimalDefault(decimal Value) : PortableDefault; + +/// +/// A boolean literal value. +/// Maps to true/false on Postgres, 1/0 on SQLite. +/// +/// The boolean value +public sealed record BoolDefault(bool Value) : PortableDefault; + +// ═══════════════════════════════════════════════════════════════════ +// FUNCTION DEFAULTS - Database functions +// ═══════════════════════════════════════════════════════════════════ + +/// +/// Generate a new random UUID. +/// Maps to gen_random_uuid() on Postgres, a UUID-generating expression on SQLite. +/// +public sealed record NewUuidDefault : PortableDefault; + +/// +/// Current timestamp (without timezone). +/// Maps to CURRENT_TIMESTAMP on both platforms. +/// +public sealed record CurrentTimestampDefault : PortableDefault; + +/// +/// Current timestamp with timezone. +/// Maps to CURRENT_TIMESTAMP on Postgres (TIMESTAMPTZ), CURRENT_TIMESTAMP on SQLite (stored as text). +/// +public sealed record CurrentTimestampTzDefault : PortableDefault; + +/// +/// Current date (no time component). +/// Maps to CURRENT_DATE on both platforms. +/// +public sealed record CurrentDateDefault : PortableDefault; + +/// +/// Current time (no date component). +/// Maps to CURRENT_TIME on both platforms. +/// +public sealed record CurrentTimeDefault : PortableDefault; + +/// +/// Null as default (for nullable columns). +/// Explicit NULL default. +/// +public sealed record NullDefault : PortableDefault; + +// ═══════════════════════════════════════════════════════════════════ +// SEQUENCE DEFAULTS - For auto-generated values +// ═══════════════════════════════════════════════════════════════════ + +/// +/// Next value from a named sequence. +/// Maps to nextval('sequence_name') on Postgres. +/// SQLite: Not supported, use AUTOINCREMENT instead. +/// +/// Name of the sequence +public sealed record NextSequenceDefault(string SequenceName) : PortableDefault; + +/// +/// Factory methods for portable defaults. +/// +public static class PortableDefaults +{ + /// Literal SQL expression (no translation). + public static LiteralDefault Literal(string expression) => new(expression); + + /// String literal, properly quoted. + public static StringDefault String(string value) => new(value); + + /// Integer literal. + public static IntDefault Int(long value) => new(value); + + /// Decimal literal. + public static DecimalDefault Decimal(decimal value) => new(value); + + /// Boolean literal (true/false on Postgres, 1/0 on SQLite). + public static BoolDefault Bool(bool value) => new(value); + + /// Boolean true. + public static BoolDefault True => new(true); + + /// Boolean false. + public static BoolDefault False => new(false); + + /// Generate a new random UUID. + public static NewUuidDefault NewUuid => new(); + + /// Current timestamp (without timezone). + public static CurrentTimestampDefault CurrentTimestamp => new(); + + /// Current timestamp with timezone. + public static CurrentTimestampTzDefault CurrentTimestampTz => new(); + + /// Current date only. + public static CurrentDateDefault CurrentDate => new(); + + /// Current time only. + public static CurrentTimeDefault CurrentTime => new(); + + /// Explicit NULL default. + public static NullDefault Null => new(); + + /// Next value from a sequence. + public static NextSequenceDefault NextSequence(string sequenceName) => new(sequenceName); +} diff --git a/Migration/Migration/SchemaBuilder.cs b/Migration/Migration/SchemaBuilder.cs index 68ba6b37..18af9be6 100644 --- a/Migration/Migration/SchemaBuilder.cs +++ b/Migration/Migration/SchemaBuilder.cs @@ -154,6 +154,52 @@ public TableBuilder Index( return this; } + /// + /// Add an expression-based index (e.g., lower(name) for case-insensitive matching). + /// Expressions are emitted verbatim in the CREATE INDEX statement. + /// + public TableBuilder ExpressionIndex( + string name, + string expression, + bool unique = false, + string? filter = null + ) + { + _indexes.Add( + new IndexDefinition + { + Name = name, + Expressions = [expression], + IsUnique = unique, + Filter = filter, + } + ); + return this; + } + + /// + /// Add a multi-expression index (e.g., lower(name), suburb_id for composite expression indexes). + /// Expressions are emitted verbatim in the CREATE INDEX statement. + /// + public TableBuilder ExpressionIndex( + string name, + string[] expressions, + bool unique = false, + string? filter = null + ) + { + _indexes.Add( + new IndexDefinition + { + Name = name, + Expressions = expressions, + IsUnique = unique, + Filter = filter, + } + ); + return this; + } + /// /// Add a foreign key to the table. /// @@ -241,6 +287,7 @@ public sealed class ColumnBuilder private readonly PortableType _type; private bool _isNullable = true; private string? _defaultValue; + private string? _defaultLqlExpression; private bool _isIdentity; private long _identitySeed = 1; private long _identityIncrement = 1; @@ -277,7 +324,7 @@ public ColumnBuilder Nullable() } /// - /// Set default value expression. + /// Set default value expression (platform-specific SQL). /// public ColumnBuilder Default(string defaultValue) { @@ -285,6 +332,17 @@ public ColumnBuilder Default(string defaultValue) return this; } + /// + /// Set default value using LQL expression (platform-independent). + /// The expression will be translated to platform-specific SQL by DDL generators. + /// Common LQL functions: now(), gen_uuid(), lower(), upper(), coalesce(). + /// + public ColumnBuilder DefaultLql(string lqlExpression) + { + _defaultLqlExpression = lqlExpression; + return this; + } + /// /// Mark as identity/auto-increment column. /// @@ -351,6 +409,7 @@ internal ColumnDefinition Build() => Type = _type, IsNullable = _isNullable, DefaultValue = _defaultValue, + DefaultLqlExpression = _defaultLqlExpression, IsIdentity = _isIdentity, IdentitySeed = _identitySeed, IdentityIncrement = _identityIncrement, diff --git a/Migration/Migration/SchemaDefinition.cs b/Migration/Migration/SchemaDefinition.cs index 4707677b..4b61e768 100644 --- a/Migration/Migration/SchemaDefinition.cs +++ b/Migration/Migration/SchemaDefinition.cs @@ -62,6 +62,13 @@ public sealed record ColumnDefinition /// SQL default expression (platform-specific, e.g., "CURRENT_TIMESTAMP"). public string? DefaultValue { get; init; } + /// + /// LQL default expression (platform-independent, e.g., "now()", "gen_uuid()"). + /// When set, DDL generators translate this to platform-specific SQL. + /// Takes precedence over DefaultValue if both are set. + /// + public string? DefaultLqlExpression { get; init; } + /// Auto-increment/identity column. public bool IsIdentity { get; init; } @@ -125,15 +132,23 @@ public sealed record PrimaryKeyDefinition /// /// Index definition (unique or non-unique). +/// Supports both column-based indexes (Columns) and expression-based indexes (Expressions). +/// When Expressions is non-empty, it takes precedence over Columns. /// public sealed record IndexDefinition { /// Index name. public string Name { get; init; } = string.Empty; - /// Columns in the index. + /// Columns in the index (quoted as identifiers). public IReadOnlyList Columns { get; init; } = []; + /// + /// LQL/SQL expressions for expression-based indexes (e.g., "lower(name)"). + /// When non-empty, these are used instead of Columns and are emitted verbatim. + /// + public IReadOnlyList Expressions { get; init; } = []; + /// Whether the index enforces uniqueness. public bool IsUnique { get; init; } diff --git a/Migration/Migration/SchemaSerializer.cs b/Migration/Migration/SchemaSerializer.cs index 2bcbd53f..b7bbdb48 100644 --- a/Migration/Migration/SchemaSerializer.cs +++ b/Migration/Migration/SchemaSerializer.cs @@ -1,15 +1,15 @@ -namespace Migration; - using System.Text.Json; using System.Text.Json.Serialization; +namespace Migration; + /// -/// Serializes and deserializes schema definitions to/from JSON. +/// Serializes and deserializes schema definitions to/from JSON and YAML. /// Used for capturing existing database schemas and storing as metadata. /// public static class SchemaSerializer { - private static readonly JsonSerializerOptions Options = new() + private static readonly JsonSerializerOptions JsonOptions = new() { WriteIndented = true, PropertyNamingPolicy = JsonNamingPolicy.CamelCase, @@ -20,19 +20,35 @@ public static class SchemaSerializer /// /// Serialize a schema definition to JSON string. /// - /// Schema to serialize - /// JSON representation of the schema + /// Schema to serialize. + /// JSON representation of the schema. public static string ToJson(SchemaDefinition schema) => - JsonSerializer.Serialize(schema, Options); + JsonSerializer.Serialize(schema, JsonOptions); /// /// Deserialize a schema definition from JSON string. /// - /// JSON string - /// Deserialized schema definition + /// JSON string. + /// Deserialized schema definition. public static SchemaDefinition FromJson(string json) => - JsonSerializer.Deserialize(json, Options) + JsonSerializer.Deserialize(json, JsonOptions) ?? throw new JsonException("Failed to deserialize schema"); + + /// + /// Serialize a schema definition to YAML string. + /// Delegates to SchemaYamlSerializer. + /// + /// Schema to serialize. + /// YAML representation of the schema. + public static string ToYaml(SchemaDefinition schema) => SchemaYamlSerializer.ToYaml(schema); + + /// + /// Deserialize a schema definition from YAML string. + /// Delegates to SchemaYamlSerializer. + /// + /// YAML string. + /// Deserialized schema definition. + public static SchemaDefinition FromYaml(string yaml) => SchemaYamlSerializer.FromYaml(yaml); } /// diff --git a/Migration/Migration/SchemaYamlSerializer.cs b/Migration/Migration/SchemaYamlSerializer.cs new file mode 100644 index 00000000..03488c23 --- /dev/null +++ b/Migration/Migration/SchemaYamlSerializer.cs @@ -0,0 +1,327 @@ +using System.Globalization; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; +using YamlDotNet.Serialization; +using YamlDotNet.Serialization.NamingConventions; +using YamlDotNet.Serialization.ObjectGraphVisitors; + +namespace Migration; + +/// +/// Serializes and deserializes schema definitions to/from YAML. +/// Used for storing schema definitions as portable configuration files. +/// +public static class SchemaYamlSerializer +{ + private static readonly ISerializer Serializer = new SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .WithTypeConverter(new PortableTypeYamlConverter()) + .WithTypeConverter(new ForeignKeyActionYamlConverter()) + .ConfigureDefaultValuesHandling( + DefaultValuesHandling.OmitDefaults + | DefaultValuesHandling.OmitNull + | DefaultValuesHandling.OmitEmptyCollections + ) + .WithEmissionPhaseObjectGraphVisitor(args => new PropertyDefaultValueFilter( + args.InnerVisitor + )) + .DisableAliases() + .Build(); + + private static readonly IDeserializer Deserializer = new DeserializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .WithTypeConverter(new PortableTypeYamlConverter()) + .WithTypeConverter(new ForeignKeyActionYamlConverter()) + .WithTypeMapping, List>() + .WithTypeMapping, List>() + .WithTypeMapping, List>() + .WithTypeMapping, List>() + .WithTypeMapping< + IReadOnlyList, + List + >() + .WithTypeMapping< + IReadOnlyList, + List + >() + .WithTypeMapping, List>() + .Build(); + + /// + /// Serialize a schema definition to YAML string. + /// + /// Schema to serialize. + /// YAML representation of the schema. + public static string ToYaml(SchemaDefinition schema) => Serializer.Serialize(schema); + + /// + /// Deserialize a schema definition from YAML string. + /// + /// YAML string. + /// Deserialized schema definition. + public static SchemaDefinition FromYaml(string yaml) => + Deserializer.Deserialize(yaml) + ?? new SchemaDefinition { Name = string.Empty, Tables = [] }; + + /// + /// Load a schema definition from a YAML file. + /// + /// Path to YAML file. + /// Deserialized schema definition. + public static SchemaDefinition FromYamlFile(string filePath) + { + var yaml = File.ReadAllText(filePath); + return FromYaml(yaml); + } + + /// + /// Save a schema definition to a YAML file. + /// + /// Schema to save. + /// Path to YAML file. + public static void ToYamlFile(SchemaDefinition schema, string filePath) + { + var yaml = ToYaml(schema); + File.WriteAllText(filePath, yaml); + } +} + +/// +/// YAML type converter for PortableType discriminated union. +/// Serializes types as simple strings like "Text", "Int", "VarChar(255)". +/// +public sealed class PortableTypeYamlConverter : IYamlTypeConverter +{ + /// + public bool Accepts(Type type) => typeof(PortableType).IsAssignableFrom(type); + + /// + public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) + { + var scalar = parser.Consume(); + return ParseType(scalar.Value); + } + + /// + public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) + { + var typeStr = value switch + { + TinyIntType => "TinyInt", + SmallIntType => "SmallInt", + IntType => "Int", + BigIntType => "BigInt", + DecimalType d => $"Decimal({d.Precision},{d.Scale})", + FloatType => "Float", + DoubleType => "Double", + MoneyType => "Money", + SmallMoneyType => "SmallMoney", + BooleanType => "Boolean", + CharType c => $"Char({c.Length})", + VarCharType v => $"VarChar({v.MaxLength})", + NCharType nc => $"NChar({nc.Length})", + NVarCharType nv when nv.MaxLength == int.MaxValue => "NVarChar(max)", + NVarCharType nv => $"NVarChar({nv.MaxLength})", + TextType => "Text", + BinaryType b => $"Binary({b.Length})", + VarBinaryType vb when vb.MaxLength == int.MaxValue => "VarBinary(max)", + VarBinaryType vb => $"VarBinary({vb.MaxLength})", + BlobType => "Blob", + DateType => "Date", + TimeType t when t.Precision == 7 => "Time", + TimeType t => $"Time({t.Precision})", + DateTimeType dt when dt.Precision == 3 => "DateTime", + DateTimeType dt => $"DateTime({dt.Precision})", + DateTimeOffsetType => "DateTimeOffset", + UuidType => "Uuid", + JsonType => "Json", + XmlType => "Xml", + RowVersionType => "RowVersion", + GeometryType g when g.Srid.HasValue => $"Geometry({g.Srid})", + GeometryType => "Geometry", + GeographyType g when g.Srid == 4326 => "Geography", + GeographyType g => $"Geography({g.Srid})", + EnumType e => $"Enum({e.Name}:{string.Join("|", e.Values)})", + _ => "Text", + }; + emitter.Emit(new Scalar(typeStr)); + } + + private static PortableType ParseType(string typeStr) + { + var trimmed = typeStr.Trim(); + + // Handle parameterized types + var parenIndex = trimmed.IndexOf('(', StringComparison.Ordinal); + if (parenIndex > 0) + { + var typeName = trimmed[..parenIndex]; + var paramsStr = trimmed[(parenIndex + 1)..^1]; + + return typeName.ToUpperInvariant() switch + { + "DECIMAL" => ParseDecimal(paramsStr), + "CHAR" => new CharType(int.Parse(paramsStr, CultureInfo.InvariantCulture)), + "VARCHAR" => new VarCharType(ParseMaxLength(paramsStr)), + "NCHAR" => new NCharType(int.Parse(paramsStr, CultureInfo.InvariantCulture)), + "NVARCHAR" => new NVarCharType(ParseMaxLength(paramsStr)), + "BINARY" => new BinaryType(int.Parse(paramsStr, CultureInfo.InvariantCulture)), + "VARBINARY" => new VarBinaryType(ParseMaxLength(paramsStr)), + "TIME" => new TimeType(int.Parse(paramsStr, CultureInfo.InvariantCulture)), + "DATETIME" => new DateTimeType(int.Parse(paramsStr, CultureInfo.InvariantCulture)), + "GEOMETRY" => new GeometryType(int.Parse(paramsStr, CultureInfo.InvariantCulture)), + "GEOGRAPHY" => new GeographyType( + int.Parse(paramsStr, CultureInfo.InvariantCulture) + ), + "ENUM" => ParseEnum(paramsStr), + _ => new TextType(), + }; + } + + // Handle simple types + return trimmed.ToUpperInvariant() switch + { + "TINYINT" => new TinyIntType(), + "SMALLINT" => new SmallIntType(), + "INT" or "INTEGER" => new IntType(), + "BIGINT" => new BigIntType(), + "FLOAT" or "REAL" => new FloatType(), + "DOUBLE" => new DoubleType(), + "MONEY" => new MoneyType(), + "SMALLMONEY" => new SmallMoneyType(), + "BOOLEAN" or "BOOL" => new BooleanType(), + "TEXT" => new TextType(), + "BLOB" => new BlobType(), + "DATE" => new DateType(), + "TIME" => new TimeType(), + "DATETIME" => new DateTimeType(), + "DATETIMEOFFSET" => new DateTimeOffsetType(), + "UUID" or "GUID" => new UuidType(), + "JSON" or "JSONB" => new JsonType(), + "XML" => new XmlType(), + "ROWVERSION" or "TIMESTAMP" => new RowVersionType(), + "GEOMETRY" => new GeometryType(null), + "GEOGRAPHY" => new GeographyType(), + _ => new TextType(), + }; + } + + private static int ParseMaxLength(string s) => + s.Equals("max", StringComparison.OrdinalIgnoreCase) + ? int.MaxValue + : int.Parse(s, CultureInfo.InvariantCulture); + + private static DecimalType ParseDecimal(string paramsStr) + { + var parts = paramsStr.Split(','); + return parts.Length == 2 + ? new DecimalType( + int.Parse(parts[0].Trim(), CultureInfo.InvariantCulture), + int.Parse(parts[1].Trim(), CultureInfo.InvariantCulture) + ) + : new DecimalType(int.Parse(parts[0].Trim(), CultureInfo.InvariantCulture), 0); + } + + private static EnumType ParseEnum(string paramsStr) + { + var colonIndex = paramsStr.IndexOf(':', StringComparison.Ordinal); + if (colonIndex > 0) + { + var name = paramsStr[..colonIndex]; + var values = paramsStr[(colonIndex + 1)..].Split('|'); + return new EnumType(name, values); + } + + return new EnumType("enum", paramsStr.Split('|')); + } +} + +/// +/// YAML type converter for ForeignKeyAction enum. +/// +public sealed class ForeignKeyActionYamlConverter : IYamlTypeConverter +{ + /// + public bool Accepts(Type type) => type == typeof(ForeignKeyAction); + + /// + public object? ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) + { + var scalar = parser.Consume(); + return scalar.Value.ToUpperInvariant() switch + { + "NOACTION" or "NO_ACTION" or "NO ACTION" => ForeignKeyAction.NoAction, + "CASCADE" => ForeignKeyAction.Cascade, + "SETNULL" or "SET_NULL" or "SET NULL" => ForeignKeyAction.SetNull, + "SETDEFAULT" or "SET_DEFAULT" or "SET DEFAULT" => ForeignKeyAction.SetDefault, + "RESTRICT" => ForeignKeyAction.Restrict, + _ => ForeignKeyAction.NoAction, + }; + } + + /// + public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) + { + var action = (ForeignKeyAction)(value ?? ForeignKeyAction.NoAction); + var str = action switch + { + ForeignKeyAction.NoAction => "NoAction", + ForeignKeyAction.Cascade => "Cascade", + ForeignKeyAction.SetNull => "SetNull", + ForeignKeyAction.SetDefault => "SetDefault", + ForeignKeyAction.Restrict => "Restrict", + _ => "NoAction", + }; + emitter.Emit(new Scalar(str)); + } +} + +/// +/// Filters out properties that have their semantic default values. +/// This handles cases where the property initializer differs from the type default. +/// +internal sealed class PropertyDefaultValueFilter(IObjectGraphVisitor next) + : ChainedObjectGraphVisitor(next) +{ + /// + /// Default values per property name -> (expected value type, default value). + /// Uses camelCase names (after naming convention applied). + /// + private static readonly Dictionary SemanticDefaults = + new() + { + // ColumnDefinition semantic defaults + { "isNullable", (typeof(bool), true) }, + { "identitySeed", (typeof(long), 1L) }, + { "identityIncrement", (typeof(long), 1L) }, + // TableDefinition and ForeignKeyDefinition semantic defaults + { "schema", (typeof(string), "public") }, + { "referencedSchema", (typeof(string), "public") }, + { "onDelete", (typeof(ForeignKeyAction), ForeignKeyAction.NoAction) }, + { "onUpdate", (typeof(ForeignKeyAction), ForeignKeyAction.NoAction) }, + }; + + /// + public override bool EnterMapping( + IPropertyDescriptor key, + IObjectDescriptor value, + IEmitter context, + ObjectSerializer serializer + ) + { + if (SemanticDefaults.TryGetValue(key.Name, out var defaultInfo)) + { + // Match by property name and ensure value type matches expectation + if ( + value.Value != null + && defaultInfo.ValueType.IsAssignableFrom(value.Value.GetType()) + && Equals(value.Value, defaultInfo.Default) + ) + { + return false; + } + } + + return base.EnterMapping(key, value, context, serializer); + } +} diff --git a/Migration/Schema.Export.Cli/Program.cs b/Migration/Schema.Export.Cli/Program.cs new file mode 100644 index 00000000..753b5290 --- /dev/null +++ b/Migration/Schema.Export.Cli/Program.cs @@ -0,0 +1,240 @@ +using System.Reflection; +using Migration; + +namespace Schema.Export.Cli; + +/// +/// CLI tool to export C# schema definitions to YAML files. +/// Usage: dotnet run -- --assembly path/to/assembly.dll --type Namespace.SchemaClass --output path/to/schema.yaml +/// +public static class Program +{ + /// + /// Entry point - loads assembly, finds schema, exports to YAML. + /// + public static int Main(string[] args) + { + var parseResult = ParseArguments(args); + + return parseResult switch + { + ParseResult.Success success => ExportSchema(success), + ParseResult.ParseError error => ShowError(error), + ParseResult.Help => ShowUsage(), + }; + } + + private static int ExportSchema(ParseResult.Success args) + { + Console.WriteLine($"Schema.Export.Cli - Export C# Schema to YAML"); + Console.WriteLine($" Assembly: {args.AssemblyPath}"); + Console.WriteLine($" Type: {args.TypeName}"); + Console.WriteLine($" Output: {args.OutputPath}"); + Console.WriteLine(); + + if (!File.Exists(args.AssemblyPath)) + { + Console.WriteLine($"Error: Assembly not found: {args.AssemblyPath}"); + return 1; + } + + try + { + var assembly = Assembly.LoadFrom(args.AssemblyPath); + var schemaType = assembly.GetType(args.TypeName); + + if (schemaType is null) + { + Console.WriteLine($"Error: Type '{args.TypeName}' not found in assembly"); + return 1; + } + + // Try to find a static property or method that returns SchemaDefinition + var schema = GetSchemaDefinition(schemaType); + + if (schema is null) + { + Console.WriteLine( + $"Error: Could not get SchemaDefinition from type '{args.TypeName}'" + ); + Console.WriteLine( + " Expected: static property 'Definition' or static method 'Build()' returning SchemaDefinition" + ); + return 1; + } + + // Export to YAML + var directory = Path.GetDirectoryName(args.OutputPath); + if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + SchemaYamlSerializer.ToYamlFile(schema, args.OutputPath); + Console.WriteLine( + $"Successfully exported schema '{schema.Name}' with {schema.Tables.Count} tables" + ); + Console.WriteLine($" Output: {args.OutputPath}"); + return 0; + } + catch (Exception ex) + { + Console.WriteLine($"Error: {ex}"); + return 1; + } + } + + private static SchemaDefinition? GetSchemaDefinition(Type schemaType) + { + // Try static property named "Definition" + var definitionProp = schemaType.GetProperty( + "Definition", + BindingFlags.Public | BindingFlags.Static + ); + + if (definitionProp?.GetValue(null) is SchemaDefinition defFromProp) + { + return defFromProp; + } + + // Try static method named "Build" + var buildMethod = schemaType.GetMethod( + "Build", + BindingFlags.Public | BindingFlags.Static, + Type.EmptyTypes + ); + + if (buildMethod?.Invoke(null, null) is SchemaDefinition defFromMethod) + { + return defFromMethod; + } + + return null; + } + + private static int ShowError(ParseResult.ParseError error) + { + Console.WriteLine($"Error: {error.Message}"); + Console.WriteLine(); + return ShowUsage(); + } + + private static int ShowUsage() + { + Console.WriteLine("Schema.Export.Cli - Export C# Schema to YAML"); + Console.WriteLine(); + Console.WriteLine("Usage:"); + Console.WriteLine( + " dotnet run --project Migration/Schema.Export.Cli/Schema.Export.Cli.csproj -- \\" + ); + Console.WriteLine(" --assembly path/to/assembly.dll \\"); + Console.WriteLine(" --type Namespace.SchemaClass \\"); + Console.WriteLine(" --output path/to/schema.yaml"); + Console.WriteLine(); + Console.WriteLine("Options:"); + Console.WriteLine(" --assembly, -a Path to compiled assembly containing schema class"); + Console.WriteLine(" --type, -t Fully qualified type name of schema class"); + Console.WriteLine(" --output, -o Path to output YAML file"); + Console.WriteLine(); + Console.WriteLine("Examples:"); + Console.WriteLine(" # Export ExampleSchema"); + Console.WriteLine(" dotnet run -- -a bin/Debug/net9.0/DataProvider.Example.dll \\"); + Console.WriteLine(" -t DataProvider.Example.ExampleSchema \\"); + Console.WriteLine(" -o example-schema.yaml"); + Console.WriteLine(); + Console.WriteLine("Schema Class Requirements:"); + Console.WriteLine(" - Static property 'Definition' returning SchemaDefinition, OR"); + Console.WriteLine(" - Static method 'Build()' returning SchemaDefinition"); + return 1; + } + + private static ParseResult ParseArguments(string[] args) + { + string? assemblyPath = null; + string? typeName = null; + string? outputPath = null; + + for (var i = 0; i < args.Length; i++) + { + var arg = args[i]; + + switch (arg) + { + case "--assembly" + or "-a": + if (i + 1 >= args.Length) + { + return new ParseResult.ParseError("--assembly requires a path argument"); + } + + assemblyPath = args[++i]; + break; + + case "--type" + or "-t": + if (i + 1 >= args.Length) + { + return new ParseResult.ParseError("--type requires a type name argument"); + } + + typeName = args[++i]; + break; + + case "--output" + or "-o": + if (i + 1 >= args.Length) + { + return new ParseResult.ParseError("--output requires a path argument"); + } + + outputPath = args[++i]; + break; + + case "--help" + or "-h": + return new ParseResult.Help(); + + default: + if (arg.StartsWith('-')) + { + return new ParseResult.ParseError($"Unknown option: {arg}"); + } + + break; + } + } + + if (string.IsNullOrEmpty(assemblyPath)) + { + return new ParseResult.ParseError("--assembly is required"); + } + + if (string.IsNullOrEmpty(typeName)) + { + return new ParseResult.ParseError("--type is required"); + } + + if (string.IsNullOrEmpty(outputPath)) + { + return new ParseResult.ParseError("--output is required"); + } + + return new ParseResult.Success(assemblyPath, typeName, outputPath); + } +} + +/// Argument parsing result base. +public abstract record ParseResult +{ + private ParseResult() { } + + /// Successfully parsed arguments. + public sealed record Success(string AssemblyPath, string TypeName, string OutputPath) + : ParseResult; + + /// Parse error. + public sealed record ParseError(string Message) : ParseResult; + + /// Help requested. + public sealed record Help : ParseResult; +} diff --git a/Migration/Schema.Export.Cli/Schema.Export.Cli.csproj b/Migration/Schema.Export.Cli/Schema.Export.Cli.csproj new file mode 100644 index 00000000..0de17615 --- /dev/null +++ b/Migration/Schema.Export.Cli/Schema.Export.Cli.csproj @@ -0,0 +1,13 @@ + + + + Exe + Schema.Export.Cli + $(NoWarn);CA1515;RS1035;CA2007 + + + + + + + diff --git a/Migration/migration_exe_spec.md b/Migration/migration_exe_spec.md new file mode 100644 index 00000000..95754fc2 --- /dev/null +++ b/Migration/migration_exe_spec.md @@ -0,0 +1,119 @@ +# Migration.Cli Specification + +NOTE: leave the JSON serialization/deserialization code as is for now, but deactivate it. The core will eventually offer JSON, but we are focusing on YAML for now. + +## Overview + +`Migration/Migration.Cli/Migration.Cli.csproj` is the **single, canonical CLI tool** for creating databases from schema definitions. All projects that need to spin up a database for code generation MUST use this executable. There is no other way. + +## Architecture + +Migration.Cli contains the DLLs for both SQLite and Postgres migrations. It is database-agnostic at the interface level - callers specify a YAML schema file path, and the CLI handles the rest. + +## Usage + +``` +dotnet run --project Migration/Migration.Cli/Migration.Cli.csproj -- \ + --schema path/to/schema.yaml \ + --output path/to/database.db \ + --provider [sqlite|postgres] +``` + +## Schema Input: YAML Only + +NOTE: leave the JSON serialization/deserialization code as is for now, but deactivate it. The core will eventually offer JSON, but we are focusing on YAML for now. + +The CLI accepts **only YAML schema files**. It does not accept: +- C# code references +- Inline schema definitions +- Project references to schema classes + +If a project defines its schema in C# code (e.g., `ExampleSchema.cs`, `ClinicalSchema.cs`), that schema MUST be serialized to YAML first. The YAML file is then passed to Migration.Cli. + +### Schema-to-YAML Workflow + +1. Schema is defined in a **separate Migrations assembly** (e.g., `MyProject.Migrations/`) with NO dependencies on generated code +2. Build step compiles the Migrations assembly first +3. Schema.Export.Cli exports C# schema to YAML file +4. Migration.Cli reads YAML and creates database +5. DataProvider code generation runs against the created database +6. Main project (e.g., `MyProject.Api/`) compiles with generated code + +### CRITICAL: Separate Migrations Assemblies + +**Schemas MUST be in separate assemblies to avoid circular build dependencies.** + +**Naming convention: Always use `*.Migrations` suffix, never `*.Schema` or `*BuildDb`.** + +Correct pattern: +``` +MyProject.Migrations/ # Schema definition only, NO generated code deps + └── MyProjectSchema.cs # Defines SchemaDefinition +MyProject.Api/ # References MyProject.Migrations, has generated code + └── Generated/ # DataProvider generated code +``` + +The Migrations assembly: +- Contains ONLY the `SchemaDefinition` class +- References ONLY `Migration` (for schema types) +- Has NO dependencies on generated code +- Can be built BEFORE code generation runs + +The API/main assembly: +- References the Migrations assembly +- Contains generated code from DataProvider +- Is built AFTER code generation + +## Why YAML? + +- **No circular dependencies**: Migration.Cli has zero project references to consumer projects +- **Clean build order**: YAML files are static assets, not compiled code +- **Portable**: Schema can be versioned, diffed, and shared without compilation +- **Single tool**: One CLI handles all schemas for all projects + +## Forbidden Patterns + +- Individual `*BuildDb` projects per consumer (causes circular builds) +- `` in Migration.Cli (circular dependency) +- Multiple CLI tools for database creation +- Hardcoded schema names/switches in the CLI +- CLI tool referencing actual schemas +- **Schema classes in the same project as generated code** (causes circular build deps) +- Migrations assemblies with dependencies on generated code +- Using `*.Schema` or `*BuildDb` naming (must use `*.Migrations`) + +## MSBuild Integration + +Consumer projects call Schema.Export.Cli then Migration.Cli in pre-build targets: + +```xml + + + + + + + + + +``` + +No project references. No schema includes. Just paths to assemblies and YAML files. + +## Build Order (CRITICAL) + +To avoid circular dependencies, the build order MUST be: + +``` +1. Migration/Migration.csproj # Core types +2. MyProject.Migrations/ # Schema definition (refs Migration only) +3. Schema.Export.Cli # Export schema to YAML +4. Migration.Cli # Create DB from YAML +5. DataProvider code generation # Generate C# from DB +6. MyProject.Api/ # Main project with generated code +``` + +The Migrations assembly MUST NOT reference: +- The API/main project +- Any generated code +- Any project that depends on generated code diff --git a/Migration/spec.md b/Migration/spec.md index fd1ef816..c191e0e8 100644 --- a/Migration/spec.md +++ b/Migration/spec.md @@ -46,16 +46,6 @@ The Migration framework is **independent** but serves as a foundation for: - **DataProvider**: Uses schema introspection for code generation - **LQL**: Can leverage schema metadata for query validation -### 1.3 Inspiration - -This framework draws inspiration from: - -- Prisma Migrate (declarative schema approach) -- EF Core Migrations (diff-based upgrades) -- Flyway (version-based migrations) - -However, it follows its own patterns per the codebase conventions (FP style, no classes, Result types). - --- ## 2. Goals & Non-Goals @@ -101,179 +91,24 @@ However, it follows its own patterns per the codebase conventions (FP style, no +-----------------------------------------------------------+ ``` -### 3.1 Core Components - -| Component | Responsibility | -|-----------|---------------| -| **Schema Definition** | Database-agnostic model of tables, columns, indexes, keys | -| **Schema Inspector** | Reads current database schema into definition model | -| **Diff Engine** | Compares desired vs actual schema, produces change set | -| **DDL Generator** | Converts change set to platform-specific SQL | -| **Migration Runner** | Executes DDL with proper transaction handling | - --- ## 4. Schema Definition Model ### 4.1 Core Records -Schema is defined using immutable records (per CLAUDE.md - no classes): - -```csharp -/// -/// Complete database schema definition. -/// -public sealed record SchemaDefinition -{ - public string Name { get; init; } = string.Empty; - public IReadOnlyList Tables { get; init; } = []; -} - -/// -/// Single table definition with columns, indexes, and all constraints. -/// -public sealed record TableDefinition -{ - /// Database schema (e.g., "public", "dbo"). - public string Schema { get; init; } = "public"; - - /// Table name. - public string Name { get; init; } = string.Empty; - - /// Column definitions in order. - public IReadOnlyList Columns { get; init; } = []; - - /// Index definitions. - public IReadOnlyList Indexes { get; init; } = []; - - /// Foreign key constraints. - public IReadOnlyList ForeignKeys { get; init; } = []; - - /// Primary key constraint. - public PrimaryKeyDefinition? PrimaryKey { get; init; } - - /// Unique constraints (semantic alternative to unique indexes). - public IReadOnlyList UniqueConstraints { get; init; } = []; - - /// Table-level check constraints (multi-column). - public IReadOnlyList CheckConstraints { get; init; } = []; - - /// Table comment/description for documentation. - public string? Comment { get; init; } -} - -/// -/// Column definition with type and all database-agnostic constraints. -/// -public sealed record ColumnDefinition -{ - /// Column name (case-insensitive for comparison). - public string Name { get; init; } = string.Empty; - - /// Portable type with full precision/scale/length info. - public PortableType Type { get; init; } - - /// Whether NULL values are allowed. - public bool IsNullable { get; init; } = true; - - /// SQL default expression (platform-specific, e.g., "CURRENT_TIMESTAMP"). - public string? DefaultValue { get; init; } - - /// Auto-increment/identity column. - public bool IsIdentity { get; init; } +Schema is defined using immutable records. The key types are: - /// Identity seed value (starting number). - public long IdentitySeed { get; init; } = 1; +- **SchemaDefinition** - Root container with schema name and list of tables +- **TableDefinition** - Table with columns, indexes, foreign keys, primary key, unique constraints, check constraints, and optional comment +- **ColumnDefinition** - Column with name, portable type, nullable flag, default value, identity settings, computed expression, collation, check constraint, and comment +- **IndexDefinition** - Index with name, columns, unique flag, and optional filter (partial index) +- **ForeignKeyDefinition** - FK with columns, referenced table/columns, and ON DELETE/UPDATE actions +- **PrimaryKeyDefinition** - PK with optional name and column list +- **UniqueConstraintDefinition** - Unique constraint with columns +- **CheckConstraintDefinition** - Check constraint with SQL boolean expression - /// Identity increment value. - public long IdentityIncrement { get; init; } = 1; - - /// Computed column expression (if computed). - public string? ComputedExpression { get; init; } - - /// Whether computed column is persisted/stored. - public bool IsComputedPersisted { get; init; } - - /// Collation for string columns (e.g., "NOCASE", "en_US.UTF-8"). - public string? Collation { get; init; } - - /// Check constraint expression for this column only. - public string? CheckConstraint { get; init; } - - /// Column comment/description for documentation. - public string? Comment { get; init; } -} - -/// -/// Check constraint that spans multiple columns. -/// -public sealed record CheckConstraintDefinition -{ - /// Constraint name. - public string Name { get; init; } = string.Empty; - - /// SQL boolean expression (e.g., "Price >= 0 AND Quantity >= 0"). - public string Expression { get; init; } = string.Empty; -} - -/// -/// Unique constraint (alternative to unique index for semantic clarity). -/// -public sealed record UniqueConstraintDefinition -{ - /// Constraint name. - public string? Name { get; init; } - - /// Columns that must be unique together. - public IReadOnlyList Columns { get; init; } = []; -} - -/// -/// Primary key constraint definition. -/// -public sealed record PrimaryKeyDefinition -{ - public string? Name { get; init; } - public IReadOnlyList Columns { get; init; } = []; -} - -/// -/// Index definition (unique or non-unique). -/// -public sealed record IndexDefinition -{ - public string Name { get; init; } = string.Empty; - public IReadOnlyList Columns { get; init; } = []; - public bool IsUnique { get; init; } - public string? Filter { get; init; } // Partial index WHERE clause -} - -/// -/// Foreign key constraint definition. -/// -public sealed record ForeignKeyDefinition -{ - public string? Name { get; init; } - public IReadOnlyList Columns { get; init; } = []; - public string ReferencedTable { get; init; } = string.Empty; - public string ReferencedSchema { get; init; } = "public"; - public IReadOnlyList ReferencedColumns { get; init; } = []; - public ForeignKeyAction OnDelete { get; init; } = ForeignKeyAction.NoAction; - public ForeignKeyAction OnUpdate { get; init; } = ForeignKeyAction.NoAction; -} - -/// -/// Foreign key referential action. -/// -public enum ForeignKeyAction -{ - NoAction, - Cascade, - SetNull, - SetDefault, - Restrict -} -``` +Foreign key actions: `NoAction`, `Cascade`, `SetNull`, `SetDefault`, `Restrict` ### 4.2 Fluent Builder (Optional) @@ -297,251 +132,75 @@ var schema = Schema.Define("MyApp") .Build(); ``` -### 4.3 JSON Schema Format - -Schema can also be defined as JSON for tooling/UI integration. The JSON format mirrors the C# records exactly: - -```json -{ - "name": "MyApp", - "tables": [ - { - "schema": "public", - "name": "Product", - "comment": "Product catalog", - "columns": [ - { - "name": "Id", - "type": { "kind": "bigint" }, - "nullable": false, - "identity": { - "seed": 1, - "increment": 1 - } - }, - { - "name": "Sku", - "type": { "kind": "char", "length": 12, "fixed": true }, - "nullable": false, - "comment": "Stock keeping unit" - }, - { - "name": "Name", - "type": { "kind": "string", "maxLength": 200 }, - "nullable": false - }, - { - "name": "Description", - "type": { "kind": "text" }, - "nullable": true - }, - { - "name": "Price", - "type": { "kind": "decimal", "precision": 10, "scale": 2 }, - "nullable": false, - "default": "0.00", - "checkConstraint": "Price >= 0" - }, - { - "name": "Weight", - "type": { "kind": "decimal", "precision": 8, "scale": 3 }, - "nullable": true, - "checkConstraint": "Weight IS NULL OR Weight > 0" - }, - { - "name": "IsActive", - "type": { "kind": "boolean" }, - "nullable": false, - "default": "true" - }, - { - "name": "Metadata", - "type": { "kind": "json" }, - "nullable": true - }, - { - "name": "CreatedAt", - "type": { "kind": "datetime", "precision": 3 }, - "nullable": false, - "default": "CURRENT_TIMESTAMP" - }, - { - "name": "ModifiedAt", - "type": { "kind": "datetimeoffset" }, - "nullable": true - }, - { - "name": "FullText", - "type": { "kind": "string", "maxLength": 500 }, - "nullable": true, - "computed": { - "expression": "Name + ' ' + COALESCE(Description, '')", - "persisted": false - } - }, - { - "name": "RowVersion", - "type": { "kind": "timestamp" }, - "nullable": false - } - ], - "primaryKey": { - "name": "PK_Product", - "columns": ["Id"] - }, - "indexes": [ - { - "name": "IX_Product_Sku", - "columns": ["Sku"], - "unique": true - }, - { - "name": "IX_Product_Active_Name", - "columns": ["IsActive", "Name"], - "unique": false, - "filter": "IsActive = 1" - } - ], - "uniqueConstraints": [ - { - "name": "UQ_Product_Name", - "columns": ["Name"] - } - ], - "checkConstraints": [ - { - "name": "CK_Product_ValidPrice", - "expression": "Price >= 0 AND (Weight IS NULL OR Weight > 0)" - } - ] - }, - { - "schema": "public", - "name": "OrderItem", - "columns": [ - { - "name": "Id", - "type": { "kind": "uuid" }, - "nullable": false, - "default": "NEWID()" - }, - { - "name": "OrderId", - "type": { "kind": "uuid" }, - "nullable": false - }, - { - "name": "ProductId", - "type": { "kind": "bigint" }, - "nullable": false - }, - { - "name": "Quantity", - "type": { "kind": "int" }, - "nullable": false, - "checkConstraint": "Quantity > 0" - }, - { - "name": "UnitPrice", - "type": { "kind": "decimal", "precision": 10, "scale": 2 }, - "nullable": false - }, - { - "name": "LineTotal", - "type": { "kind": "decimal", "precision": 12, "scale": 2 }, - "nullable": false, - "computed": { - "expression": "Quantity * UnitPrice", - "persisted": true - } - } - ], - "primaryKey": { - "columns": ["Id"] - }, - "foreignKeys": [ - { - "name": "FK_OrderItem_Product", - "columns": ["ProductId"], - "referencedTable": "Product", - "referencedSchema": "public", - "referencedColumns": ["Id"], - "onDelete": "Restrict", - "onUpdate": "Cascade" - } - ] - } - ] -} -``` - -### 4.4 JSON Type Schema Reference (Discriminated Unions) - -Type definitions in JSON format match the C# discriminated unions. The `kind` property discriminates the type, and each type has exactly the properties it needs: +### 4.3 YAML Schema Format + +Schema files use YAML format. See `migration_exe_spec.md` for CLI usage. The YAML format mirrors the C# records: + +```yaml +name: MyApp +tables: + - schema: public + name: Product + comment: Product catalog + columns: + - name: Id + type: { kind: bigint } + nullable: false + identity: { seed: 1, increment: 1 } + - name: Sku + type: { kind: char, length: 12 } + nullable: false + comment: Stock keeping unit + - name: Name + type: { kind: varchar, maxLength: 200 } + nullable: false + - name: Price + type: { kind: decimal, precision: 10, scale: 2 } + nullable: false + default: "0.00" + checkConstraint: "Price >= 0" + - name: IsActive + type: { kind: boolean } + nullable: false + default: "true" + primaryKey: + name: PK_Product + columns: [Id] + indexes: + - name: IX_Product_Sku + columns: [Sku] + unique: true + foreignKeys: [] +``` + +### 4.4 YAML Type Reference + +Type definitions use the `kind` property to discriminate: #### Types with NO parameters -| Type Kind | Properties | Example | -|-----------|-----------|---------| -| `tinyint` | (none) | `{ "kind": "tinyint" }` | -| `smallint` | (none) | `{ "kind": "smallint" }` | -| `int` | (none) | `{ "kind": "int" }` | -| `bigint` | (none) | `{ "kind": "bigint" }` | -| `float` | (none) | `{ "kind": "float" }` | -| `double` | (none) | `{ "kind": "double" }` | -| `money` | (none) | `{ "kind": "money" }` | -| `smallmoney` | (none) | `{ "kind": "smallmoney" }` | -| `text` | (none) | `{ "kind": "text" }` | -| `blob` | (none) | `{ "kind": "blob" }` | -| `date` | (none) | `{ "kind": "date" }` | -| `datetimeoffset` | (none) | `{ "kind": "datetimeoffset" }` | -| `rowversion` | (none) | `{ "kind": "rowversion" }` | -| `uuid` | (none) | `{ "kind": "uuid" }` | -| `boolean` | (none) | `{ "kind": "boolean" }` | -| `json` | (none) | `{ "kind": "json" }` | -| `xml` | (none) | `{ "kind": "xml" }` | - -#### Types with LENGTH parameter - -| Type Kind | Required | Example | -|-----------|----------|---------| -| `char` | `length` (int) | `{ "kind": "char", "length": 10 }` | -| `nchar` | `length` (int) | `{ "kind": "nchar", "length": 50 }` | -| `binary` | `length` (int) | `{ "kind": "binary", "length": 16 }` | - -#### Types with MAXLENGTH parameter - -| Type Kind | Required | Example | -|-----------|----------|---------| -| `varchar` | `maxLength` (int) | `{ "kind": "varchar", "maxLength": 255 }` | -| `nvarchar` | `maxLength` (int) | `{ "kind": "nvarchar", "maxLength": 100 }` | -| `varbinary` | `maxLength` (int) | `{ "kind": "varbinary", "maxLength": 8000 }` | - -For MAX length, use `2147483647` (int.MaxValue): -```json -{ "kind": "nvarchar", "maxLength": 2147483647 } -``` - -#### Types with PRECISION parameter - -| Type Kind | Required | Example | -|-----------|----------|---------| -| `time` | `precision` (0-7) | `{ "kind": "time", "precision": 3 }` | -| `datetime` | `precision` (0-7) | `{ "kind": "datetime", "precision": 3 }` | - -#### Types with PRECISION and SCALE parameters - -| Type Kind | Required | Example | -|-----------|----------|---------| -| `decimal` | `precision`, `scale` | `{ "kind": "decimal", "precision": 18, "scale": 2 }` | - -#### Types with SPECIAL parameters - -| Type Kind | Required | Example | -|-----------|----------|---------| -| `enum` | `name`, `values` | `{ "kind": "enum", "name": "OrderStatus", "values": ["Pending", "Shipped", "Delivered"] }` | -| `geometry` | `srid` (optional) | `{ "kind": "geometry", "srid": 4326 }` | -| `geography` | `srid` (default 4326) | `{ "kind": "geography", "srid": 4326 }` | +| Type Kind | Example | +|-----------|---------| +| `tinyint` | `kind: tinyint` | +| `smallint` | `kind: smallint` | +| `int` | `kind: int` | +| `bigint` | `kind: bigint` | +| `float` | `kind: float` | +| `double` | `kind: double` | +| `text` | `kind: text` | +| `blob` | `kind: blob` | +| `date` | `kind: date` | +| `uuid` | `kind: uuid` | +| `boolean` | `kind: boolean` | + +#### Types with parameters + +| Type Kind | Parameters | Example | +|-----------|------------|---------| +| `char` | `length` | `{ kind: char, length: 10 }` | +| `varchar` | `maxLength` | `{ kind: varchar, maxLength: 255 }` | +| `decimal` | `precision`, `scale` | `{ kind: decimal, precision: 18, scale: 2 }` | +| `datetime` | `precision` | `{ kind: datetime, precision: 3 }` | ### 4.5 Column Property Reference @@ -563,368 +222,22 @@ For MAX length, use `2147483647` (int.MaxValue): ## 5. Type System -### 5.1 Portable Types (Discriminated Unions) +### 5.1 Portable Types -The type system uses **discriminated unions** where each type record carries exactly the metadata it needs - no more, no less. Types that don't need parameters have no parameters. Types that need length have length. Types that need precision and scale have both. +The type system uses **discriminated unions** where each type record carries exactly the metadata it needs. Types without parameters (like `BigIntType`) have none. Types with parameters (like `DecimalType(int Precision, int Scale)`) carry only what they need. -```csharp -/// -/// Database-agnostic type definition. Base sealed type for discriminated union. -/// Pattern match on derived types to extract type-specific metadata. -/// -public abstract record PortableType; - -// ═══════════════════════════════════════════════════════════════════ -// INTEGER TYPES - No parameters needed (bit size is implicit in type) -// ═══════════════════════════════════════════════════════════════════ - -/// 8-bit integer: 0 to 255 (unsigned) or -128 to 127 (signed). -public sealed record TinyIntType : PortableType; - -/// 16-bit signed integer: -32,768 to 32,767. -public sealed record SmallIntType : PortableType; - -/// 32-bit signed integer: -2,147,483,648 to 2,147,483,647. -public sealed record IntType : PortableType; - -/// 64-bit signed integer: -9.2E18 to 9.2E18. -public sealed record BigIntType : PortableType; - -// ═══════════════════════════════════════════════════════════════════ -// EXACT NUMERIC TYPES - Require precision and/or scale -// ═══════════════════════════════════════════════════════════════════ - -/// -/// Exact decimal number with specified precision and scale. -/// Precision = total number of digits (1-38). -/// Scale = digits after decimal point (0 to Precision). -/// Example: DECIMAL(10,2) stores values like 12345678.99 -/// -/// Total digits (1-38) -/// Decimal places (0 to Precision) -public sealed record DecimalType(int Precision, int Scale) : PortableType; - -/// -/// Currency type with fixed precision for financial calculations. -/// Equivalent to DECIMAL(19,4) - supports values up to ~922 trillion. -/// -public sealed record MoneyType : PortableType; - -/// -/// Small currency type with reduced precision. -/// Equivalent to DECIMAL(10,4) - supports values up to ~214,748. -/// -public sealed record SmallMoneyType : PortableType; - -// ═══════════════════════════════════════════════════════════════════ -// FLOATING POINT TYPES - No parameters (IEEE standard sizes) -// ═══════════════════════════════════════════════════════════════════ - -/// 32-bit IEEE 754 floating point. ~7 significant digits. -public sealed record FloatType : PortableType; - -/// 64-bit IEEE 754 floating point. ~15 significant digits. -public sealed record DoubleType : PortableType; - -// ═══════════════════════════════════════════════════════════════════ -// STRING TYPES - Each variant has exactly the parameters it needs -// ═══════════════════════════════════════════════════════════════════ - -/// -/// Fixed-length ASCII/single-byte character string. Always padded to exact length. -/// -/// Exact character count (1-8000) -public sealed record CharType(int Length) : PortableType; - -/// -/// Variable-length ASCII/single-byte character string up to max length. -/// -/// Maximum characters (1-8000) -public sealed record VarCharType(int MaxLength) : PortableType; - -/// -/// Fixed-length Unicode character string. Always padded to exact length. -/// Uses 2 bytes per character (UCS-2/UTF-16). -/// -/// Exact character count (1-4000) -public sealed record NCharType(int Length) : PortableType; - -/// -/// Variable-length Unicode character string up to max length. -/// Uses 2 bytes per character (UCS-2/UTF-16). -/// -/// Maximum characters (1-4000, or int.MaxValue for MAX) -public sealed record NVarCharType(int MaxLength) : PortableType; - -/// -/// Unlimited length text storage. No length parameter needed. -/// Maps to TEXT (Postgres/SQLite), NVARCHAR(MAX) (SQL Server). -/// -public sealed record TextType : PortableType; - -// ═══════════════════════════════════════════════════════════════════ -// BINARY TYPES - Length-based variants -// ═══════════════════════════════════════════════════════════════════ - -/// -/// Fixed-length binary data. Always padded to exact length. -/// -/// Exact byte count (1-8000) -public sealed record BinaryType(int Length) : PortableType; - -/// -/// Variable-length binary data up to max length. -/// -/// Maximum bytes (1-8000, or int.MaxValue for MAX) -public sealed record VarBinaryType(int MaxLength) : PortableType; - -/// -/// Unlimited binary storage. No length parameter needed. -/// Maps to BLOB (SQLite), BYTEA (Postgres), VARBINARY(MAX) (SQL Server). -/// -public sealed record BlobType : PortableType; - -// ═══════════════════════════════════════════════════════════════════ -// DATE/TIME TYPES - Some need precision, some don't -// ═══════════════════════════════════════════════════════════════════ - -/// Date only (no time component). No parameters needed. -public sealed record DateType : PortableType; - -/// -/// Time only (no date component) with fractional seconds precision. -/// -/// Fractional seconds digits (0-7, default 7) -public sealed record TimeType(int Precision) : PortableType; - -/// -/// Date and time without timezone. -/// -/// Fractional seconds digits (0-7, default 3) -public sealed record DateTimeType(int Precision) : PortableType; - -/// -/// Date and time with timezone offset. No precision parameter. -/// Always stores full precision with timezone info. -/// -public sealed record DateTimeOffsetType : PortableType; - -/// -/// Row version / timestamp for optimistic concurrency. -/// Auto-generated binary value that changes on each update. -/// No parameters - platform determines size (8 bytes typically). -/// -public sealed record RowVersionType : PortableType; - -// ═══════════════════════════════════════════════════════════════════ -// OTHER TYPES - Specialized types with specific parameters -// ═══════════════════════════════════════════════════════════════════ - -/// 128-bit globally unique identifier. No parameters needed. -public sealed record UuidType : PortableType; - -/// Boolean true/false value. No parameters needed. -public sealed record BooleanType : PortableType; - -/// -/// JSON document storage. No parameters needed. -/// Maps to JSONB (Postgres), TEXT (SQLite), NVARCHAR(MAX) (SQL Server). -/// -public sealed record JsonType : PortableType; - -/// -/// XML document storage. No parameters needed. -/// Maps to XML (SQL Server/Postgres), TEXT (SQLite). -/// -public sealed record XmlType : PortableType; - -/// -/// Database enumeration type with named values. -/// Postgres: Creates actual ENUM type. -/// SQL Server/SQLite: Maps to constrained string. -/// -/// Enum type name for DDL -/// Allowed enum values in order -public sealed record EnumType(string Name, IReadOnlyList Values) : PortableType; - -/// -/// Spatial geometry type for GIS data. -/// -/// Spatial Reference ID (e.g., 4326 for WGS84) -public sealed record GeometryType(int? Srid) : PortableType; - -/// -/// Spatial geography type for Earth-surface GIS data. -/// -/// Spatial Reference ID (default 4326 for WGS84) -public sealed record GeographyType(int Srid = 4326) : PortableType; -``` - -### 5.1.1 Pattern Matching Usage - -With discriminated unions, you pattern match to extract the exact parameters each type has: +Pattern match on type to generate platform-specific DDL: ```csharp public static string ToSqlServerType(PortableType type) => type switch { - // Integer types - no parameters to extract - TinyIntType => "TINYINT", - SmallIntType => "SMALLINT", - IntType => "INT", BigIntType => "BIGINT", - - // Decimal - extract precision and scale DecimalType(var p, var s) => $"DECIMAL({p},{s})", - MoneyType => "MONEY", - SmallMoneyType => "SMALLMONEY", - - // Floating point - no parameters - FloatType => "REAL", - DoubleType => "FLOAT", - - // String types - each has exactly the parameter it needs - CharType(var len) => $"CHAR({len})", VarCharType(var max) => $"VARCHAR({max})", - NCharType(var len) => $"NCHAR({len})", - NVarCharType(var max) when max == int.MaxValue => "NVARCHAR(MAX)", - NVarCharType(var max) => $"NVARCHAR({max})", TextType => "NVARCHAR(MAX)", - - // Binary types - BinaryType(var len) => $"BINARY({len})", - VarBinaryType(var max) when max == int.MaxValue => "VARBINARY(MAX)", - VarBinaryType(var max) => $"VARBINARY({max})", - BlobType => "VARBINARY(MAX)", - - // Date/time types - extract precision where applicable - DateType => "DATE", - TimeType(var p) => $"TIME({p})", - DateTimeType(var p) => $"DATETIME2({p})", - DateTimeOffsetType => "DATETIMEOFFSET", - RowVersionType => "ROWVERSION", - - // Other types UuidType => "UNIQUEIDENTIFIER", - BooleanType => "BIT", - JsonType => "NVARCHAR(MAX)", // JSON support via NVARCHAR - XmlType => "XML", - EnumType(var name, _) => $"NVARCHAR(100)", // CHECK constraint added separately - GeometryType(_) => "GEOMETRY", - GeographyType(_) => "GEOGRAPHY", - - _ => throw new NotSupportedException($"Unknown type: {type.GetType().Name}") -}; - -public static string ToPostgresType(PortableType type) => type switch -{ - TinyIntType => "SMALLINT", // Postgres has no TINYINT - SmallIntType => "SMALLINT", - IntType => "INTEGER", - BigIntType => "BIGINT", - - DecimalType(var p, var s) => $"NUMERIC({p},{s})", - MoneyType => "NUMERIC(19,4)", - SmallMoneyType => "NUMERIC(10,4)", - - FloatType => "REAL", - DoubleType => "DOUBLE PRECISION", - - CharType(var len) => $"CHAR({len})", - VarCharType(var max) => $"VARCHAR({max})", - NCharType(var len) => $"CHAR({len})", // Postgres is always Unicode - NVarCharType(var max) => $"VARCHAR({max})", - TextType => "TEXT", - - BinaryType(_) => "BYTEA", // Postgres BYTEA is always variable - VarBinaryType(_) => "BYTEA", - BlobType => "BYTEA", - - DateType => "DATE", - TimeType(var p) => $"TIME({p})", - DateTimeType(_) => "TIMESTAMP", - DateTimeOffsetType => "TIMESTAMPTZ", - RowVersionType => "BYTEA", // Manual implementation needed - - UuidType => "UUID", - BooleanType => "BOOLEAN", - JsonType => "JSONB", - XmlType => "XML", - EnumType(var name, _) => name, // Use CREATE TYPE for enum - - GeometryType(var srid) => srid.HasValue ? $"GEOMETRY(Geometry,{srid})" : "GEOMETRY", - GeographyType(var srid) => $"GEOGRAPHY(Geography,{srid})", - - _ => throw new NotSupportedException($"Unknown type: {type.GetType().Name}") + // ... etc }; - -public static string ToSqliteType(PortableType type) => type switch -{ - // SQLite has limited type affinity - INTEGER, REAL, TEXT, BLOB - TinyIntType or SmallIntType or IntType or BigIntType => "INTEGER", - DecimalType(_, _) or MoneyType or SmallMoneyType => "REAL", - FloatType or DoubleType => "REAL", - CharType(_) or VarCharType(_) or NCharType(_) or NVarCharType(_) or TextType => "TEXT", - BinaryType(_) or VarBinaryType(_) or BlobType => "BLOB", - DateType or TimeType(_) or DateTimeType(_) or DateTimeOffsetType => "TEXT", - RowVersionType => "BLOB", - UuidType => "TEXT", - BooleanType => "INTEGER", - JsonType or XmlType => "TEXT", - EnumType(_, _) => "TEXT", - GeometryType(_) or GeographyType(_) => "BLOB", // Store as WKB - _ => throw new NotSupportedException($"Unknown type: {type.GetType().Name}") -}; -``` - -### 5.1.2 Factory Methods for Convenience - -While types are constructed directly, convenience factory methods can be provided: - -```csharp -public static class PortableTypes -{ - // Integer types - direct construction, no factory needed - public static TinyIntType TinyInt => new(); - public static SmallIntType SmallInt => new(); - public static IntType Int => new(); - public static BigIntType BigInt => new(); - - // Decimal requires parameters - public static DecimalType Decimal(int precision, int scale) => new(precision, scale); - public static MoneyType Money => new(); - - // Floating point - public static FloatType Float => new(); - public static DoubleType Double => new(); - - // Strings - factory method clarifies intent - public static CharType Char(int length) => new(length); - public static VarCharType VarChar(int maxLength) => new(maxLength); - public static NCharType NChar(int length) => new(length); - public static NVarCharType NVarChar(int maxLength) => new(maxLength); - public static NVarCharType NVarCharMax => new(int.MaxValue); - public static TextType Text => new(); - - // Binary - public static BinaryType Binary(int length) => new(length); - public static VarBinaryType VarBinary(int maxLength) => new(maxLength); - public static VarBinaryType VarBinaryMax => new(int.MaxValue); - public static BlobType Blob => new(); - - // Date/time - defaults for common cases - public static DateType Date => new(); - public static TimeType Time(int precision = 7) => new(precision); - public static DateTimeType DateTime(int precision = 3) => new(precision); - public static DateTimeOffsetType DateTimeOffset => new(); - public static RowVersionType RowVersion => new(); - - // Other - public static UuidType Uuid => new(); - public static BooleanType Boolean => new(); - public static JsonType Json => new(); - public static XmlType Xml => new(); - public static EnumType Enum(string name, params string[] values) => new(name, values); -} ``` ### 5.2 Type Mapping Table @@ -1027,33 +340,14 @@ Identity columns are handled per-platform: ### 6.1 Operation Types -The diff engine produces a list of schema operations: +The diff engine produces a list of schema operations as discriminated union records: -```csharp -/// -/// Base type for all schema operations. -/// -public abstract record SchemaOperation; - -// Table operations -public sealed record CreateTable(TableDefinition Table) : SchemaOperation; -public sealed record DropTable(string Schema, string Name) : SchemaOperation; - -// Column operations -public sealed record AddColumn(string Schema, string Table, ColumnDefinition Column) : SchemaOperation; -public sealed record DropColumn(string Schema, string Table, string Column) : SchemaOperation; -public sealed record AlterColumn(string Schema, string Table, ColumnDefinition OldColumn, ColumnDefinition NewColumn) : SchemaOperation; - -// Index operations -public sealed record CreateIndex(string Schema, string Table, IndexDefinition Index) : SchemaOperation; -public sealed record DropIndex(string Schema, string Table, string IndexName) : SchemaOperation; - -// Constraint operations -public sealed record AddPrimaryKey(string Schema, string Table, PrimaryKeyDefinition PrimaryKey) : SchemaOperation; -public sealed record DropPrimaryKey(string Schema, string Table, string? ConstraintName) : SchemaOperation; -public sealed record AddForeignKey(string Schema, string Table, ForeignKeyDefinition ForeignKey) : SchemaOperation; -public sealed record DropForeignKey(string Schema, string Table, string ConstraintName) : SchemaOperation; -``` +- **Table**: `CreateTable`, `DropTable` +- **Column**: `AddColumn`, `DropColumn`, `AlterColumn` +- **Index**: `CreateIndex`, `DropIndex` +- **Constraint**: `AddPrimaryKey`, `DropPrimaryKey`, `AddForeignKey`, `DropForeignKey` + +All operations carry the schema name, table name, and relevant definition or constraint name. ### 6.2 Additive-Only Mode (Default) @@ -1073,19 +367,12 @@ By default, the migration engine only applies **additive** operations: ### 6.3 Destructive Operations -Destructive operations require explicit configuration: +Destructive operations require explicit opt-in via `MigrationOptions`: -```csharp -var options = new MigrationOptions -{ - AllowDropTable = false, // Default: false - AllowDropColumn = false, // Default: false - AllowDropIndex = true, // Default: false (but often safe) - AllowAlterColumn = false, // Default: false -}; - -var result = MigrationRunner.Apply(connection, operations, options, logger); -``` +- `AllowDropTable` (default: false) +- `AllowDropColumn` (default: false) +- `AllowDropIndex` (default: false) +- `AllowAlterColumn` (default: false) --- @@ -1093,38 +380,10 @@ var result = MigrationRunner.Apply(connection, operations, options, logger); ### 7.1 Migration Runner -The migration runner executes operations with proper transaction handling: +`MigrationRunner` executes operations with transaction handling. Key methods: -```csharp -/// -/// Applies schema operations to a database. -/// -public static class MigrationRunner -{ - /// - /// Applies schema operations to the database. - /// - /// Database connection. - /// Operations to apply. - /// Migration options. - /// Logger for migration progress. - /// Success or migration error. - public static MigrationResult Apply( - IDbConnection connection, - IReadOnlyList operations, - MigrationOptions options, - ILogger logger - ); - - /// - /// Generates DDL without executing. - /// - public static Result GenerateDdl( - IReadOnlyList operations, - DatabasePlatform platform - ); -} -``` +- `Apply(connection, operations, options, logger)` → `MigrationResult` +- `GenerateDdl(operations, platform)` → `Result` (preview without executing) ### 7.2 Transaction Strategy @@ -1154,26 +413,7 @@ public static class MigrationRunner ### 8.1 Schema Comparison -The diff engine compares desired schema against current database state: - -```csharp -/// -/// Compares two schemas and produces operations to transform source into target. -/// -public static class SchemaDiff -{ - /// - /// Calculates operations needed to transform current schema to desired schema. - /// - /// Current database schema (from introspection). - /// Desired schema (from definition). - /// List of operations to apply. - public static IReadOnlyList Calculate( - SchemaDefinition current, - SchemaDefinition desired - ); -} -``` +`SchemaDiff.Calculate(current, desired)` compares desired schema against current database state and returns the list of operations needed to transform current into desired. ### 8.2 Comparison Rules @@ -1209,26 +449,7 @@ For each table in current not in desired: ### 8.4 Schema Introspection -Each provider implements schema introspection: - -```csharp -/// -/// Reads current database schema. -/// -public static class SchemaInspector -{ - /// - /// Reads complete schema from database. - /// - /// Database connection. - /// Logger. - /// Current schema or error. - public static Result Inspect( - IDbConnection connection, - ILogger logger - ); -} -``` +Each provider implements `SchemaInspector.Inspect(connection, logger)` → `Result` to read the current database schema. ### 8.5 Schema Capture to Metadata @@ -1239,53 +460,14 @@ public static class SchemaInspector 3. **Cross-Platform Migration**: Capture from one platform, apply to another 4. **Audit Trail**: Record point-in-time schema snapshots -#### 8.5.1 Schema Capture API - -```csharp -/// -/// Captures database schema and serializes to JSON metadata. -/// -public static class SchemaSerializer -{ - /// - /// Serialize schema definition to JSON for storage/versioning. - /// - public static string ToJson(SchemaDefinition schema); - - /// - /// Deserialize schema from JSON metadata. - /// - public static SchemaDefinition FromJson(string json); -} -``` - -#### 8.5.2 Capture Workflow - -```csharp -// 1. Connect to existing database -using var connection = new SqliteConnection("Data Source=legacy.db"); -connection.Open(); +#### 8.5.1 Schema Capture Workflow -// 2. CAPTURE existing schema -var captureResult = SchemaInspector.Inspect(connection, logger); -if (captureResult is SchemaResult.Error error) -{ - logger.LogError("Capture failed: {Error}", error.Value.Message); - return; -} -var schema = captureResult.Value; - -// 3. Serialize to metadata (for version control) -var json = SchemaSerializer.ToJson(schema); -File.WriteAllText("schema-v1.json", json); - -// 4. Later: Load from metadata and apply to new database -var savedSchema = SchemaSerializer.FromJson(File.ReadAllText("schema-v1.json")); -var operations = SchemaDiff.Calculate(emptySchema, savedSchema); -MigrationRunner.Apply(newConnection, operations, MigrationOptions.Default, logger); -``` +1. Connect to existing database +2. Call `SchemaInspector.Inspect()` to capture current schema +3. Call `SchemaSerializer.ToYaml()` to serialize for version control +4. Later: `SchemaSerializer.FromYaml()` to load, then `SchemaDiff.Calculate()` and `MigrationRunner.Apply()` -#### 8.5.3 Captured Schema Contents +#### 8.5.2 Captured Schema Contents The schema inspector MUST capture: @@ -1306,30 +488,9 @@ The schema inspector MUST capture: ### 9.1 Provider Interface -Each database platform implements DDL generation: +`DdlGenerator.Generate(operation, platform)` produces platform-specific DDL SQL. -```csharp -/// -/// Generates platform-specific DDL. -/// -public static class DdlGenerator -{ - /// - /// Generates DDL for a schema operation. - /// - /// Schema operation. - /// Target platform. - /// DDL SQL string. - public static string Generate(SchemaOperation operation, DatabasePlatform platform); -} - -public enum DatabasePlatform -{ - SQLite, - PostgreSQL, - SqlServer -} -``` +Platforms: `SQLite`, `PostgreSQL`, `SqlServer` ### 9.2 SQLite Provider @@ -1366,43 +527,20 @@ SQL Server-specific considerations: ### 10.1 Error Types -```csharp -/// -/// Base type for migration errors. -/// -public abstract record MigrationError(string Message); - -/// -/// Error during schema introspection. -/// -public sealed record IntrospectionError(string Message, Exception? Inner = null) : MigrationError(Message); - -/// -/// Error during DDL generation. -/// -public sealed record DdlGenerationError(string Message, SchemaOperation Operation) : MigrationError(Message); - -/// -/// Error during DDL execution. -/// -public sealed record ExecutionError(string Message, string Sql, Exception? Inner = null) : MigrationError(Message); - -/// -/// Validation error (e.g., destructive operation not allowed). -/// -public sealed record ValidationError(string Message, SchemaOperation Operation) : MigrationError(Message); -``` +All errors extend `MigrationError(Message)`: + +- **IntrospectionError** - Failed to read database schema +- **DdlGenerationError** - Failed to generate DDL for an operation +- **ExecutionError** - DDL execution failed (includes SQL that failed) +- **ValidationError** - Operation not allowed (e.g., destructive op without opt-in) ### 10.2 Result Types -All operations return Result types (per CLAUDE.md): +All operations return Result types (never throw). Common aliases: -```csharp -// Type aliases for common results -using MigrationResult = Result; -using InspectionResult = Result; -using DdlResult = Result; -``` +- `MigrationResult = Result` +- `InspectionResult = Result` +- `DdlResult = Result` --- @@ -1441,239 +579,23 @@ End-to-end tests are **critical** for validating that migrations work correctly ### 12.2 Greenfield Tests -Tests that spin up a fresh database and apply full schema: - -```csharp -[Fact] -public void CreateDatabaseFromScratch_SQLite() -{ - // Arrange - using var connection = new SqliteConnection("Data Source=:memory:"); - connection.Open(); - - var schema = Schema.Define("Test") - .Table("Users", t => t - .Column("Id", PortableType.Uuid, c => c.PrimaryKey()) - .Column("Email", PortableType.String(255), c => c.NotNull()) - .Index("idx_email", "Email", unique: true) - ) - .Table("Orders", t => t - .Column("Id", PortableType.Int64, c => c.PrimaryKey().Identity()) - .Column("UserId", PortableType.Uuid, c => c.NotNull()) - .Column("Total", PortableType.Decimal(10, 2)) - .ForeignKey("UserId", "Users", "Id") - ) - .Build(); - - // Act - var emptySchema = SchemaInspector.Inspect(connection, logger); - var operations = SchemaDiff.Calculate(emptySchema.Value, schema); - var result = MigrationRunner.Apply(connection, operations, MigrationOptions.Default, logger); - - // Assert - Assert.True(result is MigrationResult.Ok); - - // Verify tables exist - var inspected = SchemaInspector.Inspect(connection, logger); - Assert.Equal(2, inspected.Value.Tables.Count); - Assert.Contains(inspected.Value.Tables, t => t.Name == "Users"); - Assert.Contains(inspected.Value.Tables, t => t.Name == "Orders"); -} -``` +Create fresh database, define schema with fluent API, apply via `SchemaDiff.Calculate()` + `MigrationRunner.Apply()`, verify tables exist via introspection. ### 12.3 Upgrade Tests -Tests that add to an existing database: - -```csharp -[Fact] -public void UpgradeExistingDatabase_AddColumn() -{ - // Arrange - create initial schema - using var connection = new SqliteConnection("Data Source=:memory:"); - connection.Open(); - - var v1 = Schema.Define("Test") - .Table("Users", t => t - .Column("Id", PortableType.Uuid, c => c.PrimaryKey()) - .Column("Email", PortableType.String(255)) - ) - .Build(); - - // Apply v1 - var ops1 = SchemaDiff.Calculate(new SchemaDefinition(), v1); - _ = MigrationRunner.Apply(connection, ops1, MigrationOptions.Default, logger); - - // Act - upgrade to v2 with new column - var v2 = Schema.Define("Test") - .Table("Users", t => t - .Column("Id", PortableType.Uuid, c => c.PrimaryKey()) - .Column("Email", PortableType.String(255)) - .Column("Name", PortableType.String(100)) // NEW - .Column("CreatedAt", PortableType.DateTime) // NEW - ) - .Build(); - - var current = SchemaInspector.Inspect(connection, logger).Value; - var ops2 = SchemaDiff.Calculate(current, v2); - var result = MigrationRunner.Apply(connection, ops2, MigrationOptions.Default, logger); - - // Assert - Assert.True(result is MigrationResult.Ok); - Assert.Equal(2, ops2.Count); // Two AddColumn operations - - var final = SchemaInspector.Inspect(connection, logger).Value; - var users = final.Tables.Single(t => t.Name == "Users"); - Assert.Equal(4, users.Columns.Count); -} -``` +Apply v1 schema, then v2 with new columns. Verify diff produces `AddColumn` operations and final schema has all columns. ### 12.4 PostgreSQL Tests with Testcontainers -Real PostgreSQL testing using Docker: - -```csharp -public class PostgresMigrationTests : IAsyncLifetime -{ - private PostgreSqlContainer _postgres = null!; - private NpgsqlConnection _connection = null!; - - public async Task InitializeAsync() - { - _postgres = new PostgreSqlBuilder() - .WithImage("postgres:16-alpine") - .Build(); - await _postgres.StartAsync(); - - _connection = new NpgsqlConnection(_postgres.GetConnectionString()); - await _connection.OpenAsync(); - } - - public async Task DisposeAsync() - { - await _connection.DisposeAsync(); - await _postgres.DisposeAsync(); - } - - [Fact] - public void CreateSchema_PostgreSQL_NativeTypes() - { - // Arrange - var schema = Schema.Define("Test") - .Table("Events", t => t - .Column("Id", PortableType.Uuid, c => c.PrimaryKey()) - .Column("Data", PortableType.Json, c => c.NotNull()) - .Column("OccurredAt", PortableType.DateTimeOffset) - ) - .Build(); - - // Act - var operations = SchemaDiff.Calculate(new SchemaDefinition(), schema); - var result = MigrationRunner.Apply(_connection, operations, MigrationOptions.Default, logger); - - // Assert - Assert.True(result is MigrationResult.Ok); - - // Verify PostgreSQL-specific types - using var cmd = _connection.CreateCommand(); - cmd.CommandText = """ - SELECT column_name, data_type - FROM information_schema.columns - WHERE table_name = 'events' - """; - using var reader = cmd.ExecuteReader(); - - var columns = new Dictionary(); - while (reader.Read()) - { - columns[reader.GetString(0)] = reader.GetString(1); - } - - Assert.Equal("uuid", columns["id"]); - Assert.Equal("jsonb", columns["data"]); - Assert.Contains("timestamp", columns["occurredat"]); - } -} -``` +Use `Testcontainers.PostgreSql` to spin up real PostgreSQL. Verify native types (UUID, JSONB, TIMESTAMPTZ) are created correctly by querying `information_schema.columns`. ### 12.5 Idempotency Tests -Verify migrations can run multiple times safely: - -```csharp -[Fact] -public void Migration_IsIdempotent_NoErrorOnRerun() -{ - using var connection = new SqliteConnection("Data Source=:memory:"); - connection.Open(); - - var schema = Schema.Define("Test") - .Table("Items", t => t - .Column("Id", PortableType.Int32, c => c.PrimaryKey()) - .Column("Name", PortableType.String(50)) - .Index("idx_name", "Name") - ) - .Build(); - - // Run migration twice - for (int i = 0; i < 2; i++) - { - var current = SchemaInspector.Inspect(connection, logger).Value; - var operations = SchemaDiff.Calculate(current, schema); - var result = MigrationRunner.Apply(connection, operations, MigrationOptions.Default, logger); - - Assert.True(result is MigrationResult.Ok); - - // Second run should have 0 operations (already up to date) - if (i == 1) - { - Assert.Empty(operations); - } - } -} -``` +Run migration twice. Second run should produce zero operations (schema already matches desired state). ### 12.6 Cross-Platform Test Matrix -Each test should run against all platforms: - -```csharp -public class CrossPlatformMigrationTests -{ - public static IEnumerable Platforms => - [ - [DatabasePlatform.SQLite, () => CreateSqliteConnection()], - [DatabasePlatform.PostgreSQL, () => CreatePostgresConnection()], - [DatabasePlatform.SqlServer, () => CreateSqlServerConnection()], - ]; - - [Theory] - [MemberData(nameof(Platforms))] - public void SameSchema_WorksOnAllPlatforms(DatabasePlatform platform, Func factory) - { - using var connection = factory(); - - var schema = Schema.Define("Test") - .Table("Products", t => t - .Column("Id", PortableType.Uuid, c => c.PrimaryKey()) - .Column("Name", PortableType.String(200), c => c.NotNull()) - .Column("Price", PortableType.Decimal(10, 2)) - .Column("Active", PortableType.Boolean) - .Index("idx_name", "Name") - ) - .Build(); - - var operations = SchemaDiff.Calculate(new SchemaDefinition(), schema); - var result = MigrationRunner.Apply(connection, operations, MigrationOptions.Default, logger); - - Assert.True(result is MigrationResult.Ok); - - var inspected = SchemaInspector.Inspect(connection, logger).Value; - Assert.Single(inspected.Tables); - Assert.Equal(4, inspected.Tables[0].Columns.Count); - } -} -``` +Use `[Theory]` with `[MemberData]` to run the same schema definition against SQLite, PostgreSQL, and SQL Server. Verify identical results across all platforms. ### 12.7 Required Test Coverage @@ -1691,14 +613,14 @@ An implementation MUST include tests for: | Idempotent migration | Required | Required | Required | | Introspect and round-trip schema | Required | Required | Required | | **Schema capture from existing DB** | Required | Required | Required | -| **Schema serialize to JSON metadata** | Required | Required | Required | +| **Schema serialize to YAML metadata** | Required | Required | Required | | **Destructive op returns useful error** | Required | Required | Required | --- ## 13. Schema Capture and Metadata -A critical feature of the Migration framework is the ability to **capture existing database schemas** and serialize them to JSON metadata. This enables: +A critical feature of the Migration framework is the ability to **capture existing database schemas** and serialize them to YAML. This enables: 1. **Brownfield scenarios** - Capture existing database schema before applying migrations 2. **Schema versioning** - Store schema snapshots in source control @@ -1708,233 +630,12 @@ A critical feature of the Migration framework is the ability to **capture existi ### 13.1 Schema Serializer -```csharp -/// -/// Serializes and deserializes schema definitions to/from JSON. -/// Used for capturing existing database schemas and storing as metadata. -/// -public static class SchemaSerializer -{ - private static readonly JsonSerializerOptions Options = new() - { - WriteIndented = true, - PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, - Converters = { new PortableTypeJsonConverter() } - }; - - /// - /// Serialize a schema definition to JSON string. - /// - public static string ToJson(SchemaDefinition schema) => - JsonSerializer.Serialize(schema, Options); - - /// - /// Deserialize a schema definition from JSON string. - /// - public static SchemaDefinition FromJson(string json) => - JsonSerializer.Deserialize(json, Options) - ?? throw new JsonException("Failed to deserialize schema"); -} -``` - -### 13.2 JSON Schema Format +`SchemaSerializer.ToYaml(schema)` and `SchemaSerializer.FromYaml(yaml)` enable round-trip serialization for version control and brownfield adoption. -The JSON format uses camelCase property names and preserves all schema details: +### 13.2 Required Schema Capture Tests -```json -{ - "name": "MyDatabase", - "tables": [ - { - "schema": "public", - "name": "Users", - "columns": [ - { - "name": "Id", - "type": { "type": "Uuid" }, - "isNullable": false - }, - { - "name": "Email", - "type": { "type": "VarChar", "length": 255 }, - "isNullable": false - }, - { - "name": "Balance", - "type": { "type": "Decimal", "precision": 18, "scale": 2 }, - "isNullable": true - } - ], - "primaryKey": { - "name": "PK_Users", - "columns": ["Id"] - }, - "indexes": [ - { - "name": "idx_users_email", - "columns": ["Email"], - "isUnique": true - } - ], - "foreignKeys": [], - "uniqueConstraints": [], - "checkConstraints": [] - } - ] -} -``` - -### 13.3 PortableType JSON Converter - -The `PortableTypeJsonConverter` handles the discriminated union serialization: - -```csharp -/// -/// JSON converter for PortableType discriminated union. -/// -public sealed class PortableTypeJsonConverter : JsonConverter -{ - public override PortableType? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - using var doc = JsonDocument.ParseValue(ref reader); - var root = doc.RootElement; - var typeName = root.GetProperty("type").GetString(); - - return typeName switch - { - "Int" => new IntType(), - "BigInt" => new BigIntType(), - "VarChar" => new VarCharType(root.GetProperty("length").GetInt32()), - "Decimal" => new DecimalType( - root.GetProperty("precision").GetInt32(), - root.GetProperty("scale").GetInt32() - ), - // ... other types - _ => new TextType() - }; - } - - public override void Write(Utf8JsonWriter writer, PortableType value, JsonSerializerOptions options) - { - writer.WriteStartObject(); - switch (value) - { - case IntType: - writer.WriteString("type", "Int"); - break; - case VarCharType v: - writer.WriteString("type", "VarChar"); - writer.WriteNumber("length", v.MaxLength); - break; - case DecimalType d: - writer.WriteString("type", "Decimal"); - writer.WriteNumber("precision", d.Precision); - writer.WriteNumber("scale", d.Scale); - break; - // ... other types - } - writer.WriteEndObject(); - } -} -``` - -### 13.4 Schema Capture Workflow - -Typical workflow for capturing and using schema metadata: - -```csharp -// 1. Connect to existing database -using var connection = new SqliteConnection("Data Source=existing.db"); -connection.Open(); - -// 2. Capture current schema -var inspectResult = SqliteSchemaInspector.Inspect(connection); -if (inspectResult is InspectionResult.Error err) -{ - logger.LogError("Failed to inspect schema: {Error}", err.Value.Message); - return; -} -var currentSchema = inspectResult.Value; - -// 3. Serialize to JSON for storage -var json = SchemaSerializer.ToJson(currentSchema); -File.WriteAllText("schema-snapshot.json", json); - -// 4. Later, load and compare -var storedJson = File.ReadAllText("schema-snapshot.json"); -var storedSchema = SchemaSerializer.FromJson(storedJson); - -// 5. Compare with desired schema -var desiredSchema = Schema.Define("MyDb") - .Table("Users", t => t - .Column("Id", new UuidType(), c => c.PrimaryKey()) - .Column("Email", new VarCharType(255), c => c.NotNull()) - ) - .Build(); - -var operations = SchemaDiff.Calculate(storedSchema, desiredSchema); -``` - -### 13.5 Required Schema Capture Tests - -```csharp -[Fact] -public void SchemaCapture_ExistingDatabase_ReturnsCompleteSchema() -{ - // Arrange - create database with raw SQL (simulating existing DB) - using var connection = new SqliteConnection("Data Source=:memory:"); - connection.Open(); - using var cmd = connection.CreateCommand(); - cmd.CommandText = """ - CREATE TABLE Products ( - Id INTEGER PRIMARY KEY, - Name TEXT NOT NULL, - Price REAL - ); - CREATE INDEX idx_products_name ON Products(Name); - """; - cmd.ExecuteNonQuery(); - - // Act - capture schema - var result = SqliteSchemaInspector.Inspect(connection); - - // Assert - Assert.True(result is InspectionResult.Ok); - var schema = result.Value; - Assert.Single(schema.Tables); - var table = schema.Tables[0]; - Assert.Equal("Products", table.Name); - Assert.Equal(3, table.Columns.Count); - Assert.Single(table.Indexes); -} - -[Fact] -public void SchemaCapture_SerializesToJson_RoundTrip() -{ - // Arrange - var schema = Schema.Define("Test") - .Table("Users", t => t - .Column("Id", new UuidType(), c => c.PrimaryKey()) - .Column("Email", new VarCharType(255), c => c.NotNull()) - .Column("Balance", new DecimalType(18, 2)) - .Index("idx_email", "Email", unique: true) - ) - .Build(); - - // Act - var json = SchemaSerializer.ToJson(schema); - var roundTripped = SchemaSerializer.FromJson(json); - - // Assert - Assert.Equal(schema.Name, roundTripped.Name); - Assert.Equal(schema.Tables.Count, roundTripped.Tables.Count); - var table = roundTripped.Tables[0]; - Assert.Equal("Users", table.Name); - Assert.Equal(3, table.Columns.Count); - Assert.Single(table.Indexes); -} -``` +1. **Capture existing database** - Create DB with raw SQL, call inspector, verify complete schema returned +2. **YAML round-trip** - Serialize schema to YAML, deserialize, verify equality --- @@ -1942,92 +643,17 @@ public void SchemaCapture_SerializesToJson_RoundTrip() ### Appendix A: Sync Framework Schema -The Sync framework uses Migration to create its infrastructure tables: - -```csharp -var syncSchema = Schema.Define("_sync") - .Table("_sync_state", t => t - .Column("key", PortableType.String(255), c => c.PrimaryKey()) - .Column("value", PortableType.Text, c => c.NotNull()) - ) - .Table("_sync_session", t => t - .Column("sync_active", PortableType.Int32, c => c.NotNull().Default("0")) - ) - .Table("_sync_log", t => t - .Column("version", PortableType.Int64, c => c.PrimaryKey().Identity()) - .Column("table_name", PortableType.String(255), c => c.NotNull()) - .Column("pk_value", PortableType.Text, c => c.NotNull()) - .Column("operation", PortableType.String(10), c => c.NotNull()) - .Column("payload", PortableType.Text) - .Column("origin", PortableType.String(36), c => c.NotNull()) - .Column("timestamp", PortableType.String(30), c => c.NotNull()) - .Index("idx_sync_log_version", "version") - .Index("idx_sync_log_table", "table_name", "version") - ) - .Table("_sync_clients", t => t - .Column("origin_id", PortableType.String(36), c => c.PrimaryKey()) - .Column("last_sync_version", PortableType.Int64, c => c.NotNull().Default("0")) - .Column("last_sync_timestamp", PortableType.String(30), c => c.NotNull()) - .Column("created_at", PortableType.String(30), c => c.NotNull()) - .Index("idx_sync_clients_version", "last_sync_version") - ) - .Table("_sync_subscriptions", t => t - .Column("subscription_id", PortableType.String(36), c => c.PrimaryKey()) - .Column("origin_id", PortableType.String(36), c => c.NotNull()) - .Column("subscription_type", PortableType.String(10), c => c.NotNull()) - .Column("table_name", PortableType.String(255), c => c.NotNull()) - .Column("filter", PortableType.Text) - .Column("created_at", PortableType.String(30), c => c.NotNull()) - .Column("expires_at", PortableType.String(30)) - .Index("idx_subscriptions_table", "table_name") - .Index("idx_subscriptions_origin", "origin_id") - ) - .Build(); -``` +The Sync framework uses Migration to create infrastructure tables: `_sync_state`, `_sync_session`, `_sync_log`, `_sync_clients`, `_sync_subscriptions`. See Sync framework documentation for details. ### Appendix B: Example Usage -```csharp -// Define desired schema -var schema = Schema.Define("MyApp") - .Table("Users", t => t - .Column("Id", PortableType.Uuid, c => c.PrimaryKey()) - .Column("Email", PortableType.String(255), c => c.NotNull()) - .Column("Name", PortableType.String(100)) - .Column("CreatedAt", PortableType.DateTime, c => c.NotNull()) - .Index("idx_users_email", "Email", unique: true) - ) - .Build(); - -// Inspect current database -using var connection = new SqliteConnection("Data Source=app.db"); -connection.Open(); - -var currentResult = SchemaInspector.Inspect(connection, logger); -if (currentResult is InspectionError error) -{ - logger.LogError("Failed to inspect: {Error}", error.Message); - return; -} - -var current = ((InspectionResult.Ok)currentResult).Value; +Typical workflow: -// Calculate diff -var operations = SchemaDiff.Calculate(current, schema); - -// Apply migrations (additive only by default) -var result = MigrationRunner.Apply( - connection, - operations, - MigrationOptions.Default, - logger -); - -if (result is MigrationResult.Ok ok) -{ - logger.LogInformation("Applied {Count} operations", ok.Value.OperationsApplied); -} -``` +1. Define schema with fluent `Schema.Define()` API +2. Open connection, call `SchemaInspector.Inspect()` to get current state +3. Call `SchemaDiff.Calculate(current, desired)` to get operations +4. Call `MigrationRunner.Apply()` with operations and options +5. Log applied operation count from result ### Appendix C: Platform-Specific DDL Examples diff --git a/PR.md b/PR.md deleted file mode 100644 index 0c357c96..00000000 --- a/PR.md +++ /dev/null @@ -1,151 +0,0 @@ -# Major Release: Sync Framework, Gatekeeper Auth, and Healthcare Samples - -## Summary - -This release transforms DataProvider from a SQL code generator into a comprehensive data layer suite. It adds three major new components: - -1. **Sync Framework** - Offline-first bidirectional synchronization engine -2. **Gatekeeper** - Passwordless authentication and fine-grained authorization microservice -3. **Healthcare Samples** - FHIR-compliant clinical and scheduling APIs with React dashboard - -## Statistics - -- **373 files changed** -- **~130,000 lines added** -- **~1,600 lines removed** (cleanup of obsolete specs) - -## New Components - -### Sync Framework (`Sync/`) -A database-agnostic, offline-first synchronization framework for .NET applications. - -**Features:** -- Two-way synchronization with version-based change tracking -- Conflict resolution strategies (last-write-wins, server-wins, custom) -- Foreign key handling with automatic deferred retry -- Tombstone management for safe deletion tracking -- Real-time subscriptions via Server-Sent Events (SSE) -- SHA-256 hash verification for data integrity -- Mapping engine for heterogeneous schema sync between microservices -- Database support: SQLite and PostgreSQL - -**Projects:** -| Project | Lines | Description | -|---------|-------|-------------| -| `Sync.Core` | ~3,500 | Core sync engine, coordinator, conflict resolver | -| `Sync.SQLite` | ~2,500 | SQLite triggers, schema, repositories | -| `Sync.Postgres` | ~1,200 | PostgreSQL implementation | -| `Sync.Http` | ~800 | REST endpoints with SSE | -| `Sync.Tests` | ~5,000 | Core unit tests | -| `Sync.SQLite.Tests` | ~4,500 | SQLite integration tests | -| `Sync.Postgres.Tests` | ~1,500 | PostgreSQL integration tests | -| `Sync.Http.Tests` | ~3,500 | API endpoint tests | -| `Sync.Integration.Tests` | ~1,200 | Cross-database E2E tests | - -### Gatekeeper (`Gatekeeper/`) -An independent authentication and authorization microservice. - -**Features:** -- Passwordless authentication with WebAuthn/FIDO2 passkeys -- Role-based access control (RBAC) with hierarchical roles -- Record-level permissions for fine-grained access -- JWT session management with revocation -- Framework-agnostic REST API - -**Projects:** -| Project | Lines | Description | -|---------|-------|-------------| -| `Gatekeeper.Api` | ~1,200 | REST API with auth endpoints | -| `Gatekeeper.Migration` | ~250 | Database schema | -| `Gatekeeper.Api.Tests` | ~1,400 | Integration tests | - -### Healthcare Samples (`Samples/`) -A complete demonstration of the DataProvider suite with FHIR-compliant healthcare APIs. - -**Architecture:** -``` -Dashboard.Web (React/H5) - │ - ├──► Clinical.Api ◄──── Clinical.Sync ◄─┐ - │ (SQLite) │ - │ Patient, Encounter, Condition │ Practitioner→Provider - │ │ - └──► Scheduling.Api ◄── Scheduling.Sync ◄┘ - (SQLite) Patient→ScheduledPatient - Practitioner, Appointment -``` - -**Projects:** -| Project | Description | -|---------|-------------| -| `Clinical.Api` | FHIR Patient, Encounter, Condition, MedicationRequest | -| `Clinical.Sync` | Pulls Practitioner data from Scheduling | -| `Scheduling.Api` | FHIR Practitioner, Appointment, Schedule, Slot | -| `Scheduling.Sync` | Pulls Patient data from Clinical | -| `Dashboard.Web` | React 18 UI transpiled from C# via H5 | -| `Dashboard.Integration.Tests` | Playwright E2E tests | - -## Changes to Existing Components - -### DataProvider Core -- Enhanced `DbConnectionExtensions` and `DbTransactionExtensions` -- Improved code generation for table operations -- Better nullable handling in generated code -- Additional tests for edge cases - -### LQL -- Minor fixes to browser playground -- Improved file operation handling - -### Documentation -- Updated root README with complete suite overview -- New architecture diagrams showing component integration -- Updated CLAUDE.md with all components and coding rules -- Individual README files for Sync, Gatekeeper, and Samples - -## Breaking Changes - -None. This release adds new components without modifying existing APIs. - -## Testing - -All components include comprehensive test suites following the project's testing philosophy: - -- **E2E integration tests** with real databases (no mocks) -- **Cross-database tests** for Sync (SQLite ↔ PostgreSQL) -- **Playwright tests** for Dashboard UI - -Run all tests: -```bash -dotnet test -``` - -Run specific component: -```bash -dotnet test --filter "FullyQualifiedName~Sync" -dotnet test --filter "FullyQualifiedName~Gatekeeper" -dotnet test --filter "FullyQualifiedName~Samples" -``` - -## Dependencies - -New package dependencies: -- `Fido2.AspNet` - WebAuthn/FIDO2 implementation for Gatekeeper -- `Npgsql` - PostgreSQL driver for Sync.Postgres -- `H5` - C# to JavaScript transpiler for Dashboard - -## Documentation - -- [Sync Framework README](./Sync/README.md) -- [Sync Specification](./Sync/spec.md) -- [Gatekeeper README](./Gatekeeper/README.md) -- [Gatekeeper Specification](./Gatekeeper/spec.md) -- [Samples README](./Samples/readme.md) - -## Checklist - -- [x] All tests pass -- [x] Code formatted with `dotnet csharpier .` -- [x] Documentation updated -- [x] No breaking changes to existing APIs -- [x] Follows coding rules (no exceptions, no classes, Result types) diff --git a/Samples/.editorconfig b/Samples/.editorconfig index c9b564bb..1ea7230c 100644 --- a/Samples/.editorconfig +++ b/Samples/.editorconfig @@ -5,7 +5,6 @@ root = false # Suppress problematic analyzers in sample code dotnet_diagnostic.RS1035.severity = none dotnet_diagnostic.EPC12.severity = none -dotnet_diagnostic.CA1848.severity = none dotnet_diagnostic.CA2100.severity = none dotnet_diagnostic.CA1826.severity = none dotnet_diagnostic.IDE0037.severity = none diff --git a/Samples/Clinical/Clinical.Api.Tests/AuthorizationTests.cs b/Samples/Clinical/Clinical.Api.Tests/AuthorizationTests.cs new file mode 100644 index 00000000..5e46bb9c --- /dev/null +++ b/Samples/Clinical/Clinical.Api.Tests/AuthorizationTests.cs @@ -0,0 +1,253 @@ +using System.Net; +using System.Net.Http.Headers; +using System.Net.Http.Json; + +namespace Clinical.Api.Tests; + +/// +/// Authorization tests for Clinical.Api endpoints. +/// Tests that endpoints require proper authentication and permissions. +/// +public sealed class AuthorizationTests : IClassFixture +{ + private readonly HttpClient _client; + + /// + /// Initializes a new instance of the class. + /// + /// Shared factory instance. + public AuthorizationTests(ClinicalApiFactory factory) => _client = factory.CreateClient(); + + [Fact] + public async Task GetPatients_WithoutToken_ReturnsUnauthorized() + { + var response = await _client.GetAsync("/fhir/Patient/"); + + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task GetPatients_WithInvalidToken_ReturnsUnauthorized() + { + using var request = new HttpRequestMessage(HttpMethod.Get, "/fhir/Patient/"); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", "invalid-token"); + + var response = await _client.SendAsync(request); + + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task GetPatients_WithExpiredToken_ReturnsUnauthorized() + { + using var request = new HttpRequestMessage(HttpMethod.Get, "/fhir/Patient/"); + request.Headers.Authorization = new AuthenticationHeaderValue( + "Bearer", + TestTokenHelper.GenerateExpiredToken() + ); + + var response = await _client.SendAsync(request); + + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task GetPatients_WithValidToken_SucceedsInDevMode() + { + // In dev mode (default signing key is all zeros), Gatekeeper permission checks + // are bypassed to allow E2E testing without requiring Gatekeeper setup. + // Valid tokens pass through after local JWT validation. + using var request = new HttpRequestMessage(HttpMethod.Get, "/fhir/Patient/"); + request.Headers.Authorization = new AuthenticationHeaderValue( + "Bearer", + TestTokenHelper.GenerateNoRoleToken() + ); + + var response = await _client.SendAsync(request); + + // In dev mode, valid tokens succeed without permission checks + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + + [Fact] + public async Task CreatePatient_WithoutToken_ReturnsUnauthorized() + { + var patient = new + { + Active = true, + GivenName = "Test", + FamilyName = "Patient", + Gender = "male", + }; + + var response = await _client.PostAsJsonAsync("/fhir/Patient/", patient); + + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task GetEncounters_WithoutToken_ReturnsUnauthorized() + { + var response = await _client.GetAsync("/fhir/Patient/test-patient/Encounter/"); + + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task GetConditions_WithoutToken_ReturnsUnauthorized() + { + var response = await _client.GetAsync("/fhir/Patient/test-patient/Condition/"); + + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task GetMedicationRequests_WithoutToken_ReturnsUnauthorized() + { + var response = await _client.GetAsync("/fhir/Patient/test-patient/MedicationRequest/"); + + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task SyncChanges_WithoutToken_ReturnsUnauthorized() + { + var response = await _client.GetAsync("/sync/changes"); + + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task SyncOrigin_WithoutToken_ReturnsUnauthorized() + { + var response = await _client.GetAsync("/sync/origin"); + + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task SyncStatus_WithoutToken_ReturnsUnauthorized() + { + var response = await _client.GetAsync("/sync/status"); + + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task SyncRecords_WithoutToken_ReturnsUnauthorized() + { + var response = await _client.GetAsync("/sync/records"); + + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task SyncRetry_WithoutToken_ReturnsUnauthorized() + { + var response = await _client.PostAsync("/sync/records/test-id/retry", null); + + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task PatientSearch_WithoutToken_ReturnsUnauthorized() + { + var response = await _client.GetAsync("/fhir/Patient/_search?q=test"); + + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task GetPatientById_WithoutToken_ReturnsUnauthorized() + { + var response = await _client.GetAsync("/fhir/Patient/test-patient-id"); + + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task UpdatePatient_WithoutToken_ReturnsUnauthorized() + { + var patient = new + { + Active = true, + GivenName = "Updated", + FamilyName = "Patient", + Gender = "male", + }; + + var response = await _client.PutAsJsonAsync("/fhir/Patient/test-id", patient); + + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task CreateEncounter_WithoutToken_ReturnsUnauthorized() + { + var encounter = new + { + Status = "planned", + Class = "outpatient", + PractitionerId = "pract-1", + ServiceType = "General", + ReasonCode = "Checkup", + PeriodStart = "2024-01-01T10:00:00Z", + PeriodEnd = "2024-01-01T11:00:00Z", + Notes = "Test", + }; + + var response = await _client.PostAsJsonAsync( + "/fhir/Patient/test-patient/Encounter/", + encounter + ); + + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task CreateCondition_WithoutToken_ReturnsUnauthorized() + { + var condition = new + { + ClinicalStatus = "active", + VerificationStatus = "confirmed", + Category = "encounter-diagnosis", + Severity = "moderate", + CodeSystem = "http://snomed.info/sct", + CodeValue = "123456", + CodeDisplay = "Test Condition", + }; + + var response = await _client.PostAsJsonAsync( + "/fhir/Patient/test-patient/Condition/", + condition + ); + + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } + + [Fact] + public async Task CreateMedicationRequest_WithoutToken_ReturnsUnauthorized() + { + var medication = new + { + Status = "active", + Intent = "order", + PractitionerId = "pract-1", + EncounterId = "enc-1", + MedicationCode = "12345", + MedicationDisplay = "Test Medication", + DosageInstruction = "Take once daily", + Quantity = 30, + Unit = "tablets", + Refills = 2, + }; + + var response = await _client.PostAsJsonAsync( + "/fhir/Patient/test-patient/MedicationRequest/", + medication + ); + + Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); + } +} diff --git a/Samples/Clinical/Clinical.Api.Tests/Clinical.Api.Tests.csproj b/Samples/Clinical/Clinical.Api.Tests/Clinical.Api.Tests.csproj index f4e7c88e..7a58952d 100644 --- a/Samples/Clinical/Clinical.Api.Tests/Clinical.Api.Tests.csproj +++ b/Samples/Clinical/Clinical.Api.Tests/Clinical.Api.Tests.csproj @@ -4,7 +4,7 @@ Library true Clinical.Api.Tests - CS1591;CA1707;CA1307;CA1062;CA1515;CA2100;CA1305;CA1822;CA1859;CA1848;CA1849;CA2234;CA1812;CA2007;CA2000;xUnit1030 + CS1591;CA1707;CA1307;CA1062;CA1515;CA2100;CA1822;CA1859;CA1849;CA2234;CA1812;CA2007;CA2000;xUnit1030 @@ -24,6 +24,7 @@ + diff --git a/Samples/Clinical/Clinical.Api.Tests/ClinicalApiFactory.cs b/Samples/Clinical/Clinical.Api.Tests/ClinicalApiFactory.cs index 3d913ab1..ccb9137e 100644 --- a/Samples/Clinical/Clinical.Api.Tests/ClinicalApiFactory.cs +++ b/Samples/Clinical/Clinical.Api.Tests/ClinicalApiFactory.cs @@ -1,8 +1,8 @@ -namespace Clinical.Api.Tests; - using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Mvc.Testing; +namespace Clinical.Api.Tests; + /// /// WebApplicationFactory for Clinical.Api e2e testing. /// Just configures a temp database path - Program.cs does ALL initialization. diff --git a/Samples/Clinical/Clinical.Api.Tests/ConditionEndpointTests.cs b/Samples/Clinical/Clinical.Api.Tests/ConditionEndpointTests.cs index 5c75d3e7..4ebd60cb 100644 --- a/Samples/Clinical/Clinical.Api.Tests/ConditionEndpointTests.cs +++ b/Samples/Clinical/Clinical.Api.Tests/ConditionEndpointTests.cs @@ -1,15 +1,28 @@ -namespace Clinical.Api.Tests; - using System.Net; +using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; +namespace Clinical.Api.Tests; + /// /// E2E tests for Condition FHIR endpoints - REAL database, NO mocks. /// Each test creates its own isolated factory and database. /// public sealed class ConditionEndpointTests { + private static readonly string AuthToken = TestTokenHelper.GenerateClinicianToken(); + + private static HttpClient CreateAuthenticatedClient(ClinicalApiFactory factory) + { + var client = factory.CreateClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( + "Bearer", + AuthToken + ); + return client; + } + private static async Task CreateTestPatientAsync(HttpClient client) { var patient = new @@ -29,7 +42,7 @@ private static async Task CreateTestPatientAsync(HttpClient client) public async Task GetConditionsByPatient_ReturnsEmptyList_WhenNoConditions() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var response = await client.GetAsync($"/fhir/Patient/{patientId}/Condition/"); @@ -43,7 +56,7 @@ public async Task GetConditionsByPatient_ReturnsEmptyList_WhenNoConditions() public async Task CreateCondition_ReturnsCreated_WithValidData() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var request = new { @@ -75,7 +88,7 @@ public async Task CreateCondition_ReturnsCreated_WithValidData() public async Task CreateCondition_WithAllClinicalStatuses() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var statuses = new[] { "active", @@ -112,7 +125,7 @@ public async Task CreateCondition_WithAllClinicalStatuses() public async Task CreateCondition_WithAllSeverities() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var severities = new[] { "mild", "moderate", "severe" }; foreach (var severity in severities) @@ -142,7 +155,7 @@ public async Task CreateCondition_WithAllSeverities() public async Task CreateCondition_WithVerificationStatuses() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var statuses = new[] { "unconfirmed", @@ -179,7 +192,7 @@ public async Task CreateCondition_WithVerificationStatuses() public async Task GetConditionsByPatient_ReturnsConditions_WhenExist() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var request1 = new { @@ -211,7 +224,7 @@ public async Task GetConditionsByPatient_ReturnsConditions_WhenExist() public async Task CreateCondition_SetsRecordedDate() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var request = new { @@ -236,7 +249,7 @@ public async Task CreateCondition_SetsRecordedDate() public async Task CreateCondition_SetsVersionIdToOne() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var request = new { @@ -259,7 +272,7 @@ public async Task CreateCondition_SetsVersionIdToOne() public async Task CreateCondition_WithEncounterReference() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var encounterRequest = new @@ -297,7 +310,7 @@ public async Task CreateCondition_WithEncounterReference() public async Task CreateCondition_WithNotes() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var request = new { diff --git a/Samples/Clinical/Clinical.Api.Tests/DashboardIntegrationTests.cs b/Samples/Clinical/Clinical.Api.Tests/DashboardIntegrationTests.cs index e03106de..9f92dd78 100644 --- a/Samples/Clinical/Clinical.Api.Tests/DashboardIntegrationTests.cs +++ b/Samples/Clinical/Clinical.Api.Tests/DashboardIntegrationTests.cs @@ -1,3 +1,5 @@ +using System.Net.Http.Headers; + namespace Clinical.Api.Tests; /// @@ -9,15 +11,24 @@ namespace Clinical.Api.Tests; public sealed class DashboardIntegrationTests : IClassFixture { private readonly HttpClient _client; + private readonly string _authToken = TestTokenHelper.GenerateClinicianToken(); /// /// The actual URL where Dashboard runs (for CORS origin testing). /// private const string DashboardOrigin = "http://localhost:5173"; + /// + /// Initializes a new instance of the class. + /// + /// Shared factory instance. public DashboardIntegrationTests(ClinicalApiFactory factory) { _client = factory.CreateClient(); + _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( + "Bearer", + _authToken + ); } #region URL Configuration Tests @@ -57,6 +68,7 @@ public async Task ClinicalApi_Returns_CorsHeaders_ForDashboardOrigin() var request = new HttpRequestMessage(HttpMethod.Get, "/fhir/Patient"); request.Headers.Add("Origin", DashboardOrigin); + request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _authToken); var response = await _client.SendAsync(request); diff --git a/Samples/Clinical/Clinical.Api.Tests/EncounterEndpointTests.cs b/Samples/Clinical/Clinical.Api.Tests/EncounterEndpointTests.cs index 3794453f..610de404 100644 --- a/Samples/Clinical/Clinical.Api.Tests/EncounterEndpointTests.cs +++ b/Samples/Clinical/Clinical.Api.Tests/EncounterEndpointTests.cs @@ -1,15 +1,28 @@ -namespace Clinical.Api.Tests; - using System.Net; +using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; +namespace Clinical.Api.Tests; + /// /// E2E tests for Encounter FHIR endpoints - REAL database, NO mocks. /// Each test creates its own isolated factory and database. /// public sealed class EncounterEndpointTests { + private static readonly string AuthToken = TestTokenHelper.GenerateClinicianToken(); + + private static HttpClient CreateAuthenticatedClient(ClinicalApiFactory factory) + { + var client = factory.CreateClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( + "Bearer", + AuthToken + ); + return client; + } + private static async Task CreateTestPatientAsync(HttpClient client) { var patient = new @@ -29,7 +42,7 @@ private static async Task CreateTestPatientAsync(HttpClient client) public async Task GetEncountersByPatient_ReturnsEmptyList_WhenNoEncounters() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var response = await client.GetAsync($"/fhir/Patient/{patientId}/Encounter/"); @@ -43,7 +56,7 @@ public async Task GetEncountersByPatient_ReturnsEmptyList_WhenNoEncounters() public async Task CreateEncounter_ReturnsCreated_WithValidData() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var request = new { @@ -74,7 +87,7 @@ public async Task CreateEncounter_ReturnsCreated_WithValidData() public async Task CreateEncounter_WithAllStatuses() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var statuses = new[] { "planned", @@ -111,7 +124,7 @@ public async Task CreateEncounter_WithAllStatuses() public async Task CreateEncounter_WithAllClasses() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var classes = new[] { "ambulatory", "emergency", "inpatient", "observation", "virtual" }; foreach (var encounterClass in classes) @@ -139,7 +152,7 @@ public async Task CreateEncounter_WithAllClasses() public async Task GetEncountersByPatient_ReturnsEncounters_WhenExist() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var request1 = new { @@ -169,7 +182,7 @@ public async Task GetEncountersByPatient_ReturnsEncounters_WhenExist() public async Task CreateEncounter_SetsVersionIdToOne() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var request = new { @@ -191,7 +204,7 @@ public async Task CreateEncounter_SetsVersionIdToOne() public async Task CreateEncounter_SetsLastUpdatedTimestamp() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var request = new { @@ -215,7 +228,7 @@ public async Task CreateEncounter_SetsLastUpdatedTimestamp() public async Task CreateEncounter_WithNotes() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var request = new { @@ -241,7 +254,7 @@ public async Task CreateEncounter_WithNotes() public async Task CreateEncounter_WithPeriodEndTime() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var request = new { diff --git a/Samples/Clinical/Clinical.Api.Tests/GlobalUsings.cs b/Samples/Clinical/Clinical.Api.Tests/GlobalUsings.cs index c802f448..f68c2477 100644 --- a/Samples/Clinical/Clinical.Api.Tests/GlobalUsings.cs +++ b/Samples/Clinical/Clinical.Api.Tests/GlobalUsings.cs @@ -1 +1,2 @@ +global using Samples.Authorization; global using Xunit; diff --git a/Samples/Clinical/Clinical.Api.Tests/MedicationRequestEndpointTests.cs b/Samples/Clinical/Clinical.Api.Tests/MedicationRequestEndpointTests.cs index 7f4c4b6c..857c143e 100644 --- a/Samples/Clinical/Clinical.Api.Tests/MedicationRequestEndpointTests.cs +++ b/Samples/Clinical/Clinical.Api.Tests/MedicationRequestEndpointTests.cs @@ -1,15 +1,28 @@ -namespace Clinical.Api.Tests; - using System.Net; +using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; +namespace Clinical.Api.Tests; + /// /// E2E tests for MedicationRequest FHIR endpoints - REAL database, NO mocks. /// Each test creates its own isolated factory and database. /// public sealed class MedicationRequestEndpointTests { + private static readonly string AuthToken = TestTokenHelper.GenerateClinicianToken(); + + private static HttpClient CreateAuthenticatedClient(ClinicalApiFactory factory) + { + var client = factory.CreateClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( + "Bearer", + AuthToken + ); + return client; + } + private static async Task CreateTestPatientAsync(HttpClient client) { var patient = new @@ -29,7 +42,7 @@ private static async Task CreateTestPatientAsync(HttpClient client) public async Task GetMedicationsByPatient_ReturnsEmptyList_WhenNoMedications() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var response = await client.GetAsync($"/fhir/Patient/{patientId}/MedicationRequest/"); @@ -43,7 +56,7 @@ public async Task GetMedicationsByPatient_ReturnsEmptyList_WhenNoMedications() public async Task CreateMedicationRequest_ReturnsCreated_WithValidData() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var request = new { @@ -79,7 +92,7 @@ public async Task CreateMedicationRequest_ReturnsCreated_WithValidData() public async Task CreateMedicationRequest_WithAllStatuses() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var statuses = new[] { "active", "on-hold", "cancelled", "completed", "stopped", "draft" }; foreach (var status in statuses) @@ -110,7 +123,7 @@ public async Task CreateMedicationRequest_WithAllStatuses() public async Task CreateMedicationRequest_WithAllIntents() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var intents = new[] { "proposal", @@ -151,7 +164,7 @@ public async Task CreateMedicationRequest_WithAllIntents() public async Task GetMedicationsByPatient_ReturnsMedications_WhenExist() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var request1 = new { @@ -187,7 +200,7 @@ public async Task GetMedicationsByPatient_ReturnsMedications_WhenExist() public async Task CreateMedicationRequest_SetsVersionIdToOne() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var request = new { @@ -212,7 +225,7 @@ public async Task CreateMedicationRequest_SetsVersionIdToOne() public async Task CreateMedicationRequest_SetsAuthoredOn() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var request = new { @@ -239,7 +252,7 @@ public async Task CreateMedicationRequest_SetsAuthoredOn() public async Task CreateMedicationRequest_WithQuantityAndUnit() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var request = new { @@ -267,7 +280,7 @@ public async Task CreateMedicationRequest_WithQuantityAndUnit() public async Task CreateMedicationRequest_WithDosageInstruction() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var request = new { @@ -296,7 +309,7 @@ public async Task CreateMedicationRequest_WithDosageInstruction() public async Task CreateMedicationRequest_WithEncounterId() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var encounterRequest = new @@ -339,7 +352,7 @@ public async Task CreateMedicationRequest_WithEncounterId() public async Task CreateMedicationRequest_WithZeroRefills() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientId = await CreateTestPatientAsync(client); var request = new { diff --git a/Samples/Clinical/Clinical.Api.Tests/PatientEndpointTests.cs b/Samples/Clinical/Clinical.Api.Tests/PatientEndpointTests.cs index ac2baf37..1d44dd2c 100644 --- a/Samples/Clinical/Clinical.Api.Tests/PatientEndpointTests.cs +++ b/Samples/Clinical/Clinical.Api.Tests/PatientEndpointTests.cs @@ -1,9 +1,9 @@ -namespace Clinical.Api.Tests; - using System.Net; +using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; -using Xunit; + +namespace Clinical.Api.Tests; /// /// E2E tests for Patient FHIR endpoints - REAL database, NO mocks. @@ -12,12 +12,20 @@ namespace Clinical.Api.Tests; public sealed class PatientEndpointTests : IClassFixture { private readonly HttpClient _client; + private readonly string _authToken = TestTokenHelper.GenerateClinicianToken(); /// /// Initializes a new instance of the class. /// /// Shared factory instance. - public PatientEndpointTests(ClinicalApiFactory factory) => _client = factory.CreateClient(); + public PatientEndpointTests(ClinicalApiFactory factory) + { + _client = factory.CreateClient(); + _client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( + "Bearer", + _authToken + ); + } [Fact] public async Task GetPatients_ReturnsOk() diff --git a/Samples/Clinical/Clinical.Api.Tests/SyncEndpointTests.cs b/Samples/Clinical/Clinical.Api.Tests/SyncEndpointTests.cs index de099e33..cb715966 100644 --- a/Samples/Clinical/Clinical.Api.Tests/SyncEndpointTests.cs +++ b/Samples/Clinical/Clinical.Api.Tests/SyncEndpointTests.cs @@ -1,9 +1,10 @@ -namespace Clinical.Api.Tests; - using System.Net; +using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text.Json; +namespace Clinical.Api.Tests; + /// /// E2E tests for Sync endpoints - REAL database, NO mocks. /// Tests sync log generation and origin tracking. @@ -11,11 +12,23 @@ namespace Clinical.Api.Tests; /// public sealed class SyncEndpointTests { + private static readonly string AuthToken = TestTokenHelper.GenerateClinicianToken(); + + private static HttpClient CreateAuthenticatedClient(ClinicalApiFactory factory) + { + var client = factory.CreateClient(); + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue( + "Bearer", + AuthToken + ); + return client; + } + [Fact] public async Task GetSyncOrigin_ReturnsOriginId() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var response = await client.GetAsync("/sync/origin"); @@ -30,7 +43,7 @@ public async Task GetSyncOrigin_ReturnsOriginId() public async Task GetSyncChanges_ReturnsEmptyList_WhenNoChanges() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var response = await client.GetAsync("/sync/changes?fromVersion=999999"); @@ -43,7 +56,7 @@ public async Task GetSyncChanges_ReturnsEmptyList_WhenNoChanges() public async Task GetSyncChanges_ReturnChanges_AfterPatientCreated() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientRequest = new { Active = true, @@ -66,7 +79,7 @@ public async Task GetSyncChanges_ReturnChanges_AfterPatientCreated() public async Task GetSyncChanges_RespectsLimitParameter() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); for (var i = 0; i < 5; i++) { var patientRequest = new @@ -91,7 +104,7 @@ public async Task GetSyncChanges_RespectsLimitParameter() public async Task GetSyncChanges_ContainsTableName() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientRequest = new { Active = true, @@ -112,7 +125,7 @@ public async Task GetSyncChanges_ContainsTableName() public async Task GetSyncChanges_ContainsOperation() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientRequest = new { Active = true, @@ -141,7 +154,7 @@ public async Task GetSyncChanges_ContainsOperation() public async Task GetSyncChanges_TracksEncounterChanges() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientRequest = new { Active = true, @@ -172,7 +185,7 @@ public async Task GetSyncChanges_TracksEncounterChanges() public async Task GetSyncChanges_TracksConditionChanges() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientRequest = new { Active = true, @@ -204,7 +217,7 @@ public async Task GetSyncChanges_TracksConditionChanges() public async Task GetSyncChanges_TracksMedicationRequestChanges() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var patientRequest = new { Active = true, @@ -252,7 +265,7 @@ await client.PostAsJsonAsync( public async Task GetSyncStatus_ReturnsServiceStatus() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); var response = await client.GetAsync("/sync/status"); @@ -271,8 +284,7 @@ public async Task GetSyncStatus_ReturnsServiceStatus() ); Assert.True(result.TryGetProperty("lastSyncTime", out _)); - Assert.True(result.TryGetProperty("pendingCount", out _)); - Assert.True(result.TryGetProperty("failedCount", out _)); + Assert.True(result.TryGetProperty("totalRecords", out _)); } /// @@ -283,7 +295,7 @@ public async Task GetSyncStatus_ReturnsServiceStatus() public async Task GetSyncRecords_ReturnsPaginatedRecords() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); // Create some data to generate sync records var patientRequest = new @@ -317,7 +329,7 @@ public async Task GetSyncRecords_ReturnsPaginatedRecords() public async Task GetSyncRecords_SearchByEntityId() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); // Create a patient with known ID pattern var patientRequest = new @@ -349,7 +361,7 @@ public async Task GetSyncRecords_SearchByEntityId() public async Task PostSyncRetry_RetriesFailedRecord() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); // First we need a failed sync record to retry // For now, test that the endpoint exists and accepts the request @@ -373,7 +385,7 @@ public async Task PostSyncRetry_RetriesFailedRecord() public async Task GetSyncRecords_ContainsRequiredFields() { using var factory = new ClinicalApiFactory(); - var client = factory.CreateClient(); + var client = CreateAuthenticatedClient(factory); // Create data to generate sync records var patientRequest = new diff --git a/Samples/Clinical/Clinical.Api.Tests/SyncWorkerFaultToleranceTests.cs b/Samples/Clinical/Clinical.Api.Tests/SyncWorkerFaultToleranceTests.cs new file mode 100644 index 00000000..3ed32bc6 --- /dev/null +++ b/Samples/Clinical/Clinical.Api.Tests/SyncWorkerFaultToleranceTests.cs @@ -0,0 +1,440 @@ +using Microsoft.Extensions.Logging; + +namespace Clinical.Api.Tests; + +/// +/// Tests proving sync worker fault tolerance behavior. +/// These tests verify that sync workers: +/// 1. NEVER crash when APIs are unavailable +/// 2. Retry with exponential backoff +/// 3. Log appropriately at different failure levels +/// 4. Recover gracefully when APIs become available +/// +public sealed class SyncWorkerFaultToleranceTests +{ + /// + /// Proves that sync worker handles HttpRequestException without crashing. + /// Simulates API being completely unreachable. + /// + [Fact] + public async Task SyncWorker_HandlesHttpRequestException_WithoutCrashing() + { + // Arrange + var logMessages = new List<(LogLevel Level, string Message)>(); + var logger = new TestLogger(logMessages); + var cancellationTokenSource = new CancellationTokenSource(); + var failureCount = 0; + + // Simulate API that always fails with connection refused + Func> performSync = () => + { + failureCount++; + if (failureCount >= 3) + { + cancellationTokenSource.Cancel(); + } + throw new HttpRequestException("Connection refused (localhost:5001)"); + }; + + var worker = new FaultTolerantSyncWorker(logger, performSync); + + // Act - Run the worker until it handles 3 failures + await worker.ExecuteAsync(cancellationTokenSource.Token); + + // Assert - Worker should have handled multiple failures without crashing + Assert.True(failureCount >= 3, "Worker should have retried at least 3 times"); + Assert.Contains( + logMessages, + m => m.Message.Contains("[SYNC-RETRY]") || m.Message.Contains("[SYNC-FAULT]") + ); + Assert.Contains(logMessages, m => m.Message.Contains("Connection refused")); + } + + /// + /// Proves that sync worker uses exponential backoff when retrying. + /// + [Fact] + public async Task SyncWorker_UsesExponentialBackoff_OnConsecutiveFailures() + { + // Arrange + var logMessages = new List<(LogLevel Level, string Message)>(); + var logger = new TestLogger(logMessages); + var cancellationTokenSource = new CancellationTokenSource(); + var retryDelays = new List(); + var failureCount = 0; + + Func> performSync = () => + { + failureCount++; + if (failureCount >= 5) + { + cancellationTokenSource.Cancel(); + } + throw new HttpRequestException("Connection refused"); + }; + + var worker = new FaultTolerantSyncWorker(logger, performSync, retryDelays.Add); + + // Act + await worker.ExecuteAsync(cancellationTokenSource.Token); + + // Assert - Delays should increase (exponential backoff) + Assert.True(retryDelays.Count >= 4, "Should have recorded multiple retry delays"); + for (var i = 1; i < retryDelays.Count; i++) + { + Assert.True( + retryDelays[i] >= retryDelays[i - 1], + $"Delay should increase or stay same. Delay[{i - 1}]={retryDelays[i - 1]}, Delay[{i}]={retryDelays[i]}" + ); + } + } + + /// + /// Proves that sync worker escalates log level after multiple consecutive failures. + /// + [Fact] + public async Task SyncWorker_EscalatesLogLevel_AfterMultipleFailures() + { + // Arrange + var logMessages = new List<(LogLevel Level, string Message)>(); + var logger = new TestLogger(logMessages); + var cancellationTokenSource = new CancellationTokenSource(); + var failureCount = 0; + + Func> performSync = () => + { + failureCount++; + if (failureCount >= 5) + { + cancellationTokenSource.Cancel(); + } + throw new HttpRequestException("Connection refused"); + }; + + var worker = new FaultTolerantSyncWorker(logger, performSync); + + // Act + await worker.ExecuteAsync(cancellationTokenSource.Token); + + // Assert - Early failures should be Info, later ones should be Warning + var infoLogs = logMessages.Where(m => m.Level == LogLevel.Information).ToList(); + var warningLogs = logMessages.Where(m => m.Level == LogLevel.Warning).ToList(); + + Assert.True(infoLogs.Count > 0, "Should have info-level logs for early retries"); + Assert.True( + warningLogs.Count > 0, + "Should have warning-level logs after multiple failures" + ); + } + + /// + /// Proves that sync worker recovers and resets failure counter on success. + /// + [Fact] + public async Task SyncWorker_ResetsFailureCounter_OnSuccess() + { + // Arrange + var logMessages = new List<(LogLevel Level, string Message)>(); + var logger = new TestLogger(logMessages); + var cancellationTokenSource = new CancellationTokenSource(); + var callCount = 0; + + Func> performSync = () => + { + callCount++; + return callCount switch + { + 1 or 2 => throw new HttpRequestException("Connection refused"), // First 2 calls fail + 3 => Task.FromResult(true), // Third call succeeds + 4 => throw new HttpRequestException("Connection refused again"), // Fourth fails + _ => Task.FromException(new OperationCanceledException()), // Stop + }; + }; + + var worker = new FaultTolerantSyncWorker(logger, performSync); + + // Act + try + { + cancellationTokenSource.CancelAfter(TimeSpan.FromSeconds(5)); + await worker.ExecuteAsync(cancellationTokenSource.Token); + } + catch (OperationCanceledException) + { + // Expected + } + + // Assert - Should have logged recovery message + Assert.Contains(logMessages, m => m.Message.Contains("[SYNC-RECOVERED]")); + } + + /// + /// Proves that sync worker handles unexpected exceptions without crashing. + /// + [Fact] + public async Task SyncWorker_HandlesUnexpectedException_WithoutCrashing() + { + // Arrange + var logMessages = new List<(LogLevel Level, string Message)>(); + var logger = new TestLogger(logMessages); + var cancellationTokenSource = new CancellationTokenSource(); + var failureCount = 0; + + Func> performSync = () => + { + failureCount++; + if (failureCount >= 3) + { + cancellationTokenSource.Cancel(); + } + throw new InvalidOperationException("Unexpected database error"); + }; + + var worker = new FaultTolerantSyncWorker(logger, performSync); + + // Act + await worker.ExecuteAsync(cancellationTokenSource.Token); + + // Assert - Worker should have handled unexpected exceptions + Assert.True(failureCount >= 3, "Worker should have retried after unexpected exceptions"); + Assert.Contains(logMessages, m => m.Level == LogLevel.Error); + Assert.Contains(logMessages, m => m.Message.Contains("[SYNC-ERROR]")); + } + + /// + /// Proves that sync worker shuts down gracefully on cancellation. + /// + [Fact] + public async Task SyncWorker_ShutsDownGracefully_OnCancellation() + { + // Arrange + var logMessages = new List<(LogLevel Level, string Message)>(); + var logger = new TestLogger(logMessages); + var cancellationTokenSource = new CancellationTokenSource(); + + Func> performSync = async () => + { + await Task.Delay(100); + return true; + }; + + var worker = new FaultTolerantSyncWorker(logger, performSync); + + // Act - Cancel immediately + cancellationTokenSource.Cancel(); + await worker.ExecuteAsync(cancellationTokenSource.Token); + + // Assert - Should have logged shutdown message + Assert.Contains( + logMessages, + m => m.Message.Contains("[SYNC-SHUTDOWN]") || m.Message.Contains("[SYNC-EXIT]") + ); + } + + /// + /// Proves that backoff is capped at maximum value (30 seconds for HTTP errors). + /// + [Fact] + public async Task SyncWorker_CapsBackoff_AtMaximumValue() + { + // Arrange + var logMessages = new List<(LogLevel Level, string Message)>(); + var logger = new TestLogger(logMessages); + var cancellationTokenSource = new CancellationTokenSource(); + var retryDelays = new List(); + var failureCount = 0; + + Func> performSync = () => + { + failureCount++; + if (failureCount >= 10) + { + cancellationTokenSource.Cancel(); + } + throw new HttpRequestException("Connection refused"); + }; + + var worker = new FaultTolerantSyncWorker(logger, performSync, retryDelays.Add); + + // Act + await worker.ExecuteAsync(cancellationTokenSource.Token); + + // Assert - All delays should be capped at 30 seconds + Assert.True(retryDelays.All(d => d <= 30), "All delays should be capped at 30 seconds"); + // After enough failures, delays should hit the cap + Assert.Contains(retryDelays, d => d == 30); + } +} + +/// +/// Test implementation of fault-tolerant sync worker behavior. +/// Mirrors the actual SyncWorker fault tolerance patterns. +/// +internal sealed class FaultTolerantSyncWorker +{ + private readonly ILogger _logger; + private readonly Func> _performSync; + private readonly Action? _onRetryDelay; + + /// + /// Creates a fault-tolerant sync worker for testing. + /// + public FaultTolerantSyncWorker( + ILogger logger, + Func> performSync, + Action? onRetryDelay = null + ) + { + _logger = logger; + _performSync = performSync; + _onRetryDelay = onRetryDelay; + } + + /// + /// Executes the sync worker with fault tolerance. + /// NEVER crashes - handles all errors gracefully. + /// + public async Task ExecuteAsync(CancellationToken stoppingToken) + { + _logger.Log(LogLevel.Information, "[SYNC-START] Fault tolerant sync worker starting"); + + var consecutiveFailures = 0; + const int maxConsecutiveFailuresBeforeWarning = 3; + + while (!stoppingToken.IsCancellationRequested) + { + try + { + await _performSync().ConfigureAwait(false); + + if (consecutiveFailures > 0) + { + _logger.Log( + LogLevel.Information, + "[SYNC-RECOVERED] Sync recovered after {Count} consecutive failures", + consecutiveFailures + ); + consecutiveFailures = 0; + } + + try + { + await Task.Delay(TimeSpan.FromMilliseconds(10), stoppingToken) + .ConfigureAwait(false); + } + catch (OperationCanceledException) + { + break; + } + } + catch (HttpRequestException ex) + { + consecutiveFailures++; + var retryDelay = Math.Min(5 * consecutiveFailures, 30); + _onRetryDelay?.Invoke(retryDelay); + + if (consecutiveFailures >= maxConsecutiveFailuresBeforeWarning) + { + _logger.Log( + LogLevel.Warning, + "[SYNC-FAULT] API unreachable for {Count} consecutive attempts. Error: {Message}. Retrying in {Delay}s...", + consecutiveFailures, + ex.Message, + retryDelay + ); + } + else + { + _logger.Log( + LogLevel.Information, + "[SYNC-RETRY] API not reachable ({Message}). Attempt {Count}, retrying in {Delay}s...", + ex.Message, + consecutiveFailures, + retryDelay + ); + } + + try + { + await Task.Delay(TimeSpan.FromMilliseconds(retryDelay), stoppingToken) + .ConfigureAwait(false); + } + catch (OperationCanceledException) + { + break; + } + } + catch (TaskCanceledException) when (stoppingToken.IsCancellationRequested) + { + _logger.Log( + LogLevel.Information, + "[SYNC-SHUTDOWN] Sync worker shutting down gracefully" + ); + break; + } + catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested) + { + _logger.Log( + LogLevel.Information, + "[SYNC-SHUTDOWN] Sync worker shutting down gracefully" + ); + break; + } + catch (Exception ex) + { + consecutiveFailures++; + var retryDelay = Math.Min(10 * consecutiveFailures, 60); + _onRetryDelay?.Invoke(retryDelay); + + _logger.Log( + LogLevel.Error, + "[SYNC-ERROR] Unexpected error during sync (attempt {Count}). Retrying in {Delay}s. Error: {Message}", + consecutiveFailures, + retryDelay, + ex.Message + ); + + try + { + await Task.Delay(TimeSpan.FromMilliseconds(retryDelay), stoppingToken) + .ConfigureAwait(false); + } + catch (OperationCanceledException) + { + break; + } + } + } + + _logger.Log(LogLevel.Information, "[SYNC-EXIT] Sync worker exited"); + } +} + +/// +/// Test logger that captures log messages for assertion. +/// +internal sealed class TestLogger : ILogger +{ + private readonly List<(LogLevel Level, string Message)> _messages; + + /// + /// Creates a test logger that captures messages. + /// + public TestLogger(List<(LogLevel Level, string Message)> messages) => _messages = messages; + + /// + public IDisposable? BeginScope(TState state) + where TState : notnull => null; + + /// + public bool IsEnabled(LogLevel logLevel) => true; + + /// + public void Log( + LogLevel logLevel, + EventId eventId, + TState state, + Exception? exception, + Func formatter + ) => _messages.Add((logLevel, formatter(state, exception))); +} diff --git a/Samples/Clinical/Clinical.Api/.editorconfig b/Samples/Clinical/Clinical.Api/.editorconfig index dcd9ae5d..9501334c 100644 --- a/Samples/Clinical/Clinical.Api/.editorconfig +++ b/Samples/Clinical/Clinical.Api/.editorconfig @@ -3,7 +3,6 @@ root = false [*.cs] # Relax analyzer rules for sample code dotnet_diagnostic.CA1515.severity = none -dotnet_diagnostic.CA1848.severity = none dotnet_diagnostic.CA2100.severity = none dotnet_diagnostic.RS1035.severity = none dotnet_diagnostic.CA1508.severity = none diff --git a/Samples/Clinical/Clinical.Api/Clinical.Api.csproj b/Samples/Clinical/Clinical.Api/Clinical.Api.csproj index 9c52fdce..a262ee48 100644 --- a/Samples/Clinical/Clinical.Api/Clinical.Api.csproj +++ b/Samples/Clinical/Clinical.Api/Clinical.Api.csproj @@ -2,33 +2,49 @@ Exe - CA1848;CA1515;CA2100;RS1035;CA1508;CA2234 + CA1515;CA2100;RS1035;CA1508;CA2234 + + + + + - + + + + + PreserveNewest + + + + + + - - - - + + + + + - + diff --git a/Samples/Clinical/Clinical.Api/ClinicalSchema.cs b/Samples/Clinical/Clinical.Api/ClinicalSchema.cs deleted file mode 100644 index 97502de6..00000000 --- a/Samples/Clinical/Clinical.Api/ClinicalSchema.cs +++ /dev/null @@ -1,172 +0,0 @@ -using Migration; -using static Migration.PortableTypes; - -namespace Clinical.Api; - -/// -/// Database-independent schema definition for Clinical FHIR R4 resources. -/// See: https://hl7.org/fhir/R4/ -/// -public static class ClinicalSchema -{ - /// - /// Gets the complete Clinical database schema definition. - /// - public static SchemaDefinition Definition { get; } = BuildSchema(); - - private static SchemaDefinition BuildSchema() => - Schema - .Define("clinical") - .Table( - "fhir_Patient", - t => - t.Column("Id", Text, c => c.PrimaryKey()) - .Column("Active", Int, c => c.NotNull().Default("1")) - .Column("GivenName", Text, c => c.NotNull()) - .Column("FamilyName", Text, c => c.NotNull()) - .Column("BirthDate", Text) - .Column( - "Gender", - Text, - c => c.Check("Gender IN ('male', 'female', 'other', 'unknown')") - ) - .Column("Phone", Text) - .Column("Email", Text) - .Column("AddressLine", Text) - .Column("City", Text) - .Column("State", Text) - .Column("PostalCode", Text) - .Column("Country", Text) - .Column("LastUpdated", Text, c => c.NotNull().Default("(datetime('now'))")) - .Column("VersionId", Int, c => c.NotNull().Default("1")) - .Index("idx_fhir_patient_family", "FamilyName") - .Index("idx_fhir_patient_given", "GivenName") - ) - .Table( - "fhir_Encounter", - t => - t.Column("Id", Text, c => c.PrimaryKey()) - .Column( - "Status", - Text, - c => - c.NotNull() - .Check( - "Status IN ('planned', 'arrived', 'triaged', 'in-progress', 'onleave', 'finished', 'cancelled', 'entered-in-error')" - ) - ) - .Column( - "Class", - Text, - c => - c.NotNull() - .Check( - "Class IN ('ambulatory', 'emergency', 'inpatient', 'observation', 'virtual')" - ) - ) - .Column("PatientId", Text, c => c.NotNull()) - .Column("PractitionerId", Text) - .Column("ServiceType", Text) - .Column("ReasonCode", Text) - .Column("PeriodStart", Text, c => c.NotNull()) - .Column("PeriodEnd", Text) - .Column("Notes", Text) - .Column("LastUpdated", Text, c => c.NotNull().Default("(datetime('now'))")) - .Column("VersionId", Int, c => c.NotNull().Default("1")) - .ForeignKey("PatientId", "fhir_Patient", "Id") - .Index("idx_fhir_encounter_patient", "PatientId") - ) - .Table( - "fhir_Condition", - t => - t.Column("Id", Text, c => c.PrimaryKey()) - .Column( - "ClinicalStatus", - Text, - c => - c.NotNull() - .Check( - "ClinicalStatus IN ('active', 'recurrence', 'relapse', 'inactive', 'remission', 'resolved')" - ) - ) - .Column( - "VerificationStatus", - Text, - c => - c.Check( - "VerificationStatus IN ('unconfirmed', 'provisional', 'differential', 'confirmed', 'refuted', 'entered-in-error')" - ) - ) - .Column("Category", Text, c => c.Default("'problem-list-item'")) - .Column( - "Severity", - Text, - c => c.Check("Severity IN ('mild', 'moderate', 'severe')") - ) - .Column( - "CodeSystem", - Text, - c => c.NotNull().Default("'http://hl7.org/fhir/sid/icd-10-cm'") - ) - .Column("CodeValue", Text, c => c.NotNull()) - .Column("CodeDisplay", Text, c => c.NotNull()) - .Column("SubjectReference", Text, c => c.NotNull()) - .Column("EncounterReference", Text) - .Column("OnsetDateTime", Text) - .Column("RecordedDate", Text, c => c.NotNull().Default("(date('now'))")) - .Column("RecorderReference", Text) - .Column("NoteText", Text) - .Column("LastUpdated", Text, c => c.NotNull().Default("(datetime('now'))")) - .Column("VersionId", Int, c => c.NotNull().Default("1")) - .ForeignKey("SubjectReference", "fhir_Patient", "Id") - .Index("idx_fhir_condition_patient", "SubjectReference") - ) - .Table( - "fhir_MedicationRequest", - t => - t.Column("Id", Text, c => c.PrimaryKey()) - .Column( - "Status", - Text, - c => - c.NotNull() - .Check( - "Status IN ('active', 'on-hold', 'cancelled', 'completed', 'entered-in-error', 'stopped', 'draft')" - ) - ) - .Column( - "Intent", - Text, - c => - c.NotNull() - .Check( - "Intent IN ('proposal', 'plan', 'order', 'original-order', 'reflex-order', 'filler-order', 'instance-order', 'option')" - ) - ) - .Column("PatientId", Text, c => c.NotNull()) - .Column("PractitionerId", Text, c => c.NotNull()) - .Column("EncounterId", Text) - .Column("MedicationCode", Text, c => c.NotNull()) - .Column("MedicationDisplay", Text, c => c.NotNull()) - .Column("DosageInstruction", Text) - .Column("Quantity", Float64) - .Column("Unit", Text) - .Column("Refills", Int, c => c.NotNull().Default("0")) - .Column("AuthoredOn", Text, c => c.NotNull().Default("(datetime('now'))")) - .Column("LastUpdated", Text, c => c.NotNull().Default("(datetime('now'))")) - .Column("VersionId", Int, c => c.NotNull().Default("1")) - .ForeignKey("PatientId", "fhir_Patient", "Id") - .ForeignKey("EncounterId", "fhir_Encounter", "Id") - .Index("idx_fhir_medication_patient", "PatientId") - ) - .Table( - "sync_Provider", - t => - t.Column("ProviderId", Text, c => c.PrimaryKey()) - .Column("FirstName", Text, c => c.NotNull()) - .Column("LastName", Text, c => c.NotNull()) - .Column("Specialty", Text) - .Column("SyncedAt", Text, c => c.NotNull().Default("(datetime('now'))")) - ) - .Build(); -} diff --git a/Samples/Clinical/Clinical.Api/DatabaseSetup.cs b/Samples/Clinical/Clinical.Api/DatabaseSetup.cs index 2c75cc0e..8ea5da82 100644 --- a/Samples/Clinical/Clinical.Api/DatabaseSetup.cs +++ b/Samples/Clinical/Clinical.Api/DatabaseSetup.cs @@ -36,10 +36,13 @@ public static void Initialize(SqliteConnection connection, ILogger logger) return; } - // Use Migration tool to create schema from ClinicalSchema metadata + // Use Migration tool to create schema from YAML (source of truth) try { - foreach (var table in ClinicalSchema.Definition.Tables) + var yamlPath = Path.Combine(AppContext.BaseDirectory, "clinical-schema.yaml"); + var schema = SchemaYamlSerializer.FromYamlFile(yamlPath); + + foreach (var table in schema.Tables) { var ddl = SqliteDdlGenerator.Generate(new CreateTableOperation(table)); using var cmd = connection.CreateCommand(); @@ -48,10 +51,7 @@ public static void Initialize(SqliteConnection connection, ILogger logger) logger.Log(LogLevel.Debug, "Created table {TableName}", table.Name); } - logger.Log( - LogLevel.Information, - "Created Clinical database schema from ClinicalSchema metadata" - ); + logger.Log(LogLevel.Information, "Created Clinical database schema from YAML"); } catch (Exception ex) { diff --git a/Samples/Clinical/Clinical.Api/GlobalUsings.cs b/Samples/Clinical/Clinical.Api/GlobalUsings.cs index a30c7e12..22f6c476 100644 --- a/Samples/Clinical/Clinical.Api/GlobalUsings.cs +++ b/Samples/Clinical/Clinical.Api/GlobalUsings.cs @@ -68,9 +68,9 @@ System.Collections.Immutable.ImmutableList, Selecta.SqlError >.Ok, Selecta.SqlError>; -global using InsertError = Outcome.Result.Error; +global using InsertError = Outcome.Result.Error; // Insert result type aliases -global using InsertOk = Outcome.Result.Ok; +global using InsertOk = Outcome.Result.Ok; global using SearchPatientsError = Outcome.Result< System.Collections.Immutable.ImmutableList, Selecta.SqlError diff --git a/Samples/Clinical/Clinical.Api/Program.cs b/Samples/Clinical/Clinical.Api/Program.cs index ab444355..66c03ec0 100644 --- a/Samples/Clinical/Clinical.Api/Program.cs +++ b/Samples/Clinical/Clinical.Api/Program.cs @@ -1,9 +1,10 @@ -#pragma warning disable CS8509 // Exhaustive switch - Exhaustion analyzer handles this #pragma warning disable IDE0037 // Use inferred member name - prefer explicit for clarity in API responses +using System.Collections.Immutable; using System.Globalization; using Clinical.Api; using Microsoft.AspNetCore.Http.Json; +using Samples.Authorization; var builder = WebApplication.CreateBuilder(args); @@ -41,6 +42,22 @@ return conn; }); +// Gatekeeper configuration for authorization +var gatekeeperUrl = builder.Configuration["Gatekeeper:BaseUrl"] ?? "http://localhost:5002"; +var signingKeyBase64 = builder.Configuration["Jwt:SigningKey"]; +var signingKey = string.IsNullOrEmpty(signingKeyBase64) + ? ImmutableArray.Create(new byte[32]) // Default empty key for development (MUST configure in production) + : ImmutableArray.Create(Convert.FromBase64String(signingKeyBase64)); + +builder.Services.AddHttpClient( + "Gatekeeper", + client => + { + client.BaseAddress = new Uri(gatekeeperUrl); + client.Timeout = TimeSpan.FromSeconds(5); + } +); + var app = builder.Build(); using (var conn = new SqliteConnection(connectionString)) @@ -52,554 +69,737 @@ // Enable CORS app.UseCors("Dashboard"); +// Get HttpClientFactory for auth filters +var httpClientFactory = app.Services.GetRequiredService(); +Func getGatekeeperClient = () => httpClientFactory.CreateClient("Gatekeeper"); + var patientGroup = app.MapGroup("/fhir/Patient").WithTags("Patient"); -patientGroup.MapGet( - "/", - async ( - bool? active, - string? familyName, - string? givenName, - string? gender, - Func getConn - ) => - { - using var conn = getConn(); - var result = await conn.GetPatientsAsync( - active.HasValue ? (active.Value ? 1L : 0L) : DBNull.Value, - familyName ?? (object)DBNull.Value, - givenName ?? (object)DBNull.Value, - gender ?? (object)DBNull.Value - ) - .ConfigureAwait(false); - return result switch +patientGroup + .MapGet( + "/", + async ( + bool? active, + string? familyName, + string? givenName, + string? gender, + Func getConn + ) => { - GetPatientsOk(var patients) => Results.Ok(patients), - GetPatientsError(var err) => Results.Problem(err.Message), - }; - } -); + using var conn = getConn(); + var result = await conn.GetPatientsAsync( + active.HasValue ? (active.Value ? 1L : 0L) : DBNull.Value, + familyName ?? (object)DBNull.Value, + givenName ?? (object)DBNull.Value, + gender ?? (object)DBNull.Value + ) + .ConfigureAwait(false); + return result switch + { + GetPatientsOk(var patients) => Results.Ok(patients), + GetPatientsError(var err) => Results.Problem(err.Message), + }; + } + ) + .AddEndpointFilterFactory( + EndpointFilterFactories.RequirePermission( + FhirPermissions.PatientRead, + signingKey, + getGatekeeperClient, + app.Logger + ) + ); -patientGroup.MapGet( - "/{id}", - async (string id, Func getConn) => - { - using var conn = getConn(); - var result = await conn.GetPatientByIdAsync(id).ConfigureAwait(false); - return result switch +patientGroup + .MapGet( + "/{id}", + async (string id, Func getConn) => { - GetPatientByIdOk(var patients) when patients.Count > 0 => Results.Ok(patients[0]), - GetPatientByIdOk => Results.NotFound(), - GetPatientByIdError(var err) => Results.Problem(err.Message), - }; - } -); - -patientGroup.MapPost( - "/", - async (CreatePatientRequest request, Func getConn) => - { - using var conn = getConn(); - var transaction = await conn.BeginTransactionAsync().ConfigureAwait(false); - await using var _ = transaction.ConfigureAwait(false); - var id = Guid.NewGuid().ToString(); - var now = DateTime.UtcNow.ToString( - "yyyy-MM-ddTHH:mm:ss.fffZ", - CultureInfo.InvariantCulture - ); + using var conn = getConn(); + var result = await conn.GetPatientByIdAsync(id).ConfigureAwait(false); + return result switch + { + GetPatientByIdOk(var patients) when patients.Count > 0 => Results.Ok(patients[0]), + GetPatientByIdOk => Results.NotFound(), + GetPatientByIdError(var err) => Results.Problem(err.Message), + }; + } + ) + .AddEndpointFilterFactory( + EndpointFilterFactories.RequireResourcePermission( + FhirPermissions.PatientRead, + signingKey, + getGatekeeperClient, + app.Logger, + idParamName: "id" + ) + ); - var result = await transaction - .Insertfhir_PatientAsync( - id, - request.Active ? 1L : 0L, - request.GivenName, - request.FamilyName, - request.BirthDate, - request.Gender, - request.Phone, - request.Email, - request.AddressLine, - request.City, - request.State, - request.PostalCode, - request.Country, - now, - 1L - ) - .ConfigureAwait(false); - - if (result is InsertOk) +patientGroup + .MapPost( + "/", + async (CreatePatientRequest request, Func getConn) => { - await transaction.CommitAsync().ConfigureAwait(false); - return Results.Created( - $"/fhir/Patient/{id}", - new - { - Id = id, - Active = request.Active, - GivenName = request.GivenName, - FamilyName = request.FamilyName, - BirthDate = request.BirthDate, - Gender = request.Gender, - Phone = request.Phone, - Email = request.Email, - AddressLine = request.AddressLine, - City = request.City, - State = request.State, - PostalCode = request.PostalCode, - Country = request.Country, - LastUpdated = now, - VersionId = 1L, - } + using var conn = getConn(); + var transaction = await conn.BeginTransactionAsync().ConfigureAwait(false); + await using var _ = transaction.ConfigureAwait(false); + var id = Guid.NewGuid().ToString(); + var now = DateTime.UtcNow.ToString( + "yyyy-MM-ddTHH:mm:ss.fffZ", + CultureInfo.InvariantCulture ); - } - - return result.Match( - _ => Results.Problem("Unexpected state"), - err => Results.Problem(err.Message) - ); - } -); -patientGroup.MapPut( - "/{id}", - async (string id, UpdatePatientRequest request, Func getConn) => - { - using var conn = getConn(); + var result = await transaction + .Insertfhir_PatientAsync( + id, + request.Active ? 1L : 0L, + request.GivenName, + request.FamilyName, + request.BirthDate, + request.Gender, + request.Phone, + request.Email, + request.AddressLine, + request.City, + request.State, + request.PostalCode, + request.Country, + now, + 1L + ) + .ConfigureAwait(false); + + if (result is InsertOk) + { + await transaction.CommitAsync().ConfigureAwait(false); + return Results.Created( + $"/fhir/Patient/{id}", + new + { + Id = id, + Active = request.Active, + GivenName = request.GivenName, + FamilyName = request.FamilyName, + BirthDate = request.BirthDate, + Gender = request.Gender, + Phone = request.Phone, + Email = request.Email, + AddressLine = request.AddressLine, + City = request.City, + State = request.State, + PostalCode = request.PostalCode, + Country = request.Country, + LastUpdated = now, + VersionId = 1L, + } + ); + } - // First verify the patient exists - var existingResult = await conn.GetPatientByIdAsync(id).ConfigureAwait(false); - if (existingResult is GetPatientByIdOk(var patients) && patients.Count == 0) - { - return Results.NotFound(); + return result.Match( + _ => Results.Problem("Unexpected state"), + err => Results.Problem(err.Message) + ); } + ) + .AddEndpointFilterFactory( + EndpointFilterFactories.RequirePermission( + FhirPermissions.PatientCreate, + signingKey, + getGatekeeperClient, + app.Logger + ) + ); - if (existingResult is GetPatientByIdError(var fetchErr)) +patientGroup + .MapPut( + "/{id}", + async (string id, UpdatePatientRequest request, Func getConn) => { - return Results.Problem(fetchErr.Message); - } + using var conn = getConn(); - var existingPatient = ((GetPatientByIdOk)existingResult).Value[0]; - var newVersionId = existingPatient.VersionId + 1; + // First verify the patient exists + var existingResult = await conn.GetPatientByIdAsync(id).ConfigureAwait(false); + if (existingResult is GetPatientByIdOk(var patients) && patients.Count == 0) + { + return Results.NotFound(); + } - var transaction = await conn.BeginTransactionAsync().ConfigureAwait(false); - await using var _ = transaction.ConfigureAwait(false); - var now = DateTime.UtcNow.ToString( - "yyyy-MM-ddTHH:mm:ss.fffZ", - CultureInfo.InvariantCulture - ); + if (existingResult is GetPatientByIdError(var fetchErr)) + { + return Results.Problem(fetchErr.Message); + } - var result = await transaction - .Updatefhir_PatientAsync( - id, - request.Active ? 1L : 0L, - request.GivenName, - request.FamilyName, - request.BirthDate ?? string.Empty, - request.Gender ?? string.Empty, - request.Phone ?? string.Empty, - request.Email ?? string.Empty, - request.AddressLine ?? string.Empty, - request.City ?? string.Empty, - request.State ?? string.Empty, - request.PostalCode ?? string.Empty, - request.Country ?? string.Empty, - now, - newVersionId - ) - .ConfigureAwait(false); - - if (result is UpdateOk) - { - await transaction.CommitAsync().ConfigureAwait(false); - return Results.Ok( - new - { - Id = id, - Active = request.Active, - GivenName = request.GivenName, - FamilyName = request.FamilyName, - BirthDate = request.BirthDate, - Gender = request.Gender, - Phone = request.Phone, - Email = request.Email, - AddressLine = request.AddressLine, - City = request.City, - State = request.State, - PostalCode = request.PostalCode, - Country = request.Country, - LastUpdated = now, - VersionId = newVersionId, - } + var existingPatient = ((GetPatientByIdOk)existingResult).Value[0]; + var newVersionId = existingPatient.VersionId + 1; + + var transaction = await conn.BeginTransactionAsync().ConfigureAwait(false); + await using var _ = transaction.ConfigureAwait(false); + var now = DateTime.UtcNow.ToString( + "yyyy-MM-ddTHH:mm:ss.fffZ", + CultureInfo.InvariantCulture ); - } - return result.Match( - _ => Results.Problem("Unexpected state"), - err => Results.Problem(err.Message) - ); - } -); + var result = await transaction + .Updatefhir_PatientAsync( + id, + request.Active ? 1L : 0L, + request.GivenName, + request.FamilyName, + request.BirthDate ?? string.Empty, + request.Gender ?? string.Empty, + request.Phone ?? string.Empty, + request.Email ?? string.Empty, + request.AddressLine ?? string.Empty, + request.City ?? string.Empty, + request.State ?? string.Empty, + request.PostalCode ?? string.Empty, + request.Country ?? string.Empty, + now, + newVersionId + ) + .ConfigureAwait(false); + + if (result is UpdateOk) + { + await transaction.CommitAsync().ConfigureAwait(false); + return Results.Ok( + new + { + Id = id, + Active = request.Active, + GivenName = request.GivenName, + FamilyName = request.FamilyName, + BirthDate = request.BirthDate, + Gender = request.Gender, + Phone = request.Phone, + Email = request.Email, + AddressLine = request.AddressLine, + City = request.City, + State = request.State, + PostalCode = request.PostalCode, + Country = request.Country, + LastUpdated = now, + VersionId = newVersionId, + } + ); + } -patientGroup.MapGet( - "/_search", - async (string q, Func getConn) => - { - using var conn = getConn(); - var result = await conn.SearchPatientsAsync($"%{q}%").ConfigureAwait(false); - return result switch + return result.Match( + _ => Results.Problem("Unexpected state"), + err => Results.Problem(err.Message) + ); + } + ) + .AddEndpointFilterFactory( + EndpointFilterFactories.RequireResourcePermission( + FhirPermissions.PatientUpdate, + signingKey, + getGatekeeperClient, + app.Logger, + idParamName: "id" + ) + ); + +patientGroup + .MapGet( + "/_search", + async (string q, Func getConn) => { - SearchPatientsOk(var patients) => Results.Ok(patients), - SearchPatientsError(var err) => Results.Problem(err.Message), - }; - } -); + using var conn = getConn(); + var result = await conn.SearchPatientsAsync($"%{q}%").ConfigureAwait(false); + return result switch + { + SearchPatientsOk(var patients) => Results.Ok(patients), + SearchPatientsError(var err) => Results.Problem(err.Message), + }; + } + ) + .AddEndpointFilterFactory( + EndpointFilterFactories.RequirePermission( + FhirPermissions.PatientRead, + signingKey, + getGatekeeperClient, + app.Logger + ) + ); var encounterGroup = patientGroup.MapGroup("/{patientId}/Encounter").WithTags("Encounter"); -encounterGroup.MapGet( - "/", - async (string patientId, Func getConn) => - { - using var conn = getConn(); - var result = await conn.GetEncountersByPatientAsync(patientId).ConfigureAwait(false); - return result switch +encounterGroup + .MapGet( + "/", + async (string patientId, Func getConn) => { - GetEncountersOk(var encounters) => Results.Ok(encounters), - GetEncountersError(var err) => Results.Problem(err.Message), - }; - } -); - -encounterGroup.MapPost( - "/", - async (string patientId, CreateEncounterRequest request, Func getConn) => - { - using var conn = getConn(); - var transaction = await conn.BeginTransactionAsync().ConfigureAwait(false); - await using var _ = transaction.ConfigureAwait(false); - var id = Guid.NewGuid().ToString(); - var now = DateTime.UtcNow.ToString( - "yyyy-MM-ddTHH:mm:ss.fffZ", - CultureInfo.InvariantCulture - ); + using var conn = getConn(); + var result = await conn.GetEncountersByPatientAsync(patientId).ConfigureAwait(false); + return result switch + { + GetEncountersOk(var encounters) => Results.Ok(encounters), + GetEncountersError(var err) => Results.Problem(err.Message), + }; + } + ) + .AddEndpointFilterFactory( + EndpointFilterFactories.RequirePatientPermission( + FhirPermissions.EncounterRead, + signingKey, + getGatekeeperClient, + app.Logger + ) + ); - var result = await transaction - .Insertfhir_EncounterAsync( - id, - request.Status, - request.Class, - patientId, - request.PractitionerId, - request.ServiceType, - request.ReasonCode, - request.PeriodStart, - request.PeriodEnd, - request.Notes, - now, - 1L - ) - .ConfigureAwait(false); - - if (result is InsertOk) +encounterGroup + .MapPost( + "/", + async (string patientId, CreateEncounterRequest request, Func getConn) => { - await transaction.CommitAsync().ConfigureAwait(false); - return Results.Created( - $"/fhir/Patient/{patientId}/Encounter/{id}", - new - { - Id = id, - Status = request.Status, - Class = request.Class, - PatientId = patientId, - PractitionerId = request.PractitionerId, - ServiceType = request.ServiceType, - ReasonCode = request.ReasonCode, - PeriodStart = request.PeriodStart, - PeriodEnd = request.PeriodEnd, - Notes = request.Notes, - LastUpdated = now, - VersionId = 1L, - } + using var conn = getConn(); + var transaction = await conn.BeginTransactionAsync().ConfigureAwait(false); + await using var _ = transaction.ConfigureAwait(false); + var id = Guid.NewGuid().ToString(); + var now = DateTime.UtcNow.ToString( + "yyyy-MM-ddTHH:mm:ss.fffZ", + CultureInfo.InvariantCulture ); - } - return result switch - { - InsertOk => Results.Problem("Unexpected state"), - InsertError(var err) => Results.Problem(err.Message), - }; - } -); + var result = await transaction + .Insertfhir_EncounterAsync( + id, + request.Status, + request.Class, + patientId, + request.PractitionerId, + request.ServiceType, + request.ReasonCode, + request.PeriodStart, + request.PeriodEnd, + request.Notes, + now, + 1L + ) + .ConfigureAwait(false); + + if (result is InsertOk) + { + await transaction.CommitAsync().ConfigureAwait(false); + return Results.Created( + $"/fhir/Patient/{patientId}/Encounter/{id}", + new + { + Id = id, + Status = request.Status, + Class = request.Class, + PatientId = patientId, + PractitionerId = request.PractitionerId, + ServiceType = request.ServiceType, + ReasonCode = request.ReasonCode, + PeriodStart = request.PeriodStart, + PeriodEnd = request.PeriodEnd, + Notes = request.Notes, + LastUpdated = now, + VersionId = 1L, + } + ); + } + + return result switch + { + InsertOk => Results.Problem("Unexpected state"), + InsertError(var err) => Results.Problem(err.Message), + }; + } + ) + .AddEndpointFilterFactory( + EndpointFilterFactories.RequirePatientPermission( + FhirPermissions.EncounterCreate, + signingKey, + getGatekeeperClient, + app.Logger + ) + ); var conditionGroup = patientGroup.MapGroup("/{patientId}/Condition").WithTags("Condition"); -conditionGroup.MapGet( - "/", - async (string patientId, Func getConn) => - { - using var conn = getConn(); - var result = await conn.GetConditionsByPatientAsync(patientId).ConfigureAwait(false); - return result switch +conditionGroup + .MapGet( + "/", + async (string patientId, Func getConn) => { - GetConditionsOk(var conditions) => Results.Ok(conditions), - GetConditionsError(var err) => Results.Problem(err.Message), - }; - } -); + using var conn = getConn(); + var result = await conn.GetConditionsByPatientAsync(patientId).ConfigureAwait(false); + return result switch + { + GetConditionsOk(var conditions) => Results.Ok(conditions), + GetConditionsError(var err) => Results.Problem(err.Message), + }; + } + ) + .AddEndpointFilterFactory( + EndpointFilterFactories.RequirePatientPermission( + FhirPermissions.ConditionRead, + signingKey, + getGatekeeperClient, + app.Logger + ) + ); -conditionGroup.MapPost( - "/", - async (string patientId, CreateConditionRequest request, Func getConn) => - { - using var conn = getConn(); - var transaction = await conn.BeginTransactionAsync().ConfigureAwait(false); - await using var _ = transaction.ConfigureAwait(false); - var id = Guid.NewGuid().ToString(); - var now = DateTime.UtcNow.ToString( - "yyyy-MM-ddTHH:mm:ss.fffZ", - CultureInfo.InvariantCulture - ); - var recordedDate = DateTime.UtcNow.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture); - - var result = await transaction - .Insertfhir_ConditionAsync( - id, - request.ClinicalStatus, - request.VerificationStatus, - request.Category, - request.Severity, - request.CodeSystem, - request.CodeValue, - request.CodeDisplay, - patientId, - request.EncounterReference, - request.OnsetDateTime, - recordedDate, - request.RecorderReference, - request.NoteText, - now, - 1L - ) - .ConfigureAwait(false); - - if (result is InsertOk) +conditionGroup + .MapPost( + "/", + async (string patientId, CreateConditionRequest request, Func getConn) => { - await transaction.CommitAsync().ConfigureAwait(false); - return Results.Created( - $"/fhir/Patient/{patientId}/Condition/{id}", - new - { - Id = id, - ClinicalStatus = request.ClinicalStatus, - VerificationStatus = request.VerificationStatus, - Category = request.Category, - Severity = request.Severity, - CodeSystem = request.CodeSystem, - CodeValue = request.CodeValue, - CodeDisplay = request.CodeDisplay, - SubjectReference = patientId, - EncounterReference = request.EncounterReference, - OnsetDateTime = request.OnsetDateTime, - RecordedDate = recordedDate, - RecorderReference = request.RecorderReference, - NoteText = request.NoteText, - LastUpdated = now, - VersionId = 1L, - } + using var conn = getConn(); + var transaction = await conn.BeginTransactionAsync().ConfigureAwait(false); + await using var _ = transaction.ConfigureAwait(false); + var id = Guid.NewGuid().ToString(); + var now = DateTime.UtcNow.ToString( + "yyyy-MM-ddTHH:mm:ss.fffZ", + CultureInfo.InvariantCulture ); - } + var recordedDate = DateTime.UtcNow.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture); + + var result = await transaction + .Insertfhir_ConditionAsync( + id: id, + clinicalstatus: request.ClinicalStatus, + verificationstatus: request.VerificationStatus, + category: request.Category, + severity: request.Severity, + codesystem: request.CodeSystem, + codevalue: request.CodeValue, + codedisplay: request.CodeDisplay, + subjectreference: patientId, + encounterreference: request.EncounterReference, + onsetdatetime: request.OnsetDateTime, + recordeddate: recordedDate, + recorderreference: request.RecorderReference, + notetext: request.NoteText, + lastupdated: now, + versionid: 1L + ) + .ConfigureAwait(false); + + if (result is InsertOk) + { + await transaction.CommitAsync().ConfigureAwait(false); + return Results.Created( + $"/fhir/Patient/{patientId}/Condition/{id}", + new + { + Id = id, + ClinicalStatus = request.ClinicalStatus, + VerificationStatus = request.VerificationStatus, + Category = request.Category, + Severity = request.Severity, + CodeSystem = request.CodeSystem, + CodeValue = request.CodeValue, + CodeDisplay = request.CodeDisplay, + SubjectReference = patientId, + EncounterReference = request.EncounterReference, + OnsetDateTime = request.OnsetDateTime, + RecordedDate = recordedDate, + RecorderReference = request.RecorderReference, + NoteText = request.NoteText, + LastUpdated = now, + VersionId = 1L, + } + ); + } - return result switch - { - InsertOk => Results.Problem("Unexpected state"), - InsertError(var err) => Results.Problem(err.Message), - }; - } -); + return result switch + { + InsertOk => Results.Problem("Unexpected state"), + InsertError(var err) => Results.Problem(err.Message), + }; + } + ) + .AddEndpointFilterFactory( + EndpointFilterFactories.RequirePatientPermission( + FhirPermissions.ConditionCreate, + signingKey, + getGatekeeperClient, + app.Logger + ) + ); var medicationGroup = patientGroup .MapGroup("/{patientId}/MedicationRequest") .WithTags("MedicationRequest"); -medicationGroup.MapGet( - "/", - async (string patientId, Func getConn) => - { - using var conn = getConn(); - var result = await conn.GetMedicationsByPatientAsync(patientId).ConfigureAwait(false); - return result switch +medicationGroup + .MapGet( + "/", + async (string patientId, Func getConn) => { - GetMedicationsOk(var medications) => Results.Ok(medications), - GetMedicationsError(var err) => Results.Problem(err.Message), - }; - } -); - -medicationGroup.MapPost( - "/", - async ( - string patientId, - CreateMedicationRequestRequest request, - Func getConn - ) => - { - using var conn = getConn(); - var transaction = await conn.BeginTransactionAsync().ConfigureAwait(false); - await using var _ = transaction.ConfigureAwait(false); - var id = Guid.NewGuid().ToString(); - var now = DateTime.UtcNow.ToString( - "yyyy-MM-ddTHH:mm:ss.fffZ", - CultureInfo.InvariantCulture - ); + using var conn = getConn(); + var result = await conn.GetMedicationsByPatientAsync(patientId).ConfigureAwait(false); + return result switch + { + GetMedicationsOk(var medications) => Results.Ok(medications), + GetMedicationsError(var err) => Results.Problem(err.Message), + }; + } + ) + .AddEndpointFilterFactory( + EndpointFilterFactories.RequirePatientPermission( + FhirPermissions.MedicationRequestRead, + signingKey, + getGatekeeperClient, + app.Logger + ) + ); - var result = await transaction - .Insertfhir_MedicationRequestAsync( - id, - request.Status, - request.Intent, - patientId, - request.PractitionerId, - request.EncounterId, - request.MedicationCode, - request.MedicationDisplay, - request.DosageInstruction, - request.Quantity, - request.Unit, - request.Refills, - now, - now, - 1L - ) - .ConfigureAwait(false); - - if (result is InsertOk) +medicationGroup + .MapPost( + "/", + async ( + string patientId, + CreateMedicationRequestRequest request, + Func getConn + ) => { - await transaction.CommitAsync().ConfigureAwait(false); - return Results.Created( - $"/fhir/Patient/{patientId}/MedicationRequest/{id}", - new - { - Id = id, - Status = request.Status, - Intent = request.Intent, - PatientId = patientId, - PractitionerId = request.PractitionerId, - EncounterId = request.EncounterId, - MedicationCode = request.MedicationCode, - MedicationDisplay = request.MedicationDisplay, - DosageInstruction = request.DosageInstruction, - Quantity = request.Quantity, - Unit = request.Unit, - Refills = request.Refills, - AuthoredOn = now, - LastUpdated = now, - VersionId = 1L, - } + using var conn = getConn(); + var transaction = await conn.BeginTransactionAsync().ConfigureAwait(false); + await using var _ = transaction.ConfigureAwait(false); + var id = Guid.NewGuid().ToString(); + var now = DateTime.UtcNow.ToString( + "yyyy-MM-ddTHH:mm:ss.fffZ", + CultureInfo.InvariantCulture ); - } - return result switch - { - InsertOk => Results.Problem("Unexpected state"), - InsertError(var err) => Results.Problem(err.Message), - }; - } -); + var result = await transaction + .Insertfhir_MedicationRequestAsync( + id, + request.Status, + request.Intent, + patientId, + request.PractitionerId, + request.EncounterId, + request.MedicationCode, + request.MedicationDisplay, + request.DosageInstruction, + request.Quantity, + request.Unit, + request.Refills, + now, + now, + 1L + ) + .ConfigureAwait(false); + + if (result is InsertOk) + { + await transaction.CommitAsync().ConfigureAwait(false); + return Results.Created( + $"/fhir/Patient/{patientId}/MedicationRequest/{id}", + new + { + Id = id, + Status = request.Status, + Intent = request.Intent, + PatientId = patientId, + PractitionerId = request.PractitionerId, + EncounterId = request.EncounterId, + MedicationCode = request.MedicationCode, + MedicationDisplay = request.MedicationDisplay, + DosageInstruction = request.DosageInstruction, + Quantity = request.Quantity, + Unit = request.Unit, + Refills = request.Refills, + AuthoredOn = now, + LastUpdated = now, + VersionId = 1L, + } + ); + } + + return result switch + { + InsertOk => Results.Problem("Unexpected state"), + InsertError(var err) => Results.Problem(err.Message), + }; + } + ) + .AddEndpointFilterFactory( + EndpointFilterFactories.RequirePatientPermission( + FhirPermissions.MedicationRequestCreate, + signingKey, + getGatekeeperClient, + app.Logger + ) + ); app.MapGet( - "/sync/changes", - (long? fromVersion, int? limit, Func getConn) => - { - using var conn = getConn(); - var result = SyncLogRepository.FetchChanges(conn, fromVersion ?? 0, limit ?? 100); - return result switch + "/sync/changes", + (long? fromVersion, int? limit, Func getConn) => { - SyncLogListOk(var logs) => Results.Ok(logs), - SyncLogListError(var err) => Results.Problem(SyncHelpers.ToMessage(err)), - }; - } -); + using var conn = getConn(); + var result = SyncLogRepository.FetchChanges(conn, fromVersion ?? 0, limit ?? 100); + return result switch + { + SyncLogListOk(var logs) => Results.Ok(logs), + SyncLogListError(var err) => Results.Problem(SyncHelpers.ToMessage(err)), + }; + } + ) + .AddEndpointFilterFactory( + EndpointFilterFactories.RequirePermission( + FhirPermissions.SyncRead, + signingKey, + getGatekeeperClient, + app.Logger + ) + ); app.MapGet( - "/sync/origin", - (Func getConn) => - { - using var conn = getConn(); - var result = SyncSchema.GetOriginId(conn); - return result switch + "/sync/origin", + (Func getConn) => { - StringSyncOk(var originId) => Results.Ok(new { originId }), - StringSyncError(var err) => Results.Problem(SyncHelpers.ToMessage(err)), - }; - } -); + using var conn = getConn(); + var result = SyncSchema.GetOriginId(conn); + return result switch + { + StringSyncOk(var originId) => Results.Ok(new { originId }), + StringSyncError(var err) => Results.Problem(SyncHelpers.ToMessage(err)), + }; + } + ) + .AddEndpointFilterFactory( + EndpointFilterFactories.RequirePermission( + FhirPermissions.SyncRead, + signingKey, + getGatekeeperClient, + app.Logger + ) + ); app.MapGet( - "/sync/status", - (Func getConn) => - { - using var conn = getConn(); - var changesResult = SyncLogRepository.FetchChanges(conn, 0, 1000); - - var (totalCount, lastSyncTime) = changesResult switch + "/sync/status", + (Func getConn) => { - SyncLogListOk(var logs) => ( - logs.Count, - logs.Count > 0 - ? logs.Max(l => l.Timestamp) - : DateTime.UtcNow.ToString( + using var conn = getConn(); + var changesResult = SyncLogRepository.FetchChanges(conn, 0, 1000); + + var (totalCount, lastSyncTime) = changesResult switch + { + SyncLogListOk(var logs) => ( + logs.Count, + logs.Count > 0 + ? logs.Max(l => l.Timestamp) + : DateTime.UtcNow.ToString( + "yyyy-MM-ddTHH:mm:ss.fffZ", + CultureInfo.InvariantCulture + ) + ), + SyncLogListError => ( + 0, + DateTime.UtcNow.ToString( "yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture ) - ), - SyncLogListError => ( - 0, - DateTime.UtcNow.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture) - ), - }; - - return Results.Ok( - new - { - service = "Clinical.Api", - status = "healthy", - lastSyncTime, - totalRecords = totalCount, - failedCount = 0, - } - ); - } -); + ), + }; -app.MapGet( - "/sync/records", - (string? search, int? page, int? pageSize, Func getConn) => - { - using var conn = getConn(); - var currentPage = page ?? 1; - var size = pageSize ?? 50; - var changesResult = SyncLogRepository.FetchChanges(conn, 0, 1000); + return Results.Ok( + new + { + service = "Clinical.Api", + status = "healthy", + lastSyncTime, + totalRecords = totalCount, + failedCount = 0, + } + ); + } + ) + .AddEndpointFilterFactory( + EndpointFilterFactories.RequirePermission( + FhirPermissions.SyncRead, + signingKey, + getGatekeeperClient, + app.Logger + ) + ); - return changesResult switch +app.MapGet( + "/sync/records", + (string? search, int? page, int? pageSize, Func getConn) => { - SyncLogListOk(var logs) => Results.Ok( - BuildSyncRecordsResponse(logs, search, currentPage, size) - ), - SyncLogListError(var err) => Results.Problem(SyncHelpers.ToMessage(err)), - }; - } -); + using var conn = getConn(); + var currentPage = page ?? 1; + var size = pageSize ?? 50; + var changesResult = SyncLogRepository.FetchChanges(conn, 0, 1000); + + return changesResult switch + { + SyncLogListOk(var logs) => Results.Ok( + BuildSyncRecordsResponse(logs, search, currentPage, size) + ), + SyncLogListError(var err) => Results.Problem(SyncHelpers.ToMessage(err)), + }; + } + ) + .AddEndpointFilterFactory( + EndpointFilterFactories.RequirePermission( + FhirPermissions.SyncRead, + signingKey, + getGatekeeperClient, + app.Logger + ) + ); app.MapPost( - "/sync/records/{id}/retry", - (string id) => - { - // For now, just acknowledge the retry request - // Real implementation would mark the record for re-sync - return Results.Accepted(); - } -); + "/sync/records/{id}/retry", + (string id) => + { + // For now, just acknowledge the retry request + // Real implementation would mark the record for re-sync + return Results.Accepted(); + } + ) + .AddEndpointFilterFactory( + EndpointFilterFactories.RequirePermission( + FhirPermissions.SyncWrite, + signingKey, + getGatekeeperClient, + app.Logger + ) + ); + +app.MapGet( + "/sync/providers", + (Func getConn) => + { + using var conn = getConn(); + using var cmd = conn.CreateCommand(); + cmd.CommandText = + "SELECT ProviderId, FirstName, LastName, Specialty, SyncedAt FROM sync_Provider"; + using var reader = cmd.ExecuteReader(); + var providers = new List(); + while (reader.Read()) + { + providers.Add( + new + { + ProviderId = reader.GetString(0), + FirstName = reader.IsDBNull(1) ? null : reader.GetString(1), + LastName = reader.IsDBNull(2) ? null : reader.GetString(2), + Specialty = reader.IsDBNull(3) ? null : reader.GetString(3), + SyncedAt = reader.IsDBNull(4) ? null : reader.GetString(4), + } + ); + } + return Results.Ok(providers); + } + ) + .AddEndpointFilterFactory( + EndpointFilterFactories.RequirePermission( + FhirPermissions.SyncRead, + signingKey, + getGatekeeperClient, + app.Logger + ) + ); app.Run(); diff --git a/Samples/Clinical/Clinical.Api/clinical-schema.yaml b/Samples/Clinical/Clinical.Api/clinical-schema.yaml new file mode 100644 index 00000000..9fa8ed8f --- /dev/null +++ b/Samples/Clinical/Clinical.Api/clinical-schema.yaml @@ -0,0 +1,227 @@ +name: clinical +tables: +- name: fhir_Patient + columns: + - name: Id + type: Text + - name: Active + type: Int + defaultValue: 1 + - name: GivenName + type: Text + - name: FamilyName + type: Text + - name: BirthDate + type: Text + - name: Gender + type: Text + checkConstraint: Gender IN ('male', 'female', 'other', 'unknown') + - name: Phone + type: Text + - name: Email + type: Text + - name: AddressLine + type: Text + - name: City + type: Text + - name: State + type: Text + - name: PostalCode + type: Text + - name: Country + type: Text + - name: LastUpdated + type: Text + defaultValue: (datetime('now')) + - name: VersionId + type: Int + defaultValue: 1 + indexes: + - name: idx_fhir_patient_family + columns: + - FamilyName + - name: idx_fhir_patient_given + columns: + - GivenName + primaryKey: + name: PK_fhir_Patient + columns: + - Id +- name: fhir_Encounter + columns: + - name: Id + type: Text + - name: Status + type: Text + checkConstraint: Status IN ('planned', 'arrived', 'triaged', 'in-progress', 'onleave', 'finished', 'cancelled', 'entered-in-error') + - name: Class + type: Text + checkConstraint: Class IN ('ambulatory', 'emergency', 'inpatient', 'observation', 'virtual') + - name: PatientId + type: Text + - name: PractitionerId + type: Text + - name: ServiceType + type: Text + - name: ReasonCode + type: Text + - name: PeriodStart + type: Text + - name: PeriodEnd + type: Text + - name: Notes + type: Text + - name: LastUpdated + type: Text + defaultValue: (datetime('now')) + - name: VersionId + type: Int + defaultValue: 1 + indexes: + - name: idx_fhir_encounter_patient + columns: + - PatientId + foreignKeys: + - name: FK_fhir_Encounter_PatientId + columns: + - PatientId + referencedTable: fhir_Patient + referencedColumns: + - Id + primaryKey: + name: PK_fhir_Encounter + columns: + - Id +- name: fhir_Condition + columns: + - name: Id + type: Text + - name: ClinicalStatus + type: Text + checkConstraint: ClinicalStatus IN ('active', 'recurrence', 'relapse', 'inactive', 'remission', 'resolved') + - name: VerificationStatus + type: Text + checkConstraint: VerificationStatus IN ('unconfirmed', 'provisional', 'differential', 'confirmed', 'refuted', 'entered-in-error') + - name: Category + type: Text + defaultValue: "'problem-list-item'" + - name: Severity + type: Text + checkConstraint: Severity IN ('mild', 'moderate', 'severe') + - name: CodeSystem + type: Text + defaultValue: "'http://hl7.org/fhir/sid/icd-10-cm'" + - name: CodeValue + type: Text + - name: CodeDisplay + type: Text + - name: SubjectReference + type: Text + - name: EncounterReference + type: Text + - name: OnsetDateTime + type: Text + - name: RecordedDate + type: Text + defaultValue: (date('now')) + - name: RecorderReference + type: Text + - name: NoteText + type: Text + - name: LastUpdated + type: Text + defaultValue: (datetime('now')) + - name: VersionId + type: Int + defaultValue: 1 + indexes: + - name: idx_fhir_condition_patient + columns: + - SubjectReference + foreignKeys: + - name: FK_fhir_Condition_SubjectReference + columns: + - SubjectReference + referencedTable: fhir_Patient + referencedColumns: + - Id + primaryKey: + name: PK_fhir_Condition + columns: + - Id +- name: fhir_MedicationRequest + columns: + - name: Id + type: Text + - name: Status + type: Text + checkConstraint: Status IN ('active', 'on-hold', 'cancelled', 'completed', 'entered-in-error', 'stopped', 'draft') + - name: Intent + type: Text + checkConstraint: Intent IN ('proposal', 'plan', 'order', 'original-order', 'reflex-order', 'filler-order', 'instance-order', 'option') + - name: PatientId + type: Text + - name: PractitionerId + type: Text + - name: EncounterId + type: Text + - name: MedicationCode + type: Text + - name: MedicationDisplay + type: Text + - name: DosageInstruction + type: Text + - name: Quantity + type: Double + - name: Unit + type: Text + - name: Refills + type: Int + defaultValue: 0 + - name: AuthoredOn + type: Text + defaultValue: (datetime('now')) + - name: LastUpdated + type: Text + defaultValue: (datetime('now')) + - name: VersionId + type: Int + defaultValue: 1 + indexes: + - name: idx_fhir_medication_patient + columns: + - PatientId + foreignKeys: + - name: FK_fhir_MedicationRequest_PatientId + columns: + - PatientId + referencedTable: fhir_Patient + referencedColumns: + - Id + - name: FK_fhir_MedicationRequest_EncounterId + columns: + - EncounterId + referencedTable: fhir_Encounter + referencedColumns: + - Id + primaryKey: + name: PK_fhir_MedicationRequest + columns: + - Id +- name: sync_Provider + columns: + - name: ProviderId + type: Text + - name: FirstName + type: Text + - name: LastName + type: Text + - name: Specialty + type: Text + - name: SyncedAt + type: Text + defaultValue: (datetime('now')) + primaryKey: + name: PK_sync_Provider + columns: + - ProviderId diff --git a/Samples/Clinical/Clinical.Sync/Clinical.Sync.csproj b/Samples/Clinical/Clinical.Sync/Clinical.Sync.csproj index 6757a18e..d114cb4a 100644 --- a/Samples/Clinical/Clinical.Sync/Clinical.Sync.csproj +++ b/Samples/Clinical/Clinical.Sync/Clinical.Sync.csproj @@ -2,7 +2,7 @@ Exe - CA1515;CA1050;CA1054;CA1848;CA1849;CA2007;CA2234 + CA1515;CA1050;CA1054;CA1849;CA2007;CA2234 diff --git a/Samples/Clinical/Clinical.Sync/Program.cs b/Samples/Clinical/Clinical.Sync/Program.cs index 5918609a..cc0894a1 100644 --- a/Samples/Clinical/Clinical.Sync/Program.cs +++ b/Samples/Clinical/Clinical.Sync/Program.cs @@ -1,22 +1,36 @@ -namespace Clinical.Sync; - using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +namespace Clinical.Sync; + internal static class Program { internal static async Task Main(string[] args) { var builder = Host.CreateApplicationBuilder(args); - var clinicalDbPath = Path.Combine( - AppContext.BaseDirectory, - "..", - "Clinical.Api", - "clinical.db" - ); - var schedulingApiUrl = "http://localhost:5001"; + // Support environment variable override for testing + // Default path navigates from bin/Debug/net9.0 up to Clinical.Api/bin/Debug/net9.0 + var clinicalDbPath = + Environment.GetEnvironmentVariable("CLINICAL_DB_PATH") + ?? Path.Combine( + AppContext.BaseDirectory, + "..", + "..", + "..", + "..", + "Clinical.Api", + "bin", + "Debug", + "net9.0", + "clinical.db" + ); + var schedulingApiUrl = + Environment.GetEnvironmentVariable("SCHEDULING_API_URL") ?? "http://localhost:5001"; + + Console.WriteLine($"[Clinical.Sync] Using database: {clinicalDbPath}"); + Console.WriteLine($"[Clinical.Sync] Scheduling API URL: {schedulingApiUrl}"); builder.Services.AddSingleton>(_ => () => @@ -41,6 +55,7 @@ internal static async Task Main(string[] args) /// /// Sync change record from remote API. +/// Matches the SyncLogEntry schema returned by /sync/changes endpoint. /// [System.Diagnostics.CodeAnalysis.SuppressMessage( "Performance", @@ -50,8 +65,19 @@ internal static async Task Main(string[] args) internal sealed record SyncChange( long Version, string TableName, - string RowId, - string Operation, - string? Data, + string PkValue, + int Operation, + string? Payload, + string Origin, string Timestamp -); +) +{ + /// Insert operation (0). + public const int Insert = 0; + + /// Update operation (1). + public const int Update = 1; + + /// Delete operation (2). + public const int Delete = 2; +} diff --git a/Samples/Clinical/Clinical.Sync/SyncWorker.cs b/Samples/Clinical/Clinical.Sync/SyncWorker.cs index 23adfc3f..a7a624e7 100644 --- a/Samples/Clinical/Clinical.Sync/SyncWorker.cs +++ b/Samples/Clinical/Clinical.Sync/SyncWorker.cs @@ -1,9 +1,11 @@ -namespace Clinical.Sync; - +using System.Security.Cryptography; +using System.Text; using System.Text.Json; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +namespace Clinical.Sync; + /// /// Background service that pulls Practitioner data from Scheduling.Api and maps to sync_Provider. /// @@ -29,30 +31,101 @@ string schedulingApiUrl /// /// Executes the sync worker background service. + /// FAULT TOLERANT: This worker NEVER crashes. It handles all errors gracefully and retries indefinitely. /// protected override async Task ExecuteAsync(CancellationToken stoppingToken) { _logger.Log( LogLevel.Information, - "Clinical.Sync worker starting at {Time}", - DateTimeOffset.Now + "[SYNC-START] Clinical.Sync worker starting at {Time}. Target: {Url}", + DateTimeOffset.Now, + _schedulingApiUrl ); - await Task.Delay(2000, stoppingToken).ConfigureAwait(false); + var consecutiveFailures = 0; + const int maxConsecutiveFailuresBeforeWarning = 3; + // Main sync loop - NEVER exits except on cancellation while (!stoppingToken.IsCancellationRequested) { try { await PerformSync(stoppingToken).ConfigureAwait(false); - await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken).ConfigureAwait(false); + + // Reset failure counter on success + if (consecutiveFailures > 0) + { + _logger.Log( + LogLevel.Information, + "[SYNC-RECOVERED] Sync recovered after {Count} consecutive failures", + consecutiveFailures + ); + consecutiveFailures = 0; + } + + await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken).ConfigureAwait(false); + } + catch (HttpRequestException ex) + { + consecutiveFailures++; + var retryDelay = Math.Min(5 * consecutiveFailures, 30); // Exponential backoff up to 30s + + if (consecutiveFailures >= maxConsecutiveFailuresBeforeWarning) + { + _logger.Log( + LogLevel.Warning, + "[SYNC-FAULT] Scheduling.Api unreachable for {Count} consecutive attempts. Error: {Message}. Retrying in {Delay}s...", + consecutiveFailures, + ex.Message, + retryDelay + ); + } + else + { + _logger.Log( + LogLevel.Information, + "[SYNC-RETRY] Scheduling.Api not reachable ({Message}). Attempt {Count}, retrying in {Delay}s...", + ex.Message, + consecutiveFailures, + retryDelay + ); + } + + await Task.Delay(TimeSpan.FromSeconds(retryDelay), stoppingToken) + .ConfigureAwait(false); + } + catch (TaskCanceledException) when (stoppingToken.IsCancellationRequested) + { + _logger.Log( + LogLevel.Information, + "[SYNC-SHUTDOWN] Sync worker shutting down gracefully" + ); + break; } - catch (Exception ex) when (ex is not OperationCanceledException) + catch (Exception ex) { - _logger.Log(LogLevel.Error, ex, "Error during sync operation"); - await Task.Delay(TimeSpan.FromSeconds(60), stoppingToken).ConfigureAwait(false); + consecutiveFailures++; + var retryDelay = Math.Min(10 * consecutiveFailures, 60); // Longer backoff for unknown errors + + _logger.Log( + LogLevel.Error, + ex, + "[SYNC-ERROR] Unexpected error during sync (attempt {Count}). Retrying in {Delay}s. Error type: {Type}", + consecutiveFailures, + retryDelay, + ex.GetType().Name + ); + + await Task.Delay(TimeSpan.FromSeconds(retryDelay), stoppingToken) + .ConfigureAwait(false); } } + + _logger.Log( + LogLevel.Information, + "[SYNC-EXIT] Clinical.Sync worker exited at {Time}", + DateTimeOffset.Now + ); } private async Task PerformSync(CancellationToken cancellationToken) @@ -64,6 +137,8 @@ private async Task PerformSync(CancellationToken cancellationToken) ); using var httpClient = new HttpClient { BaseAddress = new Uri(_schedulingApiUrl) }; + httpClient.DefaultRequestHeaders.Authorization = + new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", GenerateSyncToken()); var changesResponse = await httpClient .GetAsync("/sync/changes?limit=100", cancellationToken) @@ -130,23 +205,27 @@ private void ApplyMappedChange( SyncChange change ) { - if (change.Operation == "DELETE") + // Extract the ID from PkValue which is JSON like {"Id":"uuid-here"} + var pkData = JsonSerializer.Deserialize>(change.PkValue); + var rowId = pkData?.GetValueOrDefault("Id").GetString() ?? change.PkValue; + + if (change.Operation == SyncChange.Delete) { using var cmd = conn.CreateCommand(); cmd.Transaction = (SqliteTransaction)transaction; cmd.CommandText = "DELETE FROM sync_Provider WHERE ProviderId = @id"; - cmd.Parameters.AddWithValue("@id", change.RowId); + cmd.Parameters.AddWithValue("@id", rowId); cmd.ExecuteNonQuery(); - _logger.Log(LogLevel.Debug, "Deleted provider {ProviderId}", change.RowId); + _logger.Log(LogLevel.Debug, "Deleted provider {ProviderId}", rowId); return; } - if (change.Data == null) + if (change.Payload == null) { return; } - var data = JsonSerializer.Deserialize>(change.Data); + var data = JsonSerializer.Deserialize>(change.Payload); if (data == null) { return; @@ -189,4 +268,45 @@ ON CONFLICT(ProviderId) DO UPDATE SET data.GetValueOrDefault("Id").GetString() ); } + + private static readonly string[] SyncRoles = ["sync-client", "clinician", "scheduler", "admin"]; + + /// + /// Generates a JWT token for sync worker authentication. + /// Uses the dev mode signing key (32 zeros) for E2E testing. + /// + private static string GenerateSyncToken() + { + var signingKey = new byte[32]; // 32 zeros = dev mode key + var header = Base64UrlEncode(Encoding.UTF8.GetBytes("""{"alg":"HS256","typ":"JWT"}""")); + var expiration = DateTimeOffset.UtcNow.AddHours(1).ToUnixTimeSeconds(); + var payload = Base64UrlEncode( + Encoding.UTF8.GetBytes( + JsonSerializer.Serialize( + new + { + sub = "clinical-sync-worker", + name = "Clinical Sync Worker", + email = "sync@clinical.local", + jti = Guid.NewGuid().ToString(), + exp = expiration, + roles = SyncRoles, + } + ) + ) + ); + var signature = ComputeHmacSignature(header, payload, signingKey); + return $"{header}.{payload}.{signature}"; + } + + private static string Base64UrlEncode(byte[] input) => + Convert.ToBase64String(input).TrimEnd('=').Replace('+', '-').Replace('/', '_'); + + private static string ComputeHmacSignature(string header, string payload, byte[] key) + { + var data = Encoding.UTF8.GetBytes($"{header}.{payload}"); + using var hmac = new HMACSHA256(key); + var hash = hmac.ComputeHash(data); + return Base64UrlEncode(hash); + } } diff --git a/Samples/Dashboard/Dashboard.Integration.Tests/AppointmentE2ETests.cs b/Samples/Dashboard/Dashboard.Integration.Tests/AppointmentE2ETests.cs new file mode 100644 index 00000000..29bbdeae --- /dev/null +++ b/Samples/Dashboard/Dashboard.Integration.Tests/AppointmentE2ETests.cs @@ -0,0 +1,184 @@ +using System.Text.RegularExpressions; +using Microsoft.Playwright; + +namespace Dashboard.Integration.Tests; + +/// +/// E2E tests for appointment-related functionality. +/// +[Collection("E2E Tests")] +[Trait("Category", "E2E")] +public sealed class AppointmentE2ETests +{ + private readonly E2EFixture _fixture; + + /// + /// Constructor receives shared fixture. + /// + public AppointmentE2ETests(E2EFixture fixture) => _fixture = fixture; + + /// + /// Dashboard loads and displays appointment data from Scheduling API. + /// + [Fact] + public async Task Dashboard_DisplaysAppointmentData_FromSchedulingApi() + { + var page = await _fixture.Browser!.NewPageAsync(); + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.ClickAsync("text=Appointments"); + await page.WaitForSelectorAsync( + "text=Checkup", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + var content = await page.ContentAsync(); + Assert.Contains("Checkup", content); + + await page.CloseAsync(); + } + + /// + /// Add Appointment button opens modal and creates appointment via API. + /// + [Fact] + public async Task AddAppointmentButton_OpensModal_AndCreatesAppointment() + { + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.ClickAsync("text=Appointments"); + await page.WaitForSelectorAsync( + "[data-testid='add-appointment-btn']", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + await page.ClickAsync("[data-testid='add-appointment-btn']"); + await page.WaitForSelectorAsync( + ".modal", + new PageWaitForSelectorOptions { Timeout = 5000 } + ); + + var uniqueServiceType = $"E2EConsult{DateTime.UtcNow.Ticks % 100000}"; + await page.FillAsync("[data-testid='appointment-service-type']", uniqueServiceType); + await page.ClickAsync("[data-testid='submit-appointment']"); + + await page.WaitForSelectorAsync( + $"text={uniqueServiceType}", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + using var client = E2EFixture.CreateAuthenticatedClient(); + var response = await client.GetStringAsync($"{E2EFixture.SchedulingUrl}/Appointment"); + Assert.Contains(uniqueServiceType, response); + + await page.CloseAsync(); + } + + /// + /// View Schedule button navigates to appointments view. + /// + [Fact] + public async Task ViewScheduleButton_NavigatesToAppointments() + { + var page = await _fixture.Browser!.NewPageAsync(); + + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.ClickAsync("text=View Schedule"); + await page.WaitForSelectorAsync( + "text=Appointments", + new PageWaitForSelectorOptions { Timeout = 5000 } + ); + await page.WaitForSelectorAsync( + "text=Checkup", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + var content = await page.ContentAsync(); + Assert.Contains("Checkup", content); + + await page.CloseAsync(); + } + + /// + /// Edit Appointment button opens edit page and updates appointment via API. + /// + [Fact] + public async Task EditAppointmentButton_OpensEditPage_AndUpdatesAppointment() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + + var uniqueServiceType = $"EditApptTest{DateTime.UtcNow.Ticks % 100000}"; + var startTime = DateTime.UtcNow.AddDays(7).ToString("yyyy-MM-ddTHH:mm:ss.fffZ"); + var endTime = DateTime + .UtcNow.AddDays(7) + .AddMinutes(30) + .ToString("yyyy-MM-ddTHH:mm:ss.fffZ"); + var createResponse = await client.PostAsync( + $"{E2EFixture.SchedulingUrl}/Appointment", + new StringContent( + $$$"""{"ServiceCategory": "General", "ServiceType": "{{{uniqueServiceType}}}", "Priority": "routine", "Start": "{{{startTime}}}", "End": "{{{endTime}}}", "PatientReference": "Patient/1", "PractitionerReference": "Practitioner/1"}""", + System.Text.Encoding.UTF8, + "application/json" + ) + ); + createResponse.EnsureSuccessStatusCode(); + var createdAppointmentJson = await createResponse.Content.ReadAsStringAsync(); + + var appointmentIdMatch = Regex.Match(createdAppointmentJson, "\"Id\"\\s*:\\s*\"([^\"]+)\""); + Assert.True(appointmentIdMatch.Success); + var appointmentId = appointmentIdMatch.Groups[1].Value; + + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.ClickAsync("text=Appointments"); + await page.WaitForSelectorAsync( + $"text={uniqueServiceType}", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + var editButton = await page.QuerySelectorAsync( + $"tr:has-text('{uniqueServiceType}') .btn-secondary" + ); + Assert.NotNull(editButton); + await editButton.ClickAsync(); + + await page.WaitForSelectorAsync( + "text=Edit Appointment", + new PageWaitForSelectorOptions { Timeout = 5000 } + ); + + var newServiceType = $"Edited{DateTime.UtcNow.Ticks % 100000}"; + await page.FillAsync("#appointment-service-type", newServiceType); + await page.ClickAsync("button:has-text('Save Changes')"); + + await page.WaitForSelectorAsync( + "text=Appointment updated successfully", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + var updatedAppointmentJson = await client.GetStringAsync( + $"{E2EFixture.SchedulingUrl}/Appointment/{appointmentId}" + ); + Assert.Contains(newServiceType, updatedAppointmentJson); + + await page.CloseAsync(); + } +} diff --git a/Samples/Dashboard/Dashboard.Integration.Tests/AuthE2ETests.cs b/Samples/Dashboard/Dashboard.Integration.Tests/AuthE2ETests.cs new file mode 100644 index 00000000..5adf69bd --- /dev/null +++ b/Samples/Dashboard/Dashboard.Integration.Tests/AuthE2ETests.cs @@ -0,0 +1,401 @@ +using System.Net; +using Microsoft.Playwright; + +namespace Dashboard.Integration.Tests; + +/// +/// E2E tests for authentication (login, logout, WebAuthn). +/// +[Collection("E2E Tests")] +[Trait("Category", "E2E")] +public sealed class AuthE2ETests +{ + private readonly E2EFixture _fixture; + + /// + /// Constructor receives shared fixture. + /// + public AuthE2ETests(E2EFixture fixture) => _fixture = fixture; + + /// + /// Login page uses discoverable credentials (no email required). + /// + [Fact] + public async Task LoginPage_DoesNotRequireEmailForSignIn() + { + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Type}: {msg.Text}"); + + await page.GotoAsync(E2EFixture.DashboardUrlNoTestMode); + await page.EvaluateAsync( + "() => { localStorage.removeItem('gatekeeper_token'); localStorage.removeItem('gatekeeper_user'); }" + ); + await page.ReloadAsync(); + await page.WaitForSelectorAsync( + ".login-card", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + + var pageContent = await page.ContentAsync(); + Assert.Contains("Healthcare Dashboard", pageContent); + Assert.Contains("Sign in with your passkey", pageContent); + + var emailInputVisible = await page.IsVisibleAsync("input[type='email']"); + Assert.False(emailInputVisible, "Login mode should NOT show email field"); + + var signInButton = page.Locator("button:has-text('Sign in with Passkey')"); + await signInButton.WaitForAsync(new LocatorWaitForOptions { Timeout = 5000 }); + Assert.True(await signInButton.IsVisibleAsync()); + + await page.CloseAsync(); + } + + /// + /// Registration page requires email and display name. + /// + [Fact] + public async Task LoginPage_RegistrationRequiresEmailAndDisplayName() + { + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Type}: {msg.Text}"); + + await page.GotoAsync(E2EFixture.DashboardUrlNoTestMode); + await page.EvaluateAsync( + "() => { localStorage.removeItem('gatekeeper_token'); localStorage.removeItem('gatekeeper_user'); }" + ); + await page.ReloadAsync(); + await page.WaitForSelectorAsync( + ".login-card", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + + await page.ClickAsync("button:has-text('Register')"); + await Task.Delay(500); + + var pageContent = await page.ContentAsync(); + Assert.Contains("Create your account", pageContent); + + var emailInput = page.Locator("input[type='email']"); + var displayNameInput = page.Locator("input#displayName"); + + Assert.True(await emailInput.IsVisibleAsync()); + Assert.True(await displayNameInput.IsVisibleAsync()); + + await page.CloseAsync(); + } + + /// + /// Gatekeeper API /auth/login/begin returns valid response for discoverable credentials. + /// + [Fact] + public async Task GatekeeperApi_LoginBegin_ReturnsValidDiscoverableCredentialOptions() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + + var response = await client.PostAsync( + $"{E2EFixture.GatekeeperUrl}/auth/login/begin", + new StringContent("{}", System.Text.Encoding.UTF8, "application/json") + ); + + Assert.True(response.IsSuccessStatusCode); + + var json = await response.Content.ReadAsStringAsync(); + using var doc = System.Text.Json.JsonDocument.Parse(json); + var root = doc.RootElement; + + Assert.True(root.TryGetProperty("ChallengeId", out var challengeId)); + Assert.False(string.IsNullOrEmpty(challengeId.GetString())); + + Assert.True(root.TryGetProperty("OptionsJson", out var optionsJson)); + var optionsJsonStr = optionsJson.GetString(); + Assert.False(string.IsNullOrEmpty(optionsJsonStr)); + + using var optionsDoc = System.Text.Json.JsonDocument.Parse(optionsJsonStr!); + var options = optionsDoc.RootElement; + Assert.True(options.TryGetProperty("challenge", out _)); + Assert.True(options.TryGetProperty("rpId", out _)); + } + + /// + /// Gatekeeper API /auth/register/begin returns valid response. + /// + [Fact] + public async Task GatekeeperApi_RegisterBegin_ReturnsValidOptions() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + + var response = await client.PostAsync( + $"{E2EFixture.GatekeeperUrl}/auth/register/begin", + new StringContent( + """{"Email": "test-e2e@example.com", "DisplayName": "E2E Test User"}""", + System.Text.Encoding.UTF8, + "application/json" + ) + ); + + Assert.True(response.IsSuccessStatusCode); + + var json = await response.Content.ReadAsStringAsync(); + using var doc = System.Text.Json.JsonDocument.Parse(json); + var root = doc.RootElement; + + Assert.True(root.TryGetProperty("ChallengeId", out _)); + Assert.True(root.TryGetProperty("OptionsJson", out var optionsJson)); + + using var optionsDoc = System.Text.Json.JsonDocument.Parse(optionsJson.GetString()!); + var options = optionsDoc.RootElement; + Assert.True(options.TryGetProperty("challenge", out _)); + Assert.True(options.TryGetProperty("rp", out _)); + Assert.True(options.TryGetProperty("user", out _)); + } + + /// + /// Dashboard sign-in flow calls API and handles response correctly. + /// + [Fact] + public async Task LoginPage_SignInButton_CallsApiWithoutJsonErrors() + { + var page = await _fixture.Browser!.NewPageAsync(); + var consoleErrors = new List(); + var networkRequests = new List(); + + page.Console += (_, msg) => + { + Console.WriteLine($"[BROWSER] {msg.Type}: {msg.Text}"); + if (msg.Type == "error") + consoleErrors.Add(msg.Text); + }; + + page.Request += (_, request) => + { + if (request.Url.Contains("/auth/")) + networkRequests.Add($"{request.Method} {request.Url}"); + }; + + await page.GotoAsync(E2EFixture.DashboardUrlNoTestMode); + await page.EvaluateAsync( + "() => { localStorage.removeItem('gatekeeper_token'); localStorage.removeItem('gatekeeper_user'); }" + ); + await page.ReloadAsync(); + await page.WaitForSelectorAsync( + ".login-card", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + + await page.ClickAsync("button:has-text('Sign in with Passkey')"); + await Task.Delay(3000); + + Assert.Contains(networkRequests, r => r.Contains("/auth/login/begin")); + + var hasJsonParseError = consoleErrors.Any(e => + e.Contains("undefined") || e.Contains("is not valid JSON") || e.Contains("SyntaxError") + ); + Assert.False(hasJsonParseError); + + await page.CloseAsync(); + } + + /// + /// User menu click shows dropdown with Sign Out. + /// + [Fact] + public async Task UserMenu_ClickShowsDropdownWithSignOut() + { + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Type}: {msg.Text}"); + + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + + var userMenuButton = await page.QuerySelectorAsync("[data-testid='user-menu-button']"); + Assert.NotNull(userMenuButton); + await userMenuButton.ClickAsync(); + + await page.WaitForSelectorAsync( + "[data-testid='user-dropdown']", + new PageWaitForSelectorOptions { Timeout = 5000 } + ); + + var signOutButton = await page.QuerySelectorAsync("[data-testid='logout-button']"); + Assert.NotNull(signOutButton); + Assert.True(await signOutButton.IsVisibleAsync()); + + await page.CloseAsync(); + } + + /// + /// Sign Out button click shows login page. + /// + [Fact] + public async Task SignOutButton_ClickShowsLoginPage() + { + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Type}: {msg.Text}"); + + // Use testMode URL to ensure app loads reliably + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + + await page.ClickAsync("[data-testid='user-menu-button']"); + await page.WaitForSelectorAsync( + "[data-testid='user-dropdown']", + new PageWaitForSelectorOptions { Timeout = 5000 } + ); + await page.ClickAsync("[data-testid='logout-button']"); + + await page.WaitForSelectorAsync( + "[data-testid='login-page']", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + var tokenAfterLogout = await page.EvaluateAsync( + "() => localStorage.getItem('gatekeeper_token')" + ); + Assert.Null(tokenAfterLogout); + + await page.CloseAsync(); + } + + /// + /// Gatekeeper API logout revokes token. + /// + [Fact] + public async Task GatekeeperApi_Logout_RevokesToken() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + + var logoutResponse = await client.PostAsync( + $"{E2EFixture.GatekeeperUrl}/auth/logout", + new StringContent("{}", System.Text.Encoding.UTF8, "application/json") + ); + Assert.Equal(HttpStatusCode.NoContent, logoutResponse.StatusCode); + + using var unauthClient = new HttpClient(); + var unauthResponse = await unauthClient.PostAsync( + $"{E2EFixture.GatekeeperUrl}/auth/logout", + new StringContent("{}", System.Text.Encoding.UTF8, "application/json") + ); + Assert.Equal(HttpStatusCode.Unauthorized, unauthResponse.StatusCode); + } + + /// + /// User menu displays user initials and name in dropdown. + /// + [Fact] + public async Task UserMenu_DisplaysUserInitialsAndNameInDropdown() + { + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Type}: {msg.Text}"); + + // Set custom user data BEFORE loading + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.EvaluateAsync( + @"() => { + localStorage.setItem('gatekeeper_token', 'fake-token-for-testing'); + localStorage.setItem('gatekeeper_user', JSON.stringify({ + userId: 'test-user', displayName: 'Alice Smith', email: 'alice@example.com' + })); + }" + ); + // Navigate again with testMode to pick up custom user data + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + + var avatarText = await page.TextContentAsync("[data-testid='user-menu-button']"); + Assert.Equal("AS", avatarText?.Trim()); + + await page.ClickAsync("[data-testid='user-menu-button']"); + await page.WaitForSelectorAsync( + "[data-testid='user-dropdown']", + new PageWaitForSelectorOptions { Timeout = 5000 } + ); + + var userNameText = await page.TextContentAsync(".user-dropdown-name"); + Assert.Contains("Alice Smith", userNameText); + + var emailText = await page.TextContentAsync(".user-dropdown-email"); + Assert.Contains("alice@example.com", emailText); + + await page.CloseAsync(); + } + + /// + /// First-time sign-in must work WITHOUT browser refresh. + /// + [Fact] + public async Task FirstTimeSignIn_TransitionsToDashboard_WithoutRefresh() + { + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Type}: {msg.Text}"); + + await page.GotoAsync(E2EFixture.DashboardUrlNoTestMode); + await page.EvaluateAsync( + "() => { localStorage.removeItem('gatekeeper_token'); localStorage.removeItem('gatekeeper_user'); }" + ); + await page.ReloadAsync(); + await page.WaitForSelectorAsync( + "[data-testid='login-page']", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + + // Wait for React to mount and set the __triggerLogin hook + await page.WaitForFunctionAsync( + "() => typeof window.__triggerLogin === 'function'", + new PageWaitForFunctionOptions { Timeout = 10000 } + ); + + // Use the same DEV token that testMode uses - this token is accepted by the APIs + const string devToken = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJkYXNoYm9hcmQtdXNlciIsImp0aSI6IjE1MTMwYTg0LTY4NTktNGNmMy05MjA3LTMyMGJhYWRiNzhjNSIsInJvbGVzIjpbImNsaW5pY2lhbiIsInNjaGVkdWxlciJdLCJleHAiOjIwODE5MjIxMDQsImlhdCI6MTc2NjM4OTMwNH0.mk66XyKaLWukzZOmGNwss74lSlXobt6Em0NoEbXRdKU"; + await page.EvaluateAsync( + $@"() => {{ + console.log('[TEST] Setting token and triggering login'); + localStorage.setItem('gatekeeper_token', '{devToken}'); + localStorage.setItem('gatekeeper_user', JSON.stringify({{ + userId: 'test-user-123', displayName: 'Test User', email: 'test@example.com' + }})); + window.__triggerLogin({{ userId: 'test-user-123', displayName: 'Test User', email: 'test@example.com' }}); + console.log('[TEST] Login triggered, waiting for React state update'); + }}" + ); + + // Wait longer for React state update and re-render + await Task.Delay(2000); + + try + { + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + var loginPageStillVisible = await page.IsVisibleAsync("[data-testid='login-page']"); + Assert.False( + loginPageStillVisible, + "Login page should be hidden after successful login" + ); + Assert.True( + await page.IsVisibleAsync(".sidebar"), + "Sidebar should be visible after successful login" + ); + } + catch (TimeoutException) + { + var pageContent = await page.ContentAsync(); + Console.WriteLine( + $"[TEST] Page content after timeout:\n{pageContent[..Math.Min(2000, pageContent.Length)]}" + ); + Assert.Fail("FIRST-TIME SIGN-IN BUG: App did not transition to dashboard after login."); + } + + await page.CloseAsync(); + } +} diff --git a/Samples/Dashboard/Dashboard.Integration.Tests/CalendarE2ETests.cs b/Samples/Dashboard/Dashboard.Integration.Tests/CalendarE2ETests.cs new file mode 100644 index 00000000..0a97ef83 --- /dev/null +++ b/Samples/Dashboard/Dashboard.Integration.Tests/CalendarE2ETests.cs @@ -0,0 +1,270 @@ +using Microsoft.Playwright; + +namespace Dashboard.Integration.Tests; + +/// +/// E2E tests for calendar-related functionality. +/// +[Collection("E2E Tests")] +[Trait("Category", "E2E")] +public sealed class CalendarE2ETests +{ + private readonly E2EFixture _fixture; + + /// + /// Constructor receives shared fixture. + /// + public CalendarE2ETests(E2EFixture fixture) => _fixture = fixture; + + /// + /// Calendar page displays appointments in calendar grid. + /// + [Fact] + public async Task CalendarPage_DisplaysAppointmentsInCalendarGrid() + { + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + await page.GotoAsync($"{E2EFixture.DashboardUrl}#calendar"); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.WaitForSelectorAsync( + ".calendar-grid-container", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + var content = await page.ContentAsync(); + Assert.Contains("calendar-grid", content); + Assert.Contains("Sun", content); + Assert.Contains("Mon", content); + Assert.Contains("Today", content); + + await page.CloseAsync(); + } + + /// + /// Calendar page allows clicking on a day to view appointments. + /// + [Fact] + public async Task CalendarPage_ClickOnDay_ShowsAppointmentDetails() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + + var today = DateTime.Now; + var startTime = new DateTime( + today.Year, + today.Month, + today.Day, + 14, + 0, + 0, + DateTimeKind.Local + ).ToString("yyyy-MM-ddTHH:mm:ss.fffZ"); + var endTime = new DateTime( + today.Year, + today.Month, + today.Day, + 14, + 30, + 0, + DateTimeKind.Local + ).ToString("yyyy-MM-ddTHH:mm:ss.fffZ"); + var uniqueServiceType = $"CalTest{DateTime.Now.Ticks % 100000}"; + + var createResponse = await client.PostAsync( + $"{E2EFixture.SchedulingUrl}/Appointment", + new StringContent( + $$$"""{"ServiceCategory": "General", "ServiceType": "{{{uniqueServiceType}}}", "Priority": "routine", "Start": "{{{startTime}}}", "End": "{{{endTime}}}", "PatientReference": "Patient/1", "PractitionerReference": "Practitioner/1"}""", + System.Text.Encoding.UTF8, + "application/json" + ) + ); + createResponse.EnsureSuccessStatusCode(); + + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.ClickAsync("text=Schedule"); + await page.WaitForSelectorAsync( + ".calendar-grid", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + await page.WaitForSelectorAsync( + ".calendar-cell.today.has-appointments", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + var todayCell = page.Locator(".calendar-cell.today").First; + await todayCell.ClickAsync(); + + await page.WaitForSelectorAsync( + ".calendar-details-panel h4", + new PageWaitForSelectorOptions { Timeout = 5000 } + ); + await page.WaitForSelectorAsync( + $"text={uniqueServiceType}", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + var content = await page.ContentAsync(); + Assert.Contains(uniqueServiceType, content); + + await page.CloseAsync(); + } + + /// + /// Calendar page Edit button opens edit appointment page. + /// + [Fact] + public async Task CalendarPage_EditButton_OpensEditAppointmentPage() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + + var today = DateTime.Now; + var startTime = new DateTime( + today.Year, + today.Month, + today.Day, + 15, + 0, + 0, + DateTimeKind.Local + ).ToString("yyyy-MM-ddTHH:mm:ss.fffZ"); + var endTime = new DateTime( + today.Year, + today.Month, + today.Day, + 15, + 30, + 0, + DateTimeKind.Local + ).ToString("yyyy-MM-ddTHH:mm:ss.fffZ"); + var uniqueServiceType = $"CalEdit{DateTime.Now.Ticks % 100000}"; + + var createResponse = await client.PostAsync( + $"{E2EFixture.SchedulingUrl}/Appointment", + new StringContent( + $$$"""{"ServiceCategory": "General", "ServiceType": "{{{uniqueServiceType}}}", "Priority": "routine", "Start": "{{{startTime}}}", "End": "{{{endTime}}}", "PatientReference": "Patient/1", "PractitionerReference": "Practitioner/1"}""", + System.Text.Encoding.UTF8, + "application/json" + ) + ); + createResponse.EnsureSuccessStatusCode(); + + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.ClickAsync("text=Schedule"); + await page.WaitForSelectorAsync( + ".calendar-grid", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + await page.WaitForSelectorAsync( + ".calendar-cell.today.has-appointments", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + var todayCell = page.Locator(".calendar-cell.today").First; + await todayCell.ClickAsync(); + await page.WaitForSelectorAsync( + $"text={uniqueServiceType}", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + var editButton = await page.QuerySelectorAsync( + $".calendar-appointment-item:has-text('{uniqueServiceType}') button:has-text('Edit')" + ); + Assert.NotNull(editButton); + await editButton.ClickAsync(); + + await page.WaitForSelectorAsync( + "text=Edit Appointment", + new PageWaitForSelectorOptions { Timeout = 5000 } + ); + + var content = await page.ContentAsync(); + Assert.Contains("Edit Appointment", content); + + await page.CloseAsync(); + } + + /// + /// Calendar navigation (previous/next month) works. + /// + [Fact] + public async Task CalendarPage_NavigationButtons_ChangeMonth() + { + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.ClickAsync("text=Schedule"); + await page.WaitForSelectorAsync( + ".calendar-grid", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + var currentMonthYear = await page.TextContentAsync(".text-lg.font-semibold"); + Assert.NotNull(currentMonthYear); + + var headerControls = page.Locator(".page-header .flex.items-center.gap-4"); + var nextButton = headerControls.Locator("button.btn-secondary").Nth(1); + await nextButton.ClickAsync(); + await Task.Delay(300); + + var newMonthYear = await page.TextContentAsync(".text-lg.font-semibold"); + Assert.NotEqual(currentMonthYear, newMonthYear); + + var prevButton = headerControls.Locator("button.btn-secondary").First; + await prevButton.ClickAsync(); + await Task.Delay(300); + await prevButton.ClickAsync(); + await Task.Delay(300); + + await page.ClickAsync("button:has-text('Today')"); + await Task.Delay(500); + + var todayContent = await page.ContentAsync(); + Assert.Contains("today", todayContent); + + await page.CloseAsync(); + } + + /// + /// Deep linking to calendar page works. + /// + [Fact] + public async Task CalendarPage_DeepLinkingWorks() + { + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + await page.GotoAsync($"{E2EFixture.DashboardUrl}#calendar"); + await page.WaitForSelectorAsync( + ".calendar-grid", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + + var content = await page.ContentAsync(); + Assert.Contains("Schedule", content); + Assert.Contains("calendar-grid", content); + + await page.CloseAsync(); + } +} diff --git a/Samples/Dashboard/Dashboard.Integration.Tests/Dashboard.Integration.Tests.csproj b/Samples/Dashboard/Dashboard.Integration.Tests/Dashboard.Integration.Tests.csproj index 18876e79..792f6df9 100644 --- a/Samples/Dashboard/Dashboard.Integration.Tests/Dashboard.Integration.Tests.csproj +++ b/Samples/Dashboard/Dashboard.Integration.Tests/Dashboard.Integration.Tests.csproj @@ -7,15 +7,15 @@ enable enable Dashboard.Integration.Tests - CS1591;CA1707;CA1307;CA1062;CA1515;CA2100;CA1848 + CS1591;CA1707;CA1307;CA1062;CA1515;CA2100 - - + + all - runtime; build; native; contentfiles; analyzers + runtime; build; native; contentfiles; analyzers; buildtransitive @@ -23,7 +23,10 @@ + + + @@ -31,4 +34,9 @@ + + + + + diff --git a/Samples/Dashboard/Dashboard.Integration.Tests/DashboardApiCorsTests.cs b/Samples/Dashboard/Dashboard.Integration.Tests/DashboardApiCorsTests.cs index 01a3d601..8e1afbe2 100644 --- a/Samples/Dashboard/Dashboard.Integration.Tests/DashboardApiCorsTests.cs +++ b/Samples/Dashboard/Dashboard.Integration.Tests/DashboardApiCorsTests.cs @@ -1,3 +1,6 @@ +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; using Microsoft.AspNetCore.Hosting; namespace Dashboard.Integration.Tests; @@ -99,9 +102,54 @@ public Task InitializeAsync() { _clinicalClient = _clinicalFactory.CreateClient(); _schedulingClient = _schedulingFactory.CreateClient(); + + // Add auth headers for all requests (uses dev mode signing key - 32 zeros) + var token = GenerateTestToken(); + _clinicalClient.DefaultRequestHeaders.Authorization = + new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); + _schedulingClient.DefaultRequestHeaders.Authorization = + new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); + return Task.CompletedTask; } + private static readonly string[] TestRoles = ["admin", "user"]; + + private static string GenerateTestToken() + { + var signingKey = new byte[32]; // 32 zeros = dev mode key + var header = Base64UrlEncode(Encoding.UTF8.GetBytes("""{"alg":"HS256","typ":"JWT"}""")); + var expiration = DateTimeOffset.UtcNow.AddHours(1).ToUnixTimeSeconds(); + var payload = Base64UrlEncode( + Encoding.UTF8.GetBytes( + JsonSerializer.Serialize( + new + { + sub = "cors-test-user", + name = "CORS Test User", + email = "corstest@example.com", + jti = Guid.NewGuid().ToString(), + exp = expiration, + roles = TestRoles, + } + ) + ) + ); + var signature = ComputeHmacSignature(header, payload, signingKey); + return $"{header}.{payload}.{signature}"; + } + + private static string Base64UrlEncode(byte[] input) => + Convert.ToBase64String(input).TrimEnd('=').Replace('+', '-').Replace('/', '_'); + + private static string ComputeHmacSignature(string header, string payload, byte[] key) + { + var data = Encoding.UTF8.GetBytes($"{header}.{payload}"); + using var hmac = new HMACSHA256(key); + var hash = hmac.ComputeHash(data); + return Base64UrlEncode(hash); + } + public async Task DisposeAsync() { _clinicalClient.Dispose(); diff --git a/Samples/Dashboard/Dashboard.Integration.Tests/DashboardE2ETests.cs b/Samples/Dashboard/Dashboard.Integration.Tests/DashboardE2ETests.cs index 16fb6c63..40b3e10b 100644 --- a/Samples/Dashboard/Dashboard.Integration.Tests/DashboardE2ETests.cs +++ b/Samples/Dashboard/Dashboard.Integration.Tests/DashboardE2ETests.cs @@ -1,387 +1,11 @@ -using System.Diagnostics; using System.Net; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.FileProviders; -using Microsoft.Extensions.Hosting; using Microsoft.Playwright; namespace Dashboard.Integration.Tests; /// -/// Shared fixture that starts all services ONCE for all E2E tests. -/// -public sealed class E2EFixture : IAsyncLifetime -{ - private Process? _clinicalProcess; - private Process? _schedulingProcess; - private Process? _gatekeeperProcess; - private IHost? _dashboardHost; - - /// - /// Playwright instance shared by all tests. - /// - public IPlaywright? Playwright { get; private set; } - - /// - /// Browser instance shared by all tests. - /// - public IBrowser? Browser { get; private set; } - - /// - /// Clinical API URL - SAME as real app default. - /// - public const string ClinicalUrl = "http://localhost:5080"; - - /// - /// Scheduling API URL - SAME as real app default. - /// - public const string SchedulingUrl = "http://localhost:5001"; - - /// - /// Gatekeeper Auth API URL - SAME as real app default. - /// - public const string GatekeeperUrl = "http://localhost:5002"; - - /// - /// Dashboard URL - SAME as real app default. - /// Uses testMode=true to bypass authentication in tests. - /// - public const string DashboardUrl = "http://localhost:5173?testMode=true"; - - /// - /// Dashboard URL without test mode - for auth tests. - /// - public const string DashboardUrlNoTestMode = "http://localhost:5173"; - - /// - /// Start all services ONCE for all tests. - /// - public async Task InitializeAsync() - { - // Kill any existing processes on our ports first - await KillProcessOnPortAsync(5080); - await KillProcessOnPortAsync(5001); - await KillProcessOnPortAsync(5002); - await KillProcessOnPortAsync(5173); - - // Find the project directories relative to the test assembly - var testAssemblyDir = Path.GetDirectoryName(typeof(E2EFixture).Assembly.Location)!; - var samplesDir = Path.GetFullPath( - Path.Combine(testAssemblyDir, "..", "..", "..", "..", "..") - ); - var rootDir = Path.GetFullPath(Path.Combine(samplesDir, "..")); - var clinicalProjectDir = Path.Combine(samplesDir, "Clinical", "Clinical.Api"); - var schedulingProjectDir = Path.Combine(samplesDir, "Scheduling", "Scheduling.Api"); - var gatekeeperProjectDir = Path.Combine(rootDir, "Gatekeeper", "Gatekeeper.Api"); - - Console.WriteLine($"[E2E] Test assembly dir: {testAssemblyDir}"); - Console.WriteLine($"[E2E] Samples dir: {samplesDir}"); - Console.WriteLine($"[E2E] Clinical dir: {clinicalProjectDir}"); - Console.WriteLine($"[E2E] Clinical dir exists: {Directory.Exists(clinicalProjectDir)}"); - Console.WriteLine($"[E2E] Gatekeeper dir: {gatekeeperProjectDir}"); - Console.WriteLine($"[E2E] Gatekeeper dir exists: {Directory.Exists(gatekeeperProjectDir)}"); - - // Start Clinical API using pre-built DLL with correct content root - var clinicalDll = Path.Combine( - clinicalProjectDir, - "bin", - "Debug", - "net9.0", - "Clinical.Api.dll" - ); - Console.WriteLine($"[E2E] Clinical DLL: {clinicalDll}"); - Console.WriteLine($"[E2E] Clinical DLL exists: {File.Exists(clinicalDll)}"); - _clinicalProcess = StartApiFromDll(clinicalDll, clinicalProjectDir, ClinicalUrl); - - // Start Scheduling API using pre-built DLL with correct content root - var schedulingDll = Path.Combine( - schedulingProjectDir, - "bin", - "Debug", - "net9.0", - "Scheduling.Api.dll" - ); - Console.WriteLine($"[E2E] Scheduling DLL: {schedulingDll}"); - Console.WriteLine($"[E2E] Scheduling DLL exists: {File.Exists(schedulingDll)}"); - _schedulingProcess = StartApiFromDll(schedulingDll, schedulingProjectDir, SchedulingUrl); - - // Start Gatekeeper Auth API using pre-built DLL with correct content root - var gatekeeperDll = Path.Combine( - gatekeeperProjectDir, - "bin", - "Debug", - "net9.0", - "Gatekeeper.Api.dll" - ); - Console.WriteLine($"[E2E] Gatekeeper DLL: {gatekeeperDll}"); - Console.WriteLine($"[E2E] Gatekeeper DLL exists: {File.Exists(gatekeeperDll)}"); - _gatekeeperProcess = StartApiFromDll(gatekeeperDll, gatekeeperProjectDir, GatekeeperUrl); - - // Give the processes a moment to start before polling - await Task.Delay(2000); - - // Wait for APIs to be ready - await WaitForApiAsync(ClinicalUrl, "/fhir/Patient/"); - await WaitForApiAsync(SchedulingUrl, "/Practitioner"); - await WaitForGatekeeperApiAsync(); - - // Start Dashboard static file server on port 5173 - NO config injection - _dashboardHost = CreateDashboardHost(); - await _dashboardHost.StartAsync(); - - // Seed test data - await SeedTestDataAsync(); - - // Start Playwright - Playwright = await Microsoft.Playwright.Playwright.CreateAsync(); - Browser = await Playwright.Chromium.LaunchAsync( - new BrowserTypeLaunchOptions { Headless = true } - ); - } - - /// - /// Stop all services ONCE after all tests. - /// - public async Task DisposeAsync() - { - if (Browser is not null) - await Browser.CloseAsync(); - Playwright?.Dispose(); - - if (_dashboardHost is not null) - await _dashboardHost.StopAsync(); - _dashboardHost?.Dispose(); - - StopProcess(_clinicalProcess); - StopProcess(_schedulingProcess); - StopProcess(_gatekeeperProcess); - } - - private static Process StartApiFromDll(string dllPath, string contentRoot, string url) - { - Console.WriteLine( - $"[E2E] Starting API: dotnet \"{dllPath}\" --urls \"{url}\" --contentRoot \"{contentRoot}\"" - ); - - var process = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = "dotnet", - Arguments = $"\"{dllPath}\" --urls \"{url}\" --contentRoot \"{contentRoot}\"", - WorkingDirectory = contentRoot, - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true, - }, - }; - - process.OutputDataReceived += (_, e) => - { - if (!string.IsNullOrEmpty(e.Data)) - Console.WriteLine($"[API {url}] {e.Data}"); - }; - process.ErrorDataReceived += (_, e) => - { - if (!string.IsNullOrEmpty(e.Data)) - Console.WriteLine($"[API {url} ERR] {e.Data}"); - }; - - process.Start(); - process.BeginOutputReadLine(); - process.BeginErrorReadLine(); - return process; - } - - private static void StopProcess(Process? process) - { - if (process is null || process.HasExited) - return; - - try - { - process.Kill(entireProcessTree: true); - process.WaitForExit(5000); - } - catch - { /* ignore */ - } - finally - { - process.Dispose(); - } - } - - private static async Task KillProcessOnPortAsync(int port) - { - try - { - // Use shell to kill -9 any process on the port - var psi = new ProcessStartInfo - { - FileName = "/bin/sh", - Arguments = $"-c \"lsof -ti :{port} | xargs kill -9 2>/dev/null || true\"", - UseShellExecute = false, - CreateNoWindow = true, - }; - - using var process = Process.Start(psi); - if (process is not null) - await process.WaitForExitAsync(); - - await Task.Delay(500); - } - catch - { /* ignore */ - } - } - - private static async Task WaitForApiAsync(string baseUrl, string healthEndpoint) - { - using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(2) }; - var maxRetries = 120; // Give dotnet run more time to build and start (60 seconds) - - for (var i = 0; i < maxRetries; i++) - { - try - { - var response = await client.GetAsync($"{baseUrl}{healthEndpoint}"); - if (response.IsSuccessStatusCode || response.StatusCode == HttpStatusCode.NotFound) - return; - } - catch - { /* not ready yet */ - } - - await Task.Delay(500); - } - - throw new TimeoutException($"API at {baseUrl} did not start within {maxRetries * 500}ms"); - } - - private static async Task WaitForGatekeeperApiAsync() - { - using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(2) }; - var maxRetries = 120; - - for (var i = 0; i < maxRetries; i++) - { - try - { - // Discoverable credentials flow - empty body returns valid options - var response = await client.PostAsync( - $"{GatekeeperUrl}/auth/login/begin", - new StringContent("{}", System.Text.Encoding.UTF8, "application/json") - ); - // 200 OK means API is running and discoverable credentials work - if (response.IsSuccessStatusCode) - return; - } - catch - { /* not ready yet */ - } - - await Task.Delay(500); - } - - throw new TimeoutException( - $"Gatekeeper API at {GatekeeperUrl} did not start within {maxRetries * 500}ms" - ); - } - - private static IHost CreateDashboardHost() - { - var wwwrootPath = Path.Combine(AppContext.BaseDirectory, "wwwroot"); - - return Host.CreateDefaultBuilder() - .ConfigureWebHostDefaults(webBuilder => - { - webBuilder.UseUrls("http://localhost:5173"); - webBuilder.Configure(app => - { - // Serve static files directly - NO config injection - // Dashboard index.html uses default ports which match our APIs - app.UseDefaultFiles(); - app.UseStaticFiles( - new StaticFileOptions - { - FileProvider = new PhysicalFileProvider(wwwrootPath), - } - ); - }); - }) - .Build(); - } - - private static async Task SeedTestDataAsync() - { - using var client = new HttpClient(); - - var patientResponse = await client.PostAsync( - $"{ClinicalUrl}/fhir/Patient/", - new StringContent( - """{"Active": true, "GivenName": "E2ETest", "FamilyName": "TestPatient", "Gender": "other"}""", - System.Text.Encoding.UTF8, - "application/json" - ) - ); - patientResponse.EnsureSuccessStatusCode(); - - // Seed FHIR-compliant Practitioner with all required fields - var practitionerResponse = await client.PostAsync( - $"{SchedulingUrl}/Practitioner", - new StringContent( - """{"Identifier": "DR001", "Active": true, "NameGiven": "E2EPractitioner", "NameFamily": "DrTest", "Qualification": "MD", "Specialty": "General Practice", "TelecomEmail": "drtest@hospital.org", "TelecomPhone": "+1-555-0123"}""", - System.Text.Encoding.UTF8, - "application/json" - ) - ); - practitionerResponse.EnsureSuccessStatusCode(); - - // Seed additional practitioners for realistic data - var practitioner2Response = await client.PostAsync( - $"{SchedulingUrl}/Practitioner", - new StringContent( - """{"Identifier": "DR002", "Active": true, "NameGiven": "Sarah", "NameFamily": "Johnson", "Qualification": "DO", "Specialty": "Cardiology", "TelecomEmail": "sjohnson@hospital.org", "TelecomPhone": "+1-555-0124"}""", - System.Text.Encoding.UTF8, - "application/json" - ) - ); - practitioner2Response.EnsureSuccessStatusCode(); - - var practitioner3Response = await client.PostAsync( - $"{SchedulingUrl}/Practitioner", - new StringContent( - """{"Identifier": "DR003", "Active": true, "NameGiven": "Michael", "NameFamily": "Chen", "Qualification": "MD", "Specialty": "Neurology", "TelecomEmail": "mchen@hospital.org", "TelecomPhone": "+1-555-0125"}""", - System.Text.Encoding.UTF8, - "application/json" - ) - ); - practitioner3Response.EnsureSuccessStatusCode(); - - var appointmentResponse = await client.PostAsync( - $"{SchedulingUrl}/Appointment", - new StringContent( - """{"ServiceCategory": "General", "ServiceType": "Checkup", "Start": "2025-12-20T10:00:00Z", "End": "2025-12-20T11:00:00Z", "PatientReference": "Patient/1", "PractitionerReference": "Practitioner/1", "Priority": "routine"}""", - System.Text.Encoding.UTF8, - "application/json" - ) - ); - appointmentResponse.EnsureSuccessStatusCode(); - } -} - -/// -/// Collection definition that ensures all E2E tests share the same fixture. -/// -[CollectionDefinition("E2E Tests")] -public sealed class E2ECollection : ICollectionFixture; - -/// -/// REAL E2E tests that prove the Dashboard UI can connect to the APIs. -/// Uses EXACTLY the same ports as the real app - no dynamic port bullshit. +/// Core Dashboard E2E tests. +/// Uses EXACTLY the same ports as the real app. /// [Collection("E2E Tests")] [Trait("Category", "E2E")] @@ -395,155 +19,7 @@ public sealed class DashboardE2ETests public DashboardE2ETests(E2EFixture fixture) => _fixture = fixture; /// - /// CRITICAL TEST: Dashboard loads and displays patient data from Clinical API. - /// Playwright browser loads Dashboard at localhost:5173 and verifies data from localhost:5080. - /// - [Fact] - public async Task Dashboard_DisplaysPatientData_FromClinicalApi() - { - var page = await _fixture.Browser!.NewPageAsync(); - - page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Type}: {msg.Text}"); - page.RequestFailed += (_, req) => - Console.WriteLine($"[NET FAILED] {req.Url} - {req.Failure}"); - - await page.GotoAsync(E2EFixture.DashboardUrl); - await page.WaitForSelectorAsync( - ".sidebar", - new PageWaitForSelectorOptions { Timeout = 20000 } - ); - - await page.ClickAsync("text=Patients"); - await page.WaitForSelectorAsync( - "text=TestPatient", - new PageWaitForSelectorOptions { Timeout = 10000 } - ); - - var content = await page.ContentAsync(); - Assert.Contains("TestPatient", content); - Assert.Contains("E2ETest", content); - - await page.CloseAsync(); - } - - /// - /// CRITICAL TEST: Dashboard loads and displays practitioner data from Scheduling API. - /// Verifies FHIR-compliant practitioner data including Qualification and Specialty. - /// - [Fact] - public async Task Dashboard_DisplaysPractitionerData_FromSchedulingApi() - { - var page = await _fixture.Browser!.NewPageAsync(); - page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); - - await page.GotoAsync(E2EFixture.DashboardUrl); - await page.WaitForSelectorAsync( - ".sidebar", - new PageWaitForSelectorOptions { Timeout = 20000 } - ); - - // Navigate to Practitioners page - await page.ClickAsync("text=Practitioners"); - - // Wait for practitioner cards to load - should show all seeded practitioners - await page.WaitForSelectorAsync( - "text=DrTest", - new PageWaitForSelectorOptions { Timeout = 10000 } - ); - await page.WaitForSelectorAsync( - ".practitioner-card", - new PageWaitForSelectorOptions { Timeout = 5000 } - ); - - var content = await page.ContentAsync(); - - // Verify first practitioner (E2EPractitioner DrTest) - Assert.Contains("DrTest", content); - Assert.Contains("E2EPractitioner", content); - - // Verify additional practitioners - Assert.Contains("Johnson", content); - Assert.Contains("Sarah", content); - Assert.Contains("Chen", content); - Assert.Contains("Michael", content); - - // Verify FHIR qualification data displays - Assert.Contains("MD", content); - - // Verify FHIR specialty data displays - Assert.Contains("General Practice", content); - - await page.CloseAsync(); - } - - /// - /// CRITICAL TEST: Practitioners page data comes from REAL Scheduling API. - /// Directly verifies the API returns FHIR-compliant data. - /// - [Fact] - public async Task PractitionersPage_LoadsFromSchedulingApi_WithFhirCompliantData() - { - // First verify the API directly returns FHIR data - using var client = new HttpClient(); - var apiResponse = await client.GetStringAsync($"{E2EFixture.SchedulingUrl}/Practitioner"); - - // API should return all seeded practitioners with FHIR fields - Assert.Contains("DR001", apiResponse); - Assert.Contains("E2EPractitioner", apiResponse); - Assert.Contains("DrTest", apiResponse); - Assert.Contains("MD", apiResponse); - Assert.Contains("General Practice", apiResponse); - - // Now verify the Dashboard UI displays this data - var page = await _fixture.Browser!.NewPageAsync(); - page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); - - await page.GotoAsync(E2EFixture.DashboardUrl); - await page.WaitForSelectorAsync( - ".sidebar", - new PageWaitForSelectorOptions { Timeout = 20000 } - ); - - await page.ClickAsync("text=Practitioners"); - await page.WaitForSelectorAsync( - ".practitioner-card", - new PageWaitForSelectorOptions { Timeout = 10000 } - ); - - // Count practitioner cards - should have at least 3 from seeded data - var cards = await page.QuerySelectorAllAsync(".practitioner-card"); - Assert.True(cards.Count >= 3, $"Expected at least 3 practitioner cards, got {cards.Count}"); - - await page.CloseAsync(); - } - - /// - /// CRITICAL TEST: Dashboard loads and displays appointment data from Scheduling API. - /// - [Fact] - public async Task Dashboard_DisplaysAppointmentData_FromSchedulingApi() - { - var page = await _fixture.Browser!.NewPageAsync(); - await page.GotoAsync(E2EFixture.DashboardUrl); - await page.WaitForSelectorAsync( - ".sidebar", - new PageWaitForSelectorOptions { Timeout = 20000 } - ); - - await page.ClickAsync("text=Appointments"); - await page.WaitForSelectorAsync( - "text=Checkup", - new PageWaitForSelectorOptions { Timeout = 10000 } - ); - - var content = await page.ContentAsync(); - Assert.Contains("Checkup", content); - - await page.CloseAsync(); - } - - /// - /// CRITICAL TEST: Dashboard main page shows stats from both APIs. + /// Dashboard main page shows stats from both APIs. /// [Fact] public async Task Dashboard_MainPage_ShowsStatsFromBothApis() @@ -613,7 +89,7 @@ await page.WaitForSelectorAsync( ); // Verify via API that patient was actually created - using var client = new HttpClient(); + using var client = E2EFixture.CreateAuthenticatedClient(); var response = await client.GetStringAsync($"{E2EFixture.ClinicalUrl}/fhir/Patient/"); Assert.Contains(uniqueName, response); @@ -666,7 +142,7 @@ await page.WaitForSelectorAsync( ); // Verify via API that appointment was actually created - using var client = new HttpClient(); + using var client = E2EFixture.CreateAuthenticatedClient(); var response = await client.GetStringAsync($"{E2EFixture.SchedulingUrl}/Appointment"); Assert.Contains(uniqueServiceType, response); @@ -753,7 +229,7 @@ await page.WaitForSelectorAsync( [Fact] public async Task PatientCreationApi_WorksEndToEnd() { - using var client = new HttpClient(); + using var client = E2EFixture.CreateAuthenticatedClient(); // Create a patient with a unique name var uniqueName = $"ApiTest{DateTime.UtcNow.Ticks % 100000}"; @@ -780,7 +256,7 @@ public async Task PatientCreationApi_WorksEndToEnd() [Fact] public async Task PractitionerCreationApi_WorksEndToEnd() { - using var client = new HttpClient(); + using var client = E2EFixture.CreateAuthenticatedClient(); // Create a practitioner with a unique identifier var uniqueId = $"DR{DateTime.UtcNow.Ticks % 100000}"; @@ -807,7 +283,7 @@ public async Task PractitionerCreationApi_WorksEndToEnd() [Fact] public async Task EditPatientButton_OpensEditPage_AndUpdatesPatient() { - using var client = new HttpClient(); + using var client = E2EFixture.CreateAuthenticatedClient(); // First create a patient to edit var uniqueName = $"EditTest{DateTime.UtcNow.Ticks % 100000}"; @@ -991,7 +467,7 @@ await page.WaitForSelectorAsync( [Fact] public async Task EditPatientCancelButton_UsesHistoryBack() { - using var client = new HttpClient(); + using var client = E2EFixture.CreateAuthenticatedClient(); // Create a patient to edit var uniqueName = $"CancelTest{DateTime.UtcNow.Ticks % 100000}"; @@ -1065,7 +541,7 @@ await page.WaitForSelectorAsync( [Fact] public async Task BrowserBackButton_FromEditPage_ReturnsToPatientsPage() { - using var client = new HttpClient(); + using var client = E2EFixture.CreateAuthenticatedClient(); // Create a patient to edit var uniqueName = $"BackBtnTest{DateTime.UtcNow.Ticks % 100000}"; @@ -1212,7 +688,7 @@ await page.WaitForSelectorAsync( [Fact] public async Task PatientUpdateApi_WorksEndToEnd() { - using var client = new HttpClient(); + using var client = E2EFixture.CreateAuthenticatedClient(); // Create a patient first var uniqueName = $"UpdateApiTest{DateTime.UtcNow.Ticks % 100000}"; @@ -1305,7 +781,7 @@ await page.WaitForSelectorAsync( ); // Verify via API that practitioner was actually created - using var client = new HttpClient(); + using var client = E2EFixture.CreateAuthenticatedClient(); var response = await client.GetStringAsync($"{E2EFixture.SchedulingUrl}/Practitioner"); Assert.Contains(uniqueIdentifier, response); @@ -1319,7 +795,7 @@ await page.WaitForSelectorAsync( [Fact] public async Task EditPractitionerButton_OpensEditPage_AndUpdatesPractitioner() { - using var client = new HttpClient(); + using var client = E2EFixture.CreateAuthenticatedClient(); // Create a practitioner to edit var uniqueIdentifier = $"DREdit{DateTime.UtcNow.Ticks % 100000}"; @@ -1403,7 +879,7 @@ await page.WaitForSelectorAsync( [Fact] public async Task PractitionerUpdateApi_WorksEndToEnd() { - using var client = new HttpClient(); + using var client = E2EFixture.CreateAuthenticatedClient(); // Create a practitioner first var uniqueIdentifier = $"DRApi{DateTime.UtcNow.Ticks % 100000}"; @@ -1458,7 +934,7 @@ public async Task PractitionerUpdateApi_WorksEndToEnd() [Fact] public async Task BrowserBackButton_FromEditPractitionerPage_ReturnsToPractitionersPage() { - using var client = new HttpClient(); + using var client = E2EFixture.CreateAuthenticatedClient(); // Create a practitioner to edit var uniqueIdentifier = $"DRBack{DateTime.UtcNow.Ticks % 100000}"; @@ -1589,13 +1065,13 @@ await page.WaitForSelectorAsync( new PageWaitForSelectorOptions { Timeout = 5000 } ); - // Verify filter controls exist + // Verify filter controls exist (service-filter and action-filter) await page.WaitForSelectorAsync( - "[data-testid='status-filter']", + "[data-testid='service-filter']", new PageWaitForSelectorOptions { Timeout = 5000 } ); await page.WaitForSelectorAsync( - "[data-testid='service-filter']", + "[data-testid='action-filter']", new PageWaitForSelectorOptions { Timeout = 5000 } ); @@ -1611,7 +1087,7 @@ await page.WaitForSelectorAsync( /// /// CRITICAL TEST: Sync Dashboard filters work correctly. - /// Tests status and service filtering functionality. + /// Tests service and action filtering functionality. /// [Fact] public async Task SyncDashboard_FiltersWorkCorrectly() @@ -1625,17 +1101,22 @@ await page.WaitForSelectorAsync( new PageWaitForSelectorOptions { Timeout = 20000 } ); - // Get initial record count + // Wait for sync records table to be loaded + await page.WaitForSelectorAsync( + "[data-testid='sync-records-table']", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + // Get initial record count (may be 0 initially) var initialRows = await page.QuerySelectorAllAsync( "[data-testid='sync-records-table'] tbody tr" ); var initialCount = initialRows.Count; - Assert.True(initialCount > 0, "Should have sync records displayed"); - // Filter by status - select 'failed' - await page.SelectOptionAsync("[data-testid='status-filter']", "failed"); + // Filter by service - select 'clinical' + await page.SelectOptionAsync("[data-testid='service-filter']", "clinical"); - // Wait for filter to apply and check records + // Wait for filter to apply await Task.Delay(500); var filteredRows = await page.QuerySelectorAllAsync( "[data-testid='sync-records-table'] tbody tr" @@ -1645,12 +1126,8 @@ await page.WaitForSelectorAsync( "Filtered results should be <= initial count" ); - // Verify filtered content contains 'failed' status - var content = await page.ContentAsync(); - Assert.Contains("failed", content); - // Reset filter - await page.SelectOptionAsync("[data-testid='status-filter']", "all"); + await page.SelectOptionAsync("[data-testid='service-filter']", "all"); await page.CloseAsync(); } @@ -1689,7 +1166,7 @@ await page.WaitForSelectorAsync( [Fact] public async Task EditAppointmentButton_OpensEditPage_AndUpdatesAppointment() { - using var client = new HttpClient(); + using var client = E2EFixture.CreateAuthenticatedClient(); // First create an appointment to edit var uniqueServiceType = $"EditApptTest{DateTime.UtcNow.Ticks % 100000}"; @@ -1817,7 +1294,7 @@ await page.WaitForSelectorAsync( [Fact] public async Task CalendarPage_ClickOnDay_ShowsAppointmentDetails() { - using var client = new HttpClient(); + using var client = E2EFixture.CreateAuthenticatedClient(); // Create an appointment for today - use LOCAL time since browser calendar uses local timezone var today = DateTime.Now; @@ -1910,10 +1387,10 @@ await page.WaitForSelectorAsync( [Fact] public async Task CalendarPage_EditButton_OpensEditAppointmentPage() { - using var client = new HttpClient(); + using var client = E2EFixture.CreateAuthenticatedClient(); - // Create an appointment for today - var today = DateTime.UtcNow; + // Create an appointment for today using LOCAL time (calendar uses DateTime.Now) + var today = DateTime.Now; var startTime = new DateTime( today.Year, today.Month, @@ -1921,8 +1398,10 @@ public async Task CalendarPage_EditButton_OpensEditAppointmentPage() 15, 0, 0, - DateTimeKind.Utc - ).ToString("yyyy-MM-ddTHH:mm:ss.fffZ"); + DateTimeKind.Local + ) + .ToUniversalTime() + .ToString("yyyy-MM-ddTHH:mm:ss.fffZ"); var endTime = new DateTime( today.Year, today.Month, @@ -1930,8 +1409,10 @@ public async Task CalendarPage_EditButton_OpensEditAppointmentPage() 15, 30, 0, - DateTimeKind.Utc - ).ToString("yyyy-MM-ddTHH:mm:ss.fffZ"); + DateTimeKind.Local + ) + .ToUniversalTime() + .ToString("yyyy-MM-ddTHH:mm:ss.fffZ"); var uniqueServiceType = $"CalEdit{DateTime.UtcNow.Ticks % 100000}"; var createResponse = await client.PostAsync( @@ -2166,7 +1647,7 @@ await displayNameInput.IsVisibleAsync(), [Fact] public async Task GatekeeperApi_LoginBegin_ReturnsValidDiscoverableCredentialOptions() { - using var client = new HttpClient(); + using var client = E2EFixture.CreateAuthenticatedClient(); // Call /auth/login/begin with empty body (discoverable credentials flow) var response = await client.PostAsync( @@ -2230,7 +1711,7 @@ public async Task GatekeeperApi_LoginBegin_ReturnsValidDiscoverableCredentialOpt [Fact] public async Task GatekeeperApi_RegisterBegin_ReturnsValidOptions() { - using var client = new HttpClient(); + using var client = E2EFixture.CreateAuthenticatedClient(); // Call /auth/register/begin with email and display name var response = await client.PostAsync( @@ -2411,19 +1892,24 @@ public async Task SignOutButton_ClickShowsLoginPage() var page = await _fixture.Browser!.NewPageAsync(); page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Type}: {msg.Text}"); - // Set up a fake token in localStorage to simulate being logged in + // Set up a valid test token in localStorage to simulate being logged in await page.GotoAsync(E2EFixture.DashboardUrlNoTestMode); - // Inject a fake token to simulate authenticated state + // Inject a properly-signed token to simulate authenticated state + var testToken = E2EFixture.GenerateTestToken( + userId: "test-user", + displayName: "Test User", + email: "test@example.com" + ); await page.EvaluateAsync( - @"() => { - localStorage.setItem('gatekeeper_token', 'fake-token-for-testing'); - localStorage.setItem('gatekeeper_user', JSON.stringify({ + $@"() => {{ + localStorage.setItem('gatekeeper_token', '{testToken}'); + localStorage.setItem('gatekeeper_user', JSON.stringify({{ userId: 'test-user', displayName: 'Test User', email: 'test@example.com' - })); - }" + }})); + }}" ); // Reload to pick up the token @@ -2470,18 +1956,21 @@ await page.WaitForSelectorAsync( [Fact] public async Task GatekeeperApi_Logout_RevokesToken() { - using var client = new HttpClient(); - - // First, we need to get a valid session token by creating a user and session - // For this test, we'll verify the logout endpoint returns 401 for invalid tokens - // and verify the endpoint exists - var logoutResponse = await client.PostAsync( + // Test 1: Without a Bearer token, should return 401 Unauthorized + using var unauthClient = new HttpClient(); + var unauthResponse = await unauthClient.PostAsync( $"{E2EFixture.GatekeeperUrl}/auth/logout", new StringContent("{}", System.Text.Encoding.UTF8, "application/json") ); + Assert.Equal(HttpStatusCode.Unauthorized, unauthResponse.StatusCode); - // Without a valid Bearer token, should return 401 Unauthorized - Assert.Equal(HttpStatusCode.Unauthorized, logoutResponse.StatusCode); + // Test 2: With a valid Bearer token, should return 204 NoContent (logout succeeds) + using var authClient = E2EFixture.CreateAuthenticatedClient(); + var authResponse = await authClient.PostAsync( + $"{E2EFixture.GatekeeperUrl}/auth/logout", + new StringContent("{}", System.Text.Encoding.UTF8, "application/json") + ); + Assert.Equal(HttpStatusCode.NoContent, authResponse.StatusCode); } [Fact] @@ -2490,18 +1979,23 @@ public async Task UserMenu_DisplaysUserInitialsAndNameInDropdown() var page = await _fixture.Browser!.NewPageAsync(); page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Type}: {msg.Text}"); - // Inject a user with a specific name + // Inject a user with a specific name using a properly-signed token await page.GotoAsync(E2EFixture.DashboardUrlNoTestMode); + var testToken = E2EFixture.GenerateTestToken( + userId: "test-user", + displayName: "Alice Smith", + email: "alice@example.com" + ); await page.EvaluateAsync( - @"() => { - localStorage.setItem('gatekeeper_token', 'fake-token-for-testing'); - localStorage.setItem('gatekeeper_user', JSON.stringify({ + $@"() => {{ + localStorage.setItem('gatekeeper_token', '{testToken}'); + localStorage.setItem('gatekeeper_user', JSON.stringify({{ userId: 'test-user', displayName: 'Alice Smith', email: 'alice@example.com' - })); - }" + }})); + }}" ); await page.ReloadAsync(); @@ -2560,38 +2054,45 @@ await page.WaitForSelectorAsync( var loginPageVisible = await page.IsVisibleAsync("[data-testid='login-page']"); Assert.True(loginPageVisible, "Should start on login page"); + // Wait for React to mount and set the __triggerLogin hook + await page.WaitForFunctionAsync( + "() => typeof window.__triggerLogin === 'function'", + new PageWaitForFunctionOptions { Timeout = 10000 } + ); + // Simulate what happens after successful WebAuthn authentication: // 1. Token is stored in localStorage // 2. onLogin callback is called which sets isAuthenticated=true - // This is what the LoginPage component does after successful auth (lines 2250-2252 in index.html) + // This is what the LoginPage component does after successful auth + var testToken = E2EFixture.GenerateTestToken( + userId: "test-user-123", + displayName: "Test User", + email: "test@example.com" + ); await page.EvaluateAsync( - @"() => { - // Store token and user (what setAuthToken and setAuthUser do) - localStorage.setItem('gatekeeper_token', 'simulated-jwt-token'); - localStorage.setItem('gatekeeper_user', JSON.stringify({ + $@"() => {{ + console.log('[TEST] Setting token and triggering login'); + // Store a properly-signed token and user (what setAuthToken and setAuthUser do) + localStorage.setItem('gatekeeper_token', '{testToken}'); + localStorage.setItem('gatekeeper_user', JSON.stringify({{ userId: 'test-user-123', displayName: 'Test User', email: 'test@example.com' - })); - - // Trigger the React state update by finding and calling the onLogin prop - // This simulates what happens when LoginPage calls onLogin({ userId, displayName, email }) - // The App component's handleLogin sets isAuthenticated=true + }})); - // We need to dispatch a custom event that the app listens for, - // OR we can use window.__loginCallback if exposed for testing - if (window.__triggerLogin) { - window.__triggerLogin({ - userId: 'test-user-123', - displayName: 'Test User', - email: 'test@example.com' - }); - } - }" + // Trigger the React state update by calling the exposed login handler + // This simulates what happens when LoginPage calls onLogin after successful auth + window.__triggerLogin({{ + userId: 'test-user-123', + displayName: 'Test User', + email: 'test@example.com' + }}); + console.log('[TEST] Login triggered, waiting for React state update'); + }}" ); - // Wait a bit for React to re-render - await Task.Delay(500); + // Wait for React state update and re-render + await Task.Delay(2000); // Check if sidebar is now visible (indicates successful transition to dashboard) // If this times out, the bug exists - app didn't transition without refresh @@ -2599,12 +2100,15 @@ await page.EvaluateAsync( { await page.WaitForSelectorAsync( ".sidebar", - new PageWaitForSelectorOptions { Timeout = 5000 } + new PageWaitForSelectorOptions { Timeout = 10000 } ); // Verify login page is gone var loginPageStillVisible = await page.IsVisibleAsync("[data-testid='login-page']"); - Assert.False(loginPageStillVisible, "Login page should be hidden after successful login"); + Assert.False( + loginPageStillVisible, + "Login page should be hidden after successful login" + ); // Verify sidebar is visible (dashboard state) var sidebarVisible = await page.IsVisibleAsync(".sidebar"); @@ -2614,10 +2118,10 @@ await page.WaitForSelectorAsync( { // If we get here, the bug exists - first-time sign-in doesn't work without refresh Assert.Fail( - "FIRST-TIME SIGN-IN BUG: App did not transition to dashboard after login. " + - "User must refresh the browser for login to take effect. " + - "Fix: Expose window.__triggerLogin in App component for testing, " + - "or verify onLogin callback properly triggers React state update." + "FIRST-TIME SIGN-IN BUG: App did not transition to dashboard after login. " + + "User must refresh the browser for login to take effect. " + + "Fix: Expose window.__triggerLogin in App component for testing, " + + "or verify onLogin callback properly triggers React state update." ); } diff --git a/Samples/Dashboard/Dashboard.Integration.Tests/E2EFixture.cs b/Samples/Dashboard/Dashboard.Integration.Tests/E2EFixture.cs new file mode 100644 index 00000000..3511f6bc --- /dev/null +++ b/Samples/Dashboard/Dashboard.Integration.Tests/E2EFixture.cs @@ -0,0 +1,579 @@ +using System.Diagnostics; +using System.Net; +using System.Security.Cryptography; +using System.Text; +using System.Text.Json; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Hosting; +using Microsoft.Playwright; + +namespace Dashboard.Integration.Tests; + +/// +/// Shared fixture that starts all services ONCE for all E2E tests. +/// +public sealed class E2EFixture : IAsyncLifetime +{ + private Process? _clinicalProcess; + private Process? _schedulingProcess; + private Process? _gatekeeperProcess; + private Process? _clinicalSyncProcess; + private Process? _schedulingSyncProcess; + private IHost? _dashboardHost; + + /// + /// Playwright instance shared by all tests. + /// + public IPlaywright? Playwright { get; private set; } + + /// + /// Browser instance shared by all tests. + /// + public IBrowser? Browser { get; private set; } + + /// + /// Clinical API URL - SAME as real app default. + /// + public const string ClinicalUrl = "http://localhost:5080"; + + /// + /// Scheduling API URL - SAME as real app default. + /// + public const string SchedulingUrl = "http://localhost:5001"; + + /// + /// Gatekeeper Auth API URL - SAME as real app default. + /// + public const string GatekeeperUrl = "http://localhost:5002"; + + /// + /// Dashboard URL - SAME as real app default. + /// Uses testMode=true to bypass authentication in tests. + /// + public const string DashboardUrl = "http://localhost:5173?testMode=true"; + + /// + /// Dashboard URL without test mode - for auth tests. + /// + public const string DashboardUrlNoTestMode = "http://localhost:5173"; + + /// + /// Start all services ONCE for all tests. + /// + public async Task InitializeAsync() + { + await KillProcessOnPortAsync(5080); + await KillProcessOnPortAsync(5001); + await KillProcessOnPortAsync(5002); + await KillProcessOnPortAsync(5173); + await Task.Delay(2000); + + var testAssemblyDir = Path.GetDirectoryName(typeof(E2EFixture).Assembly.Location)!; + var samplesDir = Path.GetFullPath( + Path.Combine(testAssemblyDir, "..", "..", "..", "..", "..") + ); + var rootDir = Path.GetFullPath(Path.Combine(samplesDir, "..")); + var clinicalProjectDir = Path.Combine(samplesDir, "Clinical", "Clinical.Api"); + var schedulingProjectDir = Path.Combine(samplesDir, "Scheduling", "Scheduling.Api"); + var gatekeeperProjectDir = Path.Combine(rootDir, "Gatekeeper", "Gatekeeper.Api"); + var configuration = ResolveBuildConfiguration(testAssemblyDir); + + // Delete existing databases to ensure fresh state for each test run + // This prevents sync version mismatch issues between runs + DeleteDatabaseIfExists(clinicalProjectDir, configuration, "clinical.db"); + DeleteDatabaseIfExists(schedulingProjectDir, configuration, "scheduling.db"); + DeleteDatabaseIfExists(gatekeeperProjectDir, configuration, "gatekeeper.db"); + + Console.WriteLine($"[E2E] Test assembly dir: {testAssemblyDir}"); + Console.WriteLine($"[E2E] Build configuration: {configuration}"); + Console.WriteLine($"[E2E] Samples dir: {samplesDir}"); + Console.WriteLine($"[E2E] Clinical dir: {clinicalProjectDir}"); + Console.WriteLine($"[E2E] Gatekeeper dir: {gatekeeperProjectDir}"); + + var clinicalDll = Path.Combine( + clinicalProjectDir, + "bin", + configuration, + "net9.0", + "Clinical.Api.dll" + ); + _clinicalProcess = StartApiFromDll(clinicalDll, clinicalProjectDir, ClinicalUrl); + + var schedulingDll = Path.Combine( + schedulingProjectDir, + "bin", + configuration, + "net9.0", + "Scheduling.Api.dll" + ); + _schedulingProcess = StartApiFromDll(schedulingDll, schedulingProjectDir, SchedulingUrl); + + var gatekeeperDll = Path.Combine( + gatekeeperProjectDir, + "bin", + configuration, + "net9.0", + "Gatekeeper.Api.dll" + ); + _gatekeeperProcess = StartApiFromDll(gatekeeperDll, gatekeeperProjectDir, GatekeeperUrl); + + await Task.Delay(2000); + + await WaitForApiAsync(ClinicalUrl, "/fhir/Patient/"); + await WaitForApiAsync(SchedulingUrl, "/Practitioner"); + await WaitForGatekeeperApiAsync(); + + var clinicalDbPath = Path.Combine( + clinicalProjectDir, + "bin", + configuration, + "net9.0", + "clinical.db" + ); + var schedulingDbPath = Path.Combine( + schedulingProjectDir, + "bin", + configuration, + "net9.0", + "scheduling.db" + ); + + var clinicalSyncDir = Path.Combine(samplesDir, "Clinical", "Clinical.Sync"); + var clinicalSyncDll = Path.Combine( + clinicalSyncDir, + "bin", + configuration, + "net9.0", + "Clinical.Sync.dll" + ); + if (File.Exists(clinicalSyncDll)) + { + var clinicalSyncEnv = new Dictionary + { + ["CLINICAL_DB_PATH"] = clinicalDbPath, + ["SCHEDULING_API_URL"] = SchedulingUrl, + ["POLL_INTERVAL_SECONDS"] = "5", // Fast polling for E2E tests + }; + _clinicalSyncProcess = StartSyncWorker( + clinicalSyncDll, + clinicalSyncDir, + clinicalSyncEnv + ); + } + else + { + Console.WriteLine($"[E2E] Clinical sync worker missing: {clinicalSyncDll}"); + } + + var schedulingSyncDir = Path.Combine(samplesDir, "Scheduling", "Scheduling.Sync"); + var schedulingSyncDll = Path.Combine( + schedulingSyncDir, + "bin", + configuration, + "net9.0", + "Scheduling.Sync.dll" + ); + if (File.Exists(schedulingSyncDll)) + { + var schedulingSyncEnv = new Dictionary + { + ["SCHEDULING_DB_PATH"] = schedulingDbPath, + ["CLINICAL_API_URL"] = ClinicalUrl, + ["POLL_INTERVAL_SECONDS"] = "5", // Fast polling for E2E tests + }; + _schedulingSyncProcess = StartSyncWorker( + schedulingSyncDll, + schedulingSyncDir, + schedulingSyncEnv + ); + } + else + { + Console.WriteLine($"[E2E] Scheduling sync worker missing: {schedulingSyncDll}"); + } + + await Task.Delay(2000); + + _dashboardHost = CreateDashboardHost(); + await _dashboardHost.StartAsync(); + + await SeedTestDataAsync(); + + Playwright = await Microsoft.Playwright.Playwright.CreateAsync(); + Browser = await Playwright.Chromium.LaunchAsync( + new BrowserTypeLaunchOptions { Headless = true } + ); + } + + /// + /// Stop all services ONCE after all tests. + /// Order matters: stop sync workers FIRST to prevent connection errors. + /// + public async Task DisposeAsync() + { + try + { + if (Browser is not null) + await Browser.CloseAsync(); + } + catch { } + Playwright?.Dispose(); + + StopProcess(_clinicalSyncProcess); + StopProcess(_schedulingSyncProcess); + await Task.Delay(1000); + + try + { + if (_dashboardHost is not null) + await _dashboardHost.StopAsync(TimeSpan.FromSeconds(5)); + } + catch { } + _dashboardHost?.Dispose(); + + StopProcess(_clinicalProcess); + StopProcess(_schedulingProcess); + StopProcess(_gatekeeperProcess); + + await KillProcessOnPortAsync(5080); + await KillProcessOnPortAsync(5001); + await KillProcessOnPortAsync(5002); + await KillProcessOnPortAsync(5173); + } + + private static Process StartApiFromDll(string dllPath, string contentRoot, string url) + { + var process = new Process + { + StartInfo = new ProcessStartInfo + { + FileName = "dotnet", + Arguments = $"\"{dllPath}\" --urls \"{url}\" --contentRoot \"{contentRoot}\"", + WorkingDirectory = contentRoot, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + }, + }; + + process.OutputDataReceived += (_, e) => + { + if (!string.IsNullOrEmpty(e.Data)) + Console.WriteLine($"[API {url}] {e.Data}"); + }; + process.ErrorDataReceived += (_, e) => + { + if (!string.IsNullOrEmpty(e.Data)) + Console.WriteLine($"[API {url} ERR] {e.Data}"); + }; + + process.Start(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + return process; + } + + private static Process StartSyncWorker( + string dllPath, + string workingDir, + Dictionary? envVars = null + ) + { + var startInfo = new ProcessStartInfo + { + FileName = "dotnet", + Arguments = $"\"{dllPath}\"", + WorkingDirectory = workingDir, + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, + CreateNoWindow = true, + }; + + if (envVars is not null) + { + foreach (var kvp in envVars) + startInfo.EnvironmentVariables[kvp.Key] = kvp.Value; + } + + var process = new Process { StartInfo = startInfo }; + process.OutputDataReceived += (_, e) => + { + if (!string.IsNullOrEmpty(e.Data)) + Console.WriteLine($"[SYNC] {e.Data}"); + }; + process.ErrorDataReceived += (_, e) => + { + if (!string.IsNullOrEmpty(e.Data)) + Console.WriteLine($"[SYNC ERR] {e.Data}"); + }; + + process.Start(); + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + return process; + } + + private static void StopProcess(Process? process) + { + if (process is null || process.HasExited) + return; + try + { + process.Kill(entireProcessTree: true); + process.WaitForExit(5000); + } + catch { } + finally + { + process.Dispose(); + } + } + + private static async Task KillProcessOnPortAsync(int port) + { + // Try multiple times to ensure port is released + for (var attempt = 0; attempt < 3; attempt++) + { + try + { + var psi = new ProcessStartInfo + { + FileName = "/bin/sh", + Arguments = $"-c \"lsof -ti :{port} | xargs kill -9 2>/dev/null || true\"", + UseShellExecute = false, + CreateNoWindow = true, + }; + using var process = Process.Start(psi); + if (process is not null) + await process.WaitForExitAsync(); + } + catch { } + await Task.Delay(1000); + + // Verify port is free + if (await IsPortAvailableAsync(port)) + return; + } + } + + private static Task IsPortAvailableAsync(int port) + { + try + { + using var listener = new System.Net.Sockets.TcpListener(IPAddress.Loopback, port); + listener.Start(); + listener.Stop(); + return Task.FromResult(true); + } + catch + { + return Task.FromResult(false); + } + } + + private static void DeleteDatabaseIfExists( + string projectDir, + string configuration, + string dbName + ) + { + var dbPath = Path.Combine(projectDir, "bin", configuration, "net9.0", dbName); + if (File.Exists(dbPath)) + { + try + { + File.Delete(dbPath); + Console.WriteLine($"[E2E] Deleted database: {dbPath}"); + } + catch (Exception ex) + { + Console.WriteLine($"[E2E] Could not delete {dbPath}: {ex.Message}"); + } + } + } + + private static string ResolveBuildConfiguration(string testAssemblyDir) + { + var net9Dir = new DirectoryInfo(testAssemblyDir); + var configuration = net9Dir.Parent?.Name; + return string.IsNullOrWhiteSpace(configuration) ? "Debug" : configuration; + } + + private static async Task WaitForApiAsync(string baseUrl, string healthEndpoint) + { + using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(2) }; + for (var i = 0; i < 120; i++) + { + try + { + var response = await client.GetAsync($"{baseUrl}{healthEndpoint}"); + if ( + response.IsSuccessStatusCode + || response.StatusCode == HttpStatusCode.NotFound + || response.StatusCode == HttpStatusCode.Unauthorized + || response.StatusCode == HttpStatusCode.Forbidden + ) + return; + } + catch { } + await Task.Delay(500); + } + throw new TimeoutException($"API at {baseUrl} did not start"); + } + + private static async Task WaitForGatekeeperApiAsync() + { + using var client = new HttpClient { Timeout = TimeSpan.FromSeconds(2) }; + for (var i = 0; i < 120; i++) + { + try + { + var response = await client.PostAsync( + $"{GatekeeperUrl}/auth/login/begin", + new StringContent("{}", Encoding.UTF8, "application/json") + ); + if (response.IsSuccessStatusCode) + return; + } + catch { } + await Task.Delay(500); + } + throw new TimeoutException($"Gatekeeper API did not start"); + } + + /// + /// Creates an authenticated HTTP client with test JWT token. + /// + public static HttpClient CreateAuthenticatedClient() + { + var client = new HttpClient(); + client.DefaultRequestHeaders.Authorization = + new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", GenerateTestToken()); + return client; + } + + /// + /// Generates a test JWT token with the specified user details. + /// Uses the same all-zeros signing key that the APIs use in dev mode. + /// + public static string GenerateTestToken( + string userId = "e2e-test-user", + string displayName = "E2E Test User", + string email = "e2etest@example.com" + ) + { + var signingKey = new byte[32]; + var header = Base64UrlEncode(Encoding.UTF8.GetBytes("""{"alg":"HS256","typ":"JWT"}""")); + var expiration = DateTimeOffset.UtcNow.AddHours(1).ToUnixTimeSeconds(); + var payload = Base64UrlEncode( + Encoding.UTF8.GetBytes( + JsonSerializer.Serialize( + new + { + sub = userId, + name = displayName, + email, + jti = Guid.NewGuid().ToString(), + exp = expiration, + roles = new[] { "admin", "user" }, + } + ) + ) + ); + var signature = ComputeHmacSignature(header, payload, signingKey); + return $"{header}.{payload}.{signature}"; + } + + private static string Base64UrlEncode(byte[] input) => + Convert.ToBase64String(input).Replace("+", "-").Replace("/", "_").TrimEnd('='); + + private static string ComputeHmacSignature(string header, string payload, byte[] key) + { + var data = Encoding.UTF8.GetBytes($"{header}.{payload}"); + using var hmac = new HMACSHA256(key); + return Base64UrlEncode(hmac.ComputeHash(data)); + } + + private static IHost CreateDashboardHost() + { + var wwwrootPath = Path.Combine(AppContext.BaseDirectory, "wwwroot"); + return Host.CreateDefaultBuilder() + .ConfigureWebHostDefaults(webBuilder => + { + webBuilder.UseUrls("http://localhost:5173"); + webBuilder.Configure(app => + { + app.UseDefaultFiles(); + app.UseStaticFiles( + new StaticFileOptions + { + FileProvider = new PhysicalFileProvider(wwwrootPath), + } + ); + }); + }) + .Build(); + } + + private static async Task SeedTestDataAsync() + { + using var client = CreateAuthenticatedClient(); + + await client.PostAsync( + $"{ClinicalUrl}/fhir/Patient/", + new StringContent( + """{"Active": true, "GivenName": "E2ETest", "FamilyName": "TestPatient", "Gender": "other"}""", + Encoding.UTF8, + "application/json" + ) + ); + + await client.PostAsync( + $"{SchedulingUrl}/Practitioner", + new StringContent( + """{"Identifier": "DR001", "Active": true, "NameGiven": "E2EPractitioner", "NameFamily": "DrTest", "Qualification": "MD", "Specialty": "General Practice", "TelecomEmail": "drtest@hospital.org", "TelecomPhone": "+1-555-0123"}""", + Encoding.UTF8, + "application/json" + ) + ); + + await client.PostAsync( + $"{SchedulingUrl}/Practitioner", + new StringContent( + """{"Identifier": "DR002", "Active": true, "NameGiven": "Sarah", "NameFamily": "Johnson", "Qualification": "DO", "Specialty": "Cardiology", "TelecomEmail": "sjohnson@hospital.org", "TelecomPhone": "+1-555-0124"}""", + Encoding.UTF8, + "application/json" + ) + ); + + await client.PostAsync( + $"{SchedulingUrl}/Practitioner", + new StringContent( + """{"Identifier": "DR003", "Active": true, "NameGiven": "Michael", "NameFamily": "Chen", "Qualification": "MD", "Specialty": "Neurology", "TelecomEmail": "mchen@hospital.org", "TelecomPhone": "+1-555-0125"}""", + Encoding.UTF8, + "application/json" + ) + ); + + await client.PostAsync( + $"{SchedulingUrl}/Appointment", + new StringContent( + """{"ServiceCategory": "General", "ServiceType": "Checkup", "Start": "2025-12-20T10:00:00Z", "End": "2025-12-20T11:00:00Z", "PatientReference": "Patient/1", "PractitionerReference": "Practitioner/1", "Priority": "routine"}""", + Encoding.UTF8, + "application/json" + ) + ); + } +} + +/// +/// Single collection definition for ALL E2E tests. +/// All tests share ONE E2EFixture instance to prevent port conflicts. +/// Tests within this collection run sequentially by default. +/// +[CollectionDefinition("E2E Tests")] +public sealed class E2ECollection : ICollectionFixture; diff --git a/Samples/Dashboard/Dashboard.Integration.Tests/GlobalUsings.cs b/Samples/Dashboard/Dashboard.Integration.Tests/GlobalUsings.cs index 3573a53c..bb30e84a 100644 --- a/Samples/Dashboard/Dashboard.Integration.Tests/GlobalUsings.cs +++ b/Samples/Dashboard/Dashboard.Integration.Tests/GlobalUsings.cs @@ -1,2 +1,3 @@ +global using System.Net; global using Microsoft.AspNetCore.Mvc.Testing; global using Xunit; diff --git a/Samples/Dashboard/Dashboard.Integration.Tests/NavigationE2ETests.cs b/Samples/Dashboard/Dashboard.Integration.Tests/NavigationE2ETests.cs new file mode 100644 index 00000000..b0c9accb --- /dev/null +++ b/Samples/Dashboard/Dashboard.Integration.Tests/NavigationE2ETests.cs @@ -0,0 +1,280 @@ +using System.Text.RegularExpressions; +using Microsoft.Playwright; + +namespace Dashboard.Integration.Tests; + +/// +/// E2E tests for browser navigation (back/forward, deep linking). +/// +[Collection("E2E Tests")] +[Trait("Category", "E2E")] +public sealed class NavigationE2ETests +{ + private readonly E2EFixture _fixture; + + /// + /// Constructor receives shared fixture. + /// + public NavigationE2ETests(E2EFixture fixture) => _fixture = fixture; + + /// + /// Browser back button navigates to previous view. + /// + [Fact] + public async Task BrowserBackButton_NavigatesToPreviousView() + { + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + Assert.Contains("#dashboard", page.Url); + + await page.ClickAsync("text=Patients"); + await page.WaitForSelectorAsync( + "[data-testid='add-patient-btn']", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + Assert.Contains("#patients", page.Url); + + await page.ClickAsync("text=Appointments"); + await page.WaitForSelectorAsync( + "[data-testid='add-appointment-btn']", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + Assert.Contains("#appointments", page.Url); + + await page.GoBackAsync(); + await page.WaitForSelectorAsync( + "[data-testid='add-patient-btn']", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + Assert.Contains("#patients", page.Url); + + await page.GoBackAsync(); + await page.WaitForSelectorAsync( + ".metric-card", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + Assert.Contains("#dashboard", page.Url); + + await page.CloseAsync(); + } + + /// + /// Deep linking works - navigating directly to a hash URL loads correct view. + /// + [Fact] + public async Task DeepLinking_LoadsCorrectView() + { + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + await page.GotoAsync($"{E2EFixture.DashboardUrl}#patients"); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.WaitForSelectorAsync( + "[data-testid='add-patient-btn']", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + var content = await page.ContentAsync(); + Assert.Contains("Patients", content); + + await page.GotoAsync($"{E2EFixture.DashboardUrl}#appointments"); + await page.WaitForSelectorAsync( + "[data-testid='add-appointment-btn']", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + content = await page.ContentAsync(); + Assert.Contains("Appointments", content); + + await page.CloseAsync(); + } + + /// + /// Cancel button on edit page uses history.back(). + /// + [Fact] + public async Task EditPatientCancelButton_UsesHistoryBack() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + + var uniqueName = $"CancelTest{DateTime.UtcNow.Ticks % 100000}"; + var createResponse = await client.PostAsync( + $"{E2EFixture.ClinicalUrl}/fhir/Patient/", + new StringContent( + $$$"""{"Active": true, "GivenName": "{{{uniqueName}}}", "FamilyName": "CancelTestPatient", "Gender": "male"}""", + System.Text.Encoding.UTF8, + "application/json" + ) + ); + createResponse.EnsureSuccessStatusCode(); + var createdJson = await createResponse.Content.ReadAsStringAsync(); + var patientIdMatch = Regex.Match(createdJson, "\"Id\"\\s*:\\s*\"([^\"]+)\""); + var patientId = patientIdMatch.Groups[1].Value; + + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.ClickAsync("text=Patients"); + await page.WaitForSelectorAsync( + "[data-testid='add-patient-btn']", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + await page.FillAsync("input[placeholder*='Search']", uniqueName); + await page.WaitForSelectorAsync( + $"text={uniqueName}", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + await page.ClickAsync($"[data-testid='edit-patient-{patientId}']"); + await page.WaitForSelectorAsync( + "[data-testid='edit-patient-page']", + new PageWaitForSelectorOptions { Timeout = 5000 } + ); + + await page.ClickAsync("button:has-text('Cancel')"); + await page.WaitForSelectorAsync( + "[data-testid='add-patient-btn']", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + Assert.Contains("#patients", page.Url); + Assert.DoesNotContain("/edit/", page.Url); + + await page.CloseAsync(); + } + + /// + /// Browser back button from Edit Patient page returns to patients list. + /// + [Fact] + public async Task BrowserBackButton_FromEditPage_ReturnsToPatientsPage() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + + var uniqueName = $"BackBtnTest{DateTime.UtcNow.Ticks % 100000}"; + var createResponse = await client.PostAsync( + $"{E2EFixture.ClinicalUrl}/fhir/Patient/", + new StringContent( + $$$"""{"Active": true, "GivenName": "{{{uniqueName}}}", "FamilyName": "BackButtonTest", "Gender": "female"}""", + System.Text.Encoding.UTF8, + "application/json" + ) + ); + createResponse.EnsureSuccessStatusCode(); + var createdJson = await createResponse.Content.ReadAsStringAsync(); + var patientIdMatch = Regex.Match(createdJson, "\"Id\"\\s*:\\s*\"([^\"]+)\""); + var patientId = patientIdMatch.Groups[1].Value; + + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.ClickAsync("text=Patients"); + await page.WaitForSelectorAsync( + "[data-testid='add-patient-btn']", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + await page.FillAsync("input[placeholder*='Search']", uniqueName); + await page.WaitForSelectorAsync( + $"text={uniqueName}", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + await page.ClickAsync($"[data-testid='edit-patient-{patientId}']"); + await page.WaitForSelectorAsync( + "[data-testid='edit-patient-page']", + new PageWaitForSelectorOptions { Timeout = 5000 } + ); + + await page.GoBackAsync(); + + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + await page.WaitForSelectorAsync( + "[data-testid='add-patient-btn']", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + Assert.Contains("#patients", page.Url); + + var content = await page.ContentAsync(); + Assert.Contains("Patients", content); + Assert.Contains("Add Patient", content); + + await page.GoBackAsync(); + await page.WaitForSelectorAsync( + ".metric-card", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + Assert.Contains("#dashboard", page.Url); + + await page.CloseAsync(); + } + + /// + /// Forward button works after going back. + /// + [Fact] + public async Task BrowserForwardButton_WorksAfterGoingBack() + { + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + + await page.ClickAsync("text=Patients"); + await page.WaitForSelectorAsync( + "[data-testid='add-patient-btn']", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + await page.ClickAsync("text=Practitioners"); + await page.WaitForSelectorAsync( + ".practitioner-card, .empty-state", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + Assert.Contains("#practitioners", page.Url); + + await page.GoBackAsync(); + await page.WaitForSelectorAsync( + "[data-testid='add-patient-btn']", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + Assert.Contains("#patients", page.Url); + + await page.GoForwardAsync(); + await page.WaitForSelectorAsync( + ".practitioner-card, .empty-state", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + Assert.Contains("#practitioners", page.Url); + + var content = await page.ContentAsync(); + Assert.Contains("Practitioners", content); + + await page.CloseAsync(); + } +} diff --git a/Samples/Dashboard/Dashboard.Integration.Tests/PatientE2ETests.cs b/Samples/Dashboard/Dashboard.Integration.Tests/PatientE2ETests.cs new file mode 100644 index 00000000..41e51284 --- /dev/null +++ b/Samples/Dashboard/Dashboard.Integration.Tests/PatientE2ETests.cs @@ -0,0 +1,247 @@ +using System.Text.RegularExpressions; +using Microsoft.Playwright; + +namespace Dashboard.Integration.Tests; + +/// +/// E2E tests for patient-related functionality. +/// +[Collection("E2E Tests")] +[Trait("Category", "E2E")] +public sealed class PatientE2ETests +{ + private readonly E2EFixture _fixture; + + /// + /// Constructor receives shared fixture. + /// + public PatientE2ETests(E2EFixture fixture) => _fixture = fixture; + + /// + /// Dashboard loads and displays patient data from Clinical API. + /// + [Fact] + public async Task Dashboard_DisplaysPatientData_FromClinicalApi() + { + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Type}: {msg.Text}"); + + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.ClickAsync("text=Patients"); + await page.WaitForSelectorAsync( + "text=TestPatient", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + var content = await page.ContentAsync(); + Assert.Contains("TestPatient", content); + Assert.Contains("E2ETest", content); + + await page.CloseAsync(); + } + + /// + /// Add Patient button opens modal and creates patient via API. + /// + [Fact] + public async Task AddPatientButton_OpensModal_AndCreatesPatient() + { + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.ClickAsync("text=Patients"); + await page.WaitForSelectorAsync( + "[data-testid='add-patient-btn']", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + await page.ClickAsync("[data-testid='add-patient-btn']"); + await page.WaitForSelectorAsync( + ".modal", + new PageWaitForSelectorOptions { Timeout = 5000 } + ); + + var uniqueName = $"E2ECreated{DateTime.UtcNow.Ticks % 100000}"; + await page.FillAsync("[data-testid='patient-given-name']", uniqueName); + await page.FillAsync("[data-testid='patient-family-name']", "TestCreated"); + await page.SelectOptionAsync("[data-testid='patient-gender']", "male"); + await page.ClickAsync("[data-testid='submit-patient']"); + + await page.WaitForSelectorAsync( + $"text={uniqueName}", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + using var client = E2EFixture.CreateAuthenticatedClient(); + var response = await client.GetStringAsync($"{E2EFixture.ClinicalUrl}/fhir/Patient/"); + Assert.Contains(uniqueName, response); + + await page.CloseAsync(); + } + + /// + /// Patient Search button navigates to search and finds patients. + /// + [Fact] + public async Task PatientSearchButton_NavigatesToSearch_AndFindsPatients() + { + var page = await _fixture.Browser!.NewPageAsync(); + + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.ClickAsync("text=Patient Search"); + await page.WaitForSelectorAsync( + "input[placeholder*='Search']", + new PageWaitForSelectorOptions { Timeout = 5000 } + ); + await page.FillAsync("input[placeholder*='Search']", "E2ETest"); + await page.WaitForSelectorAsync( + "text=TestPatient", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + var content = await page.ContentAsync(); + Assert.Contains("TestPatient", content); + + await page.CloseAsync(); + } + + /// + /// Patient creation API works end-to-end. + /// + [Fact] + public async Task PatientCreationApi_WorksEndToEnd() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + + var uniqueName = $"ApiTest{DateTime.UtcNow.Ticks % 100000}"; + var createResponse = await client.PostAsync( + $"{E2EFixture.ClinicalUrl}/fhir/Patient/", + new StringContent( + $$$"""{"Active": true, "GivenName": "{{{uniqueName}}}", "FamilyName": "ApiCreated", "Gender": "female"}""", + System.Text.Encoding.UTF8, + "application/json" + ) + ); + createResponse.EnsureSuccessStatusCode(); + + var listResponse = await client.GetStringAsync($"{E2EFixture.ClinicalUrl}/fhir/Patient/"); + Assert.Contains(uniqueName, listResponse); + } + + /// + /// Edit Patient button opens edit page and updates patient via API. + /// + [Fact] + public async Task EditPatientButton_OpensEditPage_AndUpdatesPatient() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + + var uniqueName = $"EditTest{DateTime.UtcNow.Ticks % 100000}"; + var createResponse = await client.PostAsync( + $"{E2EFixture.ClinicalUrl}/fhir/Patient/", + new StringContent( + $$$"""{"Active": true, "GivenName": "{{{uniqueName}}}", "FamilyName": "ToBeEdited", "Gender": "female"}""", + System.Text.Encoding.UTF8, + "application/json" + ) + ); + createResponse.EnsureSuccessStatusCode(); + var createdPatientJson = await createResponse.Content.ReadAsStringAsync(); + + var patientIdMatch = Regex.Match(createdPatientJson, "\"Id\"\\s*:\\s*\"([^\"]+)\""); + Assert.True(patientIdMatch.Success); + var patientId = patientIdMatch.Groups[1].Value; + + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.ClickAsync("text=Patients"); + await page.WaitForSelectorAsync( + "[data-testid='add-patient-btn']", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + await page.FillAsync("input[placeholder*='Search']", uniqueName); + await page.WaitForSelectorAsync( + $"text={uniqueName}", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + await page.ClickAsync($"[data-testid='edit-patient-{patientId}']"); + await page.WaitForSelectorAsync( + "[data-testid='edit-patient-page']", + new PageWaitForSelectorOptions { Timeout = 5000 } + ); + + var newFamilyName = $"Edited{DateTime.UtcNow.Ticks % 100000}"; + await page.FillAsync("[data-testid='edit-family-name']", newFamilyName); + await page.ClickAsync("[data-testid='save-patient']"); + await page.WaitForSelectorAsync( + "[data-testid='edit-success']", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + var updatedPatientJson = await client.GetStringAsync( + $"{E2EFixture.ClinicalUrl}/fhir/Patient/{patientId}" + ); + Assert.Contains(newFamilyName, updatedPatientJson); + + await page.CloseAsync(); + } + + /// + /// Patient update API works end-to-end. + /// + [Fact] + public async Task PatientUpdateApi_WorksEndToEnd() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + + var uniqueName = $"UpdateApiTest{DateTime.UtcNow.Ticks % 100000}"; + var createResponse = await client.PostAsync( + $"{E2EFixture.ClinicalUrl}/fhir/Patient/", + new StringContent( + $$$"""{"Active": true, "GivenName": "{{{uniqueName}}}", "FamilyName": "Original", "Gender": "male"}""", + System.Text.Encoding.UTF8, + "application/json" + ) + ); + createResponse.EnsureSuccessStatusCode(); + var createdPatientJson = await createResponse.Content.ReadAsStringAsync(); + + var patientIdMatch = Regex.Match(createdPatientJson, "\"Id\"\\s*:\\s*\"([^\"]+)\""); + var patientId = patientIdMatch.Groups[1].Value; + + var updatedFamilyName = $"Updated{DateTime.UtcNow.Ticks % 100000}"; + var updateResponse = await client.PutAsync( + $"{E2EFixture.ClinicalUrl}/fhir/Patient/{patientId}", + new StringContent( + $$$"""{"Active": true, "GivenName": "{{{uniqueName}}}", "FamilyName": "{{{updatedFamilyName}}}", "Gender": "male", "Email": "updated@test.com"}""", + System.Text.Encoding.UTF8, + "application/json" + ) + ); + updateResponse.EnsureSuccessStatusCode(); + + var getResponse = await client.GetStringAsync( + $"{E2EFixture.ClinicalUrl}/fhir/Patient/{patientId}" + ); + Assert.Contains(updatedFamilyName, getResponse); + Assert.Contains("updated@test.com", getResponse); + } +} diff --git a/Samples/Dashboard/Dashboard.Integration.Tests/PractitionerE2ETests.cs b/Samples/Dashboard/Dashboard.Integration.Tests/PractitionerE2ETests.cs new file mode 100644 index 00000000..74156071 --- /dev/null +++ b/Samples/Dashboard/Dashboard.Integration.Tests/PractitionerE2ETests.cs @@ -0,0 +1,318 @@ +using System.Text.RegularExpressions; +using Microsoft.Playwright; + +namespace Dashboard.Integration.Tests; + +/// +/// E2E tests for practitioner-related functionality. +/// +[Collection("E2E Tests")] +[Trait("Category", "E2E")] +public sealed class PractitionerE2ETests +{ + private readonly E2EFixture _fixture; + + /// + /// Constructor receives shared fixture. + /// + public PractitionerE2ETests(E2EFixture fixture) => _fixture = fixture; + + /// + /// Dashboard loads and displays practitioner data from Scheduling API. + /// + [Fact] + public async Task Dashboard_DisplaysPractitionerData_FromSchedulingApi() + { + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.ClickAsync("text=Practitioners"); + await page.WaitForSelectorAsync( + "text=DrTest", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + await page.WaitForSelectorAsync( + ".practitioner-card", + new PageWaitForSelectorOptions { Timeout = 5000 } + ); + + var content = await page.ContentAsync(); + Assert.Contains("DrTest", content); + Assert.Contains("E2EPractitioner", content); + Assert.Contains("Johnson", content); + Assert.Contains("MD", content); + Assert.Contains("General Practice", content); + + await page.CloseAsync(); + } + + /// + /// Practitioners page data comes from REAL Scheduling API. + /// + [Fact] + public async Task PractitionersPage_LoadsFromSchedulingApi_WithFhirCompliantData() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + var apiResponse = await client.GetStringAsync($"{E2EFixture.SchedulingUrl}/Practitioner"); + + Assert.Contains("DR001", apiResponse); + Assert.Contains("E2EPractitioner", apiResponse); + Assert.Contains("MD", apiResponse); + + var page = await _fixture.Browser!.NewPageAsync(); + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.ClickAsync("text=Practitioners"); + await page.WaitForSelectorAsync( + ".practitioner-card", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + var cards = await page.QuerySelectorAllAsync(".practitioner-card"); + Assert.True(cards.Count >= 3); + + await page.CloseAsync(); + } + + /// + /// Practitioner creation API works end-to-end. + /// + [Fact] + public async Task PractitionerCreationApi_WorksEndToEnd() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + + var uniqueId = $"DR{DateTime.UtcNow.Ticks % 100000}"; + var createResponse = await client.PostAsync( + $"{E2EFixture.SchedulingUrl}/Practitioner", + new StringContent( + $$$"""{"Identifier": "{{{uniqueId}}}", "Active": true, "NameGiven": "ApiDoctor", "NameFamily": "TestDoc", "Qualification": "MD", "Specialty": "Testing", "TelecomEmail": "test@hospital.org", "TelecomPhone": "+1-555-9999"}""", + System.Text.Encoding.UTF8, + "application/json" + ) + ); + createResponse.EnsureSuccessStatusCode(); + + var listResponse = await client.GetStringAsync($"{E2EFixture.SchedulingUrl}/Practitioner"); + Assert.Contains(uniqueId, listResponse); + } + + /// + /// Add Practitioner button opens modal and creates practitioner via API. + /// + [Fact] + public async Task AddPractitionerButton_OpensModal_AndCreatesPractitioner() + { + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.ClickAsync("text=Practitioners"); + await page.WaitForSelectorAsync( + "[data-testid='add-practitioner-btn']", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + await page.ClickAsync("[data-testid='add-practitioner-btn']"); + await page.WaitForSelectorAsync( + ".modal", + new PageWaitForSelectorOptions { Timeout = 5000 } + ); + + var uniqueIdentifier = $"DR{DateTime.UtcNow.Ticks % 100000}"; + var uniqueGivenName = $"E2EDoc{DateTime.UtcNow.Ticks % 100000}"; + await page.FillAsync("[data-testid='practitioner-identifier']", uniqueIdentifier); + await page.FillAsync("[data-testid='practitioner-given-name']", uniqueGivenName); + await page.FillAsync("[data-testid='practitioner-family-name']", "TestCreated"); + await page.FillAsync("[data-testid='practitioner-specialty']", "E2E Testing"); + await page.ClickAsync("[data-testid='submit-practitioner']"); + + await page.WaitForSelectorAsync( + $"text={uniqueGivenName}", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + using var client = E2EFixture.CreateAuthenticatedClient(); + var response = await client.GetStringAsync($"{E2EFixture.SchedulingUrl}/Practitioner"); + Assert.Contains(uniqueIdentifier, response); + + await page.CloseAsync(); + } + + /// + /// Edit Practitioner button navigates to edit page and updates practitioner. + /// + [Fact] + public async Task EditPractitionerButton_OpensEditPage_AndUpdatesPractitioner() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + + var uniqueIdentifier = $"DREdit{DateTime.UtcNow.Ticks % 100000}"; + var uniqueGivenName = $"EditTest{DateTime.UtcNow.Ticks % 100000}"; + var createResponse = await client.PostAsync( + $"{E2EFixture.SchedulingUrl}/Practitioner", + new StringContent( + $$$"""{"Identifier": "{{{uniqueIdentifier}}}", "NameFamily": "OriginalFamily", "NameGiven": "{{{uniqueGivenName}}}", "Qualification": "MD", "Specialty": "Original Specialty"}""", + System.Text.Encoding.UTF8, + "application/json" + ) + ); + createResponse.EnsureSuccessStatusCode(); + var createdJson = await createResponse.Content.ReadAsStringAsync(); + var practitionerIdMatch = Regex.Match(createdJson, "\"Id\"\\s*:\\s*\"([^\"]+)\""); + var practitionerId = practitionerIdMatch.Groups[1].Value; + + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.ClickAsync("text=Practitioners"); + await page.WaitForSelectorAsync( + $"text={uniqueGivenName}", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + var editButton = page.Locator($"[data-testid='edit-practitioner-{practitionerId}']"); + await editButton.HoverAsync(); + await editButton.ClickAsync(); + + await page.WaitForSelectorAsync( + "[data-testid='edit-practitioner-page']", + new PageWaitForSelectorOptions { Timeout = 5000 } + ); + + var newSpecialty = $"Updated Specialty {DateTime.UtcNow.Ticks % 100000}"; + await page.FillAsync("[data-testid='edit-practitioner-specialty']", newSpecialty); + await page.ClickAsync("[data-testid='save-practitioner']"); + await page.WaitForSelectorAsync( + "[data-testid='edit-practitioner-success']", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + var updatedPractitionerJson = await client.GetStringAsync( + $"{E2EFixture.SchedulingUrl}/Practitioner/{practitionerId}" + ); + Assert.Contains(newSpecialty, updatedPractitionerJson); + + await page.CloseAsync(); + } + + /// + /// Practitioner update API works end-to-end. + /// + [Fact] + public async Task PractitionerUpdateApi_WorksEndToEnd() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + + var uniqueIdentifier = $"DRApi{DateTime.UtcNow.Ticks % 100000}"; + var createResponse = await client.PostAsync( + $"{E2EFixture.SchedulingUrl}/Practitioner", + new StringContent( + $$$"""{"Identifier": "{{{uniqueIdentifier}}}", "NameFamily": "ApiOriginal", "NameGiven": "TestDoc", "Qualification": "MD", "Specialty": "Original"}""", + System.Text.Encoding.UTF8, + "application/json" + ) + ); + createResponse.EnsureSuccessStatusCode(); + var createdPractitionerJson = await createResponse.Content.ReadAsStringAsync(); + + var practitionerIdMatch = Regex.Match( + createdPractitionerJson, + "\"Id\"\\s*:\\s*\"([^\"]+)\"" + ); + var practitionerId = practitionerIdMatch.Groups[1].Value; + + var updatedSpecialty = $"ApiUpdated{DateTime.UtcNow.Ticks % 100000}"; + var updateResponse = await client.PutAsync( + $"{E2EFixture.SchedulingUrl}/Practitioner/{practitionerId}", + new StringContent( + $$$"""{"Identifier": "{{{uniqueIdentifier}}}", "Active": true, "NameFamily": "ApiUpdated", "NameGiven": "TestDoc", "Qualification": "DO", "Specialty": "{{{updatedSpecialty}}}", "TelecomEmail": "updated@hospital.com", "TelecomPhone": "555-1234"}""", + System.Text.Encoding.UTF8, + "application/json" + ) + ); + updateResponse.EnsureSuccessStatusCode(); + + var getResponse = await client.GetStringAsync( + $"{E2EFixture.SchedulingUrl}/Practitioner/{practitionerId}" + ); + Assert.Contains(updatedSpecialty, getResponse); + Assert.Contains("ApiUpdated", getResponse); + } + + /// + /// Browser back button works from Edit Practitioner page. + /// + [Fact] + public async Task BrowserBackButton_FromEditPractitionerPage_ReturnsToPractitionersPage() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + + var uniqueIdentifier = $"DRBack{DateTime.UtcNow.Ticks % 100000}"; + var uniqueGivenName = $"BackTest{DateTime.UtcNow.Ticks % 100000}"; + var createResponse = await client.PostAsync( + $"{E2EFixture.SchedulingUrl}/Practitioner", + new StringContent( + $$$"""{"Identifier": "{{{uniqueIdentifier}}}", "NameFamily": "BackButtonTest", "NameGiven": "{{{uniqueGivenName}}}", "Qualification": "MD", "Specialty": "Testing"}""", + System.Text.Encoding.UTF8, + "application/json" + ) + ); + createResponse.EnsureSuccessStatusCode(); + var createdJson = await createResponse.Content.ReadAsStringAsync(); + var practitionerIdMatch = Regex.Match(createdJson, "\"Id\"\\s*:\\s*\"([^\"]+)\""); + var practitionerId = practitionerIdMatch.Groups[1].Value; + + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.ClickAsync("text=Practitioners"); + await page.WaitForSelectorAsync( + $"text={uniqueGivenName}", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + + var editButton = page.Locator($"[data-testid='edit-practitioner-{practitionerId}']"); + await editButton.HoverAsync(); + await editButton.ClickAsync(); + await page.WaitForSelectorAsync( + "[data-testid='edit-practitioner-page']", + new PageWaitForSelectorOptions { Timeout = 5000 } + ); + + await page.GoBackAsync(); + + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + await page.WaitForSelectorAsync( + "[data-testid='add-practitioner-btn']", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + Assert.Contains("#practitioners", page.Url); + + await page.CloseAsync(); + } +} diff --git a/Samples/Dashboard/Dashboard.Integration.Tests/SyncE2ETests.cs b/Samples/Dashboard/Dashboard.Integration.Tests/SyncE2ETests.cs new file mode 100644 index 00000000..1ec5a426 --- /dev/null +++ b/Samples/Dashboard/Dashboard.Integration.Tests/SyncE2ETests.cs @@ -0,0 +1,731 @@ +using System.Net; +using Microsoft.Playwright; + +namespace Dashboard.Integration.Tests; + +/// +/// E2E tests for bidirectional sync functionality. +/// +[Collection("E2E Tests")] +[Trait("Category", "E2E")] +public sealed class SyncE2ETests +{ + private readonly E2EFixture _fixture; + + /// + /// Constructor receives shared fixture. + /// + public SyncE2ETests(E2EFixture fixture) => _fixture = fixture; + + /// + /// Sync Dashboard menu item navigates to sync page and displays sync status. + /// + [Fact] + public async Task SyncDashboard_NavigatesToSyncPage_AndDisplaysStatus() + { + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + await page.GotoAsync(E2EFixture.DashboardUrl); + await page.WaitForSelectorAsync( + ".sidebar", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.ClickAsync("text=Sync Dashboard"); + + await page.WaitForSelectorAsync( + "[data-testid='sync-page']", + new PageWaitForSelectorOptions { Timeout = 10000 } + ); + Assert.Contains("#sync", page.Url); + + await page.WaitForSelectorAsync( + "[data-testid='service-status-clinical']", + new PageWaitForSelectorOptions { Timeout = 15000 } + ); + await page.WaitForSelectorAsync( + "[data-testid='service-status-scheduling']", + new PageWaitForSelectorOptions { Timeout = 15000 } + ); + await page.WaitForSelectorAsync( + "[data-testid='sync-records-table']", + new PageWaitForSelectorOptions { Timeout = 5000 } + ); + await page.WaitForSelectorAsync( + "[data-testid='action-filter']", + new PageWaitForSelectorOptions { Timeout = 5000 } + ); + await page.WaitForSelectorAsync( + "[data-testid='service-filter']", + new PageWaitForSelectorOptions { Timeout = 5000 } + ); + + var content = await page.ContentAsync(); + Assert.Contains("Sync Dashboard", content); + Assert.Contains("Clinical.Api", content); + Assert.Contains("Scheduling.Api", content); + Assert.Contains("Sync Records", content); + + await page.CloseAsync(); + } + + /// + /// Sync Dashboard service filter shows ONLY records from selected service. + /// This test PROVES the filter works by verifying actual row content. + /// + [Fact] + public async Task SyncDashboard_ServiceFilter_ShowsOnlySelectedService() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + // Create data in both services to ensure we have records from both + var uniqueId = $"FilterTest{DateTime.UtcNow.Ticks % 1000000}"; + + // Create patient in Clinical.Api + var patientRequest = new + { + Active = true, + GivenName = $"FilterPatient{uniqueId}", + FamilyName = "ClinicalTest", + Gender = "other", + }; + await client.PostAsync( + $"{E2EFixture.ClinicalUrl}/fhir/Patient/", + new StringContent( + System.Text.Json.JsonSerializer.Serialize(patientRequest), + System.Text.Encoding.UTF8, + "application/json" + ) + ); + + // Create practitioner in Scheduling.Api + var practitionerRequest = new + { + Identifier = $"FILTER-DR-{uniqueId}", + Active = true, + NameGiven = $"FilterDoc{uniqueId}", + NameFamily = "SchedulingTest", + Qualification = "MD", + Specialty = "Testing", + }; + await client.PostAsync( + $"{E2EFixture.SchedulingUrl}/Practitioner", + new StringContent( + System.Text.Json.JsonSerializer.Serialize(practitionerRequest), + System.Text.Encoding.UTF8, + "application/json" + ) + ); + + await page.GotoAsync($"{E2EFixture.DashboardUrl}#sync"); + await page.WaitForSelectorAsync( + "[data-testid='sync-page']", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.WaitForSelectorAsync( + "[data-testid='service-status-clinical']", + new PageWaitForSelectorOptions { Timeout = 15000 } + ); + await Task.Delay(1000); // Allow data to load + + // Get initial count with all services + var allRows = await page.QuerySelectorAllAsync( + "[data-testid='sync-records-table'] tbody tr" + ); + var initialCount = allRows.Count; + Console.WriteLine($"[TEST] Initial row count (all services): {initialCount}"); + + // Filter to Clinical only + await page.SelectOptionAsync("[data-testid='service-filter']", "clinical"); + await Task.Delay(500); + + var clinicalRows = await page.QuerySelectorAllAsync( + "[data-testid='sync-records-table'] tbody tr" + ); + Console.WriteLine($"[TEST] Clinical filter row count: {clinicalRows.Count}"); + + // PROVE: Every visible row must be from Clinical service + foreach (var row in clinicalRows) + { + var serviceAttr = await row.GetAttributeAsync("data-service"); + Assert.Equal("clinical", serviceAttr); + } + + // Filter to Scheduling only + await page.SelectOptionAsync("[data-testid='service-filter']", "scheduling"); + await Task.Delay(500); + + var schedulingRows = await page.QuerySelectorAllAsync( + "[data-testid='sync-records-table'] tbody tr" + ); + Console.WriteLine($"[TEST] Scheduling filter row count: {schedulingRows.Count}"); + + // PROVE: Every visible row must be from Scheduling service + foreach (var row in schedulingRows) + { + var serviceAttr = await row.GetAttributeAsync("data-service"); + Assert.Equal("scheduling", serviceAttr); + } + + // PROVE: Combined counts should equal total (or less if overlap) + Assert.True( + clinicalRows.Count + schedulingRows.Count <= initialCount + 1, + $"Clinical ({clinicalRows.Count}) + Scheduling ({schedulingRows.Count}) should not exceed initial ({initialCount})" + ); + + await page.CloseAsync(); + } + + /// + /// Sync Dashboard action filter shows ONLY records with selected operation. + /// This test PROVES the filter works by verifying actual row content. + /// + [Fact] + public async Task SyncDashboard_ActionFilter_ShowsOnlySelectedOperation() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + // Create a patient (Insert operation = 0) + var uniqueId = $"ActionTest{DateTime.UtcNow.Ticks % 1000000}"; + var patientRequest = new + { + Active = true, + GivenName = $"ActionPatient{uniqueId}", + FamilyName = "InsertTest", + Gender = "female", + }; + var createResponse = await client.PostAsync( + $"{E2EFixture.ClinicalUrl}/fhir/Patient/", + new StringContent( + System.Text.Json.JsonSerializer.Serialize(patientRequest), + System.Text.Encoding.UTF8, + "application/json" + ) + ); + createResponse.EnsureSuccessStatusCode(); + + await page.GotoAsync($"{E2EFixture.DashboardUrl}#sync"); + await page.WaitForSelectorAsync( + "[data-testid='sync-page']", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.WaitForSelectorAsync( + "[data-testid='service-status-clinical']", + new PageWaitForSelectorOptions { Timeout = 15000 } + ); + + // Wait for sync records to actually load (not just the page) + await page.WaitForFunctionAsync( + @"() => { + const badge = document.querySelector('.badge'); + return badge && badge.textContent && !badge.textContent.includes('0 records'); + }", + new PageWaitForFunctionOptions { Timeout = 15000 } + ); + await Task.Delay(500); // Allow React to stabilize + + // Log initial state before filtering + var initialRows = await page.QuerySelectorAllAsync( + "[data-testid='sync-records-table'] tbody tr" + ); + Console.WriteLine($"[TEST] Initial row count before filter: {initialRows.Count}"); + foreach (var row in initialRows.Take(5)) + { + var op = await row.GetAttributeAsync("data-operation"); + Console.WriteLine($"[TEST] Row data-operation: {op}"); + } + + // Filter to Insert operations only (operation = 0) + await page.SelectOptionAsync("[data-testid='action-filter']", "0"); + await Task.Delay(500); // Allow React to start re-rendering + + // Wait for React to apply the filter - wait until ALL visible rows have operation=0 + // OR there are no rows (which is valid if no Insert operations exist) + await page.WaitForFunctionAsync( + @"() => { + const rows = document.querySelectorAll('[data-testid=""sync-records-table""] tbody tr'); + console.log('[Filter] Row count after filter: ' + rows.length); + if (rows.length === 0) return true; + const allMatch = Array.from(rows).every(row => { + const op = row.getAttribute('data-operation'); + console.log('[Filter] Row operation: ' + op); + return op === '0'; + }); + return allMatch; + }", + new PageWaitForFunctionOptions { Timeout = 20000 } + ); + + var insertRows = await page.QuerySelectorAllAsync( + "[data-testid='sync-records-table'] tbody tr" + ); + Console.WriteLine($"[TEST] Insert filter row count: {insertRows.Count}"); + + // PROVE: Every visible row must have Insert operation (0) + foreach (var row in insertRows) + { + var operationAttr = await row.GetAttributeAsync("data-operation"); + Assert.Equal("0", operationAttr); + } + + // Verify filter value is selected + var selectedValue = await page.EvalOnSelectorAsync( + "[data-testid='action-filter']", + "el => el.value" + ); + Assert.Equal("0", selectedValue); + + // Reset filter + await page.SelectOptionAsync("[data-testid='action-filter']", "all"); + + // Wait for React to apply the reset filter + await page.WaitForFunctionAsync( + $"() => document.querySelector('[data-testid=\"action-filter\"]').value === 'all'", + new PageWaitForFunctionOptions { Timeout = 5000 } + ); + await Task.Delay(300); // Small buffer for React re-render + + var allRows = await page.QuerySelectorAllAsync( + "[data-testid='sync-records-table'] tbody tr" + ); + Assert.True(allRows.Count >= insertRows.Count, "All rows should be >= Insert-only rows"); + + await page.CloseAsync(); + } + + /// + /// Sync Dashboard combined filters work correctly. + /// PROVES both service AND action filters can be used together. + /// + [Fact] + public async Task SyncDashboard_CombinedFilters_WorkTogether() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + // Create data in Clinical.Api + var uniqueId = $"ComboTest{DateTime.UtcNow.Ticks % 1000000}"; + var patientRequest = new + { + Active = true, + GivenName = $"ComboPatient{uniqueId}", + FamilyName = "ComboTest", + Gender = "male", + }; + await client.PostAsync( + $"{E2EFixture.ClinicalUrl}/fhir/Patient/", + new StringContent( + System.Text.Json.JsonSerializer.Serialize(patientRequest), + System.Text.Encoding.UTF8, + "application/json" + ) + ); + + await page.GotoAsync($"{E2EFixture.DashboardUrl}#sync"); + await page.WaitForSelectorAsync( + "[data-testid='sync-page']", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.WaitForSelectorAsync( + "[data-testid='service-status-clinical']", + new PageWaitForSelectorOptions { Timeout = 15000 } + ); + await Task.Delay(1000); + + // Apply both filters: Clinical + Insert + await page.SelectOptionAsync("[data-testid='service-filter']", "clinical"); + await page.SelectOptionAsync("[data-testid='action-filter']", "0"); + + // Wait for React to apply both filters + await page.WaitForFunctionAsync( + @"() => { + const rows = document.querySelectorAll('[data-testid=""sync-records-table""] tbody tr'); + if (rows.length === 0) return true; // No rows = filters applied (or empty) + return Array.from(rows).every(row => + row.getAttribute('data-service') === 'clinical' && + row.getAttribute('data-operation') === '0' + ); + }", + new PageWaitForFunctionOptions { Timeout = 5000 } + ); + + var filteredRows = await page.QuerySelectorAllAsync( + "[data-testid='sync-records-table'] tbody tr" + ); + Console.WriteLine( + $"[TEST] Combined filter (Clinical + Insert) row count: {filteredRows.Count}" + ); + + // PROVE: Every row must satisfy BOTH filters + foreach (var row in filteredRows) + { + var serviceAttr = await row.GetAttributeAsync("data-service"); + var operationAttr = await row.GetAttributeAsync("data-operation"); + Assert.Equal("clinical", serviceAttr); + Assert.Equal("0", operationAttr); + } + + // Try Scheduling + Insert + await page.SelectOptionAsync("[data-testid='service-filter']", "scheduling"); + + // Wait for React to apply the service filter change + await page.WaitForFunctionAsync( + @"() => { + const rows = document.querySelectorAll('[data-testid=""sync-records-table""] tbody tr'); + if (rows.length === 0) return true; + return Array.from(rows).every(row => + row.getAttribute('data-service') === 'scheduling' && + row.getAttribute('data-operation') === '0' + ); + }", + new PageWaitForFunctionOptions { Timeout = 5000 } + ); + + var schedulingInsertRows = await page.QuerySelectorAllAsync( + "[data-testid='sync-records-table'] tbody tr" + ); + foreach (var row in schedulingInsertRows) + { + var serviceAttr = await row.GetAttributeAsync("data-service"); + var operationAttr = await row.GetAttributeAsync("data-operation"); + Assert.Equal("scheduling", serviceAttr); + Assert.Equal("0", operationAttr); + } + + await page.CloseAsync(); + } + + /// + /// Sync Dashboard search filter works correctly. + /// PROVES search by entity ID filters correctly. + /// + [Fact] + public async Task SyncDashboard_SearchFilter_FiltersCorrectly() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + // Create a patient with a known unique identifier + var uniqueId = $"SearchTest{DateTime.UtcNow.Ticks % 1000000}"; + var patientRequest = new + { + Active = true, + GivenName = $"SearchPatient{uniqueId}", + FamilyName = "SearchTest", + Gender = "male", + }; + var createResponse = await client.PostAsync( + $"{E2EFixture.ClinicalUrl}/fhir/Patient/", + new StringContent( + System.Text.Json.JsonSerializer.Serialize(patientRequest), + System.Text.Encoding.UTF8, + "application/json" + ) + ); + createResponse.EnsureSuccessStatusCode(); + var patientJson = await createResponse.Content.ReadAsStringAsync(); + var patientDoc = System.Text.Json.JsonDocument.Parse(patientJson); + var patientId = patientDoc.RootElement.GetProperty("Id").GetString(); + + await page.GotoAsync($"{E2EFixture.DashboardUrl}#sync"); + await page.WaitForSelectorAsync( + "[data-testid='sync-page']", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.WaitForSelectorAsync( + "[data-testid='service-status-clinical']", + new PageWaitForSelectorOptions { Timeout = 15000 } + ); + await Task.Delay(1000); + + // Get initial count + var initialRows = await page.QuerySelectorAllAsync( + "[data-testid='sync-records-table'] tbody tr" + ); + var initialCount = initialRows.Count; + + // Search for the patient ID + await page.FillAsync("[data-testid='sync-search']", patientId!); + await Task.Delay(500); + + var searchRows = await page.QuerySelectorAllAsync( + "[data-testid='sync-records-table'] tbody tr" + ); + Console.WriteLine($"[TEST] Search for '{patientId}' found {searchRows.Count} rows"); + + // PROVE: Search should find at least one matching row + Assert.True( + searchRows.Count >= 1, + $"Search for patient ID '{patientId}' should find at least one row" + ); + Assert.True( + searchRows.Count < initialCount || initialCount <= 1, + "Search should filter down results (unless only 1 row exists)" + ); + + await page.CloseAsync(); + } + + /// + /// Deep linking to sync page works. + /// + [Fact] + public async Task SyncDashboard_DeepLinkingWorks() + { + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + await page.GotoAsync($"{E2EFixture.DashboardUrl}#sync"); + await page.WaitForSelectorAsync( + "[data-testid='sync-page']", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + + var content = await page.ContentAsync(); + Assert.Contains("Sync Dashboard", content); + Assert.Contains("Monitor and manage sync operations", content); + + await page.CloseAsync(); + } + + /// + /// Data added to Clinical.Api is synced to Scheduling.Api. + /// + [Fact] + public async Task Sync_ClinicalPatient_AppearsInScheduling_AfterSync() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + + var uniqueId = $"SyncTest{DateTime.UtcNow.Ticks % 1000000}"; + var patientRequest = new + { + Active = true, + GivenName = $"SyncPatient{uniqueId}", + FamilyName = "ToScheduling", + Gender = "other", + Phone = "+1-555-SYNC", + Email = $"sync{uniqueId}@test.com", + }; + + var createResponse = await client.PostAsync( + $"{E2EFixture.ClinicalUrl}/fhir/Patient/", + new StringContent( + System.Text.Json.JsonSerializer.Serialize(patientRequest), + System.Text.Encoding.UTF8, + "application/json" + ) + ); + createResponse.EnsureSuccessStatusCode(); + var patientJson = await createResponse.Content.ReadAsStringAsync(); + var patientDoc = System.Text.Json.JsonDocument.Parse(patientJson); + var patientId = patientDoc.RootElement.GetProperty("Id").GetString(); + + var clinicalGetResponse = await client.GetAsync( + $"{E2EFixture.ClinicalUrl}/fhir/Patient/{patientId}" + ); + Assert.Equal(HttpStatusCode.OK, clinicalGetResponse.StatusCode); + + var syncedToScheduling = false; + for (var i = 0; i < 18; i++) + { + await Task.Delay(5000); + + var syncPatientsResponse = await client.GetAsync( + $"{E2EFixture.SchedulingUrl}/sync/patients" + ); + if (syncPatientsResponse.IsSuccessStatusCode) + { + var patientsJson = await syncPatientsResponse.Content.ReadAsStringAsync(); + if (patientsJson.Contains(patientId!) || patientsJson.Contains(uniqueId)) + { + syncedToScheduling = true; + break; + } + } + } + + Assert.True( + syncedToScheduling, + $"Patient '{uniqueId}' created in Clinical.Api was not synced to Scheduling.Api within 90 seconds." + ); + } + + /// + /// Data added to Scheduling.Api is synced to Clinical.Api. + /// + [Fact] + public async Task Sync_SchedulingPractitioner_AppearsInClinical_AfterSync() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + + var uniqueId = $"SyncTest{DateTime.UtcNow.Ticks % 1000000}"; + var practitionerRequest = new + { + Identifier = $"SYNC-DR-{uniqueId}", + Active = true, + NameGiven = $"SyncDoctor{uniqueId}", + NameFamily = "ToClinical", + Qualification = "MD", + Specialty = "Sync Testing", + TelecomEmail = $"syncdoc{uniqueId}@hospital.org", + TelecomPhone = "+1-555-SYNC", + }; + + var createResponse = await client.PostAsync( + $"{E2EFixture.SchedulingUrl}/Practitioner", + new StringContent( + System.Text.Json.JsonSerializer.Serialize(practitionerRequest), + System.Text.Encoding.UTF8, + "application/json" + ) + ); + createResponse.EnsureSuccessStatusCode(); + var practitionerJson = await createResponse.Content.ReadAsStringAsync(); + var practitionerDoc = System.Text.Json.JsonDocument.Parse(practitionerJson); + var practitionerId = practitionerDoc.RootElement.GetProperty("Id").GetString(); + + var schedulingGetResponse = await client.GetAsync( + $"{E2EFixture.SchedulingUrl}/Practitioner/{practitionerId}" + ); + Assert.Equal(HttpStatusCode.OK, schedulingGetResponse.StatusCode); + + var syncedToClinical = false; + for (var i = 0; i < 30; i++) + { + await Task.Delay(5000); + + var syncProvidersResponse = await client.GetAsync( + $"{E2EFixture.ClinicalUrl}/sync/providers" + ); + if (syncProvidersResponse.IsSuccessStatusCode) + { + var providersJson = await syncProvidersResponse.Content.ReadAsStringAsync(); + if (providersJson.Contains(practitionerId!) || providersJson.Contains(uniqueId)) + { + syncedToClinical = true; + break; + } + } + } + + Assert.True( + syncedToClinical, + $"Practitioner '{uniqueId}' created in Scheduling.Api was not synced to Clinical.Api within 150 seconds." + ); + } + + /// + /// Sync changes appear in Dashboard UI seamlessly. + /// + [Fact] + public async Task Sync_ChangesAppearInDashboardUI_Seamlessly() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + var page = await _fixture.Browser!.NewPageAsync(); + page.Console += (_, msg) => Console.WriteLine($"[BROWSER] {msg.Text}"); + + var uniqueId = $"DashSync{DateTime.UtcNow.Ticks % 1000000}"; + var patientRequest = new + { + Active = true, + GivenName = $"DashboardSync{uniqueId}", + FamilyName = "TestPatient", + Gender = "male", + }; + + var createResponse = await client.PostAsync( + $"{E2EFixture.ClinicalUrl}/fhir/Patient/", + new StringContent( + System.Text.Json.JsonSerializer.Serialize(patientRequest), + System.Text.Encoding.UTF8, + "application/json" + ) + ); + createResponse.EnsureSuccessStatusCode(); + + await page.GotoAsync($"{E2EFixture.DashboardUrl}#sync"); + await page.WaitForSelectorAsync( + "[data-testid='sync-page']", + new PageWaitForSelectorOptions { Timeout = 20000 } + ); + await page.WaitForSelectorAsync( + "[data-testid='service-status-clinical']", + new PageWaitForSelectorOptions { Timeout = 15000 } + ); + await page.WaitForSelectorAsync( + "[data-testid='service-status-scheduling']", + new PageWaitForSelectorOptions { Timeout = 15000 } + ); + + var content = await page.ContentAsync(); + Assert.Contains("Clinical.Api", content); + Assert.Contains("Scheduling.Api", content); + Assert.Contains("Sync Records", content); + + var clinicalCardVisible = await page.IsVisibleAsync( + "[data-testid='service-status-clinical']" + ); + var schedulingCardVisible = await page.IsVisibleAsync( + "[data-testid='service-status-scheduling']" + ); + Assert.True(clinicalCardVisible); + Assert.True(schedulingCardVisible); + + await page.CloseAsync(); + } + + /// + /// Sync log entries are created when data changes. + /// + [Fact] + public async Task Sync_CreatesLogEntries_WhenDataChanges() + { + using var client = E2EFixture.CreateAuthenticatedClient(); + + var initialClinicalResponse = await client.GetAsync( + $"{E2EFixture.ClinicalUrl}/sync/records" + ); + initialClinicalResponse.EnsureSuccessStatusCode(); + var initialClinicalJson = await initialClinicalResponse.Content.ReadAsStringAsync(); + var initialClinicalDoc = System.Text.Json.JsonDocument.Parse(initialClinicalJson); + var initialClinicalCount = initialClinicalDoc.RootElement.GetProperty("total").GetInt32(); + + var uniqueId = $"LogTest{DateTime.UtcNow.Ticks % 1000000}"; + var patientRequest = new + { + Active = true, + GivenName = $"LogPatient{uniqueId}", + FamilyName = "TestSync", + Gender = "female", + }; + + var createResponse = await client.PostAsync( + $"{E2EFixture.ClinicalUrl}/fhir/Patient/", + new StringContent( + System.Text.Json.JsonSerializer.Serialize(patientRequest), + System.Text.Encoding.UTF8, + "application/json" + ) + ); + createResponse.EnsureSuccessStatusCode(); + + var updatedClinicalResponse = await client.GetAsync( + $"{E2EFixture.ClinicalUrl}/sync/records" + ); + updatedClinicalResponse.EnsureSuccessStatusCode(); + var updatedClinicalJson = await updatedClinicalResponse.Content.ReadAsStringAsync(); + var updatedClinicalDoc = System.Text.Json.JsonDocument.Parse(updatedClinicalJson); + var updatedClinicalCount = updatedClinicalDoc.RootElement.GetProperty("total").GetInt32(); + + Assert.True( + updatedClinicalCount > initialClinicalCount, + $"Sync log count should increase after creating a patient. Initial: {initialClinicalCount}, After: {updatedClinicalCount}" + ); + } +} diff --git a/Samples/Dashboard/Dashboard.Integration.Tests/xunit.runner.json b/Samples/Dashboard/Dashboard.Integration.Tests/xunit.runner.json new file mode 100644 index 00000000..c3155894 --- /dev/null +++ b/Samples/Dashboard/Dashboard.Integration.Tests/xunit.runner.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://xunit.net/schema/current/xunit.runner.schema.json", + "parallelizeAssembly": false, + "parallelizeTestCollections": false, + "maxParallelThreads": 1 +} diff --git a/Samples/Dashboard/Dashboard.Web.Tests.Runner/Dashboard.Web.Tests.Runner.csproj b/Samples/Dashboard/Dashboard.Web.Tests.Runner/Dashboard.Web.Tests.Runner.csproj index 5e0f0e25..96a6f9bd 100644 --- a/Samples/Dashboard/Dashboard.Web.Tests.Runner/Dashboard.Web.Tests.Runner.csproj +++ b/Samples/Dashboard/Dashboard.Web.Tests.Runner/Dashboard.Web.Tests.Runner.csproj @@ -5,7 +5,7 @@ true disable enable - CA1848;CA1515;CA2100;RS1035;CA1508;CA2234 + CA1515;CA2100;RS1035;CA1508;CA2234 diff --git a/Samples/Dashboard/Dashboard.Web.Tests.Runner/DashboardPlaywrightTests.cs b/Samples/Dashboard/Dashboard.Web.Tests.Runner/DashboardPlaywrightTests.cs index 500d5224..265cfbd1 100644 --- a/Samples/Dashboard/Dashboard.Web.Tests.Runner/DashboardPlaywrightTests.cs +++ b/Samples/Dashboard/Dashboard.Web.Tests.Runner/DashboardPlaywrightTests.cs @@ -1,11 +1,11 @@ -namespace Dashboard.Web.Tests.Runner; - using System; using System.IO; using System.Threading.Tasks; using Microsoft.Playwright; using Xunit; +namespace Dashboard.Web.Tests.Runner; + /// /// Playwright-based test runner that executes H5 browser tests once and validates all results. /// Runs browser once, executes tests once, then validates all test categories from the output. @@ -30,7 +30,11 @@ public async Task InitializeAsync() { _playwright = await Playwright.CreateAsync(); _browser = await _playwright.Chromium.LaunchAsync( - new BrowserTypeLaunchOptions { Headless = true } + new BrowserTypeLaunchOptions + { + Headless = true, + Args = ["--allow-file-access-from-files", "--disable-web-security"], + } ); var testHtmlPath = FindTestHtml(); diff --git a/Samples/Dashboard/Dashboard.Web.Tests/.config/dotnet-tools.json b/Samples/Dashboard/Dashboard.Web.Tests/.config/dotnet-tools.json index 3631cfd6..24273f98 100644 --- a/Samples/Dashboard/Dashboard.Web.Tests/.config/dotnet-tools.json +++ b/Samples/Dashboard/Dashboard.Web.Tests/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "h5-compiler": { - "version": "25.8.60248", + "version": "24.11.53871", "commands": [ "h5" ], diff --git a/Samples/Dashboard/Dashboard.Web.Tests/Dashboard.Web.Tests.csproj b/Samples/Dashboard/Dashboard.Web.Tests/Dashboard.Web.Tests.csproj index b8a31a04..2754764e 100644 --- a/Samples/Dashboard/Dashboard.Web.Tests/Dashboard.Web.Tests.csproj +++ b/Samples/Dashboard/Dashboard.Web.Tests/Dashboard.Web.Tests.csproj @@ -1,4 +1,4 @@ - + Library @@ -7,7 +7,7 @@ enable Dashboard.Tests false - CS0626;CS1591;CA1812;CA1848;CA2100;CS8632 + CS0626;CS1591;CA1812;CA2100;CS8632 H5 true @@ -22,8 +22,8 @@ - - + + diff --git a/Samples/Dashboard/Dashboard.Web.Tests/Program.cs b/Samples/Dashboard/Dashboard.Web.Tests/Program.cs index 22acbcc2..8e3378ad 100644 --- a/Samples/Dashboard/Dashboard.Web.Tests/Program.cs +++ b/Samples/Dashboard/Dashboard.Web.Tests/Program.cs @@ -1,9 +1,9 @@ +using Dashboard.Tests.TestLib; +using Dashboard.Tests.Tests; +using H5; + namespace Dashboard.Tests { - using Dashboard.Tests.TestLib; - using Dashboard.Tests.Tests; - using H5; - /// /// Test entry point - runs all dashboard tests in the browser. /// diff --git a/Samples/Dashboard/Dashboard.Web.Tests/TestData/MockData.cs b/Samples/Dashboard/Dashboard.Web.Tests/TestData/MockData.cs index c4c7afde..672a9591 100644 --- a/Samples/Dashboard/Dashboard.Web.Tests/TestData/MockData.cs +++ b/Samples/Dashboard/Dashboard.Web.Tests/TestData/MockData.cs @@ -1,7 +1,7 @@ +using System.Collections.Generic; + namespace Dashboard.Tests.TestData { - using System.Collections.Generic; - /// /// Mock data for dashboard tests. /// diff --git a/Samples/Dashboard/Dashboard.Web.Tests/TestLib/Assert.cs b/Samples/Dashboard/Dashboard.Web.Tests/TestLib/Assert.cs index 269f4878..74833fc7 100644 --- a/Samples/Dashboard/Dashboard.Web.Tests/TestLib/Assert.cs +++ b/Samples/Dashboard/Dashboard.Web.Tests/TestLib/Assert.cs @@ -1,9 +1,9 @@ +using System; +using System.Collections.Generic; +using H5; + namespace Dashboard.Tests.TestLib { - using System; - using System.Collections.Generic; - using H5; - /// /// Assertion helpers for tests. /// diff --git a/Samples/Dashboard/Dashboard.Web.Tests/TestLib/MockFetch.cs b/Samples/Dashboard/Dashboard.Web.Tests/TestLib/MockFetch.cs index 97864d62..ea8a76e7 100644 --- a/Samples/Dashboard/Dashboard.Web.Tests/TestLib/MockFetch.cs +++ b/Samples/Dashboard/Dashboard.Web.Tests/TestLib/MockFetch.cs @@ -1,10 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using H5; + namespace Dashboard.Tests.TestLib { - using System; - using System.Collections.Generic; - using System.Threading.Tasks; - using H5; - /// /// Mock fetch function factory for testing API calls. /// Intercepts HTTP requests and returns predefined responses. diff --git a/Samples/Dashboard/Dashboard.Web.Tests/TestLib/TestRunner.cs b/Samples/Dashboard/Dashboard.Web.Tests/TestLib/TestRunner.cs index 67446f67..e82cadd2 100644 --- a/Samples/Dashboard/Dashboard.Web.Tests/TestLib/TestRunner.cs +++ b/Samples/Dashboard/Dashboard.Web.Tests/TestLib/TestRunner.cs @@ -1,10 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using H5; + namespace Dashboard.Tests.TestLib { - using System; - using System.Collections.Generic; - using System.Threading.Tasks; - using H5; - /// /// Test runner for browser-based tests. /// diff --git a/Samples/Dashboard/Dashboard.Web.Tests/TestLib/TestingLibrary.cs b/Samples/Dashboard/Dashboard.Web.Tests/TestLib/TestingLibrary.cs index 79305b8c..14b30359 100644 --- a/Samples/Dashboard/Dashboard.Web.Tests/TestLib/TestingLibrary.cs +++ b/Samples/Dashboard/Dashboard.Web.Tests/TestLib/TestingLibrary.cs @@ -1,9 +1,9 @@ +using System; +using System.Threading.Tasks; +using H5; + namespace Dashboard.Tests.TestLib { - using System; - using System.Threading.Tasks; - using H5; - /// /// C# wrapper for React Testing Library. /// Provides render, query, and interaction methods. @@ -13,7 +13,7 @@ public static class TestingLibrary /// /// Renders a React component and returns a RenderResult for querying. /// - public static RenderResult Render(Dashboard.React.ReactElement element) + public static RenderResult Render(React.ReactElement element) { var result = Script.Call("TestingLibrary.render", element); return new RenderResult(result); @@ -151,7 +151,7 @@ public void Unmount() /// /// Re-renders the component with new props. /// - public void Rerender(Dashboard.React.ReactElement element) + public void Rerender(React.ReactElement element) { _ = Script.Get(_result, "rerender"); Script.Write("rerender(element)"); diff --git a/Samples/Dashboard/Dashboard.Web.Tests/Tests/DashboardTests.cs b/Samples/Dashboard/Dashboard.Web.Tests/Tests/DashboardTests.cs index 9074ea80..3bf52d37 100644 --- a/Samples/Dashboard/Dashboard.Web.Tests/Tests/DashboardTests.cs +++ b/Samples/Dashboard/Dashboard.Web.Tests/Tests/DashboardTests.cs @@ -1,10 +1,10 @@ +using System.Threading.Tasks; +using Dashboard.Tests.TestData; +using Dashboard.Tests.TestLib; +using static Dashboard.Tests.TestLib.TestRunner; + namespace Dashboard.Tests.Tests { - using System.Threading.Tasks; - using Dashboard.Tests.TestData; - using Dashboard.Tests.TestLib; - using static Dashboard.Tests.TestLib.TestRunner; - /// /// Comprehensive end-to-end tests for the Healthcare Dashboard. /// Tests the ENTIRE application from the root App component. diff --git a/Samples/Dashboard/Dashboard.Web.Tests/wwwroot/react-dom.development.js b/Samples/Dashboard/Dashboard.Web.Tests/wwwroot/react-dom.development.js new file mode 100644 index 00000000..57a309ce --- /dev/null +++ b/Samples/Dashboard/Dashboard.Web.Tests/wwwroot/react-dom.development.js @@ -0,0 +1,29924 @@ +/** + * @license React + * react-dom.development.js + * + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('react')) : + typeof define === 'function' && define.amd ? define(['exports', 'react'], factory) : + (global = global || self, factory(global.ReactDOM = {}, global.React)); +}(this, (function (exports, React) { 'use strict'; + + var ReactSharedInternals = React.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED; + + var suppressWarning = false; + function setSuppressWarning(newSuppressWarning) { + { + suppressWarning = newSuppressWarning; + } + } // In DEV, calls to console.warn and console.error get replaced + // by calls to these methods by a Babel plugin. + // + // In PROD (or in packages without access to React internals), + // they are left as they are instead. + + function warn(format) { + { + if (!suppressWarning) { + for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + printWarning('warn', format, args); + } + } + } + function error(format) { + { + if (!suppressWarning) { + for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { + args[_key2 - 1] = arguments[_key2]; + } + + printWarning('error', format, args); + } + } + } + + function printWarning(level, format, args) { + // When changing this logic, you might want to also + // update consoleWithStackDev.www.js as well. + { + var ReactDebugCurrentFrame = ReactSharedInternals.ReactDebugCurrentFrame; + var stack = ReactDebugCurrentFrame.getStackAddendum(); + + if (stack !== '') { + format += '%s'; + args = args.concat([stack]); + } // eslint-disable-next-line react-internal/safe-string-coercion + + + var argsWithFormat = args.map(function (item) { + return String(item); + }); // Careful: RN currently depends on this prefix + + argsWithFormat.unshift('Warning: ' + format); // We intentionally don't use spread (or .apply) directly because it + // breaks IE9: https://github.com/facebook/react/issues/13610 + // eslint-disable-next-line react-internal/no-production-logging + + Function.prototype.apply.call(console[level], console, argsWithFormat); + } + } + + var FunctionComponent = 0; + var ClassComponent = 1; + var IndeterminateComponent = 2; // Before we know whether it is function or class + + var HostRoot = 3; // Root of a host tree. Could be nested inside another node. + + var HostPortal = 4; // A subtree. Could be an entry point to a different renderer. + + var HostComponent = 5; + var HostText = 6; + var Fragment = 7; + var Mode = 8; + var ContextConsumer = 9; + var ContextProvider = 10; + var ForwardRef = 11; + var Profiler = 12; + var SuspenseComponent = 13; + var MemoComponent = 14; + var SimpleMemoComponent = 15; + var LazyComponent = 16; + var IncompleteClassComponent = 17; + var DehydratedFragment = 18; + var SuspenseListComponent = 19; + var ScopeComponent = 21; + var OffscreenComponent = 22; + var LegacyHiddenComponent = 23; + var CacheComponent = 24; + var TracingMarkerComponent = 25; + + // ----------------------------------------------------------------------------- + + var enableClientRenderFallbackOnTextMismatch = true; // TODO: Need to review this code one more time before landing + // the react-reconciler package. + + var enableNewReconciler = false; // Support legacy Primer support on internal FB www + + var enableLazyContextPropagation = false; // FB-only usage. The new API has different semantics. + + var enableLegacyHidden = false; // Enables unstable_avoidThisFallback feature in Fiber + + var enableSuspenseAvoidThisFallback = false; // Enables unstable_avoidThisFallback feature in Fizz + // React DOM Chopping Block + // + // Similar to main Chopping Block but only flags related to React DOM. These are + // grouped because we will likely batch all of them into a single major release. + // ----------------------------------------------------------------------------- + // Disable support for comment nodes as React DOM containers. Already disabled + // in open source, but www codebase still relies on it. Need to remove. + + var disableCommentsAsDOMContainers = true; // Disable javascript: URL strings in href for XSS protection. + // and client rendering, mostly to allow JSX attributes to apply to the custom + // element's object properties instead of only HTML attributes. + // https://github.com/facebook/react/issues/11347 + + var enableCustomElementPropertySupport = false; // Disables children for