truthound 1.0.8__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- truthound/__init__.py +162 -0
- truthound/adapters.py +100 -0
- truthound/api.py +365 -0
- truthound/audit/__init__.py +248 -0
- truthound/audit/core.py +967 -0
- truthound/audit/filters.py +620 -0
- truthound/audit/formatters.py +707 -0
- truthound/audit/logger.py +902 -0
- truthound/audit/middleware.py +571 -0
- truthound/audit/storage.py +1083 -0
- truthound/benchmark/__init__.py +123 -0
- truthound/benchmark/base.py +757 -0
- truthound/benchmark/comparison.py +635 -0
- truthound/benchmark/generators.py +706 -0
- truthound/benchmark/reporters.py +718 -0
- truthound/benchmark/runner.py +635 -0
- truthound/benchmark/scenarios.py +712 -0
- truthound/cache.py +252 -0
- truthound/checkpoint/__init__.py +136 -0
- truthound/checkpoint/actions/__init__.py +164 -0
- truthound/checkpoint/actions/base.py +324 -0
- truthound/checkpoint/actions/custom.py +234 -0
- truthound/checkpoint/actions/discord_notify.py +290 -0
- truthound/checkpoint/actions/email_notify.py +405 -0
- truthound/checkpoint/actions/github_action.py +406 -0
- truthound/checkpoint/actions/opsgenie.py +1499 -0
- truthound/checkpoint/actions/pagerduty.py +226 -0
- truthound/checkpoint/actions/slack_notify.py +233 -0
- truthound/checkpoint/actions/store_result.py +249 -0
- truthound/checkpoint/actions/teams_notify.py +1570 -0
- truthound/checkpoint/actions/telegram_notify.py +419 -0
- truthound/checkpoint/actions/update_docs.py +552 -0
- truthound/checkpoint/actions/webhook.py +293 -0
- truthound/checkpoint/analytics/__init__.py +147 -0
- truthound/checkpoint/analytics/aggregations/__init__.py +23 -0
- truthound/checkpoint/analytics/aggregations/rollup.py +481 -0
- truthound/checkpoint/analytics/aggregations/time_bucket.py +306 -0
- truthound/checkpoint/analytics/analyzers/__init__.py +17 -0
- truthound/checkpoint/analytics/analyzers/anomaly.py +386 -0
- truthound/checkpoint/analytics/analyzers/base.py +270 -0
- truthound/checkpoint/analytics/analyzers/forecast.py +421 -0
- truthound/checkpoint/analytics/analyzers/trend.py +314 -0
- truthound/checkpoint/analytics/models.py +292 -0
- truthound/checkpoint/analytics/protocols.py +549 -0
- truthound/checkpoint/analytics/service.py +718 -0
- truthound/checkpoint/analytics/stores/__init__.py +16 -0
- truthound/checkpoint/analytics/stores/base.py +306 -0
- truthound/checkpoint/analytics/stores/memory_store.py +353 -0
- truthound/checkpoint/analytics/stores/sqlite_store.py +557 -0
- truthound/checkpoint/analytics/stores/timescale_store.py +501 -0
- truthound/checkpoint/async_actions.py +794 -0
- truthound/checkpoint/async_base.py +708 -0
- truthound/checkpoint/async_checkpoint.py +617 -0
- truthound/checkpoint/async_runner.py +639 -0
- truthound/checkpoint/checkpoint.py +527 -0
- truthound/checkpoint/ci/__init__.py +61 -0
- truthound/checkpoint/ci/detector.py +355 -0
- truthound/checkpoint/ci/reporter.py +436 -0
- truthound/checkpoint/ci/templates.py +454 -0
- truthound/checkpoint/circuitbreaker/__init__.py +133 -0
- truthound/checkpoint/circuitbreaker/breaker.py +542 -0
- truthound/checkpoint/circuitbreaker/core.py +252 -0
- truthound/checkpoint/circuitbreaker/detection.py +459 -0
- truthound/checkpoint/circuitbreaker/middleware.py +389 -0
- truthound/checkpoint/circuitbreaker/registry.py +357 -0
- truthound/checkpoint/distributed/__init__.py +139 -0
- truthound/checkpoint/distributed/backends/__init__.py +35 -0
- truthound/checkpoint/distributed/backends/celery_backend.py +503 -0
- truthound/checkpoint/distributed/backends/kubernetes_backend.py +696 -0
- truthound/checkpoint/distributed/backends/local_backend.py +397 -0
- truthound/checkpoint/distributed/backends/ray_backend.py +625 -0
- truthound/checkpoint/distributed/base.py +774 -0
- truthound/checkpoint/distributed/orchestrator.py +765 -0
- truthound/checkpoint/distributed/protocols.py +842 -0
- truthound/checkpoint/distributed/registry.py +449 -0
- truthound/checkpoint/idempotency/__init__.py +120 -0
- truthound/checkpoint/idempotency/core.py +295 -0
- truthound/checkpoint/idempotency/fingerprint.py +454 -0
- truthound/checkpoint/idempotency/locking.py +604 -0
- truthound/checkpoint/idempotency/service.py +592 -0
- truthound/checkpoint/idempotency/stores.py +653 -0
- truthound/checkpoint/monitoring/__init__.py +134 -0
- truthound/checkpoint/monitoring/aggregators/__init__.py +15 -0
- truthound/checkpoint/monitoring/aggregators/base.py +372 -0
- truthound/checkpoint/monitoring/aggregators/realtime.py +300 -0
- truthound/checkpoint/monitoring/aggregators/window.py +493 -0
- truthound/checkpoint/monitoring/collectors/__init__.py +17 -0
- truthound/checkpoint/monitoring/collectors/base.py +257 -0
- truthound/checkpoint/monitoring/collectors/memory_collector.py +617 -0
- truthound/checkpoint/monitoring/collectors/prometheus_collector.py +451 -0
- truthound/checkpoint/monitoring/collectors/redis_collector.py +518 -0
- truthound/checkpoint/monitoring/events.py +410 -0
- truthound/checkpoint/monitoring/protocols.py +636 -0
- truthound/checkpoint/monitoring/service.py +578 -0
- truthound/checkpoint/monitoring/views/__init__.py +17 -0
- truthound/checkpoint/monitoring/views/base.py +172 -0
- truthound/checkpoint/monitoring/views/queue_view.py +220 -0
- truthound/checkpoint/monitoring/views/task_view.py +240 -0
- truthound/checkpoint/monitoring/views/worker_view.py +263 -0
- truthound/checkpoint/registry.py +337 -0
- truthound/checkpoint/runner.py +356 -0
- truthound/checkpoint/transaction/__init__.py +133 -0
- truthound/checkpoint/transaction/base.py +389 -0
- truthound/checkpoint/transaction/compensatable.py +537 -0
- truthound/checkpoint/transaction/coordinator.py +576 -0
- truthound/checkpoint/transaction/executor.py +622 -0
- truthound/checkpoint/transaction/idempotency.py +534 -0
- truthound/checkpoint/transaction/saga/__init__.py +143 -0
- truthound/checkpoint/transaction/saga/builder.py +584 -0
- truthound/checkpoint/transaction/saga/definition.py +515 -0
- truthound/checkpoint/transaction/saga/event_store.py +542 -0
- truthound/checkpoint/transaction/saga/patterns.py +833 -0
- truthound/checkpoint/transaction/saga/runner.py +718 -0
- truthound/checkpoint/transaction/saga/state_machine.py +793 -0
- truthound/checkpoint/transaction/saga/strategies.py +780 -0
- truthound/checkpoint/transaction/saga/testing.py +886 -0
- truthound/checkpoint/triggers/__init__.py +58 -0
- truthound/checkpoint/triggers/base.py +237 -0
- truthound/checkpoint/triggers/event.py +385 -0
- truthound/checkpoint/triggers/schedule.py +355 -0
- truthound/cli.py +2358 -0
- truthound/cli_modules/__init__.py +124 -0
- truthound/cli_modules/advanced/__init__.py +45 -0
- truthound/cli_modules/advanced/benchmark.py +343 -0
- truthound/cli_modules/advanced/docs.py +225 -0
- truthound/cli_modules/advanced/lineage.py +209 -0
- truthound/cli_modules/advanced/ml.py +320 -0
- truthound/cli_modules/advanced/realtime.py +196 -0
- truthound/cli_modules/checkpoint/__init__.py +46 -0
- truthound/cli_modules/checkpoint/init.py +114 -0
- truthound/cli_modules/checkpoint/list.py +71 -0
- truthound/cli_modules/checkpoint/run.py +159 -0
- truthound/cli_modules/checkpoint/validate.py +67 -0
- truthound/cli_modules/common/__init__.py +71 -0
- truthound/cli_modules/common/errors.py +414 -0
- truthound/cli_modules/common/options.py +419 -0
- truthound/cli_modules/common/output.py +507 -0
- truthound/cli_modules/common/protocol.py +552 -0
- truthound/cli_modules/core/__init__.py +48 -0
- truthound/cli_modules/core/check.py +123 -0
- truthound/cli_modules/core/compare.py +104 -0
- truthound/cli_modules/core/learn.py +57 -0
- truthound/cli_modules/core/mask.py +77 -0
- truthound/cli_modules/core/profile.py +65 -0
- truthound/cli_modules/core/scan.py +61 -0
- truthound/cli_modules/profiler/__init__.py +51 -0
- truthound/cli_modules/profiler/auto_profile.py +175 -0
- truthound/cli_modules/profiler/metadata.py +107 -0
- truthound/cli_modules/profiler/suite.py +283 -0
- truthound/cli_modules/registry.py +431 -0
- truthound/cli_modules/scaffolding/__init__.py +89 -0
- truthound/cli_modules/scaffolding/base.py +631 -0
- truthound/cli_modules/scaffolding/commands.py +545 -0
- truthound/cli_modules/scaffolding/plugins.py +1072 -0
- truthound/cli_modules/scaffolding/reporters.py +594 -0
- truthound/cli_modules/scaffolding/validators.py +1127 -0
- truthound/common/__init__.py +18 -0
- truthound/common/resilience/__init__.py +130 -0
- truthound/common/resilience/bulkhead.py +266 -0
- truthound/common/resilience/circuit_breaker.py +516 -0
- truthound/common/resilience/composite.py +332 -0
- truthound/common/resilience/config.py +292 -0
- truthound/common/resilience/protocols.py +217 -0
- truthound/common/resilience/rate_limiter.py +404 -0
- truthound/common/resilience/retry.py +341 -0
- truthound/datadocs/__init__.py +260 -0
- truthound/datadocs/base.py +571 -0
- truthound/datadocs/builder.py +761 -0
- truthound/datadocs/charts.py +764 -0
- truthound/datadocs/dashboard/__init__.py +63 -0
- truthound/datadocs/dashboard/app.py +576 -0
- truthound/datadocs/dashboard/components.py +584 -0
- truthound/datadocs/dashboard/state.py +240 -0
- truthound/datadocs/engine/__init__.py +46 -0
- truthound/datadocs/engine/context.py +376 -0
- truthound/datadocs/engine/pipeline.py +618 -0
- truthound/datadocs/engine/registry.py +469 -0
- truthound/datadocs/exporters/__init__.py +49 -0
- truthound/datadocs/exporters/base.py +198 -0
- truthound/datadocs/exporters/html.py +178 -0
- truthound/datadocs/exporters/json_exporter.py +253 -0
- truthound/datadocs/exporters/markdown.py +284 -0
- truthound/datadocs/exporters/pdf.py +392 -0
- truthound/datadocs/i18n/__init__.py +86 -0
- truthound/datadocs/i18n/catalog.py +960 -0
- truthound/datadocs/i18n/formatting.py +505 -0
- truthound/datadocs/i18n/loader.py +256 -0
- truthound/datadocs/i18n/plurals.py +378 -0
- truthound/datadocs/renderers/__init__.py +42 -0
- truthound/datadocs/renderers/base.py +401 -0
- truthound/datadocs/renderers/custom.py +342 -0
- truthound/datadocs/renderers/jinja.py +697 -0
- truthound/datadocs/sections.py +736 -0
- truthound/datadocs/styles.py +931 -0
- truthound/datadocs/themes/__init__.py +101 -0
- truthound/datadocs/themes/base.py +336 -0
- truthound/datadocs/themes/default.py +417 -0
- truthound/datadocs/themes/enterprise.py +419 -0
- truthound/datadocs/themes/loader.py +336 -0
- truthound/datadocs/themes.py +301 -0
- truthound/datadocs/transformers/__init__.py +57 -0
- truthound/datadocs/transformers/base.py +268 -0
- truthound/datadocs/transformers/enrichers.py +544 -0
- truthound/datadocs/transformers/filters.py +447 -0
- truthound/datadocs/transformers/i18n.py +468 -0
- truthound/datadocs/versioning/__init__.py +62 -0
- truthound/datadocs/versioning/diff.py +639 -0
- truthound/datadocs/versioning/storage.py +497 -0
- truthound/datadocs/versioning/version.py +358 -0
- truthound/datasources/__init__.py +223 -0
- truthound/datasources/_async_protocols.py +222 -0
- truthound/datasources/_protocols.py +159 -0
- truthound/datasources/adapters.py +428 -0
- truthound/datasources/async_base.py +599 -0
- truthound/datasources/async_factory.py +511 -0
- truthound/datasources/base.py +516 -0
- truthound/datasources/factory.py +433 -0
- truthound/datasources/nosql/__init__.py +47 -0
- truthound/datasources/nosql/base.py +487 -0
- truthound/datasources/nosql/elasticsearch.py +801 -0
- truthound/datasources/nosql/mongodb.py +636 -0
- truthound/datasources/pandas_optimized.py +582 -0
- truthound/datasources/pandas_source.py +216 -0
- truthound/datasources/polars_source.py +395 -0
- truthound/datasources/spark_source.py +479 -0
- truthound/datasources/sql/__init__.py +154 -0
- truthound/datasources/sql/base.py +710 -0
- truthound/datasources/sql/bigquery.py +410 -0
- truthound/datasources/sql/cloud_base.py +199 -0
- truthound/datasources/sql/databricks.py +471 -0
- truthound/datasources/sql/mysql.py +316 -0
- truthound/datasources/sql/oracle.py +427 -0
- truthound/datasources/sql/postgresql.py +321 -0
- truthound/datasources/sql/redshift.py +479 -0
- truthound/datasources/sql/snowflake.py +439 -0
- truthound/datasources/sql/sqlite.py +286 -0
- truthound/datasources/sql/sqlserver.py +437 -0
- truthound/datasources/streaming/__init__.py +47 -0
- truthound/datasources/streaming/base.py +350 -0
- truthound/datasources/streaming/kafka.py +670 -0
- truthound/decorators.py +98 -0
- truthound/docs/__init__.py +69 -0
- truthound/docs/extractor.py +971 -0
- truthound/docs/generator.py +601 -0
- truthound/docs/parser.py +1037 -0
- truthound/docs/renderer.py +999 -0
- truthound/drift/__init__.py +22 -0
- truthound/drift/compare.py +189 -0
- truthound/drift/detectors.py +464 -0
- truthound/drift/report.py +160 -0
- truthound/execution/__init__.py +65 -0
- truthound/execution/_protocols.py +324 -0
- truthound/execution/base.py +576 -0
- truthound/execution/distributed/__init__.py +179 -0
- truthound/execution/distributed/aggregations.py +731 -0
- truthound/execution/distributed/arrow_bridge.py +817 -0
- truthound/execution/distributed/base.py +550 -0
- truthound/execution/distributed/dask_engine.py +976 -0
- truthound/execution/distributed/mixins.py +766 -0
- truthound/execution/distributed/protocols.py +756 -0
- truthound/execution/distributed/ray_engine.py +1127 -0
- truthound/execution/distributed/registry.py +446 -0
- truthound/execution/distributed/spark_engine.py +1011 -0
- truthound/execution/distributed/validator_adapter.py +682 -0
- truthound/execution/pandas_engine.py +401 -0
- truthound/execution/polars_engine.py +497 -0
- truthound/execution/pushdown/__init__.py +230 -0
- truthound/execution/pushdown/ast.py +1550 -0
- truthound/execution/pushdown/builder.py +1550 -0
- truthound/execution/pushdown/dialects.py +1072 -0
- truthound/execution/pushdown/executor.py +829 -0
- truthound/execution/pushdown/optimizer.py +1041 -0
- truthound/execution/sql_engine.py +518 -0
- truthound/infrastructure/__init__.py +189 -0
- truthound/infrastructure/audit.py +1515 -0
- truthound/infrastructure/config.py +1133 -0
- truthound/infrastructure/encryption.py +1132 -0
- truthound/infrastructure/logging.py +1503 -0
- truthound/infrastructure/metrics.py +1220 -0
- truthound/lineage/__init__.py +89 -0
- truthound/lineage/base.py +746 -0
- truthound/lineage/impact_analysis.py +474 -0
- truthound/lineage/integrations/__init__.py +22 -0
- truthound/lineage/integrations/openlineage.py +548 -0
- truthound/lineage/tracker.py +512 -0
- truthound/lineage/visualization/__init__.py +33 -0
- truthound/lineage/visualization/protocols.py +145 -0
- truthound/lineage/visualization/renderers/__init__.py +20 -0
- truthound/lineage/visualization/renderers/cytoscape.py +329 -0
- truthound/lineage/visualization/renderers/d3.py +331 -0
- truthound/lineage/visualization/renderers/graphviz.py +276 -0
- truthound/lineage/visualization/renderers/mermaid.py +308 -0
- truthound/maskers.py +113 -0
- truthound/ml/__init__.py +124 -0
- truthound/ml/anomaly_models/__init__.py +31 -0
- truthound/ml/anomaly_models/ensemble.py +362 -0
- truthound/ml/anomaly_models/isolation_forest.py +444 -0
- truthound/ml/anomaly_models/statistical.py +392 -0
- truthound/ml/base.py +1178 -0
- truthound/ml/drift_detection/__init__.py +26 -0
- truthound/ml/drift_detection/concept.py +381 -0
- truthound/ml/drift_detection/distribution.py +361 -0
- truthound/ml/drift_detection/feature.py +442 -0
- truthound/ml/drift_detection/multivariate.py +495 -0
- truthound/ml/monitoring/__init__.py +88 -0
- truthound/ml/monitoring/alerting/__init__.py +33 -0
- truthound/ml/monitoring/alerting/handlers.py +427 -0
- truthound/ml/monitoring/alerting/rules.py +508 -0
- truthound/ml/monitoring/collectors/__init__.py +19 -0
- truthound/ml/monitoring/collectors/composite.py +105 -0
- truthound/ml/monitoring/collectors/drift.py +324 -0
- truthound/ml/monitoring/collectors/performance.py +179 -0
- truthound/ml/monitoring/collectors/quality.py +369 -0
- truthound/ml/monitoring/monitor.py +536 -0
- truthound/ml/monitoring/protocols.py +451 -0
- truthound/ml/monitoring/stores/__init__.py +15 -0
- truthound/ml/monitoring/stores/memory.py +201 -0
- truthound/ml/monitoring/stores/prometheus.py +296 -0
- truthound/ml/rule_learning/__init__.py +25 -0
- truthound/ml/rule_learning/constraint_miner.py +443 -0
- truthound/ml/rule_learning/pattern_learner.py +499 -0
- truthound/ml/rule_learning/profile_learner.py +462 -0
- truthound/multitenancy/__init__.py +326 -0
- truthound/multitenancy/core.py +852 -0
- truthound/multitenancy/integration.py +597 -0
- truthound/multitenancy/isolation.py +630 -0
- truthound/multitenancy/manager.py +770 -0
- truthound/multitenancy/middleware.py +765 -0
- truthound/multitenancy/quota.py +537 -0
- truthound/multitenancy/resolvers.py +603 -0
- truthound/multitenancy/storage.py +703 -0
- truthound/observability/__init__.py +307 -0
- truthound/observability/context.py +531 -0
- truthound/observability/instrumentation.py +611 -0
- truthound/observability/logging.py +887 -0
- truthound/observability/metrics.py +1157 -0
- truthound/observability/tracing/__init__.py +178 -0
- truthound/observability/tracing/baggage.py +310 -0
- truthound/observability/tracing/config.py +426 -0
- truthound/observability/tracing/exporter.py +787 -0
- truthound/observability/tracing/integration.py +1018 -0
- truthound/observability/tracing/otel/__init__.py +146 -0
- truthound/observability/tracing/otel/adapter.py +982 -0
- truthound/observability/tracing/otel/bridge.py +1177 -0
- truthound/observability/tracing/otel/compat.py +681 -0
- truthound/observability/tracing/otel/config.py +691 -0
- truthound/observability/tracing/otel/detection.py +327 -0
- truthound/observability/tracing/otel/protocols.py +426 -0
- truthound/observability/tracing/processor.py +561 -0
- truthound/observability/tracing/propagator.py +757 -0
- truthound/observability/tracing/provider.py +569 -0
- truthound/observability/tracing/resource.py +515 -0
- truthound/observability/tracing/sampler.py +487 -0
- truthound/observability/tracing/span.py +676 -0
- truthound/plugins/__init__.py +198 -0
- truthound/plugins/base.py +599 -0
- truthound/plugins/cli.py +680 -0
- truthound/plugins/dependencies/__init__.py +42 -0
- truthound/plugins/dependencies/graph.py +422 -0
- truthound/plugins/dependencies/resolver.py +417 -0
- truthound/plugins/discovery.py +379 -0
- truthound/plugins/docs/__init__.py +46 -0
- truthound/plugins/docs/extractor.py +444 -0
- truthound/plugins/docs/renderer.py +499 -0
- truthound/plugins/enterprise_manager.py +877 -0
- truthound/plugins/examples/__init__.py +19 -0
- truthound/plugins/examples/custom_validators.py +317 -0
- truthound/plugins/examples/slack_notifier.py +312 -0
- truthound/plugins/examples/xml_reporter.py +254 -0
- truthound/plugins/hooks.py +558 -0
- truthound/plugins/lifecycle/__init__.py +43 -0
- truthound/plugins/lifecycle/hot_reload.py +402 -0
- truthound/plugins/lifecycle/manager.py +371 -0
- truthound/plugins/manager.py +736 -0
- truthound/plugins/registry.py +338 -0
- truthound/plugins/security/__init__.py +93 -0
- truthound/plugins/security/exceptions.py +332 -0
- truthound/plugins/security/policies.py +348 -0
- truthound/plugins/security/protocols.py +643 -0
- truthound/plugins/security/sandbox/__init__.py +45 -0
- truthound/plugins/security/sandbox/context.py +158 -0
- truthound/plugins/security/sandbox/engines/__init__.py +19 -0
- truthound/plugins/security/sandbox/engines/container.py +379 -0
- truthound/plugins/security/sandbox/engines/noop.py +144 -0
- truthound/plugins/security/sandbox/engines/process.py +336 -0
- truthound/plugins/security/sandbox/factory.py +211 -0
- truthound/plugins/security/signing/__init__.py +57 -0
- truthound/plugins/security/signing/service.py +330 -0
- truthound/plugins/security/signing/trust_store.py +368 -0
- truthound/plugins/security/signing/verifier.py +459 -0
- truthound/plugins/versioning/__init__.py +41 -0
- truthound/plugins/versioning/constraints.py +297 -0
- truthound/plugins/versioning/resolver.py +329 -0
- truthound/profiler/__init__.py +1729 -0
- truthound/profiler/_lazy.py +452 -0
- truthound/profiler/ab_testing/__init__.py +80 -0
- truthound/profiler/ab_testing/analysis.py +449 -0
- truthound/profiler/ab_testing/base.py +257 -0
- truthound/profiler/ab_testing/experiment.py +395 -0
- truthound/profiler/ab_testing/tracking.py +368 -0
- truthound/profiler/auto_threshold.py +1170 -0
- truthound/profiler/base.py +579 -0
- truthound/profiler/cache_patterns.py +911 -0
- truthound/profiler/caching.py +1303 -0
- truthound/profiler/column_profiler.py +712 -0
- truthound/profiler/comparison.py +1007 -0
- truthound/profiler/custom_patterns.py +1170 -0
- truthound/profiler/dashboard/__init__.py +50 -0
- truthound/profiler/dashboard/app.py +476 -0
- truthound/profiler/dashboard/components.py +457 -0
- truthound/profiler/dashboard/config.py +72 -0
- truthound/profiler/distributed/__init__.py +83 -0
- truthound/profiler/distributed/base.py +281 -0
- truthound/profiler/distributed/dask_backend.py +498 -0
- truthound/profiler/distributed/local_backend.py +293 -0
- truthound/profiler/distributed/profiler.py +304 -0
- truthound/profiler/distributed/ray_backend.py +374 -0
- truthound/profiler/distributed/spark_backend.py +375 -0
- truthound/profiler/distributed.py +1366 -0
- truthound/profiler/enterprise_sampling.py +1065 -0
- truthound/profiler/errors.py +488 -0
- truthound/profiler/evolution/__init__.py +91 -0
- truthound/profiler/evolution/alerts.py +426 -0
- truthound/profiler/evolution/changes.py +206 -0
- truthound/profiler/evolution/compatibility.py +365 -0
- truthound/profiler/evolution/detector.py +372 -0
- truthound/profiler/evolution/protocols.py +121 -0
- truthound/profiler/generators/__init__.py +48 -0
- truthound/profiler/generators/base.py +384 -0
- truthound/profiler/generators/ml_rules.py +375 -0
- truthound/profiler/generators/pattern_rules.py +384 -0
- truthound/profiler/generators/schema_rules.py +267 -0
- truthound/profiler/generators/stats_rules.py +324 -0
- truthound/profiler/generators/suite_generator.py +857 -0
- truthound/profiler/i18n.py +1542 -0
- truthound/profiler/incremental.py +554 -0
- truthound/profiler/incremental_validation.py +1710 -0
- truthound/profiler/integration/__init__.py +73 -0
- truthound/profiler/integration/adapters.py +345 -0
- truthound/profiler/integration/context.py +371 -0
- truthound/profiler/integration/executor.py +527 -0
- truthound/profiler/integration/naming.py +75 -0
- truthound/profiler/integration/protocols.py +243 -0
- truthound/profiler/memory.py +1185 -0
- truthound/profiler/migration/__init__.py +60 -0
- truthound/profiler/migration/base.py +345 -0
- truthound/profiler/migration/manager.py +444 -0
- truthound/profiler/migration/v1_0_to_v1_1.py +484 -0
- truthound/profiler/ml/__init__.py +73 -0
- truthound/profiler/ml/base.py +244 -0
- truthound/profiler/ml/classifier.py +507 -0
- truthound/profiler/ml/feature_extraction.py +604 -0
- truthound/profiler/ml/pretrained.py +448 -0
- truthound/profiler/ml_inference.py +1276 -0
- truthound/profiler/native_patterns.py +815 -0
- truthound/profiler/observability.py +1184 -0
- truthound/profiler/process_timeout.py +1566 -0
- truthound/profiler/progress.py +568 -0
- truthound/profiler/progress_callbacks.py +1734 -0
- truthound/profiler/quality.py +1345 -0
- truthound/profiler/resilience.py +1180 -0
- truthound/profiler/sampled_matcher.py +794 -0
- truthound/profiler/sampling.py +1288 -0
- truthound/profiler/scheduling/__init__.py +82 -0
- truthound/profiler/scheduling/protocols.py +214 -0
- truthound/profiler/scheduling/scheduler.py +474 -0
- truthound/profiler/scheduling/storage.py +457 -0
- truthound/profiler/scheduling/triggers.py +449 -0
- truthound/profiler/schema.py +603 -0
- truthound/profiler/streaming.py +685 -0
- truthound/profiler/streaming_patterns.py +1354 -0
- truthound/profiler/suite_cli.py +625 -0
- truthound/profiler/suite_config.py +789 -0
- truthound/profiler/suite_export.py +1268 -0
- truthound/profiler/table_profiler.py +547 -0
- truthound/profiler/timeout.py +565 -0
- truthound/profiler/validation.py +1532 -0
- truthound/profiler/visualization/__init__.py +118 -0
- truthound/profiler/visualization/base.py +346 -0
- truthound/profiler/visualization/generator.py +1259 -0
- truthound/profiler/visualization/plotly_renderer.py +811 -0
- truthound/profiler/visualization/renderers.py +669 -0
- truthound/profiler/visualization/sections.py +540 -0
- truthound/profiler/visualization.py +2122 -0
- truthound/profiler/yaml_validation.py +1151 -0
- truthound/py.typed +0 -0
- truthound/ratelimit/__init__.py +248 -0
- truthound/ratelimit/algorithms.py +1108 -0
- truthound/ratelimit/core.py +573 -0
- truthound/ratelimit/integration.py +532 -0
- truthound/ratelimit/limiter.py +663 -0
- truthound/ratelimit/middleware.py +700 -0
- truthound/ratelimit/policy.py +792 -0
- truthound/ratelimit/storage.py +763 -0
- truthound/rbac/__init__.py +340 -0
- truthound/rbac/core.py +976 -0
- truthound/rbac/integration.py +760 -0
- truthound/rbac/manager.py +1052 -0
- truthound/rbac/middleware.py +842 -0
- truthound/rbac/policy.py +954 -0
- truthound/rbac/storage.py +878 -0
- truthound/realtime/__init__.py +141 -0
- truthound/realtime/adapters/__init__.py +43 -0
- truthound/realtime/adapters/base.py +533 -0
- truthound/realtime/adapters/kafka.py +487 -0
- truthound/realtime/adapters/kinesis.py +479 -0
- truthound/realtime/adapters/mock.py +243 -0
- truthound/realtime/base.py +553 -0
- truthound/realtime/factory.py +382 -0
- truthound/realtime/incremental.py +660 -0
- truthound/realtime/processing/__init__.py +67 -0
- truthound/realtime/processing/exactly_once.py +575 -0
- truthound/realtime/processing/state.py +547 -0
- truthound/realtime/processing/windows.py +647 -0
- truthound/realtime/protocols.py +569 -0
- truthound/realtime/streaming.py +605 -0
- truthound/realtime/testing/__init__.py +32 -0
- truthound/realtime/testing/containers.py +615 -0
- truthound/realtime/testing/fixtures.py +484 -0
- truthound/report.py +280 -0
- truthound/reporters/__init__.py +46 -0
- truthound/reporters/_protocols.py +30 -0
- truthound/reporters/base.py +324 -0
- truthound/reporters/ci/__init__.py +66 -0
- truthound/reporters/ci/azure.py +436 -0
- truthound/reporters/ci/base.py +509 -0
- truthound/reporters/ci/bitbucket.py +567 -0
- truthound/reporters/ci/circleci.py +547 -0
- truthound/reporters/ci/detection.py +364 -0
- truthound/reporters/ci/factory.py +182 -0
- truthound/reporters/ci/github.py +388 -0
- truthound/reporters/ci/gitlab.py +471 -0
- truthound/reporters/ci/jenkins.py +525 -0
- truthound/reporters/console_reporter.py +299 -0
- truthound/reporters/factory.py +211 -0
- truthound/reporters/html_reporter.py +524 -0
- truthound/reporters/json_reporter.py +256 -0
- truthound/reporters/markdown_reporter.py +280 -0
- truthound/reporters/sdk/__init__.py +174 -0
- truthound/reporters/sdk/builder.py +558 -0
- truthound/reporters/sdk/mixins.py +1150 -0
- truthound/reporters/sdk/schema.py +1493 -0
- truthound/reporters/sdk/templates.py +666 -0
- truthound/reporters/sdk/testing.py +968 -0
- truthound/scanners.py +170 -0
- truthound/scheduling/__init__.py +122 -0
- truthound/scheduling/cron.py +1136 -0
- truthound/scheduling/presets.py +212 -0
- truthound/schema.py +275 -0
- truthound/secrets/__init__.py +173 -0
- truthound/secrets/base.py +618 -0
- truthound/secrets/cloud.py +682 -0
- truthound/secrets/integration.py +507 -0
- truthound/secrets/manager.py +633 -0
- truthound/secrets/oidc/__init__.py +172 -0
- truthound/secrets/oidc/base.py +902 -0
- truthound/secrets/oidc/credential_provider.py +623 -0
- truthound/secrets/oidc/exchangers.py +1001 -0
- truthound/secrets/oidc/github/__init__.py +110 -0
- truthound/secrets/oidc/github/claims.py +718 -0
- truthound/secrets/oidc/github/enhanced_provider.py +693 -0
- truthound/secrets/oidc/github/trust_policy.py +742 -0
- truthound/secrets/oidc/github/verification.py +723 -0
- truthound/secrets/oidc/github/workflow.py +691 -0
- truthound/secrets/oidc/providers.py +825 -0
- truthound/secrets/providers.py +506 -0
- truthound/secrets/resolver.py +495 -0
- truthound/stores/__init__.py +177 -0
- truthound/stores/backends/__init__.py +18 -0
- truthound/stores/backends/_protocols.py +340 -0
- truthound/stores/backends/azure_blob.py +530 -0
- truthound/stores/backends/concurrent_filesystem.py +915 -0
- truthound/stores/backends/connection_pool.py +1365 -0
- truthound/stores/backends/database.py +743 -0
- truthound/stores/backends/filesystem.py +538 -0
- truthound/stores/backends/gcs.py +399 -0
- truthound/stores/backends/memory.py +354 -0
- truthound/stores/backends/s3.py +434 -0
- truthound/stores/backpressure/__init__.py +84 -0
- truthound/stores/backpressure/base.py +375 -0
- truthound/stores/backpressure/circuit_breaker.py +434 -0
- truthound/stores/backpressure/monitor.py +376 -0
- truthound/stores/backpressure/strategies.py +677 -0
- truthound/stores/base.py +551 -0
- truthound/stores/batching/__init__.py +65 -0
- truthound/stores/batching/base.py +305 -0
- truthound/stores/batching/buffer.py +370 -0
- truthound/stores/batching/store.py +248 -0
- truthound/stores/batching/writer.py +521 -0
- truthound/stores/caching/__init__.py +60 -0
- truthound/stores/caching/backends.py +684 -0
- truthound/stores/caching/base.py +356 -0
- truthound/stores/caching/store.py +305 -0
- truthound/stores/compression/__init__.py +193 -0
- truthound/stores/compression/adaptive.py +694 -0
- truthound/stores/compression/base.py +514 -0
- truthound/stores/compression/pipeline.py +868 -0
- truthound/stores/compression/providers.py +672 -0
- truthound/stores/compression/streaming.py +832 -0
- truthound/stores/concurrency/__init__.py +81 -0
- truthound/stores/concurrency/atomic.py +556 -0
- truthound/stores/concurrency/index.py +775 -0
- truthound/stores/concurrency/locks.py +576 -0
- truthound/stores/concurrency/manager.py +482 -0
- truthound/stores/encryption/__init__.py +297 -0
- truthound/stores/encryption/base.py +952 -0
- truthound/stores/encryption/keys.py +1191 -0
- truthound/stores/encryption/pipeline.py +903 -0
- truthound/stores/encryption/providers.py +953 -0
- truthound/stores/encryption/streaming.py +950 -0
- truthound/stores/expectations.py +227 -0
- truthound/stores/factory.py +246 -0
- truthound/stores/migration/__init__.py +75 -0
- truthound/stores/migration/base.py +480 -0
- truthound/stores/migration/manager.py +347 -0
- truthound/stores/migration/registry.py +382 -0
- truthound/stores/migration/store.py +559 -0
- truthound/stores/observability/__init__.py +106 -0
- truthound/stores/observability/audit.py +718 -0
- truthound/stores/observability/config.py +270 -0
- truthound/stores/observability/factory.py +208 -0
- truthound/stores/observability/metrics.py +636 -0
- truthound/stores/observability/protocols.py +410 -0
- truthound/stores/observability/store.py +570 -0
- truthound/stores/observability/tracing.py +784 -0
- truthound/stores/replication/__init__.py +76 -0
- truthound/stores/replication/base.py +260 -0
- truthound/stores/replication/monitor.py +269 -0
- truthound/stores/replication/store.py +439 -0
- truthound/stores/replication/syncer.py +391 -0
- truthound/stores/results.py +359 -0
- truthound/stores/retention/__init__.py +77 -0
- truthound/stores/retention/base.py +378 -0
- truthound/stores/retention/policies.py +621 -0
- truthound/stores/retention/scheduler.py +279 -0
- truthound/stores/retention/store.py +526 -0
- truthound/stores/streaming/__init__.py +138 -0
- truthound/stores/streaming/base.py +801 -0
- truthound/stores/streaming/database.py +984 -0
- truthound/stores/streaming/filesystem.py +719 -0
- truthound/stores/streaming/reader.py +629 -0
- truthound/stores/streaming/s3.py +843 -0
- truthound/stores/streaming/writer.py +790 -0
- truthound/stores/tiering/__init__.py +108 -0
- truthound/stores/tiering/base.py +462 -0
- truthound/stores/tiering/manager.py +249 -0
- truthound/stores/tiering/policies.py +692 -0
- truthound/stores/tiering/store.py +526 -0
- truthound/stores/versioning/__init__.py +56 -0
- truthound/stores/versioning/base.py +376 -0
- truthound/stores/versioning/store.py +660 -0
- truthound/stores/versioning/strategies.py +353 -0
- truthound/types.py +56 -0
- truthound/validators/__init__.py +774 -0
- truthound/validators/aggregate/__init__.py +27 -0
- truthound/validators/aggregate/central.py +116 -0
- truthound/validators/aggregate/extremes.py +116 -0
- truthound/validators/aggregate/spread.py +118 -0
- truthound/validators/aggregate/sum.py +64 -0
- truthound/validators/aggregate/type.py +78 -0
- truthound/validators/anomaly/__init__.py +93 -0
- truthound/validators/anomaly/base.py +431 -0
- truthound/validators/anomaly/ml_based.py +1190 -0
- truthound/validators/anomaly/multivariate.py +647 -0
- truthound/validators/anomaly/statistical.py +599 -0
- truthound/validators/base.py +1089 -0
- truthound/validators/business_rule/__init__.py +46 -0
- truthound/validators/business_rule/base.py +147 -0
- truthound/validators/business_rule/checksum.py +509 -0
- truthound/validators/business_rule/financial.py +526 -0
- truthound/validators/cache.py +733 -0
- truthound/validators/completeness/__init__.py +39 -0
- truthound/validators/completeness/conditional.py +73 -0
- truthound/validators/completeness/default.py +98 -0
- truthound/validators/completeness/empty.py +103 -0
- truthound/validators/completeness/nan.py +337 -0
- truthound/validators/completeness/null.py +152 -0
- truthound/validators/cross_table/__init__.py +17 -0
- truthound/validators/cross_table/aggregate.py +333 -0
- truthound/validators/cross_table/row_count.py +122 -0
- truthound/validators/datetime/__init__.py +29 -0
- truthound/validators/datetime/format.py +78 -0
- truthound/validators/datetime/freshness.py +269 -0
- truthound/validators/datetime/order.py +73 -0
- truthound/validators/datetime/parseable.py +185 -0
- truthound/validators/datetime/range.py +202 -0
- truthound/validators/datetime/timezone.py +69 -0
- truthound/validators/distribution/__init__.py +49 -0
- truthound/validators/distribution/distribution.py +128 -0
- truthound/validators/distribution/monotonic.py +119 -0
- truthound/validators/distribution/outlier.py +178 -0
- truthound/validators/distribution/quantile.py +80 -0
- truthound/validators/distribution/range.py +254 -0
- truthound/validators/distribution/set.py +125 -0
- truthound/validators/distribution/statistical.py +459 -0
- truthound/validators/drift/__init__.py +79 -0
- truthound/validators/drift/base.py +427 -0
- truthound/validators/drift/multi_feature.py +401 -0
- truthound/validators/drift/numeric.py +395 -0
- truthound/validators/drift/psi.py +446 -0
- truthound/validators/drift/statistical.py +510 -0
- truthound/validators/enterprise.py +1658 -0
- truthound/validators/geospatial/__init__.py +80 -0
- truthound/validators/geospatial/base.py +97 -0
- truthound/validators/geospatial/boundary.py +238 -0
- truthound/validators/geospatial/coordinate.py +351 -0
- truthound/validators/geospatial/distance.py +399 -0
- truthound/validators/geospatial/polygon.py +665 -0
- truthound/validators/i18n/__init__.py +308 -0
- truthound/validators/i18n/bidi.py +571 -0
- truthound/validators/i18n/catalogs.py +570 -0
- truthound/validators/i18n/dialects.py +763 -0
- truthound/validators/i18n/extended_catalogs.py +549 -0
- truthound/validators/i18n/formatting.py +1434 -0
- truthound/validators/i18n/loader.py +1020 -0
- truthound/validators/i18n/messages.py +521 -0
- truthound/validators/i18n/plural.py +683 -0
- truthound/validators/i18n/protocols.py +855 -0
- truthound/validators/i18n/tms.py +1162 -0
- truthound/validators/localization/__init__.py +53 -0
- truthound/validators/localization/base.py +122 -0
- truthound/validators/localization/chinese.py +362 -0
- truthound/validators/localization/japanese.py +275 -0
- truthound/validators/localization/korean.py +524 -0
- truthound/validators/memory/__init__.py +94 -0
- truthound/validators/memory/approximate_knn.py +506 -0
- truthound/validators/memory/base.py +547 -0
- truthound/validators/memory/sgd_online.py +719 -0
- truthound/validators/memory/streaming_ecdf.py +753 -0
- truthound/validators/ml_feature/__init__.py +54 -0
- truthound/validators/ml_feature/base.py +249 -0
- truthound/validators/ml_feature/correlation.py +299 -0
- truthound/validators/ml_feature/leakage.py +344 -0
- truthound/validators/ml_feature/null_impact.py +270 -0
- truthound/validators/ml_feature/scale.py +264 -0
- truthound/validators/multi_column/__init__.py +89 -0
- truthound/validators/multi_column/arithmetic.py +284 -0
- truthound/validators/multi_column/base.py +231 -0
- truthound/validators/multi_column/comparison.py +273 -0
- truthound/validators/multi_column/consistency.py +312 -0
- truthound/validators/multi_column/statistical.py +299 -0
- truthound/validators/optimization/__init__.py +164 -0
- truthound/validators/optimization/aggregation.py +563 -0
- truthound/validators/optimization/covariance.py +556 -0
- truthound/validators/optimization/geo.py +626 -0
- truthound/validators/optimization/graph.py +587 -0
- truthound/validators/optimization/orchestrator.py +970 -0
- truthound/validators/optimization/profiling.py +1312 -0
- truthound/validators/privacy/__init__.py +223 -0
- truthound/validators/privacy/base.py +635 -0
- truthound/validators/privacy/ccpa.py +670 -0
- truthound/validators/privacy/gdpr.py +728 -0
- truthound/validators/privacy/global_patterns.py +604 -0
- truthound/validators/privacy/plugins.py +867 -0
- truthound/validators/profiling/__init__.py +52 -0
- truthound/validators/profiling/base.py +175 -0
- truthound/validators/profiling/cardinality.py +312 -0
- truthound/validators/profiling/entropy.py +391 -0
- truthound/validators/profiling/frequency.py +455 -0
- truthound/validators/pushdown_support.py +660 -0
- truthound/validators/query/__init__.py +91 -0
- truthound/validators/query/aggregate.py +346 -0
- truthound/validators/query/base.py +246 -0
- truthound/validators/query/column.py +249 -0
- truthound/validators/query/expression.py +274 -0
- truthound/validators/query/result.py +323 -0
- truthound/validators/query/row_count.py +264 -0
- truthound/validators/referential/__init__.py +80 -0
- truthound/validators/referential/base.py +395 -0
- truthound/validators/referential/cascade.py +391 -0
- truthound/validators/referential/circular.py +563 -0
- truthound/validators/referential/foreign_key.py +624 -0
- truthound/validators/referential/orphan.py +485 -0
- truthound/validators/registry.py +112 -0
- truthound/validators/schema/__init__.py +41 -0
- truthound/validators/schema/column_count.py +142 -0
- truthound/validators/schema/column_exists.py +80 -0
- truthound/validators/schema/column_order.py +82 -0
- truthound/validators/schema/column_pair.py +85 -0
- truthound/validators/schema/column_pair_set.py +195 -0
- truthound/validators/schema/column_type.py +94 -0
- truthound/validators/schema/multi_column.py +53 -0
- truthound/validators/schema/multi_column_aggregate.py +175 -0
- truthound/validators/schema/referential.py +274 -0
- truthound/validators/schema/table_schema.py +91 -0
- truthound/validators/schema_validator.py +219 -0
- truthound/validators/sdk/__init__.py +250 -0
- truthound/validators/sdk/builder.py +680 -0
- truthound/validators/sdk/decorators.py +474 -0
- truthound/validators/sdk/enterprise/__init__.py +211 -0
- truthound/validators/sdk/enterprise/docs.py +725 -0
- truthound/validators/sdk/enterprise/fuzzing.py +659 -0
- truthound/validators/sdk/enterprise/licensing.py +709 -0
- truthound/validators/sdk/enterprise/manager.py +543 -0
- truthound/validators/sdk/enterprise/resources.py +628 -0
- truthound/validators/sdk/enterprise/sandbox.py +766 -0
- truthound/validators/sdk/enterprise/signing.py +603 -0
- truthound/validators/sdk/enterprise/templates.py +865 -0
- truthound/validators/sdk/enterprise/versioning.py +659 -0
- truthound/validators/sdk/templates.py +757 -0
- truthound/validators/sdk/testing.py +807 -0
- truthound/validators/security/__init__.py +181 -0
- truthound/validators/security/redos/__init__.py +182 -0
- truthound/validators/security/redos/core.py +861 -0
- truthound/validators/security/redos/cpu_monitor.py +593 -0
- truthound/validators/security/redos/cve_database.py +791 -0
- truthound/validators/security/redos/ml/__init__.py +155 -0
- truthound/validators/security/redos/ml/base.py +785 -0
- truthound/validators/security/redos/ml/datasets.py +618 -0
- truthound/validators/security/redos/ml/features.py +359 -0
- truthound/validators/security/redos/ml/models.py +1000 -0
- truthound/validators/security/redos/ml/predictor.py +507 -0
- truthound/validators/security/redos/ml/storage.py +632 -0
- truthound/validators/security/redos/ml/training.py +571 -0
- truthound/validators/security/redos/ml_analyzer.py +937 -0
- truthound/validators/security/redos/optimizer.py +674 -0
- truthound/validators/security/redos/profiler.py +682 -0
- truthound/validators/security/redos/re2_engine.py +709 -0
- truthound/validators/security/redos.py +886 -0
- truthound/validators/security/sql_security.py +1247 -0
- truthound/validators/streaming/__init__.py +126 -0
- truthound/validators/streaming/base.py +292 -0
- truthound/validators/streaming/completeness.py +210 -0
- truthound/validators/streaming/mixin.py +575 -0
- truthound/validators/streaming/range.py +308 -0
- truthound/validators/streaming/sources.py +846 -0
- truthound/validators/string/__init__.py +57 -0
- truthound/validators/string/casing.py +158 -0
- truthound/validators/string/charset.py +96 -0
- truthound/validators/string/format.py +501 -0
- truthound/validators/string/json.py +77 -0
- truthound/validators/string/json_schema.py +184 -0
- truthound/validators/string/length.py +104 -0
- truthound/validators/string/like_pattern.py +237 -0
- truthound/validators/string/regex.py +202 -0
- truthound/validators/string/regex_extended.py +435 -0
- truthound/validators/table/__init__.py +88 -0
- truthound/validators/table/base.py +78 -0
- truthound/validators/table/column_count.py +198 -0
- truthound/validators/table/freshness.py +362 -0
- truthound/validators/table/row_count.py +251 -0
- truthound/validators/table/schema.py +333 -0
- truthound/validators/table/size.py +285 -0
- truthound/validators/timeout/__init__.py +102 -0
- truthound/validators/timeout/advanced/__init__.py +247 -0
- truthound/validators/timeout/advanced/circuit_breaker.py +675 -0
- truthound/validators/timeout/advanced/prediction.py +773 -0
- truthound/validators/timeout/advanced/priority.py +618 -0
- truthound/validators/timeout/advanced/redis_backend.py +770 -0
- truthound/validators/timeout/advanced/retry.py +721 -0
- truthound/validators/timeout/advanced/sampling.py +788 -0
- truthound/validators/timeout/advanced/sla.py +661 -0
- truthound/validators/timeout/advanced/telemetry.py +804 -0
- truthound/validators/timeout/cascade.py +477 -0
- truthound/validators/timeout/deadline.py +657 -0
- truthound/validators/timeout/degradation.py +525 -0
- truthound/validators/timeout/distributed.py +597 -0
- truthound/validators/timeseries/__init__.py +89 -0
- truthound/validators/timeseries/base.py +326 -0
- truthound/validators/timeseries/completeness.py +617 -0
- truthound/validators/timeseries/gap.py +485 -0
- truthound/validators/timeseries/monotonic.py +310 -0
- truthound/validators/timeseries/seasonality.py +422 -0
- truthound/validators/timeseries/trend.py +510 -0
- truthound/validators/uniqueness/__init__.py +59 -0
- truthound/validators/uniqueness/approximate.py +475 -0
- truthound/validators/uniqueness/distinct_values.py +253 -0
- truthound/validators/uniqueness/duplicate.py +118 -0
- truthound/validators/uniqueness/primary_key.py +140 -0
- truthound/validators/uniqueness/unique.py +191 -0
- truthound/validators/uniqueness/within_record.py +599 -0
- truthound/validators/utils.py +756 -0
- truthound-1.0.8.dist-info/METADATA +474 -0
- truthound-1.0.8.dist-info/RECORD +877 -0
- truthound-1.0.8.dist-info/WHEEL +4 -0
- truthound-1.0.8.dist-info/entry_points.txt +2 -0
- truthound-1.0.8.dist-info/licenses/LICENSE +190 -0
|
@@ -0,0 +1,1550 @@
|
|
|
1
|
+
"""SQL Abstract Syntax Tree (AST) for Query Pushdown.
|
|
2
|
+
|
|
3
|
+
This module defines the AST nodes that represent SQL queries in a
|
|
4
|
+
database-agnostic way. The AST can be traversed and transformed
|
|
5
|
+
into dialect-specific SQL strings.
|
|
6
|
+
|
|
7
|
+
Design Principles:
|
|
8
|
+
- Immutable nodes for thread safety
|
|
9
|
+
- Visitor pattern support for traversal
|
|
10
|
+
- Type-safe construction
|
|
11
|
+
- Full SQL expression coverage
|
|
12
|
+
|
|
13
|
+
Example:
|
|
14
|
+
>>> from truthound.execution.pushdown.ast import (
|
|
15
|
+
... SelectStatement,
|
|
16
|
+
... Column,
|
|
17
|
+
... Table,
|
|
18
|
+
... BinaryExpression,
|
|
19
|
+
... ComparisonOp,
|
|
20
|
+
... Literal,
|
|
21
|
+
... )
|
|
22
|
+
>>>
|
|
23
|
+
>>> # Build: SELECT * FROM users WHERE age > 18
|
|
24
|
+
>>> stmt = SelectStatement(
|
|
25
|
+
... select_items=[Column("*")],
|
|
26
|
+
... from_clause=FromClause(Table("users")),
|
|
27
|
+
... where_clause=WhereClause(
|
|
28
|
+
... BinaryExpression(
|
|
29
|
+
... Column("age"),
|
|
30
|
+
... ComparisonOp.GT,
|
|
31
|
+
... Literal(18),
|
|
32
|
+
... )
|
|
33
|
+
... ),
|
|
34
|
+
... )
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from __future__ import annotations
|
|
38
|
+
|
|
39
|
+
from abc import ABC, abstractmethod
|
|
40
|
+
from dataclasses import dataclass, field
|
|
41
|
+
from enum import Enum, auto
|
|
42
|
+
from typing import Any, Generic, Sequence, TypeVar
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# =============================================================================
|
|
46
|
+
# Base Classes
|
|
47
|
+
# =============================================================================
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class SQLNode(ABC):
|
|
51
|
+
"""Base class for all SQL AST nodes.
|
|
52
|
+
|
|
53
|
+
All AST nodes inherit from this class, providing a common
|
|
54
|
+
interface for traversal and transformation.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
@abstractmethod
|
|
58
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
59
|
+
"""Accept a visitor for traversal.
|
|
60
|
+
|
|
61
|
+
Args:
|
|
62
|
+
visitor: The visitor to accept.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
Result from the visitor.
|
|
66
|
+
"""
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
def children(self) -> Sequence["SQLNode"]:
|
|
70
|
+
"""Get child nodes for traversal.
|
|
71
|
+
|
|
72
|
+
Returns:
|
|
73
|
+
Sequence of child nodes.
|
|
74
|
+
"""
|
|
75
|
+
return []
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class Expression(SQLNode):
|
|
79
|
+
"""Base class for SQL expressions.
|
|
80
|
+
|
|
81
|
+
Expressions are nodes that evaluate to a value, such as:
|
|
82
|
+
- Column references
|
|
83
|
+
- Literals
|
|
84
|
+
- Function calls
|
|
85
|
+
- Binary/Unary operations
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
def __and__(self, other: "Expression") -> "BinaryExpression":
|
|
89
|
+
"""AND operator."""
|
|
90
|
+
return BinaryExpression(self, LogicalOp.AND, other)
|
|
91
|
+
|
|
92
|
+
def __or__(self, other: "Expression") -> "BinaryExpression":
|
|
93
|
+
"""OR operator."""
|
|
94
|
+
return BinaryExpression(self, LogicalOp.OR, other)
|
|
95
|
+
|
|
96
|
+
def __invert__(self) -> "UnaryExpression":
|
|
97
|
+
"""NOT operator."""
|
|
98
|
+
return UnaryExpression(UnaryOp.NOT, self)
|
|
99
|
+
|
|
100
|
+
def __eq__(self, other: Any) -> "BinaryExpression": # type: ignore[override]
|
|
101
|
+
"""Equality comparison."""
|
|
102
|
+
return BinaryExpression(self, ComparisonOp.EQ, _to_expression(other))
|
|
103
|
+
|
|
104
|
+
def __ne__(self, other: Any) -> "BinaryExpression": # type: ignore[override]
|
|
105
|
+
"""Inequality comparison."""
|
|
106
|
+
return BinaryExpression(self, ComparisonOp.NE, _to_expression(other))
|
|
107
|
+
|
|
108
|
+
def __lt__(self, other: Any) -> "BinaryExpression":
|
|
109
|
+
"""Less than comparison."""
|
|
110
|
+
return BinaryExpression(self, ComparisonOp.LT, _to_expression(other))
|
|
111
|
+
|
|
112
|
+
def __le__(self, other: Any) -> "BinaryExpression":
|
|
113
|
+
"""Less than or equal comparison."""
|
|
114
|
+
return BinaryExpression(self, ComparisonOp.LE, _to_expression(other))
|
|
115
|
+
|
|
116
|
+
def __gt__(self, other: Any) -> "BinaryExpression":
|
|
117
|
+
"""Greater than comparison."""
|
|
118
|
+
return BinaryExpression(self, ComparisonOp.GT, _to_expression(other))
|
|
119
|
+
|
|
120
|
+
def __ge__(self, other: Any) -> "BinaryExpression":
|
|
121
|
+
"""Greater than or equal comparison."""
|
|
122
|
+
return BinaryExpression(self, ComparisonOp.GE, _to_expression(other))
|
|
123
|
+
|
|
124
|
+
def __add__(self, other: Any) -> "BinaryExpression":
|
|
125
|
+
"""Addition."""
|
|
126
|
+
return BinaryExpression(self, ArithmeticOp.ADD, _to_expression(other))
|
|
127
|
+
|
|
128
|
+
def __sub__(self, other: Any) -> "BinaryExpression":
|
|
129
|
+
"""Subtraction."""
|
|
130
|
+
return BinaryExpression(self, ArithmeticOp.SUB, _to_expression(other))
|
|
131
|
+
|
|
132
|
+
def __mul__(self, other: Any) -> "BinaryExpression":
|
|
133
|
+
"""Multiplication."""
|
|
134
|
+
return BinaryExpression(self, ArithmeticOp.MUL, _to_expression(other))
|
|
135
|
+
|
|
136
|
+
def __truediv__(self, other: Any) -> "BinaryExpression":
|
|
137
|
+
"""Division."""
|
|
138
|
+
return BinaryExpression(self, ArithmeticOp.DIV, _to_expression(other))
|
|
139
|
+
|
|
140
|
+
def __mod__(self, other: Any) -> "BinaryExpression":
|
|
141
|
+
"""Modulo."""
|
|
142
|
+
return BinaryExpression(self, ArithmeticOp.MOD, _to_expression(other))
|
|
143
|
+
|
|
144
|
+
def __neg__(self) -> "UnaryExpression":
|
|
145
|
+
"""Negation."""
|
|
146
|
+
return UnaryExpression(UnaryOp.MINUS, self)
|
|
147
|
+
|
|
148
|
+
def alias(self, name: str) -> "Alias":
|
|
149
|
+
"""Create an alias for this expression."""
|
|
150
|
+
return Alias(self, name)
|
|
151
|
+
|
|
152
|
+
def asc(self) -> "OrderByItem":
|
|
153
|
+
"""Create ascending order by item."""
|
|
154
|
+
return OrderByItem(self, SortOrder.ASC)
|
|
155
|
+
|
|
156
|
+
def desc(self) -> "OrderByItem":
|
|
157
|
+
"""Create descending order by item."""
|
|
158
|
+
return OrderByItem(self, SortOrder.DESC)
|
|
159
|
+
|
|
160
|
+
def is_null(self) -> "UnaryExpression":
|
|
161
|
+
"""IS NULL check."""
|
|
162
|
+
return UnaryExpression(UnaryOp.IS_NULL, self)
|
|
163
|
+
|
|
164
|
+
def is_not_null(self) -> "UnaryExpression":
|
|
165
|
+
"""IS NOT NULL check."""
|
|
166
|
+
return UnaryExpression(UnaryOp.IS_NOT_NULL, self)
|
|
167
|
+
|
|
168
|
+
def in_(self, values: Sequence[Any]) -> "InExpression":
|
|
169
|
+
"""IN operator."""
|
|
170
|
+
exprs = [_to_expression(v) for v in values]
|
|
171
|
+
return InExpression(self, exprs, negated=False)
|
|
172
|
+
|
|
173
|
+
def not_in(self, values: Sequence[Any]) -> "InExpression":
|
|
174
|
+
"""NOT IN operator."""
|
|
175
|
+
exprs = [_to_expression(v) for v in values]
|
|
176
|
+
return InExpression(self, exprs, negated=True)
|
|
177
|
+
|
|
178
|
+
def between(self, low: Any, high: Any) -> "BetweenExpression":
|
|
179
|
+
"""BETWEEN operator."""
|
|
180
|
+
return BetweenExpression(
|
|
181
|
+
self,
|
|
182
|
+
_to_expression(low),
|
|
183
|
+
_to_expression(high),
|
|
184
|
+
negated=False,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
def not_between(self, low: Any, high: Any) -> "BetweenExpression":
|
|
188
|
+
"""NOT BETWEEN operator."""
|
|
189
|
+
return BetweenExpression(
|
|
190
|
+
self,
|
|
191
|
+
_to_expression(low),
|
|
192
|
+
_to_expression(high),
|
|
193
|
+
negated=True,
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
def like(self, pattern: str) -> "BinaryExpression":
|
|
197
|
+
"""LIKE operator."""
|
|
198
|
+
return BinaryExpression(self, ComparisonOp.LIKE, Literal(pattern))
|
|
199
|
+
|
|
200
|
+
def not_like(self, pattern: str) -> "BinaryExpression":
|
|
201
|
+
"""NOT LIKE operator."""
|
|
202
|
+
return BinaryExpression(self, ComparisonOp.NOT_LIKE, Literal(pattern))
|
|
203
|
+
|
|
204
|
+
def ilike(self, pattern: str) -> "BinaryExpression":
|
|
205
|
+
"""ILIKE operator (case-insensitive LIKE)."""
|
|
206
|
+
return BinaryExpression(self, ComparisonOp.ILIKE, Literal(pattern))
|
|
207
|
+
|
|
208
|
+
def regexp(self, pattern: str) -> "BinaryExpression":
|
|
209
|
+
"""Regular expression match."""
|
|
210
|
+
return BinaryExpression(self, ComparisonOp.REGEXP, Literal(pattern))
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
class Statement(SQLNode):
|
|
214
|
+
"""Base class for SQL statements (SELECT, INSERT, etc.)."""
|
|
215
|
+
pass
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
# =============================================================================
|
|
219
|
+
# Enums
|
|
220
|
+
# =============================================================================
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class QueryType(Enum):
|
|
224
|
+
"""Type of SQL query."""
|
|
225
|
+
|
|
226
|
+
SELECT = "select"
|
|
227
|
+
INSERT = "insert"
|
|
228
|
+
UPDATE = "update"
|
|
229
|
+
DELETE = "delete"
|
|
230
|
+
MERGE = "merge"
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
class ComparisonOp(Enum):
|
|
234
|
+
"""SQL comparison operators."""
|
|
235
|
+
|
|
236
|
+
EQ = "="
|
|
237
|
+
NE = "<>"
|
|
238
|
+
LT = "<"
|
|
239
|
+
LE = "<="
|
|
240
|
+
GT = ">"
|
|
241
|
+
GE = ">="
|
|
242
|
+
LIKE = "LIKE"
|
|
243
|
+
NOT_LIKE = "NOT LIKE"
|
|
244
|
+
ILIKE = "ILIKE" # Case-insensitive LIKE
|
|
245
|
+
REGEXP = "REGEXP"
|
|
246
|
+
SIMILAR_TO = "SIMILAR TO"
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
class LogicalOp(Enum):
|
|
250
|
+
"""SQL logical operators."""
|
|
251
|
+
|
|
252
|
+
AND = "AND"
|
|
253
|
+
OR = "OR"
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
class ArithmeticOp(Enum):
|
|
257
|
+
"""SQL arithmetic operators."""
|
|
258
|
+
|
|
259
|
+
ADD = "+"
|
|
260
|
+
SUB = "-"
|
|
261
|
+
MUL = "*"
|
|
262
|
+
DIV = "/"
|
|
263
|
+
MOD = "%"
|
|
264
|
+
POWER = "^"
|
|
265
|
+
|
|
266
|
+
|
|
267
|
+
class UnaryOp(Enum):
|
|
268
|
+
"""SQL unary operators."""
|
|
269
|
+
|
|
270
|
+
NOT = "NOT"
|
|
271
|
+
MINUS = "-"
|
|
272
|
+
PLUS = "+"
|
|
273
|
+
IS_NULL = "IS NULL"
|
|
274
|
+
IS_NOT_NULL = "IS NOT NULL"
|
|
275
|
+
EXISTS = "EXISTS"
|
|
276
|
+
NOT_EXISTS = "NOT EXISTS"
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
class BinaryOp(Enum):
|
|
280
|
+
"""Combined binary operator enum for convenience."""
|
|
281
|
+
|
|
282
|
+
# Comparison
|
|
283
|
+
EQ = "="
|
|
284
|
+
NE = "<>"
|
|
285
|
+
LT = "<"
|
|
286
|
+
LE = "<="
|
|
287
|
+
GT = ">"
|
|
288
|
+
GE = ">="
|
|
289
|
+
# Logical
|
|
290
|
+
AND = "AND"
|
|
291
|
+
OR = "OR"
|
|
292
|
+
# Arithmetic
|
|
293
|
+
ADD = "+"
|
|
294
|
+
SUB = "-"
|
|
295
|
+
MUL = "*"
|
|
296
|
+
DIV = "/"
|
|
297
|
+
MOD = "%"
|
|
298
|
+
# String
|
|
299
|
+
LIKE = "LIKE"
|
|
300
|
+
ILIKE = "ILIKE"
|
|
301
|
+
CONCAT = "||"
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
class JoinType(Enum):
|
|
305
|
+
"""SQL JOIN types."""
|
|
306
|
+
|
|
307
|
+
INNER = "INNER JOIN"
|
|
308
|
+
LEFT = "LEFT JOIN"
|
|
309
|
+
LEFT_OUTER = "LEFT OUTER JOIN"
|
|
310
|
+
RIGHT = "RIGHT JOIN"
|
|
311
|
+
RIGHT_OUTER = "RIGHT OUTER JOIN"
|
|
312
|
+
FULL = "FULL JOIN"
|
|
313
|
+
FULL_OUTER = "FULL OUTER JOIN"
|
|
314
|
+
CROSS = "CROSS JOIN"
|
|
315
|
+
NATURAL = "NATURAL JOIN"
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
class SortOrder(Enum):
|
|
319
|
+
"""SQL sort order."""
|
|
320
|
+
|
|
321
|
+
ASC = "ASC"
|
|
322
|
+
DESC = "DESC"
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
class NullsPosition(Enum):
|
|
326
|
+
"""Position of NULLs in ORDER BY."""
|
|
327
|
+
|
|
328
|
+
FIRST = "NULLS FIRST"
|
|
329
|
+
LAST = "NULLS LAST"
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
class FrameType(Enum):
|
|
333
|
+
"""Window frame type."""
|
|
334
|
+
|
|
335
|
+
ROWS = "ROWS"
|
|
336
|
+
RANGE = "RANGE"
|
|
337
|
+
GROUPS = "GROUPS"
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
class FrameBoundType(Enum):
|
|
341
|
+
"""Window frame bound type."""
|
|
342
|
+
|
|
343
|
+
UNBOUNDED_PRECEDING = "UNBOUNDED PRECEDING"
|
|
344
|
+
PRECEDING = "PRECEDING"
|
|
345
|
+
CURRENT_ROW = "CURRENT ROW"
|
|
346
|
+
FOLLOWING = "FOLLOWING"
|
|
347
|
+
UNBOUNDED_FOLLOWING = "UNBOUNDED FOLLOWING"
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
class SetOperation(Enum):
|
|
351
|
+
"""SQL set operations."""
|
|
352
|
+
|
|
353
|
+
UNION = "UNION"
|
|
354
|
+
UNION_ALL = "UNION ALL"
|
|
355
|
+
INTERSECT = "INTERSECT"
|
|
356
|
+
INTERSECT_ALL = "INTERSECT ALL"
|
|
357
|
+
EXCEPT = "EXCEPT"
|
|
358
|
+
EXCEPT_ALL = "EXCEPT ALL"
|
|
359
|
+
|
|
360
|
+
|
|
361
|
+
# =============================================================================
|
|
362
|
+
# Literals
|
|
363
|
+
# =============================================================================
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
@dataclass(frozen=True, eq=False)
|
|
367
|
+
class Literal(Expression):
|
|
368
|
+
"""A literal value (number, string, boolean, etc.).
|
|
369
|
+
|
|
370
|
+
Attributes:
|
|
371
|
+
value: The literal value.
|
|
372
|
+
"""
|
|
373
|
+
|
|
374
|
+
value: Any
|
|
375
|
+
|
|
376
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
377
|
+
return visitor.visit_literal(self)
|
|
378
|
+
|
|
379
|
+
def __repr__(self) -> str:
|
|
380
|
+
return f"Literal({self.value!r})"
|
|
381
|
+
|
|
382
|
+
|
|
383
|
+
@dataclass(frozen=True, eq=False)
|
|
384
|
+
class NullLiteral(Expression):
|
|
385
|
+
"""SQL NULL literal."""
|
|
386
|
+
|
|
387
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
388
|
+
return visitor.visit_null_literal(self)
|
|
389
|
+
|
|
390
|
+
def __repr__(self) -> str:
|
|
391
|
+
return "NULL"
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
@dataclass(frozen=True, eq=False)
|
|
395
|
+
class BooleanLiteral(Expression):
|
|
396
|
+
"""SQL boolean literal (TRUE/FALSE)."""
|
|
397
|
+
|
|
398
|
+
value: bool
|
|
399
|
+
|
|
400
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
401
|
+
return visitor.visit_boolean_literal(self)
|
|
402
|
+
|
|
403
|
+
def __repr__(self) -> str:
|
|
404
|
+
return "TRUE" if self.value else "FALSE"
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
@dataclass(frozen=True, eq=False)
|
|
408
|
+
class ArrayLiteral(Expression):
|
|
409
|
+
"""SQL array literal."""
|
|
410
|
+
|
|
411
|
+
elements: tuple[Expression, ...]
|
|
412
|
+
|
|
413
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
414
|
+
return visitor.visit_array_literal(self)
|
|
415
|
+
|
|
416
|
+
def children(self) -> Sequence[SQLNode]:
|
|
417
|
+
return self.elements
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
# =============================================================================
|
|
421
|
+
# Identifiers
|
|
422
|
+
# =============================================================================
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
@dataclass(frozen=True, eq=False)
|
|
426
|
+
class Identifier(Expression):
|
|
427
|
+
"""A SQL identifier (table name, column name, etc.).
|
|
428
|
+
|
|
429
|
+
Attributes:
|
|
430
|
+
name: The identifier name.
|
|
431
|
+
quoted: Whether the identifier should be quoted.
|
|
432
|
+
"""
|
|
433
|
+
|
|
434
|
+
name: str
|
|
435
|
+
quoted: bool = False
|
|
436
|
+
|
|
437
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
438
|
+
return visitor.visit_identifier(self)
|
|
439
|
+
|
|
440
|
+
def __repr__(self) -> str:
|
|
441
|
+
return f"Identifier({self.name!r})"
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
@dataclass(frozen=True, eq=False)
|
|
445
|
+
class Column(Expression):
|
|
446
|
+
"""A column reference.
|
|
447
|
+
|
|
448
|
+
Attributes:
|
|
449
|
+
name: Column name.
|
|
450
|
+
table: Optional table/alias name.
|
|
451
|
+
schema: Optional schema name.
|
|
452
|
+
"""
|
|
453
|
+
|
|
454
|
+
name: str
|
|
455
|
+
table: str | None = None
|
|
456
|
+
schema: str | None = None
|
|
457
|
+
|
|
458
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
459
|
+
return visitor.visit_column(self)
|
|
460
|
+
|
|
461
|
+
def __repr__(self) -> str:
|
|
462
|
+
parts = []
|
|
463
|
+
if self.schema:
|
|
464
|
+
parts.append(self.schema)
|
|
465
|
+
if self.table:
|
|
466
|
+
parts.append(self.table)
|
|
467
|
+
parts.append(self.name)
|
|
468
|
+
return f"Column({'.'.join(parts)})"
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
@dataclass(frozen=True)
|
|
472
|
+
class Table(SQLNode):
|
|
473
|
+
"""A table reference.
|
|
474
|
+
|
|
475
|
+
Attributes:
|
|
476
|
+
name: Table name.
|
|
477
|
+
schema: Optional schema name.
|
|
478
|
+
catalog: Optional catalog/database name.
|
|
479
|
+
alias: Optional table alias.
|
|
480
|
+
"""
|
|
481
|
+
|
|
482
|
+
name: str
|
|
483
|
+
schema: str | None = None
|
|
484
|
+
catalog: str | None = None
|
|
485
|
+
alias: str | None = None
|
|
486
|
+
|
|
487
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
488
|
+
return visitor.visit_table(self)
|
|
489
|
+
|
|
490
|
+
def __repr__(self) -> str:
|
|
491
|
+
parts = []
|
|
492
|
+
if self.catalog:
|
|
493
|
+
parts.append(self.catalog)
|
|
494
|
+
if self.schema:
|
|
495
|
+
parts.append(self.schema)
|
|
496
|
+
parts.append(self.name)
|
|
497
|
+
result = '.'.join(parts)
|
|
498
|
+
if self.alias:
|
|
499
|
+
result += f" AS {self.alias}"
|
|
500
|
+
return f"Table({result})"
|
|
501
|
+
|
|
502
|
+
|
|
503
|
+
@dataclass(frozen=True, eq=False)
|
|
504
|
+
class Alias(Expression):
|
|
505
|
+
"""An aliased expression.
|
|
506
|
+
|
|
507
|
+
Attributes:
|
|
508
|
+
expression: The expression being aliased.
|
|
509
|
+
alias: The alias name.
|
|
510
|
+
"""
|
|
511
|
+
|
|
512
|
+
expression: Expression
|
|
513
|
+
alias: str
|
|
514
|
+
|
|
515
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
516
|
+
return visitor.visit_alias(self)
|
|
517
|
+
|
|
518
|
+
def children(self) -> Sequence[SQLNode]:
|
|
519
|
+
return [self.expression]
|
|
520
|
+
|
|
521
|
+
def __repr__(self) -> str:
|
|
522
|
+
return f"Alias({self.expression!r} AS {self.alias})"
|
|
523
|
+
|
|
524
|
+
|
|
525
|
+
@dataclass(frozen=True, eq=False)
|
|
526
|
+
class Star(Expression):
|
|
527
|
+
"""SELECT * or table.* expression.
|
|
528
|
+
|
|
529
|
+
Attributes:
|
|
530
|
+
table: Optional table name for table.* syntax.
|
|
531
|
+
"""
|
|
532
|
+
|
|
533
|
+
table: str | None = None
|
|
534
|
+
|
|
535
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
536
|
+
return visitor.visit_star(self)
|
|
537
|
+
|
|
538
|
+
def __repr__(self) -> str:
|
|
539
|
+
if self.table:
|
|
540
|
+
return f"Star({self.table}.*)"
|
|
541
|
+
return "Star(*)"
|
|
542
|
+
|
|
543
|
+
|
|
544
|
+
# =============================================================================
|
|
545
|
+
# Expressions
|
|
546
|
+
# =============================================================================
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
@dataclass(frozen=True, eq=False)
|
|
550
|
+
class BinaryExpression(Expression):
|
|
551
|
+
"""A binary expression (left op right).
|
|
552
|
+
|
|
553
|
+
Attributes:
|
|
554
|
+
left: Left operand.
|
|
555
|
+
operator: Binary operator.
|
|
556
|
+
right: Right operand.
|
|
557
|
+
"""
|
|
558
|
+
|
|
559
|
+
left: Expression
|
|
560
|
+
operator: ComparisonOp | LogicalOp | ArithmeticOp | BinaryOp
|
|
561
|
+
right: Expression
|
|
562
|
+
|
|
563
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
564
|
+
return visitor.visit_binary_expression(self)
|
|
565
|
+
|
|
566
|
+
def children(self) -> Sequence[SQLNode]:
|
|
567
|
+
return [self.left, self.right]
|
|
568
|
+
|
|
569
|
+
def __repr__(self) -> str:
|
|
570
|
+
op = self.operator.value if isinstance(self.operator, Enum) else self.operator
|
|
571
|
+
return f"({self.left!r} {op} {self.right!r})"
|
|
572
|
+
|
|
573
|
+
|
|
574
|
+
@dataclass(frozen=True, eq=False)
|
|
575
|
+
class UnaryExpression(Expression):
|
|
576
|
+
"""A unary expression (op operand).
|
|
577
|
+
|
|
578
|
+
Attributes:
|
|
579
|
+
operator: Unary operator.
|
|
580
|
+
operand: The operand.
|
|
581
|
+
"""
|
|
582
|
+
|
|
583
|
+
operator: UnaryOp
|
|
584
|
+
operand: Expression
|
|
585
|
+
|
|
586
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
587
|
+
return visitor.visit_unary_expression(self)
|
|
588
|
+
|
|
589
|
+
def children(self) -> Sequence[SQLNode]:
|
|
590
|
+
return [self.operand]
|
|
591
|
+
|
|
592
|
+
def __repr__(self) -> str:
|
|
593
|
+
return f"({self.operator.value} {self.operand!r})"
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
@dataclass(frozen=True, eq=False)
|
|
597
|
+
class InExpression(Expression):
|
|
598
|
+
"""IN expression (expr IN (values)).
|
|
599
|
+
|
|
600
|
+
Attributes:
|
|
601
|
+
expression: The expression to test.
|
|
602
|
+
values: List of values or subquery.
|
|
603
|
+
negated: Whether this is NOT IN.
|
|
604
|
+
"""
|
|
605
|
+
|
|
606
|
+
expression: Expression
|
|
607
|
+
values: tuple[Expression, ...] | "SelectStatement"
|
|
608
|
+
negated: bool = False
|
|
609
|
+
|
|
610
|
+
def __init__(
|
|
611
|
+
self,
|
|
612
|
+
expression: Expression,
|
|
613
|
+
values: Sequence[Expression] | "SelectStatement",
|
|
614
|
+
negated: bool = False,
|
|
615
|
+
):
|
|
616
|
+
object.__setattr__(self, "expression", expression)
|
|
617
|
+
if isinstance(values, SelectStatement):
|
|
618
|
+
object.__setattr__(self, "values", values)
|
|
619
|
+
else:
|
|
620
|
+
object.__setattr__(self, "values", tuple(values))
|
|
621
|
+
object.__setattr__(self, "negated", negated)
|
|
622
|
+
|
|
623
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
624
|
+
return visitor.visit_in_expression(self)
|
|
625
|
+
|
|
626
|
+
def children(self) -> Sequence[SQLNode]:
|
|
627
|
+
children: list[SQLNode] = [self.expression]
|
|
628
|
+
if isinstance(self.values, SelectStatement):
|
|
629
|
+
children.append(self.values)
|
|
630
|
+
else:
|
|
631
|
+
children.extend(self.values)
|
|
632
|
+
return children
|
|
633
|
+
|
|
634
|
+
def __repr__(self) -> str:
|
|
635
|
+
op = "NOT IN" if self.negated else "IN"
|
|
636
|
+
return f"({self.expression!r} {op} {self.values!r})"
|
|
637
|
+
|
|
638
|
+
|
|
639
|
+
@dataclass(frozen=True, eq=False)
|
|
640
|
+
class BetweenExpression(Expression):
|
|
641
|
+
"""BETWEEN expression (expr BETWEEN low AND high).
|
|
642
|
+
|
|
643
|
+
Attributes:
|
|
644
|
+
expression: The expression to test.
|
|
645
|
+
low: Lower bound.
|
|
646
|
+
high: Upper bound.
|
|
647
|
+
negated: Whether this is NOT BETWEEN.
|
|
648
|
+
"""
|
|
649
|
+
|
|
650
|
+
expression: Expression
|
|
651
|
+
low: Expression
|
|
652
|
+
high: Expression
|
|
653
|
+
negated: bool = False
|
|
654
|
+
|
|
655
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
656
|
+
return visitor.visit_between_expression(self)
|
|
657
|
+
|
|
658
|
+
def children(self) -> Sequence[SQLNode]:
|
|
659
|
+
return [self.expression, self.low, self.high]
|
|
660
|
+
|
|
661
|
+
def __repr__(self) -> str:
|
|
662
|
+
op = "NOT BETWEEN" if self.negated else "BETWEEN"
|
|
663
|
+
return f"({self.expression!r} {op} {self.low!r} AND {self.high!r})"
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
@dataclass(frozen=True, eq=False)
|
|
667
|
+
class ExistsExpression(Expression):
|
|
668
|
+
"""EXISTS expression.
|
|
669
|
+
|
|
670
|
+
Attributes:
|
|
671
|
+
subquery: The subquery to test.
|
|
672
|
+
negated: Whether this is NOT EXISTS.
|
|
673
|
+
"""
|
|
674
|
+
|
|
675
|
+
subquery: "SelectStatement"
|
|
676
|
+
negated: bool = False
|
|
677
|
+
|
|
678
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
679
|
+
return visitor.visit_exists_expression(self)
|
|
680
|
+
|
|
681
|
+
def children(self) -> Sequence[SQLNode]:
|
|
682
|
+
return [self.subquery]
|
|
683
|
+
|
|
684
|
+
def __repr__(self) -> str:
|
|
685
|
+
op = "NOT EXISTS" if self.negated else "EXISTS"
|
|
686
|
+
return f"({op} {self.subquery!r})"
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
@dataclass(frozen=True, eq=False)
|
|
690
|
+
class SubqueryExpression(Expression):
|
|
691
|
+
"""A subquery used as an expression.
|
|
692
|
+
|
|
693
|
+
Attributes:
|
|
694
|
+
subquery: The subquery.
|
|
695
|
+
"""
|
|
696
|
+
|
|
697
|
+
subquery: "SelectStatement"
|
|
698
|
+
|
|
699
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
700
|
+
return visitor.visit_subquery_expression(self)
|
|
701
|
+
|
|
702
|
+
def children(self) -> Sequence[SQLNode]:
|
|
703
|
+
return [self.subquery]
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
@dataclass(frozen=True, eq=False)
|
|
707
|
+
class CastExpression(Expression):
|
|
708
|
+
"""CAST expression (CAST(expr AS type)).
|
|
709
|
+
|
|
710
|
+
Attributes:
|
|
711
|
+
expression: The expression to cast.
|
|
712
|
+
target_type: The target SQL type.
|
|
713
|
+
"""
|
|
714
|
+
|
|
715
|
+
expression: Expression
|
|
716
|
+
target_type: str
|
|
717
|
+
|
|
718
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
719
|
+
return visitor.visit_cast_expression(self)
|
|
720
|
+
|
|
721
|
+
def children(self) -> Sequence[SQLNode]:
|
|
722
|
+
return [self.expression]
|
|
723
|
+
|
|
724
|
+
def __repr__(self) -> str:
|
|
725
|
+
return f"CAST({self.expression!r} AS {self.target_type})"
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
@dataclass(frozen=True)
|
|
729
|
+
class WhenClause(SQLNode):
|
|
730
|
+
"""WHEN clause in CASE expression.
|
|
731
|
+
|
|
732
|
+
Attributes:
|
|
733
|
+
condition: The condition to test.
|
|
734
|
+
result: The result if condition is true.
|
|
735
|
+
"""
|
|
736
|
+
|
|
737
|
+
condition: Expression
|
|
738
|
+
result: Expression
|
|
739
|
+
|
|
740
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
741
|
+
return visitor.visit_when_clause(self)
|
|
742
|
+
|
|
743
|
+
def children(self) -> Sequence[SQLNode]:
|
|
744
|
+
return [self.condition, self.result]
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
@dataclass(frozen=True, eq=False)
|
|
748
|
+
class CaseExpression(Expression):
|
|
749
|
+
"""CASE expression.
|
|
750
|
+
|
|
751
|
+
Attributes:
|
|
752
|
+
operand: Optional operand for simple CASE.
|
|
753
|
+
when_clauses: List of WHEN clauses.
|
|
754
|
+
else_result: Optional ELSE result.
|
|
755
|
+
"""
|
|
756
|
+
|
|
757
|
+
when_clauses: tuple[WhenClause, ...]
|
|
758
|
+
operand: Expression | None = None
|
|
759
|
+
else_result: Expression | None = None
|
|
760
|
+
|
|
761
|
+
def __init__(
|
|
762
|
+
self,
|
|
763
|
+
when_clauses: Sequence[WhenClause],
|
|
764
|
+
operand: Expression | None = None,
|
|
765
|
+
else_result: Expression | None = None,
|
|
766
|
+
):
|
|
767
|
+
object.__setattr__(self, "when_clauses", tuple(when_clauses))
|
|
768
|
+
object.__setattr__(self, "operand", operand)
|
|
769
|
+
object.__setattr__(self, "else_result", else_result)
|
|
770
|
+
|
|
771
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
772
|
+
return visitor.visit_case_expression(self)
|
|
773
|
+
|
|
774
|
+
def children(self) -> Sequence[SQLNode]:
|
|
775
|
+
children: list[SQLNode] = []
|
|
776
|
+
if self.operand:
|
|
777
|
+
children.append(self.operand)
|
|
778
|
+
children.extend(self.when_clauses)
|
|
779
|
+
if self.else_result:
|
|
780
|
+
children.append(self.else_result)
|
|
781
|
+
return children
|
|
782
|
+
|
|
783
|
+
|
|
784
|
+
# =============================================================================
|
|
785
|
+
# Function Calls
|
|
786
|
+
# =============================================================================
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
@dataclass(frozen=True, eq=False)
|
|
790
|
+
class FunctionCall(Expression):
|
|
791
|
+
"""A SQL function call.
|
|
792
|
+
|
|
793
|
+
Attributes:
|
|
794
|
+
name: Function name.
|
|
795
|
+
arguments: Function arguments.
|
|
796
|
+
distinct: Whether DISTINCT is applied (for aggregates).
|
|
797
|
+
filter_clause: Optional FILTER clause.
|
|
798
|
+
"""
|
|
799
|
+
|
|
800
|
+
name: str
|
|
801
|
+
arguments: tuple[Expression, ...]
|
|
802
|
+
distinct: bool = False
|
|
803
|
+
filter_clause: Expression | None = None
|
|
804
|
+
|
|
805
|
+
def __init__(
|
|
806
|
+
self,
|
|
807
|
+
name: str,
|
|
808
|
+
arguments: Sequence[Expression] | None = None,
|
|
809
|
+
distinct: bool = False,
|
|
810
|
+
filter_clause: Expression | None = None,
|
|
811
|
+
):
|
|
812
|
+
object.__setattr__(self, "name", name)
|
|
813
|
+
object.__setattr__(self, "arguments", tuple(arguments or []))
|
|
814
|
+
object.__setattr__(self, "distinct", distinct)
|
|
815
|
+
object.__setattr__(self, "filter_clause", filter_clause)
|
|
816
|
+
|
|
817
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
818
|
+
return visitor.visit_function_call(self)
|
|
819
|
+
|
|
820
|
+
def children(self) -> Sequence[SQLNode]:
|
|
821
|
+
children: list[SQLNode] = list(self.arguments)
|
|
822
|
+
if self.filter_clause:
|
|
823
|
+
children.append(self.filter_clause)
|
|
824
|
+
return children
|
|
825
|
+
|
|
826
|
+
def __repr__(self) -> str:
|
|
827
|
+
args = ", ".join(repr(a) for a in self.arguments)
|
|
828
|
+
distinct = "DISTINCT " if self.distinct else ""
|
|
829
|
+
return f"{self.name}({distinct}{args})"
|
|
830
|
+
|
|
831
|
+
|
|
832
|
+
@dataclass(frozen=True, eq=False)
|
|
833
|
+
class AggregateFunction(Expression):
|
|
834
|
+
"""An aggregate function call (COUNT, SUM, AVG, etc.).
|
|
835
|
+
|
|
836
|
+
Attributes:
|
|
837
|
+
name: Function name (COUNT, SUM, AVG, MIN, MAX, etc.).
|
|
838
|
+
argument: The argument to aggregate.
|
|
839
|
+
distinct: Whether DISTINCT is applied.
|
|
840
|
+
filter_clause: Optional FILTER (WHERE ...) clause.
|
|
841
|
+
order_by: Optional ORDER BY for ordered-set aggregates.
|
|
842
|
+
"""
|
|
843
|
+
|
|
844
|
+
name: str
|
|
845
|
+
argument: Expression | None
|
|
846
|
+
distinct: bool = False
|
|
847
|
+
filter_clause: Expression | None = None
|
|
848
|
+
order_by: tuple["OrderByItem", ...] | None = None
|
|
849
|
+
|
|
850
|
+
def __init__(
|
|
851
|
+
self,
|
|
852
|
+
name: str,
|
|
853
|
+
argument: Expression | None = None,
|
|
854
|
+
distinct: bool = False,
|
|
855
|
+
filter_clause: Expression | None = None,
|
|
856
|
+
order_by: Sequence["OrderByItem"] | None = None,
|
|
857
|
+
):
|
|
858
|
+
object.__setattr__(self, "name", name)
|
|
859
|
+
object.__setattr__(self, "argument", argument)
|
|
860
|
+
object.__setattr__(self, "distinct", distinct)
|
|
861
|
+
object.__setattr__(self, "filter_clause", filter_clause)
|
|
862
|
+
object.__setattr__(
|
|
863
|
+
self, "order_by", tuple(order_by) if order_by else None
|
|
864
|
+
)
|
|
865
|
+
|
|
866
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
867
|
+
return visitor.visit_aggregate_function(self)
|
|
868
|
+
|
|
869
|
+
def children(self) -> Sequence[SQLNode]:
|
|
870
|
+
children: list[SQLNode] = []
|
|
871
|
+
if self.argument:
|
|
872
|
+
children.append(self.argument)
|
|
873
|
+
if self.filter_clause:
|
|
874
|
+
children.append(self.filter_clause)
|
|
875
|
+
if self.order_by:
|
|
876
|
+
children.extend(self.order_by)
|
|
877
|
+
return children
|
|
878
|
+
|
|
879
|
+
def over(self, window: "WindowSpec | str | None" = None) -> "WindowFunction":
|
|
880
|
+
"""Add OVER clause to create a window function."""
|
|
881
|
+
return WindowFunction(self, window)
|
|
882
|
+
|
|
883
|
+
def __repr__(self) -> str:
|
|
884
|
+
if self.argument:
|
|
885
|
+
distinct = "DISTINCT " if self.distinct else ""
|
|
886
|
+
return f"{self.name}({distinct}{self.argument!r})"
|
|
887
|
+
return f"{self.name}(*)"
|
|
888
|
+
|
|
889
|
+
|
|
890
|
+
@dataclass(frozen=True)
|
|
891
|
+
class FrameBound(SQLNode):
|
|
892
|
+
"""Window frame bound.
|
|
893
|
+
|
|
894
|
+
Attributes:
|
|
895
|
+
bound_type: Type of bound.
|
|
896
|
+
offset: Optional numeric offset.
|
|
897
|
+
"""
|
|
898
|
+
|
|
899
|
+
bound_type: FrameBoundType
|
|
900
|
+
offset: int | None = None
|
|
901
|
+
|
|
902
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
903
|
+
return visitor.visit_frame_bound(self)
|
|
904
|
+
|
|
905
|
+
def __repr__(self) -> str:
|
|
906
|
+
if self.offset is not None:
|
|
907
|
+
if self.bound_type == FrameBoundType.PRECEDING:
|
|
908
|
+
return f"{self.offset} PRECEDING"
|
|
909
|
+
elif self.bound_type == FrameBoundType.FOLLOWING:
|
|
910
|
+
return f"{self.offset} FOLLOWING"
|
|
911
|
+
return self.bound_type.value
|
|
912
|
+
|
|
913
|
+
|
|
914
|
+
@dataclass(frozen=True)
|
|
915
|
+
class WindowSpec(SQLNode):
|
|
916
|
+
"""Window specification for window functions.
|
|
917
|
+
|
|
918
|
+
Attributes:
|
|
919
|
+
partition_by: Optional PARTITION BY columns.
|
|
920
|
+
order_by: Optional ORDER BY items.
|
|
921
|
+
frame_type: Optional frame type (ROWS, RANGE, GROUPS).
|
|
922
|
+
frame_start: Optional frame start bound.
|
|
923
|
+
frame_end: Optional frame end bound.
|
|
924
|
+
name: Optional window name (for WINDOW clause).
|
|
925
|
+
"""
|
|
926
|
+
|
|
927
|
+
partition_by: tuple[Expression, ...] | None = None
|
|
928
|
+
order_by: tuple["OrderByItem", ...] | None = None
|
|
929
|
+
frame_type: FrameType | None = None
|
|
930
|
+
frame_start: FrameBound | None = None
|
|
931
|
+
frame_end: FrameBound | None = None
|
|
932
|
+
name: str | None = None
|
|
933
|
+
|
|
934
|
+
def __init__(
|
|
935
|
+
self,
|
|
936
|
+
partition_by: Sequence[Expression] | None = None,
|
|
937
|
+
order_by: Sequence["OrderByItem"] | None = None,
|
|
938
|
+
frame_type: FrameType | None = None,
|
|
939
|
+
frame_start: FrameBound | None = None,
|
|
940
|
+
frame_end: FrameBound | None = None,
|
|
941
|
+
name: str | None = None,
|
|
942
|
+
):
|
|
943
|
+
object.__setattr__(
|
|
944
|
+
self, "partition_by", tuple(partition_by) if partition_by else None
|
|
945
|
+
)
|
|
946
|
+
object.__setattr__(
|
|
947
|
+
self, "order_by", tuple(order_by) if order_by else None
|
|
948
|
+
)
|
|
949
|
+
object.__setattr__(self, "frame_type", frame_type)
|
|
950
|
+
object.__setattr__(self, "frame_start", frame_start)
|
|
951
|
+
object.__setattr__(self, "frame_end", frame_end)
|
|
952
|
+
object.__setattr__(self, "name", name)
|
|
953
|
+
|
|
954
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
955
|
+
return visitor.visit_window_spec(self)
|
|
956
|
+
|
|
957
|
+
def children(self) -> Sequence[SQLNode]:
|
|
958
|
+
children: list[SQLNode] = []
|
|
959
|
+
if self.partition_by:
|
|
960
|
+
children.extend(self.partition_by)
|
|
961
|
+
if self.order_by:
|
|
962
|
+
children.extend(self.order_by)
|
|
963
|
+
if self.frame_start:
|
|
964
|
+
children.append(self.frame_start)
|
|
965
|
+
if self.frame_end:
|
|
966
|
+
children.append(self.frame_end)
|
|
967
|
+
return children
|
|
968
|
+
|
|
969
|
+
|
|
970
|
+
@dataclass(frozen=True, eq=False)
|
|
971
|
+
class WindowFunction(Expression):
|
|
972
|
+
"""A window function expression.
|
|
973
|
+
|
|
974
|
+
Attributes:
|
|
975
|
+
function: The aggregate or window function.
|
|
976
|
+
window_spec: The window specification or named window.
|
|
977
|
+
"""
|
|
978
|
+
|
|
979
|
+
function: AggregateFunction | FunctionCall
|
|
980
|
+
window_spec: WindowSpec | str | None
|
|
981
|
+
|
|
982
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
983
|
+
return visitor.visit_window_function(self)
|
|
984
|
+
|
|
985
|
+
def children(self) -> Sequence[SQLNode]:
|
|
986
|
+
children: list[SQLNode] = [self.function]
|
|
987
|
+
if isinstance(self.window_spec, WindowSpec):
|
|
988
|
+
children.append(self.window_spec)
|
|
989
|
+
return children
|
|
990
|
+
|
|
991
|
+
def __repr__(self) -> str:
|
|
992
|
+
if self.window_spec:
|
|
993
|
+
return f"{self.function!r} OVER ({self.window_spec!r})"
|
|
994
|
+
return f"{self.function!r} OVER ()"
|
|
995
|
+
|
|
996
|
+
|
|
997
|
+
# =============================================================================
|
|
998
|
+
# SELECT Clauses
|
|
999
|
+
# =============================================================================
|
|
1000
|
+
|
|
1001
|
+
|
|
1002
|
+
@dataclass(frozen=True)
|
|
1003
|
+
class SelectItem(SQLNode):
|
|
1004
|
+
"""A single item in SELECT clause.
|
|
1005
|
+
|
|
1006
|
+
Attributes:
|
|
1007
|
+
expression: The expression.
|
|
1008
|
+
alias: Optional alias.
|
|
1009
|
+
"""
|
|
1010
|
+
|
|
1011
|
+
expression: Expression
|
|
1012
|
+
alias: str | None = None
|
|
1013
|
+
|
|
1014
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
1015
|
+
return visitor.visit_select_item(self)
|
|
1016
|
+
|
|
1017
|
+
def children(self) -> Sequence[SQLNode]:
|
|
1018
|
+
return [self.expression]
|
|
1019
|
+
|
|
1020
|
+
|
|
1021
|
+
@dataclass(frozen=True)
|
|
1022
|
+
class FromClause(SQLNode):
|
|
1023
|
+
"""FROM clause.
|
|
1024
|
+
|
|
1025
|
+
Attributes:
|
|
1026
|
+
source: Table, subquery, or joined tables.
|
|
1027
|
+
"""
|
|
1028
|
+
|
|
1029
|
+
source: Table | "SelectStatement" | "JoinClause"
|
|
1030
|
+
|
|
1031
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
1032
|
+
return visitor.visit_from_clause(self)
|
|
1033
|
+
|
|
1034
|
+
def children(self) -> Sequence[SQLNode]:
|
|
1035
|
+
return [self.source]
|
|
1036
|
+
|
|
1037
|
+
|
|
1038
|
+
@dataclass(frozen=True)
|
|
1039
|
+
class JoinClause(SQLNode):
|
|
1040
|
+
"""A JOIN clause.
|
|
1041
|
+
|
|
1042
|
+
Attributes:
|
|
1043
|
+
left: Left side of join.
|
|
1044
|
+
right: Right side of join.
|
|
1045
|
+
join_type: Type of join.
|
|
1046
|
+
condition: Optional join condition (for ON clause).
|
|
1047
|
+
using_columns: Optional USING columns.
|
|
1048
|
+
"""
|
|
1049
|
+
|
|
1050
|
+
left: Table | "SelectStatement" | "JoinClause"
|
|
1051
|
+
right: Table | "SelectStatement"
|
|
1052
|
+
join_type: JoinType
|
|
1053
|
+
condition: Expression | None = None
|
|
1054
|
+
using_columns: tuple[str, ...] | None = None
|
|
1055
|
+
|
|
1056
|
+
def __init__(
|
|
1057
|
+
self,
|
|
1058
|
+
left: Table | "SelectStatement" | "JoinClause",
|
|
1059
|
+
right: Table | "SelectStatement",
|
|
1060
|
+
join_type: JoinType,
|
|
1061
|
+
condition: Expression | None = None,
|
|
1062
|
+
using_columns: Sequence[str] | None = None,
|
|
1063
|
+
):
|
|
1064
|
+
object.__setattr__(self, "left", left)
|
|
1065
|
+
object.__setattr__(self, "right", right)
|
|
1066
|
+
object.__setattr__(self, "join_type", join_type)
|
|
1067
|
+
object.__setattr__(self, "condition", condition)
|
|
1068
|
+
object.__setattr__(
|
|
1069
|
+
self, "using_columns", tuple(using_columns) if using_columns else None
|
|
1070
|
+
)
|
|
1071
|
+
|
|
1072
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
1073
|
+
return visitor.visit_join_clause(self)
|
|
1074
|
+
|
|
1075
|
+
def children(self) -> Sequence[SQLNode]:
|
|
1076
|
+
children: list[SQLNode] = [self.left, self.right]
|
|
1077
|
+
if self.condition:
|
|
1078
|
+
children.append(self.condition)
|
|
1079
|
+
return children
|
|
1080
|
+
|
|
1081
|
+
|
|
1082
|
+
@dataclass(frozen=True)
|
|
1083
|
+
class WhereClause(SQLNode):
|
|
1084
|
+
"""WHERE clause.
|
|
1085
|
+
|
|
1086
|
+
Attributes:
|
|
1087
|
+
condition: The filter condition.
|
|
1088
|
+
"""
|
|
1089
|
+
|
|
1090
|
+
condition: Expression
|
|
1091
|
+
|
|
1092
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
1093
|
+
return visitor.visit_where_clause(self)
|
|
1094
|
+
|
|
1095
|
+
def children(self) -> Sequence[SQLNode]:
|
|
1096
|
+
return [self.condition]
|
|
1097
|
+
|
|
1098
|
+
|
|
1099
|
+
@dataclass(frozen=True)
|
|
1100
|
+
class GroupByClause(SQLNode):
|
|
1101
|
+
"""GROUP BY clause.
|
|
1102
|
+
|
|
1103
|
+
Attributes:
|
|
1104
|
+
expressions: Grouping expressions.
|
|
1105
|
+
with_rollup: Whether to include ROLLUP.
|
|
1106
|
+
with_cube: Whether to include CUBE.
|
|
1107
|
+
grouping_sets: Optional grouping sets.
|
|
1108
|
+
"""
|
|
1109
|
+
|
|
1110
|
+
expressions: tuple[Expression, ...]
|
|
1111
|
+
with_rollup: bool = False
|
|
1112
|
+
with_cube: bool = False
|
|
1113
|
+
grouping_sets: tuple[tuple[Expression, ...], ...] | None = None
|
|
1114
|
+
|
|
1115
|
+
def __init__(
|
|
1116
|
+
self,
|
|
1117
|
+
expressions: Sequence[Expression],
|
|
1118
|
+
with_rollup: bool = False,
|
|
1119
|
+
with_cube: bool = False,
|
|
1120
|
+
grouping_sets: Sequence[Sequence[Expression]] | None = None,
|
|
1121
|
+
):
|
|
1122
|
+
object.__setattr__(self, "expressions", tuple(expressions))
|
|
1123
|
+
object.__setattr__(self, "with_rollup", with_rollup)
|
|
1124
|
+
object.__setattr__(self, "with_cube", with_cube)
|
|
1125
|
+
if grouping_sets:
|
|
1126
|
+
object.__setattr__(
|
|
1127
|
+
self,
|
|
1128
|
+
"grouping_sets",
|
|
1129
|
+
tuple(tuple(gs) for gs in grouping_sets),
|
|
1130
|
+
)
|
|
1131
|
+
else:
|
|
1132
|
+
object.__setattr__(self, "grouping_sets", None)
|
|
1133
|
+
|
|
1134
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
1135
|
+
return visitor.visit_group_by_clause(self)
|
|
1136
|
+
|
|
1137
|
+
def children(self) -> Sequence[SQLNode]:
|
|
1138
|
+
return list(self.expressions)
|
|
1139
|
+
|
|
1140
|
+
|
|
1141
|
+
@dataclass(frozen=True)
|
|
1142
|
+
class HavingClause(SQLNode):
|
|
1143
|
+
"""HAVING clause.
|
|
1144
|
+
|
|
1145
|
+
Attributes:
|
|
1146
|
+
condition: The filter condition (applied after GROUP BY).
|
|
1147
|
+
"""
|
|
1148
|
+
|
|
1149
|
+
condition: Expression
|
|
1150
|
+
|
|
1151
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
1152
|
+
return visitor.visit_having_clause(self)
|
|
1153
|
+
|
|
1154
|
+
def children(self) -> Sequence[SQLNode]:
|
|
1155
|
+
return [self.condition]
|
|
1156
|
+
|
|
1157
|
+
|
|
1158
|
+
@dataclass(frozen=True)
|
|
1159
|
+
class OrderByItem(SQLNode):
|
|
1160
|
+
"""A single item in ORDER BY clause.
|
|
1161
|
+
|
|
1162
|
+
Attributes:
|
|
1163
|
+
expression: The expression to sort by.
|
|
1164
|
+
order: Sort order (ASC/DESC).
|
|
1165
|
+
nulls: Position of NULLs.
|
|
1166
|
+
"""
|
|
1167
|
+
|
|
1168
|
+
expression: Expression
|
|
1169
|
+
order: SortOrder = SortOrder.ASC
|
|
1170
|
+
nulls: NullsPosition | None = None
|
|
1171
|
+
|
|
1172
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
1173
|
+
return visitor.visit_order_by_item(self)
|
|
1174
|
+
|
|
1175
|
+
def children(self) -> Sequence[SQLNode]:
|
|
1176
|
+
return [self.expression]
|
|
1177
|
+
|
|
1178
|
+
|
|
1179
|
+
@dataclass(frozen=True)
|
|
1180
|
+
class OrderByClause(SQLNode):
|
|
1181
|
+
"""ORDER BY clause.
|
|
1182
|
+
|
|
1183
|
+
Attributes:
|
|
1184
|
+
items: Order by items.
|
|
1185
|
+
"""
|
|
1186
|
+
|
|
1187
|
+
items: tuple[OrderByItem, ...]
|
|
1188
|
+
|
|
1189
|
+
def __init__(self, items: Sequence[OrderByItem]):
|
|
1190
|
+
object.__setattr__(self, "items", tuple(items))
|
|
1191
|
+
|
|
1192
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
1193
|
+
return visitor.visit_order_by_clause(self)
|
|
1194
|
+
|
|
1195
|
+
def children(self) -> Sequence[SQLNode]:
|
|
1196
|
+
return list(self.items)
|
|
1197
|
+
|
|
1198
|
+
|
|
1199
|
+
@dataclass(frozen=True)
|
|
1200
|
+
class LimitClause(SQLNode):
|
|
1201
|
+
"""LIMIT clause.
|
|
1202
|
+
|
|
1203
|
+
Attributes:
|
|
1204
|
+
count: Maximum number of rows.
|
|
1205
|
+
"""
|
|
1206
|
+
|
|
1207
|
+
count: int | Expression
|
|
1208
|
+
|
|
1209
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
1210
|
+
return visitor.visit_limit_clause(self)
|
|
1211
|
+
|
|
1212
|
+
def children(self) -> Sequence[SQLNode]:
|
|
1213
|
+
if isinstance(self.count, Expression):
|
|
1214
|
+
return [self.count]
|
|
1215
|
+
return []
|
|
1216
|
+
|
|
1217
|
+
|
|
1218
|
+
@dataclass(frozen=True)
|
|
1219
|
+
class OffsetClause(SQLNode):
|
|
1220
|
+
"""OFFSET clause.
|
|
1221
|
+
|
|
1222
|
+
Attributes:
|
|
1223
|
+
offset: Number of rows to skip.
|
|
1224
|
+
"""
|
|
1225
|
+
|
|
1226
|
+
offset: int | Expression
|
|
1227
|
+
|
|
1228
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
1229
|
+
return visitor.visit_offset_clause(self)
|
|
1230
|
+
|
|
1231
|
+
def children(self) -> Sequence[SQLNode]:
|
|
1232
|
+
if isinstance(self.offset, Expression):
|
|
1233
|
+
return [self.offset]
|
|
1234
|
+
return []
|
|
1235
|
+
|
|
1236
|
+
|
|
1237
|
+
# =============================================================================
|
|
1238
|
+
# Statements
|
|
1239
|
+
# =============================================================================
|
|
1240
|
+
|
|
1241
|
+
|
|
1242
|
+
@dataclass(frozen=True)
|
|
1243
|
+
class SelectStatement(Statement):
|
|
1244
|
+
"""A complete SELECT statement.
|
|
1245
|
+
|
|
1246
|
+
Attributes:
|
|
1247
|
+
select_items: Items to select.
|
|
1248
|
+
from_clause: Optional FROM clause.
|
|
1249
|
+
where_clause: Optional WHERE clause.
|
|
1250
|
+
group_by_clause: Optional GROUP BY clause.
|
|
1251
|
+
having_clause: Optional HAVING clause.
|
|
1252
|
+
order_by_clause: Optional ORDER BY clause.
|
|
1253
|
+
limit_clause: Optional LIMIT clause.
|
|
1254
|
+
offset_clause: Optional OFFSET clause.
|
|
1255
|
+
distinct: Whether to use SELECT DISTINCT.
|
|
1256
|
+
ctes: Optional Common Table Expressions.
|
|
1257
|
+
"""
|
|
1258
|
+
|
|
1259
|
+
select_items: tuple[SelectItem | Expression, ...]
|
|
1260
|
+
from_clause: FromClause | None = None
|
|
1261
|
+
where_clause: WhereClause | None = None
|
|
1262
|
+
group_by_clause: GroupByClause | None = None
|
|
1263
|
+
having_clause: HavingClause | None = None
|
|
1264
|
+
order_by_clause: OrderByClause | None = None
|
|
1265
|
+
limit_clause: LimitClause | None = None
|
|
1266
|
+
offset_clause: OffsetClause | None = None
|
|
1267
|
+
distinct: bool = False
|
|
1268
|
+
ctes: tuple["CTEClause", ...] | None = None
|
|
1269
|
+
|
|
1270
|
+
def __init__(
|
|
1271
|
+
self,
|
|
1272
|
+
select_items: Sequence[SelectItem | Expression],
|
|
1273
|
+
from_clause: FromClause | None = None,
|
|
1274
|
+
where_clause: WhereClause | None = None,
|
|
1275
|
+
group_by_clause: GroupByClause | None = None,
|
|
1276
|
+
having_clause: HavingClause | None = None,
|
|
1277
|
+
order_by_clause: OrderByClause | None = None,
|
|
1278
|
+
limit_clause: LimitClause | None = None,
|
|
1279
|
+
offset_clause: OffsetClause | None = None,
|
|
1280
|
+
distinct: bool = False,
|
|
1281
|
+
ctes: Sequence["CTEClause"] | None = None,
|
|
1282
|
+
):
|
|
1283
|
+
object.__setattr__(self, "select_items", tuple(select_items))
|
|
1284
|
+
object.__setattr__(self, "from_clause", from_clause)
|
|
1285
|
+
object.__setattr__(self, "where_clause", where_clause)
|
|
1286
|
+
object.__setattr__(self, "group_by_clause", group_by_clause)
|
|
1287
|
+
object.__setattr__(self, "having_clause", having_clause)
|
|
1288
|
+
object.__setattr__(self, "order_by_clause", order_by_clause)
|
|
1289
|
+
object.__setattr__(self, "limit_clause", limit_clause)
|
|
1290
|
+
object.__setattr__(self, "offset_clause", offset_clause)
|
|
1291
|
+
object.__setattr__(self, "distinct", distinct)
|
|
1292
|
+
object.__setattr__(self, "ctes", tuple(ctes) if ctes else None)
|
|
1293
|
+
|
|
1294
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
1295
|
+
return visitor.visit_select_statement(self)
|
|
1296
|
+
|
|
1297
|
+
def children(self) -> Sequence[SQLNode]:
|
|
1298
|
+
children: list[SQLNode] = list(self.select_items)
|
|
1299
|
+
if self.ctes:
|
|
1300
|
+
children.extend(self.ctes)
|
|
1301
|
+
if self.from_clause:
|
|
1302
|
+
children.append(self.from_clause)
|
|
1303
|
+
if self.where_clause:
|
|
1304
|
+
children.append(self.where_clause)
|
|
1305
|
+
if self.group_by_clause:
|
|
1306
|
+
children.append(self.group_by_clause)
|
|
1307
|
+
if self.having_clause:
|
|
1308
|
+
children.append(self.having_clause)
|
|
1309
|
+
if self.order_by_clause:
|
|
1310
|
+
children.append(self.order_by_clause)
|
|
1311
|
+
if self.limit_clause:
|
|
1312
|
+
children.append(self.limit_clause)
|
|
1313
|
+
if self.offset_clause:
|
|
1314
|
+
children.append(self.offset_clause)
|
|
1315
|
+
return children
|
|
1316
|
+
|
|
1317
|
+
|
|
1318
|
+
@dataclass(frozen=True)
|
|
1319
|
+
class CTEClause(SQLNode):
|
|
1320
|
+
"""Common Table Expression (WITH clause).
|
|
1321
|
+
|
|
1322
|
+
Attributes:
|
|
1323
|
+
name: CTE name.
|
|
1324
|
+
query: The CTE query.
|
|
1325
|
+
columns: Optional column names.
|
|
1326
|
+
recursive: Whether this is a recursive CTE.
|
|
1327
|
+
"""
|
|
1328
|
+
|
|
1329
|
+
name: str
|
|
1330
|
+
query: SelectStatement
|
|
1331
|
+
columns: tuple[str, ...] | None = None
|
|
1332
|
+
recursive: bool = False
|
|
1333
|
+
|
|
1334
|
+
def __init__(
|
|
1335
|
+
self,
|
|
1336
|
+
name: str,
|
|
1337
|
+
query: SelectStatement,
|
|
1338
|
+
columns: Sequence[str] | None = None,
|
|
1339
|
+
recursive: bool = False,
|
|
1340
|
+
):
|
|
1341
|
+
object.__setattr__(self, "name", name)
|
|
1342
|
+
object.__setattr__(self, "query", query)
|
|
1343
|
+
object.__setattr__(self, "columns", tuple(columns) if columns else None)
|
|
1344
|
+
object.__setattr__(self, "recursive", recursive)
|
|
1345
|
+
|
|
1346
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
1347
|
+
return visitor.visit_cte_clause(self)
|
|
1348
|
+
|
|
1349
|
+
def children(self) -> Sequence[SQLNode]:
|
|
1350
|
+
return [self.query]
|
|
1351
|
+
|
|
1352
|
+
|
|
1353
|
+
@dataclass(frozen=True)
|
|
1354
|
+
class SetOperationStatement(Statement):
|
|
1355
|
+
"""A set operation (UNION, INTERSECT, EXCEPT).
|
|
1356
|
+
|
|
1357
|
+
Attributes:
|
|
1358
|
+
left: Left query.
|
|
1359
|
+
right: Right query.
|
|
1360
|
+
operation: Set operation type.
|
|
1361
|
+
"""
|
|
1362
|
+
|
|
1363
|
+
left: SelectStatement | "SetOperationStatement"
|
|
1364
|
+
right: SelectStatement | "SetOperationStatement"
|
|
1365
|
+
operation: SetOperation
|
|
1366
|
+
|
|
1367
|
+
def accept(self, visitor: "SQLVisitor") -> Any:
|
|
1368
|
+
return visitor.visit_set_operation(self)
|
|
1369
|
+
|
|
1370
|
+
def children(self) -> Sequence[SQLNode]:
|
|
1371
|
+
return [self.left, self.right]
|
|
1372
|
+
|
|
1373
|
+
|
|
1374
|
+
# =============================================================================
|
|
1375
|
+
# Visitor Pattern
|
|
1376
|
+
# =============================================================================
|
|
1377
|
+
|
|
1378
|
+
|
|
1379
|
+
class SQLVisitor(ABC):
|
|
1380
|
+
"""Abstract visitor for SQL AST traversal.
|
|
1381
|
+
|
|
1382
|
+
Implement this class to transform or analyze SQL AST nodes.
|
|
1383
|
+
"""
|
|
1384
|
+
|
|
1385
|
+
@abstractmethod
|
|
1386
|
+
def visit_literal(self, node: Literal) -> Any:
|
|
1387
|
+
pass
|
|
1388
|
+
|
|
1389
|
+
@abstractmethod
|
|
1390
|
+
def visit_null_literal(self, node: NullLiteral) -> Any:
|
|
1391
|
+
pass
|
|
1392
|
+
|
|
1393
|
+
@abstractmethod
|
|
1394
|
+
def visit_boolean_literal(self, node: BooleanLiteral) -> Any:
|
|
1395
|
+
pass
|
|
1396
|
+
|
|
1397
|
+
@abstractmethod
|
|
1398
|
+
def visit_array_literal(self, node: ArrayLiteral) -> Any:
|
|
1399
|
+
pass
|
|
1400
|
+
|
|
1401
|
+
@abstractmethod
|
|
1402
|
+
def visit_identifier(self, node: Identifier) -> Any:
|
|
1403
|
+
pass
|
|
1404
|
+
|
|
1405
|
+
@abstractmethod
|
|
1406
|
+
def visit_column(self, node: Column) -> Any:
|
|
1407
|
+
pass
|
|
1408
|
+
|
|
1409
|
+
@abstractmethod
|
|
1410
|
+
def visit_table(self, node: Table) -> Any:
|
|
1411
|
+
pass
|
|
1412
|
+
|
|
1413
|
+
@abstractmethod
|
|
1414
|
+
def visit_alias(self, node: Alias) -> Any:
|
|
1415
|
+
pass
|
|
1416
|
+
|
|
1417
|
+
@abstractmethod
|
|
1418
|
+
def visit_star(self, node: Star) -> Any:
|
|
1419
|
+
pass
|
|
1420
|
+
|
|
1421
|
+
@abstractmethod
|
|
1422
|
+
def visit_binary_expression(self, node: BinaryExpression) -> Any:
|
|
1423
|
+
pass
|
|
1424
|
+
|
|
1425
|
+
@abstractmethod
|
|
1426
|
+
def visit_unary_expression(self, node: UnaryExpression) -> Any:
|
|
1427
|
+
pass
|
|
1428
|
+
|
|
1429
|
+
@abstractmethod
|
|
1430
|
+
def visit_in_expression(self, node: InExpression) -> Any:
|
|
1431
|
+
pass
|
|
1432
|
+
|
|
1433
|
+
@abstractmethod
|
|
1434
|
+
def visit_between_expression(self, node: BetweenExpression) -> Any:
|
|
1435
|
+
pass
|
|
1436
|
+
|
|
1437
|
+
@abstractmethod
|
|
1438
|
+
def visit_exists_expression(self, node: ExistsExpression) -> Any:
|
|
1439
|
+
pass
|
|
1440
|
+
|
|
1441
|
+
@abstractmethod
|
|
1442
|
+
def visit_subquery_expression(self, node: SubqueryExpression) -> Any:
|
|
1443
|
+
pass
|
|
1444
|
+
|
|
1445
|
+
@abstractmethod
|
|
1446
|
+
def visit_cast_expression(self, node: CastExpression) -> Any:
|
|
1447
|
+
pass
|
|
1448
|
+
|
|
1449
|
+
@abstractmethod
|
|
1450
|
+
def visit_when_clause(self, node: WhenClause) -> Any:
|
|
1451
|
+
pass
|
|
1452
|
+
|
|
1453
|
+
@abstractmethod
|
|
1454
|
+
def visit_case_expression(self, node: CaseExpression) -> Any:
|
|
1455
|
+
pass
|
|
1456
|
+
|
|
1457
|
+
@abstractmethod
|
|
1458
|
+
def visit_function_call(self, node: FunctionCall) -> Any:
|
|
1459
|
+
pass
|
|
1460
|
+
|
|
1461
|
+
@abstractmethod
|
|
1462
|
+
def visit_aggregate_function(self, node: AggregateFunction) -> Any:
|
|
1463
|
+
pass
|
|
1464
|
+
|
|
1465
|
+
@abstractmethod
|
|
1466
|
+
def visit_frame_bound(self, node: FrameBound) -> Any:
|
|
1467
|
+
pass
|
|
1468
|
+
|
|
1469
|
+
@abstractmethod
|
|
1470
|
+
def visit_window_spec(self, node: WindowSpec) -> Any:
|
|
1471
|
+
pass
|
|
1472
|
+
|
|
1473
|
+
@abstractmethod
|
|
1474
|
+
def visit_window_function(self, node: WindowFunction) -> Any:
|
|
1475
|
+
pass
|
|
1476
|
+
|
|
1477
|
+
@abstractmethod
|
|
1478
|
+
def visit_select_item(self, node: SelectItem) -> Any:
|
|
1479
|
+
pass
|
|
1480
|
+
|
|
1481
|
+
@abstractmethod
|
|
1482
|
+
def visit_from_clause(self, node: FromClause) -> Any:
|
|
1483
|
+
pass
|
|
1484
|
+
|
|
1485
|
+
@abstractmethod
|
|
1486
|
+
def visit_join_clause(self, node: JoinClause) -> Any:
|
|
1487
|
+
pass
|
|
1488
|
+
|
|
1489
|
+
@abstractmethod
|
|
1490
|
+
def visit_where_clause(self, node: WhereClause) -> Any:
|
|
1491
|
+
pass
|
|
1492
|
+
|
|
1493
|
+
@abstractmethod
|
|
1494
|
+
def visit_group_by_clause(self, node: GroupByClause) -> Any:
|
|
1495
|
+
pass
|
|
1496
|
+
|
|
1497
|
+
@abstractmethod
|
|
1498
|
+
def visit_having_clause(self, node: HavingClause) -> Any:
|
|
1499
|
+
pass
|
|
1500
|
+
|
|
1501
|
+
@abstractmethod
|
|
1502
|
+
def visit_order_by_item(self, node: OrderByItem) -> Any:
|
|
1503
|
+
pass
|
|
1504
|
+
|
|
1505
|
+
@abstractmethod
|
|
1506
|
+
def visit_order_by_clause(self, node: OrderByClause) -> Any:
|
|
1507
|
+
pass
|
|
1508
|
+
|
|
1509
|
+
@abstractmethod
|
|
1510
|
+
def visit_limit_clause(self, node: LimitClause) -> Any:
|
|
1511
|
+
pass
|
|
1512
|
+
|
|
1513
|
+
@abstractmethod
|
|
1514
|
+
def visit_offset_clause(self, node: OffsetClause) -> Any:
|
|
1515
|
+
pass
|
|
1516
|
+
|
|
1517
|
+
@abstractmethod
|
|
1518
|
+
def visit_select_statement(self, node: SelectStatement) -> Any:
|
|
1519
|
+
pass
|
|
1520
|
+
|
|
1521
|
+
@abstractmethod
|
|
1522
|
+
def visit_cte_clause(self, node: CTEClause) -> Any:
|
|
1523
|
+
pass
|
|
1524
|
+
|
|
1525
|
+
@abstractmethod
|
|
1526
|
+
def visit_set_operation(self, node: SetOperationStatement) -> Any:
|
|
1527
|
+
pass
|
|
1528
|
+
|
|
1529
|
+
|
|
1530
|
+
# =============================================================================
|
|
1531
|
+
# Helper Functions
|
|
1532
|
+
# =============================================================================
|
|
1533
|
+
|
|
1534
|
+
|
|
1535
|
+
def _to_expression(value: Any) -> Expression:
|
|
1536
|
+
"""Convert a value to an Expression.
|
|
1537
|
+
|
|
1538
|
+
Args:
|
|
1539
|
+
value: Value to convert.
|
|
1540
|
+
|
|
1541
|
+
Returns:
|
|
1542
|
+
Expression representing the value.
|
|
1543
|
+
"""
|
|
1544
|
+
if isinstance(value, Expression):
|
|
1545
|
+
return value
|
|
1546
|
+
if value is None:
|
|
1547
|
+
return NullLiteral()
|
|
1548
|
+
if isinstance(value, bool):
|
|
1549
|
+
return BooleanLiteral(value)
|
|
1550
|
+
return Literal(value)
|