sqlty 0.0.1.dev1__tar.gz → 0.2.0__tar.gz
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.
- sqlty-0.2.0/PKG-INFO +212 -0
- sqlty-0.2.0/README.md +178 -0
- sqlty-0.2.0/pyproject.toml +172 -0
- sqlty-0.2.0/rust/Cargo.lock +876 -0
- sqlty-0.2.0/rust/Cargo.toml +58 -0
- sqlty-0.2.0/rust/crates/sqlty-ext/Cargo.toml +22 -0
- sqlty-0.2.0/rust/crates/sqlty-ext/build.rs +51 -0
- sqlty-0.2.0/rust/crates/sqlty-ext/src/lib.rs +949 -0
- sqlty-0.2.0/rust/crates/sqlty-python/Cargo.toml +17 -0
- sqlty-0.2.0/rust/crates/sqlty-python/src/checker.rs +1620 -0
- sqlty-0.2.0/rust/crates/sqlty-python/src/edit.rs +54 -0
- sqlty-0.2.0/rust/crates/sqlty-python/src/fixer.rs +240 -0
- sqlty-0.2.0/rust/crates/sqlty-python/src/lib.rs +41 -0
- sqlty-0.2.0/rust/crates/sqlty-python/src/options.rs +50 -0
- sqlty-0.2.0/rust/crates/sqlty-python/src/rewriter.rs +1528 -0
- sqlty-0.2.0/rust/crates/sqlty-python/src/rules.rs +360 -0
- sqlty-0.2.0/rust/crates/sqlty-python/src/scan.rs +201 -0
- sqlty-0.2.0/rust/crates/sqlty-python/src/template.rs +212 -0
- sqlty-0.2.0/rust/crates/sqlty-python/src/tokens.rs +721 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/Cargo.toml +28 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/build.rs +47 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/README.md +263 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/common/aggregates.inference.toml +101 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/common/arithmetic.inference.toml +54 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/common/comparison.inference.toml +60 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/common/concat.inference.toml +35 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/common/conditional.inference.toml +17 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/common/datetime.inference.toml +25 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/common/json.inference.toml +78 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/common/math.inference.toml +17 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/common/strings.inference.toml +33 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/common/window.inference.toml +41 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/compile.rs +1055 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/dialects.toml +6 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/mysql/aggregates.inference.toml +49 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/mysql/arithmetic.inference.toml +132 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/mysql/bits.inference.toml +10 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/mysql/comparison.inference.toml +28 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/mysql/concat.inference.toml +9 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/mysql/conditional.inference.toml +19 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/mysql/datetime.inference.toml +136 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/mysql/encryption.inference.toml +46 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/mysql/json.inference.toml +102 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/mysql/math.inference.toml +84 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/mysql/misc.inference.toml +109 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/mysql/spatial.inference.toml +120 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/mysql/strings.inference.toml +228 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/mysql/system.inference.toml +118 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/admin.inference.toml +145 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/aggregates.inference.toml +178 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/arithmetic.inference.toml +527 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/arrays.inference.toml +120 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/binary.inference.toml +46 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/comparison.inference.toml +20 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/concat.inference.toml +69 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/conditional.inference.toml +10 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/datetime.inference.toml +127 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/enums.inference.toml +19 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/events.inference.toml +19 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/extensions.inference.toml +28 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/formatting.inference.toml +28 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/geometry.inference.toml +175 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/json.inference.toml +258 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/math.inference.toml +92 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/merge.inference.toml +10 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/network.inference.toml +64 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/ranges.inference.toml +136 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/rows.inference.toml +10 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/sequences.inference.toml +10 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/series.inference.toml +19 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/strings.inference.toml +101 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/system.inference.toml +353 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/textsearch.inference.toml +162 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/uuid.inference.toml +28 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/postgres/xml.inference.toml +55 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/sqlite/aggregates.inference.toml +29 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/sqlite/arithmetic.inference.toml +54 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/sqlite/bitwise.inference.toml +17 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/sqlite/datetime.inference.toml +28 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/sqlite/fts.inference.toml +37 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/sqlite/json.inference.toml +100 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/sqlite/math.inference.toml +38 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/sqlite/scalar.inference.toml +91 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/rules/sqlite/strings.inference.toml +10 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/api.rs +194 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/dialect.rs +171 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/dml.rs +745 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/explain.rs +131 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/expr.rs +815 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/functions.rs +565 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/grouping.rs +244 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/hashing.rs +30 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/inference.rs +403 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/json.rs +738 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/literals.rs +254 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/memo.rs +125 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/merged_star.rs +178 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/mod.rs +132 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/names.rs +351 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/narrowing.rs +776 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/nullability.rs +1477 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/operators.rs +725 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/params.rs +1119 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/pg_record_functions.rs +599 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/preprocess.rs +472 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/query.rs +1099 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/references.rs +102 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/registry.rs +264 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/resolve.rs +776 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/respell.rs +557 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/scope.rs +1016 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__dml__tests__returning_columns_in_queries.snap +49 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__expr__tests__arrays_and_subscripts.snap +36 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__expr__tests__case_expressions.snap +28 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__expr__tests__casts.snap +41 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__expr__tests__casts_without_a_schema_do_not_complain.snap +9 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__expr__tests__composite_fields_and_json_access.snap +23 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__expr__tests__literals_and_unary_operators.snap +29 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__expr__tests__misc_expressions.snap +38 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__expr__tests__mysql_and_sqlite_expressions.snap +30 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__functions__tests__aggregates_in_postgres.snap +44 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__functions__tests__function_availability_differs_by_dialect.snap +37 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__functions__tests__return_types_follow_the_argument.snap +74 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__functions__tests__unknown_functions_are_untyped_and_explained.snap +16 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__literals__tests__value_sets_flow_through_expressions.snap +43 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__names__tests__column_names_follow_postgres_figure_colname.snap +34 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__operators__tests__arithmetic_promotes_numbers_and_types_date_math.snap +149 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__operators__tests__binary_operators_in_queries.snap +58 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__operators__tests__common_type_follows_postgres_union_rules.snap +24 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__operators__tests__division_differs_between_postgres_and_mysql.snap +13 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__query__tests__column_names_come_from_the_first_branch.snap +9 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__query__tests__ctes.snap +18 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__query__tests__dialects_name_and_type_the_same_query_differently.snap +33 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__query__tests__projection_forms.snap +42 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__query__tests__recursive_ctes_settle_nullability.snap +17 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__query__tests__set_operations_combine_nullability.snap +27 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__query__tests__set_operations_unify_types.snap +30 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__query__tests__statements_that_are_not_queries_have_no_columns.snap +21 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__query__tests__unsupported_query_forms_are_explained.snap +14 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__query__tests__values_lists.snap +51 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__resolve__tests__ambiguous_names_are_reported_once_per_query.snap +30 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__resolve__tests__cte_columns_are_resolved_like_tables.snap +18 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__resolve__tests__names_are_not_judged_without_a_schema.snap +15 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__resolve__tests__nullability_comes_from_the_declaration.snap +15 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__resolve__tests__quoted_names_keep_their_case_in_postgres_only.snap +29 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__resolve__tests__star_over_unreadable_sources_names_the_reason.snap +35 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__resolve__tests__system_columns_exist_without_being_declared.snap +29 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__resolve__tests__unknown_names_say_what_is_missing.snap +47 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__resolve__tests__unqualified_names_resolve_to_the_first_source_that_has_them.snap +24 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__scope__tests__correlated_subqueries_see_the_outer_scope.snap +21 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__scope__tests__derived_tables_ctes_and_views.snap +36 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__scope__tests__nullability_follows_the_join_kind.snap +33 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__scope__tests__opaque_sources_explain_themselves.snap +51 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__scope__tests__star_expansion_over_joins.snap +58 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/snapshots/sqlty_sql__analyzer__scope__tests__table_names_fold_by_dialect.snap +28 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/sql_type.rs +927 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/star_expand.rs +257 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/table_functions.rs +308 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/test_support.rs +62 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/tests.rs +421 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/text_names.rs +174 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/analyzer/typed.rs +111 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/catalog/alter.rs +401 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/catalog/builtin_catalog.sql +190 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/catalog/columns.rs +368 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/catalog/loader.rs +954 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/catalog/mod.rs +977 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/catalog/names.rs +165 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/catalog/read.rs +385 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/catalog/resolve.rs +485 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/ddl.rs +1150 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/diagnostics.rs +274 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/json.rs +486 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/lib.rs +40 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/placeholders.rs +324 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/src/values.rs +344 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/tests/inference_compiler.rs +462 -0
- sqlty-0.2.0/rust/crates/sqlty-sql/tests/timing.rs +70 -0
- sqlty-0.2.0/skills/sqlty/SKILL.md +81 -0
- sqlty-0.2.0/skills/sqlty-adopt/SKILL.md +125 -0
- sqlty-0.2.0/skills/sqlty-breaking/SKILL.md +130 -0
- sqlty-0.2.0/skills/sqlty-check/SKILL.md +144 -0
- sqlty-0.2.0/skills/sqlty-ci/SKILL.md +120 -0
- sqlty-0.2.0/skills/sqlty-rewrite/SKILL.md +134 -0
- sqlty-0.2.0/src/sqlty/__init__.py +99 -0
- {sqlty-0.0.1.dev1 → sqlty-0.2.0}/src/sqlty/__main__.py +1 -1
- sqlty-0.2.0/src/sqlty/_breaking.py +314 -0
- sqlty-0.2.0/src/sqlty/_check.py +911 -0
- sqlty-0.2.0/src/sqlty/_cli.py +1264 -0
- sqlty-0.2.0/src/sqlty/_config.py +666 -0
- sqlty-0.2.0/src/sqlty/_core.pyi +109 -0
- sqlty-0.2.0/src/sqlty/_decode.py +62 -0
- sqlty-0.2.0/src/sqlty/_drivers.py +274 -0
- sqlty-0.2.0/src/sqlty/_gitignore.py +58 -0
- sqlty-0.2.0/src/sqlty/_helpers.py +629 -0
- sqlty-0.2.0/src/sqlty/_json_shape.py +299 -0
- sqlty-0.2.0/src/sqlty/_json_types.py +225 -0
- sqlty-0.2.0/src/sqlty/_lint.py +364 -0
- sqlty-0.2.0/src/sqlty/_params.py +210 -0
- sqlty-0.2.0/src/sqlty/_psycopg.py +269 -0
- sqlty-0.2.0/src/sqlty/_snapshot.py +169 -0
- sqlty-0.2.0/src/sqlty/_source.py +654 -0
- sqlty-0.2.0/src/sqlty/_type_extractor/__init__.py +51 -0
- sqlty-0.2.0/src/sqlty/_type_extractor/ast_fields.py +869 -0
- sqlty-0.2.0/src/sqlty/_type_extractor/compat.py +252 -0
- sqlty-0.2.0/src/sqlty/_type_extractor/extract.py +228 -0
- sqlty-0.2.0/src/sqlty/_type_extractor/generic.py +95 -0
- sqlty-0.2.0/src/sqlty/_type_extractor/info.py +161 -0
- sqlty-0.2.0/src/sqlty/_type_extractor/sandbox.py +385 -0
- sqlty-0.2.0/src/sqlty/_type_extractor/subtype.py +156 -0
- sqlty-0.2.0/src/sqlty/core.py +571 -0
- sqlty-0.2.0/src/sqlty/drivers/psycopg.py +104 -0
- sqlty-0.2.0/src/sqlty/magic.py +126 -0
- sqlty-0.2.0/src/sqlty/rules/ambiguous-column.md +48 -0
- sqlty-0.2.0/src/sqlty/rules/column-count-mismatch.md +30 -0
- sqlty-0.2.0/src/sqlty/rules/column-name-mismatch.md +34 -0
- sqlty-0.2.0/src/sqlty/rules/column-type-mismatch.md +75 -0
- sqlty-0.2.0/src/sqlty/rules/fstring-skipped.md +23 -0
- sqlty-0.2.0/src/sqlty/rules/inline-sql-comment.md +27 -0
- sqlty-0.2.0/src/sqlty/rules/insert-missing-column.md +63 -0
- sqlty-0.2.0/src/sqlty/rules/insert-value-count.md +58 -0
- sqlty-0.2.0/src/sqlty/rules/json-shape-mismatch.md +96 -0
- sqlty-0.2.0/src/sqlty/rules/literal-mismatch.md +83 -0
- sqlty-0.2.0/src/sqlty/rules/mysql-sql-marker.md +32 -0
- sqlty-0.2.0/src/sqlty/rules/narrow-type.md +50 -0
- sqlty-0.2.0/src/sqlty/rules/nullable-mismatch.md +76 -0
- sqlty-0.2.0/src/sqlty/rules/param-count-mismatch.md +66 -0
- sqlty-0.2.0/src/sqlty/rules/param-type-mismatch.md +77 -0
- sqlty-0.2.0/src/sqlty/rules/redundant-literal.md +68 -0
- sqlty-0.2.0/src/sqlty/rules/redundant-optional.md +100 -0
- sqlty-0.2.0/src/sqlty/rules/select-star-no-schema.md +24 -0
- sqlty-0.2.0/src/sqlty/rules/select-star.md +77 -0
- sqlty-0.2.0/src/sqlty/rules/stale-rewrite.md +33 -0
- sqlty-0.2.0/src/sqlty/rules/stale-type.md +56 -0
- sqlty-0.2.0/src/sqlty/rules/tstring-skipped.md +68 -0
- sqlty-0.2.0/src/sqlty/rules/typeddict-inheritance.md +35 -0
- sqlty-0.2.0/src/sqlty/rules/typeddict-key-mismatch.md +38 -0
- sqlty-0.2.0/src/sqlty/rules/unchecked-query.md +34 -0
- sqlty-0.2.0/src/sqlty/rules/ungrouped-column.md +67 -0
- sqlty-0.2.0/src/sqlty/rules/unknown-column.md +68 -0
- sqlty-0.2.0/src/sqlty/rules/unknown-function.md +49 -0
- sqlty-0.2.0/src/sqlty/rules/unknown-table.md +47 -0
- sqlty-0.2.0/src/sqlty/rules/unknown-type.md +74 -0
- sqlty-0.2.0/src/sqlty/rules/unparsed-query.md +46 -0
- sqlty-0.2.0/src/sqlty/rules/unsupported-syntax.md +69 -0
- sqlty-0.2.0/src/sqlty/rules/untyped-query.md +39 -0
- sqlty-0.2.0/src/sqlty/rules/unused-ignore.md +36 -0
- sqlty-0.2.0/src/sqlty_magic.pth +1 -0
- sqlty-0.0.1.dev1/.gitignore +0 -20
- sqlty-0.0.1.dev1/PKG-INFO +0 -187
- sqlty-0.0.1.dev1/README.md +0 -138
- sqlty-0.0.1.dev1/pyproject.toml +0 -139
- sqlty-0.0.1.dev1/src/sqlty/__init__.py +0 -55
- sqlty-0.0.1.dev1/src/sqlty/_ast_builder.py +0 -211
- sqlty-0.0.1.dev1/src/sqlty/_ast_unparser.py +0 -215
- sqlty-0.0.1.dev1/src/sqlty/_auto_generator.py +0 -44
- sqlty-0.0.1.dev1/src/sqlty/_collector.py +0 -126
- sqlty-0.0.1.dev1/src/sqlty/_config.py +0 -114
- sqlty-0.0.1.dev1/src/sqlty/_generator.py +0 -587
- sqlty-0.0.1.dev1/src/sqlty/_registry.py +0 -146
- sqlty-0.0.1.dev1/src/sqlty/_sql_analyzer.py +0 -228
- sqlty-0.0.1.dev1/src/sqlty/_stub_merger.py +0 -275
- sqlty-0.0.1.dev1/src/sqlty/drivers/psycopg.py +0 -12
- {sqlty-0.0.1.dev1 → sqlty-0.2.0}/LICENSE +0 -0
- {sqlty-0.0.1.dev1 → sqlty-0.2.0}/src/sqlty/py.typed +0 -0
sqlty-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: sqlty
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Classifier: Development Status :: 4 - Beta
|
|
5
|
+
Classifier: Environment :: Console
|
|
6
|
+
Classifier: Intended Audience :: Developers
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.15
|
|
11
|
+
Classifier: Programming Language :: Rust
|
|
12
|
+
Classifier: Programming Language :: SQL
|
|
13
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
15
|
+
Classifier: Topic :: Database
|
|
16
|
+
Classifier: Typing :: Typed
|
|
17
|
+
Requires-Dist: pathspec>=0.12.1
|
|
18
|
+
Requires-Dist: typing-extensions>=4.15.0 ; python_full_version < '3.15'
|
|
19
|
+
Requires-Dist: psycopg>=3.2.12 ; extra == 'psycopg'
|
|
20
|
+
Provides-Extra: psycopg
|
|
21
|
+
License-File: LICENSE
|
|
22
|
+
Summary: Type-safe SQL queries for Python
|
|
23
|
+
Keywords: sql,type-hints,mypy,type-safety,static-analysis,code-generation
|
|
24
|
+
Author-email: Jiri Kuncar <jiri.kuncar@gmail.com>
|
|
25
|
+
License-Expression: MIT
|
|
26
|
+
Requires-Python: >=3.14
|
|
27
|
+
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
|
|
28
|
+
Project-URL: Changelog, https://github.com/jirikuncar/sqlty/releases
|
|
29
|
+
Project-URL: Documentation, https://jirikuncar.github.io/sqlty/
|
|
30
|
+
Project-URL: Homepage, https://github.com/jirikuncar/sqlty
|
|
31
|
+
Project-URL: Issues, https://github.com/jirikuncar/sqlty/issues
|
|
32
|
+
Project-URL: Repository, https://github.com/jirikuncar/sqlty
|
|
33
|
+
|
|
34
|
+
# SQLTy
|
|
35
|
+
|
|
36
|
+
[](https://pypi.org/project/sqlty/)
|
|
37
|
+
[](https://www.python.org/downloads/)
|
|
38
|
+
[](https://github.com/jirikuncar/sqlty/actions/workflows/tests.yml)
|
|
39
|
+
[](https://opensource.org/licenses/MIT)
|
|
40
|
+
|
|
41
|
+
Your SQL already knows its types. SQLTy tells Python.
|
|
42
|
+
|
|
43
|
+
Type-safe raw SQL without an ORM or a database connection. sqlty reads row and
|
|
44
|
+
parameter types from your SQL and schema, and checks them on every commit, so a
|
|
45
|
+
migration that changes a column is caught in review, not in production.
|
|
46
|
+
|
|
47
|
+
<p align="center">
|
|
48
|
+
<img src="https://raw.githubusercontent.com/jirikuncar/sqlty/main/docs/assets/demo.gif" alt="A terminal session: sqlty rewrite adds the row type to a query, a migration makes email nullable, sqlty check --fix updates the type and mypy reports the line that would crash" width="720">
|
|
49
|
+
<br>
|
|
50
|
+
<em>A migration makes <code>email</code> nullable. sqlty and mypy find the code that would crash.</em>
|
|
51
|
+
(<a href="https://github.com/jirikuncar/sqlty/blob/main/docs/assets/demo.tape">view source</a>)
|
|
52
|
+
</p>
|
|
53
|
+
|
|
54
|
+
**Documentation: [jirikuncar.github.io/sqlty](https://jirikuncar.github.io/sqlty/)**
|
|
55
|
+
|
|
56
|
+
**Fits if** you write SQL as string literals on Python 3.14+, against
|
|
57
|
+
PostgreSQL, SQLite or MySQL, with a schema dump or SQL migrations in the
|
|
58
|
+
repository. Rows are typed end to end with psycopg 3; with other drivers sqlty
|
|
59
|
+
checks the query and its parameters, and you annotate what the cursor returns
|
|
60
|
+
([Drivers](https://jirikuncar.github.io/sqlty/guide/drivers-and-orms/)).
|
|
61
|
+
|
|
62
|
+
> **Beta.** APIs and configuration may change before 1.0. Feedback welcome in
|
|
63
|
+
> [GitHub Issues](https://github.com/jirikuncar/sqlty/issues).
|
|
64
|
+
|
|
65
|
+
## How it works
|
|
66
|
+
|
|
67
|
+
Drivers return rows of `Any`, so nothing ties a query's columns to the code
|
|
68
|
+
that reads them. Start a query with `--sql` and a newline, and
|
|
69
|
+
`sqlty rewrite .` adds the row type inferred from your `CREATE TABLE`
|
|
70
|
+
statements:
|
|
71
|
+
|
|
72
|
+
```toml
|
|
73
|
+
# pyproject.toml
|
|
74
|
+
[tool.sqlty]
|
|
75
|
+
schema = "schema.sql"
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
`schema.sql`:
|
|
79
|
+
|
|
80
|
+
```sql
|
|
81
|
+
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, email VARCHAR(255) NOT NULL);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
```python
|
|
85
|
+
query = "--sql\nSELECT id, name, email FROM users"
|
|
86
|
+
# rewritten: query = SQL[tuple[int, str, str]]("--sql\nSELECT id, name, email FROM users")
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
`SQL[T]` is a `str` subclass, so the query still runs anywhere a string does,
|
|
90
|
+
and mypy, pyright and ty see `T`. Without a schema, casts such as `id::INTEGER`
|
|
91
|
+
type the columns; a query with neither stays as it is. With a space instead of
|
|
92
|
+
the newline, `"--sql SELECT ..."` is a SQL comment, and `sqlty check` warns.
|
|
93
|
+
|
|
94
|
+
## What it catches
|
|
95
|
+
|
|
96
|
+
### A migration changes a column
|
|
97
|
+
|
|
98
|
+
```toml
|
|
99
|
+
[tool.sqlty]
|
|
100
|
+
schema = "schema.sql"
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
A migration dropped `NOT NULL` from `email`.
|
|
104
|
+
|
|
105
|
+
`schema.sql`:
|
|
106
|
+
|
|
107
|
+
```sql
|
|
108
|
+
CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT);
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from sqlty import SQL
|
|
113
|
+
|
|
114
|
+
# error: [narrow-type] "SQL[tuple[int, str]] should be SQL[tuple[int, str | None]]"
|
|
115
|
+
query = SQL[tuple[int, str]]("--sql\nSELECT id, email FROM users")
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`sqlty check --fix .` updates the type, and your type checker finds every
|
|
119
|
+
caller that now needs a `None` check. `narrow-type` is an error, so CI fails on
|
|
120
|
+
it; an annotation that is only wider than the rows, such as `str | None` for a
|
|
121
|
+
column made `NOT NULL`, is a `stale-type` warning
|
|
122
|
+
([CI](https://jirikuncar.github.io/sqlty/recipes/ci/)). For rolling deploys,
|
|
123
|
+
`sqlty breaking --against origin/main` exits 1 when old code breaks on the new
|
|
124
|
+
schema or new code on the old one
|
|
125
|
+
([Breaking schema changes](https://jirikuncar.github.io/sqlty/guide/breaking-changes/)).
|
|
126
|
+
|
|
127
|
+
### A row type that doesn't match its query
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
from typing import TypedDict
|
|
131
|
+
from sqlty import SQL
|
|
132
|
+
|
|
133
|
+
class User(TypedDict):
|
|
134
|
+
id: str
|
|
135
|
+
name: str
|
|
136
|
+
|
|
137
|
+
# error: [column-type-mismatch] "User.id is str but query column 'id' infers int"
|
|
138
|
+
query = SQL[User]("--sql\nSELECT id::INTEGER, name::TEXT FROM users")
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
`TypedDict`, `NamedTuple`, dataclasses and Pydantic models are all checked
|
|
142
|
+
([Row types](https://jirikuncar.github.io/sqlty/guide/row-types/)).
|
|
143
|
+
|
|
144
|
+
### Wrong parameters, misspelled columns
|
|
145
|
+
|
|
146
|
+
```toml
|
|
147
|
+
[tool.sqlty]
|
|
148
|
+
dialect = "sqlite"
|
|
149
|
+
schema = "schema.sql"
|
|
150
|
+
|
|
151
|
+
[tool.sqlty.rules]
|
|
152
|
+
unknown-column = "warn"
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
`schema.sql`:
|
|
156
|
+
|
|
157
|
+
```sql
|
|
158
|
+
CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT);
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
import sqlite3
|
|
163
|
+
from sqlty import SQL
|
|
164
|
+
|
|
165
|
+
conn = sqlite3.connect("app.db")
|
|
166
|
+
|
|
167
|
+
# error: [param-count-mismatch] "query has 1 parameter, the call passes 2"
|
|
168
|
+
conn.execute(SQL[tuple[str | None]]("--sql\nSELECT email FROM users WHERE id = ?"), (1, 2))
|
|
169
|
+
|
|
170
|
+
# error: [unknown-column] "column `emial` does not exist in `users`"
|
|
171
|
+
conn.execute("--sql\nSELECT emial FROM users")
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
`?`, `$1`, `%s` and `:name` placeholders are checked on sqlite3, psycopg and
|
|
175
|
+
asyncpg calls ([Parameters](https://jirikuncar.github.io/sqlty/guide/parameters/)).
|
|
176
|
+
These are the mistakes coding agents make: every problem is one
|
|
177
|
+
`file:line: [rule] message` line and a non-zero exit code they can act on, and
|
|
178
|
+
the [sqlty skills](https://jirikuncar.github.io/sqlty/recipes/agents/) teach
|
|
179
|
+
them the conventions. In Claude Code, `/plugin install sqlty --marketplace jirikuncar/sqlty`
|
|
180
|
+
installs them.
|
|
181
|
+
|
|
182
|
+
## Get started
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
pip install "sqlty[psycopg]" # or: uv add sqlty; the extra adds the psycopg helpers
|
|
186
|
+
printf '%s\n' 'q = "--sql\nSELECT id::INTEGER, name::TEXT FROM users"' > queries.py
|
|
187
|
+
sqlty rewrite -v . # q = SQL[tuple[int, str]]("--sql\n...")
|
|
188
|
+
sqlty check . # report drift and mismatches
|
|
189
|
+
sqlty strip . # undo: back to plain strings
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
`rewrite` and `strip` only touch the wrapper, never the SQL, so you can adopt
|
|
193
|
+
sqlty one module at a time and back out. New project:
|
|
194
|
+
[Quickstart](https://jirikuncar.github.io/sqlty/quickstart/). Existing codebase:
|
|
195
|
+
[Migration](https://jirikuncar.github.io/sqlty/recipes/migration/), starting
|
|
196
|
+
with `pg_dump --schema-only app > schema.sql`.
|
|
197
|
+
|
|
198
|
+
Not a fit for ORMs, SQL built at runtime, or runtime validation;
|
|
199
|
+
[Why SQLTy?](https://jirikuncar.github.io/sqlty/motivation/) compares it with
|
|
200
|
+
sqlc, sqlx, PgTyped and others.
|
|
201
|
+
|
|
202
|
+
## About
|
|
203
|
+
|
|
204
|
+
The engine is Rust ([sqlparser-rs](https://github.com/apache/datafusion-sqlparser-rs)
|
|
205
|
+
via [PyO3](https://pyo3.rs)), with wheels for Linux, macOS and Windows. SQLTy is
|
|
206
|
+
developed with AI coding agents, mainly Claude Code; every change is reviewed by
|
|
207
|
+
a maintainer (see the
|
|
208
|
+
[AI-assisted contributions](https://github.com/jirikuncar/sqlty/blob/main/CONTRIBUTING.md#ai-assisted-contributions)
|
|
209
|
+
policy). [Contributing](https://github.com/jirikuncar/sqlty/blob/main/CONTRIBUTING.md) ·
|
|
210
|
+
[Credits](https://jirikuncar.github.io/sqlty/credits/) ·
|
|
211
|
+
MIT [License](https://github.com/jirikuncar/sqlty/blob/main/LICENSE).
|
|
212
|
+
|
sqlty-0.2.0/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# SQLTy
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/sqlty/)
|
|
4
|
+
[](https://www.python.org/downloads/)
|
|
5
|
+
[](https://github.com/jirikuncar/sqlty/actions/workflows/tests.yml)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
Your SQL already knows its types. SQLTy tells Python.
|
|
9
|
+
|
|
10
|
+
Type-safe raw SQL without an ORM or a database connection. sqlty reads row and
|
|
11
|
+
parameter types from your SQL and schema, and checks them on every commit, so a
|
|
12
|
+
migration that changes a column is caught in review, not in production.
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<img src="https://raw.githubusercontent.com/jirikuncar/sqlty/main/docs/assets/demo.gif" alt="A terminal session: sqlty rewrite adds the row type to a query, a migration makes email nullable, sqlty check --fix updates the type and mypy reports the line that would crash" width="720">
|
|
16
|
+
<br>
|
|
17
|
+
<em>A migration makes <code>email</code> nullable. sqlty and mypy find the code that would crash.</em>
|
|
18
|
+
(<a href="https://github.com/jirikuncar/sqlty/blob/main/docs/assets/demo.tape">view source</a>)
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
**Documentation: [jirikuncar.github.io/sqlty](https://jirikuncar.github.io/sqlty/)**
|
|
22
|
+
|
|
23
|
+
**Fits if** you write SQL as string literals on Python 3.14+, against
|
|
24
|
+
PostgreSQL, SQLite or MySQL, with a schema dump or SQL migrations in the
|
|
25
|
+
repository. Rows are typed end to end with psycopg 3; with other drivers sqlty
|
|
26
|
+
checks the query and its parameters, and you annotate what the cursor returns
|
|
27
|
+
([Drivers](https://jirikuncar.github.io/sqlty/guide/drivers-and-orms/)).
|
|
28
|
+
|
|
29
|
+
> **Beta.** APIs and configuration may change before 1.0. Feedback welcome in
|
|
30
|
+
> [GitHub Issues](https://github.com/jirikuncar/sqlty/issues).
|
|
31
|
+
|
|
32
|
+
## How it works
|
|
33
|
+
|
|
34
|
+
Drivers return rows of `Any`, so nothing ties a query's columns to the code
|
|
35
|
+
that reads them. Start a query with `--sql` and a newline, and
|
|
36
|
+
`sqlty rewrite .` adds the row type inferred from your `CREATE TABLE`
|
|
37
|
+
statements:
|
|
38
|
+
|
|
39
|
+
```toml
|
|
40
|
+
# pyproject.toml
|
|
41
|
+
[tool.sqlty]
|
|
42
|
+
schema = "schema.sql"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`schema.sql`:
|
|
46
|
+
|
|
47
|
+
```sql
|
|
48
|
+
CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT NOT NULL, email VARCHAR(255) NOT NULL);
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
query = "--sql\nSELECT id, name, email FROM users"
|
|
53
|
+
# rewritten: query = SQL[tuple[int, str, str]]("--sql\nSELECT id, name, email FROM users")
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`SQL[T]` is a `str` subclass, so the query still runs anywhere a string does,
|
|
57
|
+
and mypy, pyright and ty see `T`. Without a schema, casts such as `id::INTEGER`
|
|
58
|
+
type the columns; a query with neither stays as it is. With a space instead of
|
|
59
|
+
the newline, `"--sql SELECT ..."` is a SQL comment, and `sqlty check` warns.
|
|
60
|
+
|
|
61
|
+
## What it catches
|
|
62
|
+
|
|
63
|
+
### A migration changes a column
|
|
64
|
+
|
|
65
|
+
```toml
|
|
66
|
+
[tool.sqlty]
|
|
67
|
+
schema = "schema.sql"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
A migration dropped `NOT NULL` from `email`.
|
|
71
|
+
|
|
72
|
+
`schema.sql`:
|
|
73
|
+
|
|
74
|
+
```sql
|
|
75
|
+
CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT);
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from sqlty import SQL
|
|
80
|
+
|
|
81
|
+
# error: [narrow-type] "SQL[tuple[int, str]] should be SQL[tuple[int, str | None]]"
|
|
82
|
+
query = SQL[tuple[int, str]]("--sql\nSELECT id, email FROM users")
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
`sqlty check --fix .` updates the type, and your type checker finds every
|
|
86
|
+
caller that now needs a `None` check. `narrow-type` is an error, so CI fails on
|
|
87
|
+
it; an annotation that is only wider than the rows, such as `str | None` for a
|
|
88
|
+
column made `NOT NULL`, is a `stale-type` warning
|
|
89
|
+
([CI](https://jirikuncar.github.io/sqlty/recipes/ci/)). For rolling deploys,
|
|
90
|
+
`sqlty breaking --against origin/main` exits 1 when old code breaks on the new
|
|
91
|
+
schema or new code on the old one
|
|
92
|
+
([Breaking schema changes](https://jirikuncar.github.io/sqlty/guide/breaking-changes/)).
|
|
93
|
+
|
|
94
|
+
### A row type that doesn't match its query
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from typing import TypedDict
|
|
98
|
+
from sqlty import SQL
|
|
99
|
+
|
|
100
|
+
class User(TypedDict):
|
|
101
|
+
id: str
|
|
102
|
+
name: str
|
|
103
|
+
|
|
104
|
+
# error: [column-type-mismatch] "User.id is str but query column 'id' infers int"
|
|
105
|
+
query = SQL[User]("--sql\nSELECT id::INTEGER, name::TEXT FROM users")
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`TypedDict`, `NamedTuple`, dataclasses and Pydantic models are all checked
|
|
109
|
+
([Row types](https://jirikuncar.github.io/sqlty/guide/row-types/)).
|
|
110
|
+
|
|
111
|
+
### Wrong parameters, misspelled columns
|
|
112
|
+
|
|
113
|
+
```toml
|
|
114
|
+
[tool.sqlty]
|
|
115
|
+
dialect = "sqlite"
|
|
116
|
+
schema = "schema.sql"
|
|
117
|
+
|
|
118
|
+
[tool.sqlty.rules]
|
|
119
|
+
unknown-column = "warn"
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`schema.sql`:
|
|
123
|
+
|
|
124
|
+
```sql
|
|
125
|
+
CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
```python
|
|
129
|
+
import sqlite3
|
|
130
|
+
from sqlty import SQL
|
|
131
|
+
|
|
132
|
+
conn = sqlite3.connect("app.db")
|
|
133
|
+
|
|
134
|
+
# error: [param-count-mismatch] "query has 1 parameter, the call passes 2"
|
|
135
|
+
conn.execute(SQL[tuple[str | None]]("--sql\nSELECT email FROM users WHERE id = ?"), (1, 2))
|
|
136
|
+
|
|
137
|
+
# error: [unknown-column] "column `emial` does not exist in `users`"
|
|
138
|
+
conn.execute("--sql\nSELECT emial FROM users")
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
`?`, `$1`, `%s` and `:name` placeholders are checked on sqlite3, psycopg and
|
|
142
|
+
asyncpg calls ([Parameters](https://jirikuncar.github.io/sqlty/guide/parameters/)).
|
|
143
|
+
These are the mistakes coding agents make: every problem is one
|
|
144
|
+
`file:line: [rule] message` line and a non-zero exit code they can act on, and
|
|
145
|
+
the [sqlty skills](https://jirikuncar.github.io/sqlty/recipes/agents/) teach
|
|
146
|
+
them the conventions. In Claude Code, `/plugin install sqlty --marketplace jirikuncar/sqlty`
|
|
147
|
+
installs them.
|
|
148
|
+
|
|
149
|
+
## Get started
|
|
150
|
+
|
|
151
|
+
```bash
|
|
152
|
+
pip install "sqlty[psycopg]" # or: uv add sqlty; the extra adds the psycopg helpers
|
|
153
|
+
printf '%s\n' 'q = "--sql\nSELECT id::INTEGER, name::TEXT FROM users"' > queries.py
|
|
154
|
+
sqlty rewrite -v . # q = SQL[tuple[int, str]]("--sql\n...")
|
|
155
|
+
sqlty check . # report drift and mismatches
|
|
156
|
+
sqlty strip . # undo: back to plain strings
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`rewrite` and `strip` only touch the wrapper, never the SQL, so you can adopt
|
|
160
|
+
sqlty one module at a time and back out. New project:
|
|
161
|
+
[Quickstart](https://jirikuncar.github.io/sqlty/quickstart/). Existing codebase:
|
|
162
|
+
[Migration](https://jirikuncar.github.io/sqlty/recipes/migration/), starting
|
|
163
|
+
with `pg_dump --schema-only app > schema.sql`.
|
|
164
|
+
|
|
165
|
+
Not a fit for ORMs, SQL built at runtime, or runtime validation;
|
|
166
|
+
[Why SQLTy?](https://jirikuncar.github.io/sqlty/motivation/) compares it with
|
|
167
|
+
sqlc, sqlx, PgTyped and others.
|
|
168
|
+
|
|
169
|
+
## About
|
|
170
|
+
|
|
171
|
+
The engine is Rust ([sqlparser-rs](https://github.com/apache/datafusion-sqlparser-rs)
|
|
172
|
+
via [PyO3](https://pyo3.rs)), with wheels for Linux, macOS and Windows. SQLTy is
|
|
173
|
+
developed with AI coding agents, mainly Claude Code; every change is reviewed by
|
|
174
|
+
a maintainer (see the
|
|
175
|
+
[AI-assisted contributions](https://github.com/jirikuncar/sqlty/blob/main/CONTRIBUTING.md#ai-assisted-contributions)
|
|
176
|
+
policy). [Contributing](https://github.com/jirikuncar/sqlty/blob/main/CONTRIBUTING.md) ·
|
|
177
|
+
[Credits](https://jirikuncar.github.io/sqlty/credits/) ·
|
|
178
|
+
MIT [License](https://github.com/jirikuncar/sqlty/blob/main/LICENSE).
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "sqlty"
|
|
3
|
+
dynamic = ["version"]
|
|
4
|
+
description = "Type-safe SQL queries for Python"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.14"
|
|
7
|
+
license = "MIT"
|
|
8
|
+
license-files = ["LICENSE"]
|
|
9
|
+
authors = [{ name = "Jiri Kuncar", email = "jiri.kuncar@gmail.com" }]
|
|
10
|
+
keywords = [
|
|
11
|
+
"sql",
|
|
12
|
+
"type-hints",
|
|
13
|
+
"mypy",
|
|
14
|
+
"type-safety",
|
|
15
|
+
"static-analysis",
|
|
16
|
+
"code-generation",
|
|
17
|
+
]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Development Status :: 4 - Beta",
|
|
20
|
+
"Environment :: Console",
|
|
21
|
+
"Intended Audience :: Developers",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
24
|
+
"Programming Language :: Python :: 3.14",
|
|
25
|
+
"Programming Language :: Python :: 3.15",
|
|
26
|
+
"Programming Language :: Rust",
|
|
27
|
+
"Programming Language :: SQL",
|
|
28
|
+
"Topic :: Software Development :: Code Generators",
|
|
29
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
30
|
+
"Topic :: Database",
|
|
31
|
+
"Typing :: Typed",
|
|
32
|
+
]
|
|
33
|
+
dependencies = [
|
|
34
|
+
"pathspec>=0.12.1",
|
|
35
|
+
"typing-extensions>=4.15.0; python_version < '3.15'",
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://github.com/jirikuncar/sqlty"
|
|
40
|
+
Documentation = "https://jirikuncar.github.io/sqlty/"
|
|
41
|
+
Repository = "https://github.com/jirikuncar/sqlty"
|
|
42
|
+
Changelog = "https://github.com/jirikuncar/sqlty/releases"
|
|
43
|
+
Issues = "https://github.com/jirikuncar/sqlty/issues"
|
|
44
|
+
|
|
45
|
+
[project.scripts]
|
|
46
|
+
sqlty = "sqlty._cli:main"
|
|
47
|
+
|
|
48
|
+
[project.optional-dependencies]
|
|
49
|
+
psycopg = ["psycopg>=3.2.12"]
|
|
50
|
+
|
|
51
|
+
[build-system]
|
|
52
|
+
requires = ["maturin>=1.12.1,<2.0"]
|
|
53
|
+
build-backend = "maturin"
|
|
54
|
+
|
|
55
|
+
[tool.maturin]
|
|
56
|
+
python-source = "src"
|
|
57
|
+
module-name = "sqlty._core"
|
|
58
|
+
features = ["pyo3/extension-module"]
|
|
59
|
+
manifest-path = "rust/crates/sqlty-ext/Cargo.toml"
|
|
60
|
+
# Ship the startup hook at the wheel root so it lands in site-packages and
|
|
61
|
+
# registers the lazy "sqlty" codec lookup (see docs/guide/magic-codec.md).
|
|
62
|
+
# The path is relative to the project root (maturin >= 1.12.1): the wheel gets
|
|
63
|
+
# it at the root (python-source prefix stripped), the sdist at src/.
|
|
64
|
+
# The Agent Skills are compiled into the extension, so only the sdist needs them.
|
|
65
|
+
include = ["src/sqlty_magic.pth", { path = "skills/**/*", format = "sdist" }]
|
|
66
|
+
|
|
67
|
+
[dependency-groups]
|
|
68
|
+
dev = [
|
|
69
|
+
"inline-snapshot>=0.15.0",
|
|
70
|
+
"maturin>=1.12.1,<2.0",
|
|
71
|
+
"mypy>=2.4.0",
|
|
72
|
+
"prek>=0.5.4",
|
|
73
|
+
"pyright>=1.1.414",
|
|
74
|
+
"pytest>=8.4.2",
|
|
75
|
+
"pytest-cov>=6.0.0",
|
|
76
|
+
"pytest-randomly>=3.16.0",
|
|
77
|
+
"pyyaml>=6.0.2",
|
|
78
|
+
"ruff>=0.16.10",
|
|
79
|
+
"ty>=0.0.85",
|
|
80
|
+
"rumdl>=0.3.0",
|
|
81
|
+
"vale>=3.22.0.0",
|
|
82
|
+
"zizmor>=1.31.1",
|
|
83
|
+
]
|
|
84
|
+
cli = ["rich>=15.0.0"]
|
|
85
|
+
# Only the DuckDB type tests need it, and it has no wheel for every Python.
|
|
86
|
+
duckdb = ["duckdb>=1.4.0"]
|
|
87
|
+
psycopg = ["psycopg>=3.2.12"]
|
|
88
|
+
fuzz = [
|
|
89
|
+
"hypothesis>=6.140",
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
[tool.pytest.ini_options]
|
|
93
|
+
addopts = ["-p", "tests._mdtest.plugin", "--cov-report=term-missing", "--cov-report=html", "-v"]
|
|
94
|
+
pythonpath = ["."]
|
|
95
|
+
testpaths = ["tests", "docs", "src/sqlty/rules", "README.md"]
|
|
96
|
+
mdtest_paths = ["tests/mdtest", "docs", "src/sqlty/rules", "README.md", "skills"]
|
|
97
|
+
markers = [
|
|
98
|
+
"e2e: end-to-end tests (subprocess + inline-snapshot)",
|
|
99
|
+
"slow: slow tests that can be skipped with -m 'not slow'",
|
|
100
|
+
"pg: needs a PostgreSQL server (SQLTY_PG_DSN); skipped without it",
|
|
101
|
+
]
|
|
102
|
+
|
|
103
|
+
[tool.coverage.run]
|
|
104
|
+
concurrency = ["multiprocessing"]
|
|
105
|
+
parallel = true
|
|
106
|
+
sigterm = true
|
|
107
|
+
patch = ["subprocess"]
|
|
108
|
+
omit = ["tests/*", "examples/*"]
|
|
109
|
+
|
|
110
|
+
[tool.coverage.paths]
|
|
111
|
+
source = ["src/"]
|
|
112
|
+
|
|
113
|
+
[tool.coverage.combine]
|
|
114
|
+
show_missing = true
|
|
115
|
+
|
|
116
|
+
[tool.mypy]
|
|
117
|
+
python_version = "3.15"
|
|
118
|
+
strict = true
|
|
119
|
+
warn_return_any = true
|
|
120
|
+
warn_unused_configs = true
|
|
121
|
+
disallow_untyped_defs = true
|
|
122
|
+
disallow_any_generics = true
|
|
123
|
+
check_untyped_defs = true
|
|
124
|
+
warn_redundant_casts = true
|
|
125
|
+
warn_unused_ignores = true
|
|
126
|
+
warn_no_return = true
|
|
127
|
+
|
|
128
|
+
[[tool.mypy.overrides]]
|
|
129
|
+
module = "psycopg.*"
|
|
130
|
+
ignore_missing_imports = true
|
|
131
|
+
|
|
132
|
+
[tool.ty.environment]
|
|
133
|
+
# Python version will be inferred from requires-python
|
|
134
|
+
root = ["./src", "./examples"]
|
|
135
|
+
|
|
136
|
+
[tool.ty.src]
|
|
137
|
+
include = ["src", "examples"]
|
|
138
|
+
|
|
139
|
+
[tool.ruff]
|
|
140
|
+
target-version = "py314"
|
|
141
|
+
line-length = 100
|
|
142
|
+
# Markdown code blocks are test data (mdtest, README) and must not be reformatted.
|
|
143
|
+
extend-exclude = ["tests/typechecks", "*.md"]
|
|
144
|
+
|
|
145
|
+
[tool.ruff.lint]
|
|
146
|
+
select = [
|
|
147
|
+
"E", # pycodestyle errors
|
|
148
|
+
"W", # pycodestyle warnings
|
|
149
|
+
"F", # pyflakes
|
|
150
|
+
"I", # isort
|
|
151
|
+
"UP", # pyupgrade
|
|
152
|
+
"B", # flake8-bugbear
|
|
153
|
+
"C4", # flake8-comprehensions
|
|
154
|
+
"SIM", # flake8-simplify
|
|
155
|
+
]
|
|
156
|
+
ignore = [
|
|
157
|
+
"E501", # line too long (handled by formatter)
|
|
158
|
+
"SIM102", # nested if statements (can be more readable)
|
|
159
|
+
]
|
|
160
|
+
|
|
161
|
+
[tool.ruff.format]
|
|
162
|
+
quote-style = "double"
|
|
163
|
+
indent-style = "space"
|
|
164
|
+
|
|
165
|
+
[tool.rumdl]
|
|
166
|
+
# Long lines are tables and links; spaces in code spans show `--sql ` on purpose.
|
|
167
|
+
disable = ["MD013", "MD038"]
|
|
168
|
+
exclude = ["site", ".vale", ".github", "tests", "scripts/compat/reports", "PLAN-FUTURE.md", "rust/target", "proofs/.lake"]
|
|
169
|
+
|
|
170
|
+
[tool.rumdl.per-file-ignores]
|
|
171
|
+
# The demo GIF needs HTML for its width and centering.
|
|
172
|
+
"README.md" = ["MD033"]
|