dataface 0.1.2__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.
- d3_format/__init__.py +14 -0
- d3_format/errors.py +19 -0
- d3_format/format.py +551 -0
- d3_format/spec.py +159 -0
- dataface/DATAFACE_SYNTAX.md +1135 -0
- dataface/__init__.py +93 -0
- dataface/_docs_site.py +20 -0
- dataface/_install_hint.py +26 -0
- dataface/agent_api/__init__.py +79 -0
- dataface/agent_api/_init_templates/__init__.py +0 -0
- dataface/agent_api/_init_templates/agents_dft_snippet.md +26 -0
- dataface/agent_api/_init_templates/dataface.yml +15 -0
- dataface/agent_api/_init_templates/faces-dataface.yml +144 -0
- dataface/agent_api/_init_templates/index.md +24 -0
- dataface/agent_api/_paths.py +118 -0
- dataface/agent_api/_project_agents_md.py +43 -0
- dataface/agent_api/_session_store.py +486 -0
- dataface/agent_api/_state.py +28 -0
- dataface/agent_api/chat.py +221 -0
- dataface/agent_api/dashboards.py +257 -0
- dataface/agent_api/describe.py +366 -0
- dataface/agent_api/describe_query.py +120 -0
- dataface/agent_api/docs/__init__.py +25 -0
- dataface/agent_api/docs/_loader.py +292 -0
- dataface/agent_api/docs/yaml-reference.md +2757 -0
- dataface/agent_api/file_refs.py +118 -0
- dataface/agent_api/init.py +126 -0
- dataface/agent_api/inspect.py +128 -0
- dataface/agent_api/mcp_install.py +170 -0
- dataface/agent_api/query.py +274 -0
- dataface/agent_api/schema.py +658 -0
- dataface/agent_api/schema_search.py +284 -0
- dataface/agent_api/search.py +270 -0
- dataface/agent_api/skill_install.py +141 -0
- dataface/agent_api/skill_render.py +90 -0
- dataface/agent_api/skills.py +293 -0
- dataface/agent_api/surface_aliases.yaml +128 -0
- dataface/agent_api/validate.py +175 -0
- dataface/agent_api/validate_query.py +84 -0
- dataface/ai/__init__.py +39 -0
- dataface/ai/agent.py +139 -0
- dataface/ai/context.py +45 -0
- dataface/ai/events.py +62 -0
- dataface/ai/external_mcp.py +610 -0
- dataface/ai/generate_sql.py +96 -0
- dataface/ai/llm.py +403 -0
- dataface/ai/mcp/__init__.py +51 -0
- dataface/ai/mcp/server.py +289 -0
- dataface/ai/memories.py +85 -0
- dataface/ai/prompts.py +177 -0
- dataface/ai/schema_context.py +138 -0
- dataface/ai/skills/before-after-comparison/SKILL.md +102 -0
- dataface/ai/skills/before-after-comparison/examples/before-after-comparison.yml +24 -0
- dataface/ai/skills/dashboard-build/SKILL.md +212 -0
- dataface/ai/skills/dashboard-build/examples/_smoke.yml +15 -0
- dataface/ai/skills/dashboard-design/SKILL.md +182 -0
- dataface/ai/skills/dashboard-review/SKILL.md +113 -0
- dataface/ai/skills/dashboard-structural-review/SKILL.md +173 -0
- dataface/ai/skills/dashboard-visual-review/SKILL.md +139 -0
- dataface/ai/skills/dataface-mcp-setup/SKILL.md +177 -0
- dataface/ai/skills/dataface-troubleshooting/SKILL.md +225 -0
- dataface/ai/skills/drill-down-link/SKILL.md +112 -0
- dataface/ai/skills/drill-down-link/examples/drill-down-link.yml +27 -0
- dataface/ai/skills/faceted-small-multiples/SKILL.md +116 -0
- dataface/ai/skills/faceted-small-multiples/examples/faceted-small-multiples.yml +33 -0
- dataface/ai/skills/filter-bar-with-variables/SKILL.md +105 -0
- dataface/ai/skills/filter-bar-with-variables/examples/filter-bar-with-variables.yml +49 -0
- dataface/ai/skills/kpi-row/SKILL.md +101 -0
- dataface/ai/skills/kpi-row/examples/kpi-row.yml +55 -0
- dataface/ai/skills/report-design/SKILL.md +184 -0
- dataface/ai/skills/single-metric-bignum/SKILL.md +90 -0
- dataface/ai/skills/single-metric-bignum/examples/single-metric-bignum.yml +27 -0
- dataface/ai/skills/table-heavy-ops-dashboard/SKILL.md +114 -0
- dataface/ai/skills/table-heavy-ops-dashboard/examples/table-heavy-ops-dashboard.yml +48 -0
- dataface/ai/skills/time-series-trend/SKILL.md +93 -0
- dataface/ai/skills/time-series-trend/examples/time-series-trend.yml +26 -0
- dataface/ai/skills/top-n-with-detail/SKILL.md +98 -0
- dataface/ai/skills/top-n-with-detail/examples/top-n-with-detail.yml +45 -0
- dataface/ai/skills/two-by-two-grid-overview/SKILL.md +78 -0
- dataface/ai/skills/two-by-two-grid-overview/examples/two-by-two-grid-overview.yml +64 -0
- dataface/ai/tool_schemas.py +132 -0
- dataface/ai/tools/__init__.py +312 -0
- dataface/ai/yaml_utils.py +57 -0
- dataface/cli/__init__.py +3 -0
- dataface/cli/_console.py +48 -0
- dataface/cli/_error_format.py +83 -0
- dataface/cli/_extras.py +190 -0
- dataface/cli/_json_output.py +8 -0
- dataface/cli/_parsing.py +17 -0
- dataface/cli/_version_info.py +56 -0
- dataface/cli/commands/__init__.py +3 -0
- dataface/cli/commands/_agent_input.py +205 -0
- dataface/cli/commands/_agent_server.py +115 -0
- dataface/cli/commands/chat.py +645 -0
- dataface/cli/commands/describe.py +107 -0
- dataface/cli/commands/docs.py +131 -0
- dataface/cli/commands/extension.py +179 -0
- dataface/cli/commands/init.py +240 -0
- dataface/cli/commands/inspect.py +94 -0
- dataface/cli/commands/mcp_init.py +167 -0
- dataface/cli/commands/query.py +386 -0
- dataface/cli/commands/render.py +291 -0
- dataface/cli/commands/schema.py +411 -0
- dataface/cli/commands/search.py +49 -0
- dataface/cli/commands/serve.py +114 -0
- dataface/cli/commands/skills.py +133 -0
- dataface/cli/commands/skills_init.py +161 -0
- dataface/cli/commands/validate.py +63 -0
- dataface/cli/main.py +1501 -0
- dataface/core/__init__.py +75 -0
- dataface/core/compile/__init__.py +244 -0
- dataface/core/compile/_jinja_helpers.py +78 -0
- dataface/core/compile/channel.py +222 -0
- dataface/core/compile/chart_focus.py +101 -0
- dataface/core/compile/chart_resolved.py +169 -0
- dataface/core/compile/chart_type_detection.py +489 -0
- dataface/core/compile/chart_update.py +261 -0
- dataface/core/compile/colors.py +64 -0
- dataface/core/compile/compiler.py +904 -0
- dataface/core/compile/config.py +823 -0
- dataface/core/compile/custom_chart_types.py +208 -0
- dataface/core/compile/data_table_attachment.py +1287 -0
- dataface/core/compile/detect.py +110 -0
- dataface/core/compile/errors.py +302 -0
- dataface/core/compile/filter_injection.py +319 -0
- dataface/core/compile/introspection.py +527 -0
- dataface/core/compile/jinja.py +511 -0
- dataface/core/compile/labels_env.py +52 -0
- dataface/core/compile/markdown.py +154 -0
- dataface/core/compile/meta.py +388 -0
- dataface/core/compile/models/__init__.py +0 -0
- dataface/core/compile/models/chart/__init__.py +0 -0
- dataface/core/compile/models/chart/authored.py +2137 -0
- dataface/core/compile/models/chart/compiled.py +398 -0
- dataface/core/compile/models/config.py +347 -0
- dataface/core/compile/models/face/__init__.py +0 -0
- dataface/core/compile/models/face/authored.py +659 -0
- dataface/core/compile/models/face/compiled.py +522 -0
- dataface/core/compile/models/factories.py +201 -0
- dataface/core/compile/models/markers.py +40 -0
- dataface/core/compile/models/palette.py +36 -0
- dataface/core/compile/models/primitives.py +415 -0
- dataface/core/compile/models/query/__init__.py +0 -0
- dataface/core/compile/models/query/authored.py +246 -0
- dataface/core/compile/models/query/compiled.py +710 -0
- dataface/core/compile/models/refs.py +137 -0
- dataface/core/compile/models/source.py +611 -0
- dataface/core/compile/models/style/__init__.py +0 -0
- dataface/core/compile/models/style/authored.py +481 -0
- dataface/core/compile/models/style/compiled.py +3399 -0
- dataface/core/compile/models/style/merged.py +1682 -0
- dataface/core/compile/models/theme.py +362 -0
- dataface/core/compile/models/variable/__init__.py +0 -0
- dataface/core/compile/models/variable/authored.py +254 -0
- dataface/core/compile/models/vega_lite/__init__.py +0 -0
- dataface/core/compile/models/vega_lite/config.py +510 -0
- dataface/core/compile/models/vega_lite/contracts.py +171 -0
- dataface/core/compile/normalize_charts.py +494 -0
- dataface/core/compile/normalize_layout.py +1000 -0
- dataface/core/compile/normalize_queries.py +297 -0
- dataface/core/compile/normalize_variables.py +489 -0
- dataface/core/compile/normalizer.py +543 -0
- dataface/core/compile/palette.py +1100 -0
- dataface/core/compile/parameterized.py +658 -0
- dataface/core/compile/parser.py +228 -0
- dataface/core/compile/schema.py +20 -0
- dataface/core/compile/schema_renderers/__init__.py +0 -0
- dataface/core/compile/schema_renderers/json_schema.py +163 -0
- dataface/core/compile/schema_renderers/prompt.py +152 -0
- dataface/core/compile/schema_renderers/vscode_schema.py +301 -0
- dataface/core/compile/sizing.py +2126 -0
- dataface/core/compile/sources.py +518 -0
- dataface/core/compile/sql_authoring_lint.py +56 -0
- dataface/core/compile/style_cascade.py +471 -0
- dataface/core/compile/typography.py +299 -0
- dataface/core/compile/validator.py +301 -0
- dataface/core/compile/variables.py +53 -0
- dataface/core/compile/vega_config.py +98 -0
- dataface/core/compile/vega_lite/__init__.py +6 -0
- dataface/core/compile/vega_lite/validation.py +95 -0
- dataface/core/compile/yaml_error_formatter.py +838 -0
- dataface/core/connections.py +38 -0
- dataface/core/dashboard.py +358 -0
- dataface/core/defaults/default_config.yml +101 -0
- dataface/core/defaults/palettes/categorical/category-10-dark.yml +32 -0
- dataface/core/defaults/palettes/categorical/category-10-light.yml +43 -0
- dataface/core/defaults/palettes/categorical/category-10.yml +31 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-blue.yml +22 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-brown.yml +29 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-green.yml +20 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-orange.yml +21 -0
- dataface/core/defaults/palettes/categorical/category-6-tonal-purple.yml +20 -0
- dataface/core/defaults/palettes/categorical/editorial-10-dark.yml +32 -0
- dataface/core/defaults/palettes/categorical/editorial-10.yml +40 -0
- dataface/core/defaults/palettes/categorical/hero-6.yml +17 -0
- dataface/core/defaults/palettes/categorical/single-blue.yml +11 -0
- dataface/core/defaults/palettes/categorical/tableau.yml +20 -0
- dataface/core/defaults/palettes/data/xkcd_colors.json +3803 -0
- dataface/core/defaults/palettes/diverging/blue-red.yml +25 -0
- dataface/core/defaults/palettes/diverging/coolwarm.yml +24 -0
- dataface/core/defaults/palettes/diverging/crimson-green.yml +23 -0
- dataface/core/defaults/palettes/diverging/orange-teal.yml +23 -0
- dataface/core/defaults/palettes/diverging/sunset.yml +24 -0
- dataface/core/defaults/palettes/scaffold/dft-creams.yml +38 -0
- dataface/core/defaults/palettes/scaffold/dft-grays.yml +53 -0
- dataface/core/defaults/palettes/sequential/amber.yml +22 -0
- dataface/core/defaults/palettes/sequential/blue.yml +22 -0
- dataface/core/defaults/palettes/sequential/brown.yml +22 -0
- dataface/core/defaults/palettes/sequential/gray.yml +22 -0
- dataface/core/defaults/palettes/sequential/green.yml +22 -0
- dataface/core/defaults/palettes/sequential/purple.yml +22 -0
- dataface/core/defaults/palettes/sequential/rust.yml +22 -0
- dataface/core/defaults/palettes/sequential/teal.yml +22 -0
- dataface/core/defaults/palettes/tone/negative.yml +32 -0
- dataface/core/defaults/palettes/tone/positive.yml +22 -0
- dataface/core/defaults/palettes/tone/warning.yml +22 -0
- dataface/core/defaults/themes/_base.yaml +786 -0
- dataface/core/defaults/themes/bi.yaml +16 -0
- dataface/core/defaults/themes/carbong100.yaml +41 -0
- dataface/core/defaults/themes/cream.yaml +122 -0
- dataface/core/defaults/themes/dark.yaml +40 -0
- dataface/core/defaults/themes/diagnostics-title-angle-extreme.yaml +9 -0
- dataface/core/defaults/themes/diagnostics-title-baseline-extreme.yaml +9 -0
- dataface/core/defaults/themes/diagnostics-title-baseline.yaml +24 -0
- dataface/core/defaults/themes/diagnostics-title-center.yaml +8 -0
- dataface/core/defaults/themes/diagnostics-title-color-extreme.yaml +24 -0
- dataface/core/defaults/themes/diagnostics-title-font-extreme.yaml +25 -0
- dataface/core/defaults/themes/diagnostics-title-left.yaml +8 -0
- dataface/core/defaults/themes/diagnostics-title-offset-extreme.yaml +9 -0
- dataface/core/defaults/themes/diagnostics-title-size-extreme.yaml +24 -0
- dataface/core/defaults/themes/diagnostics-title-weight-extreme.yaml +24 -0
- dataface/core/defaults/themes/editorial.yaml +147 -0
- dataface/core/defaults/themes/light.yaml +30 -0
- dataface/core/defaults/themes/looker.yaml +17 -0
- dataface/core/defaults/themes/stark.yaml +134 -0
- dataface/core/errors/__init__.py +67 -0
- dataface/core/errors/codes_compile.py +56 -0
- dataface/core/errors/codes_execute.py +177 -0
- dataface/core/errors/codes_render.py +106 -0
- dataface/core/errors/codes_unknown.py +15 -0
- dataface/core/errors/hints.py +74 -0
- dataface/core/errors/registry.py +42 -0
- dataface/core/errors/structured.py +92 -0
- dataface/core/execute/__init__.py +91 -0
- dataface/core/execute/adapters/__init__.py +49 -0
- dataface/core/execute/adapters/adapter_registry.py +400 -0
- dataface/core/execute/adapters/base.py +245 -0
- dataface/core/execute/adapters/csv_adapter.py +239 -0
- dataface/core/execute/adapters/dbt_adapter.py +283 -0
- dataface/core/execute/adapters/dbt_adapter_factory.py +212 -0
- dataface/core/execute/adapters/dbt_macro_loader.py +95 -0
- dataface/core/execute/adapters/dbt_utils.py +150 -0
- dataface/core/execute/adapters/http_adapter.py +224 -0
- dataface/core/execute/adapters/metricflow_adapter.py +94 -0
- dataface/core/execute/adapters/schema_resolver_adapter.py +144 -0
- dataface/core/execute/adapters/sql_adapter.py +710 -0
- dataface/core/execute/adapters/values_adapter.py +58 -0
- dataface/core/execute/batch.py +744 -0
- dataface/core/execute/cache_backend.py +135 -0
- dataface/core/execute/cache_keys.py +66 -0
- dataface/core/execute/dbt_jinja.py +21 -0
- dataface/core/execute/dialects/__init__.py +121 -0
- dataface/core/execute/dialects/athena.py +75 -0
- dataface/core/execute/dialects/base.py +302 -0
- dataface/core/execute/dialects/bigquery.py +38 -0
- dataface/core/execute/dialects/databricks.py +68 -0
- dataface/core/execute/dialects/duckdb.py +35 -0
- dataface/core/execute/dialects/mysql.py +68 -0
- dataface/core/execute/dialects/postgres.py +39 -0
- dataface/core/execute/dialects/redshift.py +12 -0
- dataface/core/execute/dialects/snowflake.py +51 -0
- dataface/core/execute/dialects/sqlserver.py +92 -0
- dataface/core/execute/duckdb_cache.py +712 -0
- dataface/core/execute/duckdb_config.py +26 -0
- dataface/core/execute/errors.py +213 -0
- dataface/core/execute/executor.py +1249 -0
- dataface/core/execute/parallel.py +162 -0
- dataface/core/execute/setup_sql.py +58 -0
- dataface/core/execute/source_registry.py +72 -0
- dataface/core/execute/source_resolver.py +255 -0
- dataface/core/execute/sql_guard.py +387 -0
- dataface/core/execute/sql_literals.py +199 -0
- dataface/core/fonts.py +52 -0
- dataface/core/inspect/__init__.py +32 -0
- dataface/core/inspect/cache_factory.py +98 -0
- dataface/core/inspect/db_types.py +162 -0
- dataface/core/inspect/dbt_schema.py +96 -0
- dataface/core/inspect/defaults.yml +37 -0
- dataface/core/inspect/fanout_risk.py +109 -0
- dataface/core/inspect/manifest_utils.py +77 -0
- dataface/core/inspect/partials/categorical.yml +40 -0
- dataface/core/inspect/partials/date.yml +40 -0
- dataface/core/inspect/partials/numeric.yml +55 -0
- dataface/core/inspect/partition_types.py +38 -0
- dataface/core/inspect/query_validator.py +975 -0
- dataface/core/inspect/renderer.py +354 -0
- dataface/core/inspect/resolver.py +808 -0
- dataface/core/inspect/search.py +461 -0
- dataface/core/inspect/sources/__init__.py +32 -0
- dataface/core/inspect/sources/dbt.py +738 -0
- dataface/core/inspect/sources/duckdb_utils.py +66 -0
- dataface/core/inspect/templates/__init__.py +1 -0
- dataface/core/inspect/templates/categorical_column.yml +196 -0
- dataface/core/inspect/templates/charts.yml +109 -0
- dataface/core/inspect/templates/date_column.yml +248 -0
- dataface/core/inspect/templates/model.yml +138 -0
- dataface/core/inspect/templates/numeric_column.yml +261 -0
- dataface/core/inspect/templates/quality.yml +80 -0
- dataface/core/inspect/templates/string_column.yml +263 -0
- dataface/core/project_roots.py +165 -0
- dataface/core/render/__init__.py +87 -0
- dataface/core/render/board_links.py +176 -0
- dataface/core/render/chart/__init__.py +27 -0
- dataface/core/render/chart/arc_attached_table.py +251 -0
- dataface/core/render/chart/artifacts.py +16 -0
- dataface/core/render/chart/callout.py +225 -0
- dataface/core/render/chart/decisions.py +358 -0
- dataface/core/render/chart/geo.py +700 -0
- dataface/core/render/chart/kpi.py +916 -0
- dataface/core/render/chart/labels.py +76 -0
- dataface/core/render/chart/pipeline.py +818 -0
- dataface/core/render/chart/presentation.py +36 -0
- dataface/core/render/chart/profile.py +3438 -0
- dataface/core/render/chart/render_single.py +347 -0
- dataface/core/render/chart/renderers.py +193 -0
- dataface/core/render/chart/rendering.py +565 -0
- dataface/core/render/chart/serialization.py +90 -0
- dataface/core/render/chart/spark.py +496 -0
- dataface/core/render/chart/spark_bar.py +370 -0
- dataface/core/render/chart/spec_builders.py +154 -0
- dataface/core/render/chart/standard_renderer.py +2645 -0
- dataface/core/render/chart/table.py +2957 -0
- dataface/core/render/chart/table_support.py +1452 -0
- dataface/core/render/chart/tick_values.py +66 -0
- dataface/core/render/chart/time_unit_detect.py +809 -0
- dataface/core/render/chart/title_overflow.py +157 -0
- dataface/core/render/chart/type_inference.py +122 -0
- dataface/core/render/chart/validation.py +99 -0
- dataface/core/render/chart/vega_lite.py +125 -0
- dataface/core/render/chart/vega_lite_types.py +268 -0
- dataface/core/render/chart/vl_field_maps.py +346 -0
- dataface/core/render/chart_interactivity.py +24 -0
- dataface/core/render/control_registry.py +287 -0
- dataface/core/render/converters/__init__.py +24 -0
- dataface/core/render/converters/chart.py +276 -0
- dataface/core/render/converters/html.py +98 -0
- dataface/core/render/converters/pdf.py +40 -0
- dataface/core/render/converters/png.py +41 -0
- dataface/core/render/errors.py +144 -0
- dataface/core/render/face_api.py +160 -0
- dataface/core/render/faces.py +1194 -0
- dataface/core/render/font_measurement.py +48 -0
- dataface/core/render/font_support.py +197 -0
- dataface/core/render/fonts/DFTSansTabular-Regular.ttf +0 -0
- dataface/core/render/fonts/DFTSansTabular-Regular.woff2 +0 -0
- dataface/core/render/fonts/DFTSerifOldstyleProportional-Regular.ttf +0 -0
- dataface/core/render/fonts/DFTSerifOldstyleTabular-Regular.ttf +0 -0
- dataface/core/render/fonts/InterVariable.ttf +0 -0
- dataface/core/render/fonts/InterVariable.woff2 +0 -0
- dataface/core/render/fonts/NOTO_COLOR_EMOJI_LICENSE.txt +93 -0
- dataface/core/render/fonts/NOTO_EMOJI_LICENSE.txt +93 -0
- dataface/core/render/fonts/NotoColorEmoji-Regular.ttf +0 -0
- dataface/core/render/fonts/NotoColorEmoji-Regular.woff2 +0 -0
- dataface/core/render/fonts/NotoEmoji-Regular.ttf +0 -0
- dataface/core/render/fonts/NotoEmoji-Regular.woff2 +0 -0
- dataface/core/render/fonts/SOURCE_CODE_PRO_LICENSE.txt +93 -0
- dataface/core/render/fonts/SOURCE_SERIF_4_LICENSE.txt +98 -0
- dataface/core/render/fonts/SourceCodePro-Regular.ttf +0 -0
- dataface/core/render/fonts/SourceSerif4-Regular.ttf +0 -0
- dataface/core/render/fonts/_emoji_font_face.css +43 -0
- dataface/core/render/fonts/source-serif-4-variable-latin.woff2 +0 -0
- dataface/core/render/format_utils.py +329 -0
- dataface/core/render/geo_defaults.yml +28 -0
- dataface/core/render/json_format.py +146 -0
- dataface/core/render/layout_sizing.py +865 -0
- dataface/core/render/layouts.py +541 -0
- dataface/core/render/markdown_defaults.yml +16 -0
- dataface/core/render/missing_vars_prompt.py +79 -0
- dataface/core/render/placeholder.py +389 -0
- dataface/core/render/render_result.py +14 -0
- dataface/core/render/renderer.py +467 -0
- dataface/core/render/script_embedding.py +16 -0
- dataface/core/render/svg_utils.py +212 -0
- dataface/core/render/template_loader.py +69 -0
- dataface/core/render/templates/controls/_styles.css +606 -0
- dataface/core/render/templates/controls/checkbox.html +16 -0
- dataface/core/render/templates/controls/date.html +16 -0
- dataface/core/render/templates/controls/number.html +19 -0
- dataface/core/render/templates/controls/readonly.html +9 -0
- dataface/core/render/templates/controls/select.html +21 -0
- dataface/core/render/templates/controls/slider.html +22 -0
- dataface/core/render/templates/controls/text.html +16 -0
- dataface/core/render/templates/scripts/chart_interactivity.js +191 -0
- dataface/core/render/templates/scripts/variables.js +976 -0
- dataface/core/render/templates/svg/grid_pattern.svg +3 -0
- dataface/core/render/templates/svg/styles.css +51 -0
- dataface/core/render/terminal.py +311 -0
- dataface/core/render/terminal_charts.py +563 -0
- dataface/core/render/terminal_defaults.yml +2 -0
- dataface/core/render/terminal_layouts.py +299 -0
- dataface/core/render/terminal_text.py +31 -0
- dataface/core/render/text/__init__.py +1 -0
- dataface/core/render/text/case.py +113 -0
- dataface/core/render/text_format.py +129 -0
- dataface/core/render/utils.py +106 -0
- dataface/core/render/variable_controls.py +946 -0
- dataface/core/render/variable_input_refinement.py +140 -0
- dataface/core/render/warnings/__init__.py +15 -0
- dataface/core/render/warnings/bar_color_1_to_1_with_x.py +80 -0
- dataface/core/render/warnings/base.py +44 -0
- dataface/core/render/warnings/fanout_risk.py +15 -0
- dataface/core/render/warnings/from_query_diagnostic.py +56 -0
- dataface/core/render/warnings/missing_join_predicate.py +13 -0
- dataface/core/render/warnings/query_parse_error.py +14 -0
- dataface/core/render/warnings/query_returned_zero_rows.py +42 -0
- dataface/core/render/warnings/reaggregation.py +14 -0
- dataface/core/render/warnings/registry.py +45 -0
- dataface/core/render/warnings/suppression.py +46 -0
- dataface/core/render/warnings/temporal_single_point.py +63 -0
- dataface/core/render/warnings/unreferenced_chart.py +15 -0
- dataface/core/render/warnings/y_encoding_mostly_null.py +76 -0
- dataface/core/render/yaml_format.py +167 -0
- dataface/core/resolve_face.py +195 -0
- dataface/core/schema/__init__.py +0 -0
- dataface/core/schema/guidance.py +151 -0
- dataface/core/scoped_paths.py +59 -0
- dataface/core/serve/__init__.py +14 -0
- dataface/core/serve/bootstrap.py +39 -0
- dataface/core/serve/embedded.py +57 -0
- dataface/core/serve/port.py +129 -0
- dataface/core/serve/server.py +938 -0
- dataface/core/serve/templates/__init__.py +0 -0
- dataface/core/serve/templates/directory.yml +6 -0
- dataface/core/serve/templates/error.html.j2 +217 -0
- dataface/core/utils.py +121 -0
- dataface/core/validate.py +64 -0
- dataface/integrations/__init__.py +0 -0
- dataface/integrations/highlighting.py +351 -0
- dataface/integrations/markdown.py +537 -0
- dataface/py.typed +0 -0
- dataface-0.1.2.dist-info/METADATA +375 -0
- dataface-0.1.2.dist-info/RECORD +455 -0
- dataface-0.1.2.dist-info/WHEEL +4 -0
- dataface-0.1.2.dist-info/entry_points.txt +2 -0
- dataface-0.1.2.dist-info/licenses/LICENSE +202 -0
- mdsvg/__init__.py +168 -0
- mdsvg/fonts.py +656 -0
- mdsvg/images.py +299 -0
- mdsvg/parser.py +629 -0
- mdsvg/playground.py +284 -0
- mdsvg/py.typed +2 -0
- mdsvg/renderer.py +1623 -0
- mdsvg/style.py +355 -0
- mdsvg/types.py +200 -0
- mdsvg/utils.py +86 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
"""Base SQL dialect interface for database-specific behavior.
|
|
2
|
+
|
|
3
|
+
Stage: EXECUTE
|
|
4
|
+
Purpose: Define the abstract interface for database-specific SQL generation.
|
|
5
|
+
|
|
6
|
+
This module provides the base SQLDialect class that all database-specific
|
|
7
|
+
dialects inherit from. Each dialect encapsulates its own quirks for:
|
|
8
|
+
- Temp table syntax
|
|
9
|
+
- Parameter placeholders
|
|
10
|
+
- Filter macros
|
|
11
|
+
- (Future) SQL transpilation
|
|
12
|
+
|
|
13
|
+
Rather than spreading conditionals throughout the codebase, each dialect
|
|
14
|
+
encapsulates its own behavior.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
from abc import ABC, abstractmethod
|
|
18
|
+
|
|
19
|
+
# Allowlist of valid SQL operators for filter() method
|
|
20
|
+
# This prevents SQL injection via operator parameter
|
|
21
|
+
VALID_OPERATORS = frozenset(
|
|
22
|
+
{
|
|
23
|
+
"=",
|
|
24
|
+
"!=",
|
|
25
|
+
"<>",
|
|
26
|
+
">",
|
|
27
|
+
"<",
|
|
28
|
+
">=",
|
|
29
|
+
"<=",
|
|
30
|
+
"LIKE",
|
|
31
|
+
"NOT LIKE",
|
|
32
|
+
"ILIKE",
|
|
33
|
+
"NOT ILIKE",
|
|
34
|
+
"IN",
|
|
35
|
+
"NOT IN",
|
|
36
|
+
"IS",
|
|
37
|
+
"IS NOT",
|
|
38
|
+
"BETWEEN",
|
|
39
|
+
"NOT BETWEEN",
|
|
40
|
+
"~",
|
|
41
|
+
"~*",
|
|
42
|
+
"!~",
|
|
43
|
+
"!~*", # PostgreSQL regex operators
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class SQLDialect(ABC):
|
|
49
|
+
"""Abstract base class for SQL dialect implementations.
|
|
50
|
+
|
|
51
|
+
Each database dialect inherits from this class and implements
|
|
52
|
+
database-specific behavior for SQL generation.
|
|
53
|
+
|
|
54
|
+
Attributes:
|
|
55
|
+
name: The canonical name of the dialect (e.g., 'postgres', 'mysql')
|
|
56
|
+
|
|
57
|
+
Example:
|
|
58
|
+
>>> dialect = PostgresDialect()
|
|
59
|
+
>>> dialect.param(1)
|
|
60
|
+
'$1'
|
|
61
|
+
>>> dialect.temp_table('tmp', 'SELECT 1')
|
|
62
|
+
'CREATE TEMP TABLE tmp AS SELECT 1'
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
name: str
|
|
66
|
+
|
|
67
|
+
# --- Temp Tables ---
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def supports_temp_tables(self) -> bool:
|
|
71
|
+
"""Check if the dialect supports temporary tables.
|
|
72
|
+
|
|
73
|
+
Returns:
|
|
74
|
+
True if temp tables are supported, False otherwise.
|
|
75
|
+
Default is True for most databases.
|
|
76
|
+
"""
|
|
77
|
+
return True
|
|
78
|
+
|
|
79
|
+
def temp_table(self, name: str, sql: str) -> str:
|
|
80
|
+
"""Generate CREATE TEMP TABLE statement.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
name: Name of the temporary table to create
|
|
84
|
+
sql: SQL query to populate the table
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
Complete CREATE TEMP TABLE statement
|
|
88
|
+
|
|
89
|
+
Raises:
|
|
90
|
+
NotImplementedError: If the dialect doesn't support temp tables
|
|
91
|
+
"""
|
|
92
|
+
return f"CREATE TEMP TABLE {name} AS {sql}"
|
|
93
|
+
|
|
94
|
+
def temp_table_ref(self, name: str) -> str:
|
|
95
|
+
"""Generate reference to a temporary table.
|
|
96
|
+
|
|
97
|
+
Override for dialects with special temp table naming (e.g., SQL Server #name).
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
name: Name of the temporary table
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
How to reference the temp table in SQL
|
|
104
|
+
"""
|
|
105
|
+
return name
|
|
106
|
+
|
|
107
|
+
def drop_temp_table(self, name: str) -> str:
|
|
108
|
+
"""Generate DROP TEMP TABLE statement.
|
|
109
|
+
|
|
110
|
+
Args:
|
|
111
|
+
name: Name of the temporary table to drop
|
|
112
|
+
|
|
113
|
+
Returns:
|
|
114
|
+
Complete DROP TABLE IF EXISTS statement
|
|
115
|
+
"""
|
|
116
|
+
return f"DROP TABLE IF EXISTS {self.temp_table_ref(name)}"
|
|
117
|
+
|
|
118
|
+
# --- Parameters ---
|
|
119
|
+
|
|
120
|
+
@abstractmethod
|
|
121
|
+
def param(self, index: int) -> str:
|
|
122
|
+
"""Generate parameter placeholder for the given index.
|
|
123
|
+
|
|
124
|
+
This is the only abstract method that must be implemented by all dialects,
|
|
125
|
+
as parameter syntax varies significantly between databases.
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
index: 1-based parameter index
|
|
129
|
+
|
|
130
|
+
Returns:
|
|
131
|
+
Parameter placeholder string (e.g., '$1', '?', '%s', '@p1')
|
|
132
|
+
"""
|
|
133
|
+
pass
|
|
134
|
+
|
|
135
|
+
def params(self, count: int) -> list[str]:
|
|
136
|
+
"""Generate a list of parameter placeholders.
|
|
137
|
+
|
|
138
|
+
Args:
|
|
139
|
+
count: Number of parameters needed
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
List of parameter placeholders (e.g., ['$1', '$2', '$3'])
|
|
143
|
+
"""
|
|
144
|
+
return [self.param(i) for i in range(1, count + 1)]
|
|
145
|
+
|
|
146
|
+
# --- Filters ---
|
|
147
|
+
|
|
148
|
+
def filter(self, column: str, operator: str, param_index: int) -> str:
|
|
149
|
+
"""Generate filter expression with parameterized value.
|
|
150
|
+
|
|
151
|
+
This is the primary filter method that matches the Jinja filter macro:
|
|
152
|
+
{{ filter('column', '=', variable) }}
|
|
153
|
+
|
|
154
|
+
For null-safe filtering (same SQL regardless of value), use
|
|
155
|
+
filter_nullable() instead.
|
|
156
|
+
|
|
157
|
+
Security note: The operator is validated against an allowlist to prevent
|
|
158
|
+
SQL injection. Column names should come from schema definitions, not
|
|
159
|
+
user input.
|
|
160
|
+
|
|
161
|
+
Args:
|
|
162
|
+
column: Column name to filter on (must be from schema, not user input)
|
|
163
|
+
operator: SQL operator (=, !=, >, <, >=, <=, LIKE, IN, etc.)
|
|
164
|
+
param_index: Parameter index for the value
|
|
165
|
+
|
|
166
|
+
Returns:
|
|
167
|
+
SQL expression (e.g., 'column = $1', 'column > $1')
|
|
168
|
+
|
|
169
|
+
Raises:
|
|
170
|
+
ValueError: If operator is not in the allowlist
|
|
171
|
+
|
|
172
|
+
Example:
|
|
173
|
+
>>> dialect = PostgresDialect()
|
|
174
|
+
>>> dialect.filter('country', '=', 1)
|
|
175
|
+
'country = $1'
|
|
176
|
+
>>> dialect.filter('price', '>', 2)
|
|
177
|
+
'price > $2'
|
|
178
|
+
"""
|
|
179
|
+
# Validate operator to prevent SQL injection
|
|
180
|
+
op_upper = operator.upper().strip()
|
|
181
|
+
if op_upper not in VALID_OPERATORS:
|
|
182
|
+
raise ValueError(
|
|
183
|
+
f"Invalid SQL operator: {operator!r}. "
|
|
184
|
+
f"Must be one of: {', '.join(sorted(VALID_OPERATORS))}"
|
|
185
|
+
)
|
|
186
|
+
return f"{column} {operator} {self.param(param_index)}"
|
|
187
|
+
|
|
188
|
+
def filter_nullable(self, column: str, operator: str, param_index: int) -> str:
|
|
189
|
+
"""Generate null-safe filter expression.
|
|
190
|
+
|
|
191
|
+
Produces SQL that handles NULL parameter values gracefully, allowing
|
|
192
|
+
the same query string to be used regardless of whether filtering is
|
|
193
|
+
applied. This enables query plan caching.
|
|
194
|
+
|
|
195
|
+
When the parameter is NULL, all rows match (no filtering).
|
|
196
|
+
When the parameter has a value, normal filtering applies.
|
|
197
|
+
|
|
198
|
+
Pattern: (param IS NULL OR column operator param)
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
column: Column name to filter on (must be from schema, not user input)
|
|
202
|
+
operator: SQL operator (=, !=, >, <, >=, <=, LIKE, etc.)
|
|
203
|
+
param_index: Parameter index for the value
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
SQL expression like '($1 IS NULL OR column = $1)'
|
|
207
|
+
|
|
208
|
+
Raises:
|
|
209
|
+
ValueError: If operator is not in the allowlist
|
|
210
|
+
|
|
211
|
+
Example:
|
|
212
|
+
>>> dialect = PostgresDialect()
|
|
213
|
+
>>> dialect.filter_nullable('country', '=', 1)
|
|
214
|
+
'($1 IS NULL OR country = $1)'
|
|
215
|
+
|
|
216
|
+
# With param='USA': filters to USA
|
|
217
|
+
# With param=NULL: ($1 IS NULL) is true, all rows match
|
|
218
|
+
"""
|
|
219
|
+
# Validate operator to prevent SQL injection
|
|
220
|
+
op_upper = operator.upper().strip()
|
|
221
|
+
if op_upper not in VALID_OPERATORS:
|
|
222
|
+
raise ValueError(
|
|
223
|
+
f"Invalid SQL operator: {operator!r}. "
|
|
224
|
+
f"Must be one of: {', '.join(sorted(VALID_OPERATORS))}"
|
|
225
|
+
)
|
|
226
|
+
param = self.param(param_index)
|
|
227
|
+
return f"({param} IS NULL OR {column} {operator} {param})"
|
|
228
|
+
|
|
229
|
+
# --- Param Conversion ---
|
|
230
|
+
|
|
231
|
+
uses_named_params: bool = False
|
|
232
|
+
"""Whether this dialect uses named parameters (dict) vs positional (list).
|
|
233
|
+
|
|
234
|
+
Positional dialects (default): $1, ?, %s — params passed as list.
|
|
235
|
+
Named dialects: @param1, @p1, :param1 — params passed as dict.
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
dbapi_placeholder: str = ""
|
|
239
|
+
"""The placeholder format that DB-API drivers for this dialect expect.
|
|
240
|
+
|
|
241
|
+
Most DB-API drivers use either '?' (qmark) or '%s' (pyformat).
|
|
242
|
+
Override for dialects where param() doesn't match the DB-API driver.
|
|
243
|
+
Empty string means no rewriting is needed (param() already matches).
|
|
244
|
+
"""
|
|
245
|
+
|
|
246
|
+
def rewrite_sql_for_dbapi(self, sql: str, param_count: int) -> str:
|
|
247
|
+
"""Rewrite SQL to replace dialect placeholders with DB-API driver placeholders.
|
|
248
|
+
|
|
249
|
+
For dialects where param() matches what the driver expects (SQLite '?',
|
|
250
|
+
MySQL '%s'), this is a no-op. For dialects where they differ (Postgres '$1'
|
|
251
|
+
→ '%s', SQL Server '@p1' → '%s'), this replaces each placeholder.
|
|
252
|
+
"""
|
|
253
|
+
placeholder = self.dbapi_placeholder
|
|
254
|
+
if not placeholder:
|
|
255
|
+
return sql
|
|
256
|
+
# Replace each dialect placeholder with the DB-API placeholder,
|
|
257
|
+
# going in reverse order to avoid $1 matching inside $10
|
|
258
|
+
for i in range(param_count, 0, -1):
|
|
259
|
+
sql = sql.replace(self.param(i), placeholder)
|
|
260
|
+
return sql
|
|
261
|
+
|
|
262
|
+
def build_params(self, params: list) -> list | dict:
|
|
263
|
+
"""Convert positional param list to the format this dialect expects.
|
|
264
|
+
|
|
265
|
+
Positional dialects return the list as-is.
|
|
266
|
+
Named dialects convert to a dict keyed by param name (without prefix).
|
|
267
|
+
When dbapi_placeholder is set, SQL has been rewritten to positional
|
|
268
|
+
format, so params stay as a list regardless of uses_named_params.
|
|
269
|
+
"""
|
|
270
|
+
if self.dbapi_placeholder or not self.uses_named_params:
|
|
271
|
+
return params
|
|
272
|
+
return self._params_to_dict(params)
|
|
273
|
+
|
|
274
|
+
def _params_to_dict(self, params: list) -> dict:
|
|
275
|
+
"""Convert param list to named dict. Override for custom key format."""
|
|
276
|
+
# Default: strip the prefix/sigil from param(i) to get the key
|
|
277
|
+
# e.g. @param1 -> param1, @p1 -> p1, :param1 -> param1
|
|
278
|
+
return {self.param(i + 1).lstrip("@:"): v for i, v in enumerate(params)}
|
|
279
|
+
|
|
280
|
+
# --- Quoting ---
|
|
281
|
+
|
|
282
|
+
def quote_identifier(self, identifier: str) -> str:
|
|
283
|
+
"""Quote an identifier (table name, column name, etc.).
|
|
284
|
+
|
|
285
|
+
Default uses double quotes which works for PostgreSQL, BigQuery, etc.
|
|
286
|
+
Override for databases with different quoting conventions.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
identifier: The identifier to quote
|
|
290
|
+
|
|
291
|
+
Returns:
|
|
292
|
+
Quoted identifier string
|
|
293
|
+
"""
|
|
294
|
+
# Escape any existing double quotes
|
|
295
|
+
escaped = identifier.replace('"', '""')
|
|
296
|
+
return f'"{escaped}"'
|
|
297
|
+
|
|
298
|
+
# --- Representation ---
|
|
299
|
+
|
|
300
|
+
def __repr__(self) -> str:
|
|
301
|
+
"""Return string representation of the dialect."""
|
|
302
|
+
return f"{self.__class__.__name__}(name={self.name!r})"
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""BigQuery dialect implementation.
|
|
2
|
+
|
|
3
|
+
BigQuery uses:
|
|
4
|
+
- @param1, @param2 for named parameter placeholders
|
|
5
|
+
- CREATE TEMP TABLE x AS for temporary tables
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataface.core.execute.dialects.base import SQLDialect
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BigQueryDialect(SQLDialect):
|
|
12
|
+
"""BigQuery dialect with @paramN parameter style.
|
|
13
|
+
|
|
14
|
+
BigQuery uses named parameters with @ prefix and supports temp tables.
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
>>> dialect = BigQueryDialect()
|
|
18
|
+
>>> dialect.param(1)
|
|
19
|
+
'@param1'
|
|
20
|
+
>>> dialect.filter('category', '=', 1)
|
|
21
|
+
'category = @param1'
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
name = "bigquery"
|
|
25
|
+
uses_named_params = True
|
|
26
|
+
|
|
27
|
+
def param(self, index: int) -> str:
|
|
28
|
+
"""Generate BigQuery parameter placeholder.
|
|
29
|
+
|
|
30
|
+
BigQuery uses @paramN named style.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
index: 1-based parameter index
|
|
34
|
+
|
|
35
|
+
Returns:
|
|
36
|
+
Parameter placeholder in @paramN format
|
|
37
|
+
"""
|
|
38
|
+
return f"@param{index}"
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""Databricks dialect implementation.
|
|
2
|
+
|
|
3
|
+
Databricks uses:
|
|
4
|
+
- :param1, :param2 for named parameter placeholders
|
|
5
|
+
- CREATE TEMPORARY VIEW x AS for temporary views (not tables)
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataface.core.execute.dialects.base import SQLDialect
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DatabricksDialect(SQLDialect):
|
|
12
|
+
"""Databricks dialect with :paramN parameter style.
|
|
13
|
+
|
|
14
|
+
Databricks (Spark SQL) uses named parameters with : prefix.
|
|
15
|
+
It creates temporary views instead of temporary tables.
|
|
16
|
+
|
|
17
|
+
Example:
|
|
18
|
+
>>> dialect = DatabricksDialect()
|
|
19
|
+
>>> dialect.param(1)
|
|
20
|
+
':param1'
|
|
21
|
+
>>> dialect.temp_table('tmp', 'SELECT 1')
|
|
22
|
+
'CREATE OR REPLACE TEMPORARY VIEW tmp AS SELECT 1'
|
|
23
|
+
>>> dialect.filter('category', '=', 1)
|
|
24
|
+
'category = :param1'
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
name = "databricks"
|
|
28
|
+
uses_named_params = True
|
|
29
|
+
|
|
30
|
+
def param(self, index: int) -> str:
|
|
31
|
+
"""Generate Databricks parameter placeholder.
|
|
32
|
+
|
|
33
|
+
Databricks uses :paramN named style.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
index: 1-based parameter index
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Parameter placeholder in :paramN format
|
|
40
|
+
"""
|
|
41
|
+
return f":param{index}"
|
|
42
|
+
|
|
43
|
+
def temp_table(self, name: str, sql: str) -> str:
|
|
44
|
+
"""Generate Databricks temporary view creation.
|
|
45
|
+
|
|
46
|
+
Databricks uses TEMPORARY VIEW instead of TEMP TABLE.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
name: Name of the temporary view
|
|
50
|
+
sql: SQL query for the view
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
CREATE OR REPLACE TEMPORARY VIEW statement
|
|
54
|
+
"""
|
|
55
|
+
return f"CREATE OR REPLACE TEMPORARY VIEW {name} AS {sql}"
|
|
56
|
+
|
|
57
|
+
def drop_temp_table(self, name: str) -> str:
|
|
58
|
+
"""Generate Databricks DROP VIEW statement.
|
|
59
|
+
|
|
60
|
+
Since Databricks uses views, we drop the view.
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
name: Name of the temporary view
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
DROP VIEW IF EXISTS statement
|
|
67
|
+
"""
|
|
68
|
+
return f"DROP VIEW IF EXISTS {name}"
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""DuckDB dialect implementation.
|
|
2
|
+
|
|
3
|
+
DuckDB uses:
|
|
4
|
+
- $1, $2, ... for parameter placeholders (also supports ?)
|
|
5
|
+
- CREATE TEMP TABLE x AS for temporary tables
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataface.core.execute.dialects.base import SQLDialect
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DuckDBDialect(SQLDialect):
|
|
12
|
+
"""DuckDB dialect with $1, $2 parameter style.
|
|
13
|
+
|
|
14
|
+
DuckDB is similar to PostgreSQL and uses the same parameter syntax.
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
>>> dialect = DuckDBDialect()
|
|
18
|
+
>>> dialect.param(1)
|
|
19
|
+
'$1'
|
|
20
|
+
>>> dialect.filter('category', '=', 1)
|
|
21
|
+
'category = $1'
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
name = "duckdb"
|
|
25
|
+
|
|
26
|
+
def param(self, index: int) -> str:
|
|
27
|
+
"""Generate DuckDB parameter placeholder.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
index: 1-based parameter index
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
Parameter placeholder in $N format
|
|
34
|
+
"""
|
|
35
|
+
return f"${index}"
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""MySQL dialect implementation.
|
|
2
|
+
|
|
3
|
+
MySQL uses:
|
|
4
|
+
- %s for parameter placeholders (positional)
|
|
5
|
+
- CREATE TEMPORARY TABLE x AS for temporary tables
|
|
6
|
+
- Backticks for identifier quoting
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from dataface.core.execute.dialects.base import SQLDialect
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MySQLDialect(SQLDialect):
|
|
13
|
+
"""MySQL dialect with %s parameter style.
|
|
14
|
+
|
|
15
|
+
MySQL uses format-style parameter placeholders and TEMPORARY keyword
|
|
16
|
+
for temp tables.
|
|
17
|
+
|
|
18
|
+
Example:
|
|
19
|
+
>>> dialect = MySQLDialect()
|
|
20
|
+
>>> dialect.param()
|
|
21
|
+
'%s'
|
|
22
|
+
>>> dialect.temp_table('tmp', 'SELECT 1')
|
|
23
|
+
'CREATE TEMPORARY TABLE tmp AS SELECT 1'
|
|
24
|
+
>>> dialect.filter('category', '=', 1)
|
|
25
|
+
'category = %s'
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
name = "mysql"
|
|
29
|
+
|
|
30
|
+
def param(self, index: int) -> str:
|
|
31
|
+
"""Generate MySQL parameter placeholder.
|
|
32
|
+
|
|
33
|
+
MySQL uses %s for all parameters (positional, not indexed).
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
index: 1-based parameter index (ignored for MySQL)
|
|
37
|
+
|
|
38
|
+
Returns:
|
|
39
|
+
Parameter placeholder '%s'
|
|
40
|
+
"""
|
|
41
|
+
return "%s"
|
|
42
|
+
|
|
43
|
+
def temp_table(self, name: str, sql: str) -> str:
|
|
44
|
+
"""Generate MySQL CREATE TEMPORARY TABLE statement.
|
|
45
|
+
|
|
46
|
+
MySQL uses TEMPORARY instead of TEMP.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
name: Name of the temporary table
|
|
50
|
+
sql: SQL query to populate the table
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
CREATE TEMPORARY TABLE statement
|
|
54
|
+
"""
|
|
55
|
+
return f"CREATE TEMPORARY TABLE {name} AS {sql}"
|
|
56
|
+
|
|
57
|
+
def quote_identifier(self, identifier: str) -> str:
|
|
58
|
+
"""Quote an identifier using MySQL backticks.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
identifier: The identifier to quote
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Backtick-quoted identifier
|
|
65
|
+
"""
|
|
66
|
+
# Escape backticks by doubling them
|
|
67
|
+
escaped = identifier.replace("`", "``")
|
|
68
|
+
return f"`{escaped}`"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""PostgreSQL dialect implementation.
|
|
2
|
+
|
|
3
|
+
PostgreSQL uses:
|
|
4
|
+
- $1, $2, ... for parameter placeholders
|
|
5
|
+
- CREATE TEMP TABLE x AS for temporary tables
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataface.core.execute.dialects.base import SQLDialect
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PostgresDialect(SQLDialect):
|
|
12
|
+
"""PostgreSQL dialect with $1, $2 parameter style.
|
|
13
|
+
|
|
14
|
+
PostgreSQL is the default dialect and uses standard SQL syntax
|
|
15
|
+
with numbered parameter placeholders.
|
|
16
|
+
|
|
17
|
+
Example:
|
|
18
|
+
>>> dialect = PostgresDialect()
|
|
19
|
+
>>> dialect.param(1)
|
|
20
|
+
'$1'
|
|
21
|
+
>>> dialect.params(3)
|
|
22
|
+
['$1', '$2', '$3']
|
|
23
|
+
>>> dialect.filter('country', '=', 1)
|
|
24
|
+
'country = $1'
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
name = "postgres"
|
|
28
|
+
dbapi_placeholder = "%s"
|
|
29
|
+
|
|
30
|
+
def param(self, index: int) -> str:
|
|
31
|
+
"""Generate PostgreSQL parameter placeholder.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
index: 1-based parameter index
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Parameter placeholder in $N format
|
|
38
|
+
"""
|
|
39
|
+
return f"${index}"
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""Redshift dialect implementation.
|
|
2
|
+
|
|
3
|
+
Redshift is PostgreSQL-based (8.0.2), so it inherits all PostgreSQL behavior.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from dataface.core.execute.dialects.postgres import PostgresDialect
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class RedshiftDialect(PostgresDialect):
|
|
10
|
+
"""Redshift dialect — identical to PostgreSQL."""
|
|
11
|
+
|
|
12
|
+
name = "redshift"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""Snowflake dialect implementation.
|
|
2
|
+
|
|
3
|
+
Snowflake uses:
|
|
4
|
+
- ? for positional parameters or :name for named parameters
|
|
5
|
+
- CREATE TEMPORARY TABLE x AS for temporary tables
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dataface.core.execute.dialects.base import SQLDialect
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SnowflakeDialect(SQLDialect):
|
|
12
|
+
"""Snowflake dialect with ? parameter style.
|
|
13
|
+
|
|
14
|
+
Snowflake uses positional ? placeholders and has TEMPORARY tables.
|
|
15
|
+
|
|
16
|
+
Example:
|
|
17
|
+
>>> dialect = SnowflakeDialect()
|
|
18
|
+
>>> dialect.param()
|
|
19
|
+
'?'
|
|
20
|
+
>>> dialect.temp_table('tmp', 'SELECT 1')
|
|
21
|
+
'CREATE TEMPORARY TABLE tmp AS SELECT 1'
|
|
22
|
+
>>> dialect.filter('category', '=', 1)
|
|
23
|
+
'category = ?'
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
name = "snowflake"
|
|
27
|
+
|
|
28
|
+
def param(self, index: int) -> str:
|
|
29
|
+
"""Generate Snowflake parameter placeholder.
|
|
30
|
+
|
|
31
|
+
Snowflake uses ? for positional parameters.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
index: 1-based parameter index (ignored for positional)
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Parameter placeholder '?'
|
|
38
|
+
"""
|
|
39
|
+
return "?"
|
|
40
|
+
|
|
41
|
+
def temp_table(self, name: str, sql: str) -> str:
|
|
42
|
+
"""Generate Snowflake CREATE TEMPORARY TABLE statement.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
name: Name of the temporary table
|
|
46
|
+
sql: SQL query to populate the table
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
CREATE TEMPORARY TABLE statement
|
|
50
|
+
"""
|
|
51
|
+
return f"CREATE TEMPORARY TABLE {name} AS {sql}"
|