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