tai-sql 0.7.2__tar.gz → 0.7.9__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.
- {tai_sql-0.7.2 → tai_sql-0.7.9}/PKG-INFO +4 -1
- {tai_sql-0.7.2 → tai_sql-0.7.9}/pyproject.toml +5 -1
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/__init__.py +2 -0
- tai_sql-0.7.9/tai_sql/cli/commands/cmd_hash_migrate/__init__.py +3 -0
- tai_sql-0.7.9/tai_sql/cli/commands/cmd_hash_migrate/main.py +164 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_init/model.py +2 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_schema/schemas/auth.py +12 -3
- tai_sql-0.7.9/tai_sql/cli/commands/cmd_schema/schemas/chatbot.py +637 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/main.py +2 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/feed/README.md +17 -1
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/feed/executor.py +11 -2
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/feed/feeder.py +24 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/feed/transform.py +49 -7
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/__init__.py +9 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/context.py +1 -0
- tai_sql-0.7.9/tai_sql/generators/python_client/templates/base.py.jinja2 +90 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/domain/dao.py.jinja2 +25 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/domain/dtos.py.jinja2 +19 -3
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/domain/model.py.jinja2 +35 -7
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/macros/filters.jinja2 +7 -7
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/macros/read.jinja2 +53 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/macros/upsert.jinja2 +1 -1
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/macros/write.jinja2 +53 -4
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/analysis/column_analyzer.py +70 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/columns.py +8 -2
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/model/column.py +4 -1
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/model/entity.py +5 -0
- tai_sql-0.7.2/tai_sql/cli/commands/cmd_schema/schemas/chatbot.py +0 -318
- tai_sql-0.7.2/tai_sql/generators/python_client/templates/base.py.jinja2 +0 -46
- {tai_sql-0.7.2 → tai_sql-0.7.9}/LICENSE +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/README.md +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_backup/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_backup/main.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_deploy/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_deploy/config.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_deploy/local.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_deploy/main.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_deploy/workflow.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_feed/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_feed/main.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_generate/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_generate/funcs.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_generate/main.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_info/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_info/main.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_info/status.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_init/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_init/main.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_install/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_install/main.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_ping/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_ping/main.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_pull/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_pull/funcs.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_pull/main.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_restore/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_restore/main.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_rules/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_rules/main.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_schema/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_schema/model.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_schema/new.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_schema/schemas/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_schema/schemas/base.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_schema/schemas/default.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_schema/set_default.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/prompts.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/utils/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/utils/connectivity.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/utils/create.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/utils/github/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/utils/github/auth.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/utils/github/client.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/utils/github/models.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/errors.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/preamble.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/services/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/services/backup.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/connection/README.md +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/connection/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/connection/provider.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/connection/recipe.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/connection/session.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/connection/sources.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/crypto.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/drivers/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/drivers/base.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/drivers/bigquery.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/drivers/capabilities.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/drivers/mysql.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/drivers/postgresql.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/drivers/sqlserver.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/errors/README.md +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/errors/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/errors/collector.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/errors/render.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/errors/types.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/feed/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/feed/collect.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/feed/plan.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/feed/presenter.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/feed/records.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/base.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/README.md +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/assets/README.md +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/assets/cytoscape.min.js +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/builder.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/generator.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/graphviz_render.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/html_render.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/layout.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/model.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/templates/er.html.jinja2 +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/themes.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/calculated.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/domain/__init__.py.jinja2 +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/facade.py.jinja2 +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/macros/aggregation.jinja2 +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/macros/dataframe.jinja2 +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/macros/hierarchy.jinja2 +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/macros/triggers.jinja2 +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/macros/vector.jinja2 +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/session.py.jinja2 +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/shared/__init__.py.jinja2 +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/shared/aggregation.py.jinja2 +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/shared/context.py.jinja2 +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/shared/utils/__init__.py.jinja2 +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/shared/utils/encoders.py.jinja2 +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/shared/utils/funcs.py.jinja2 +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/shared/utils/hierarchy.py.jinja2 +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/shared/utils/rls.py.jinja2 +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/shared/vector.py.jinja2 +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/triggers/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/triggers/analyzer.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/triggers/renderer.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/triggers/steps.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/triggers/transformer.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/rules/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/rules/templates/schema.md.jinja2 +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/introspect/README.md +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/introspect/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/introspect/blueprint.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/introspect/build.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/introspect/defaults.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/introspect/naming.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/introspect/reader.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/introspect/templates/schema.py.jinja2 +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/introspect/types.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/introspect/writer.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/README.md +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/analysis/README.md +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/analysis/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/analysis/calculated.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/analysis/collector.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/analysis/constraints.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/analysis/hints.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/analysis/relation_analyzer.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/analysis/schema_analyzer.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/README.md +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/annotations.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/base.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/calculated.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/constraints.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/datasource.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/descriptors.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/encoders.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/enumerations.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/relations.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/table.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/trigger_api.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/triggers.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/view.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/mapping/README.md +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/mapping/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/mapping/sqlalchemy_mapper.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/model/README.md +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/model/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/model/constraints.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/model/enumeration.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/model/relation.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/model/schema.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/model/trigger.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/naming.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/ordering.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/registry.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/types.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/project/README.md +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/project/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/project/config.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/project/context.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/project/manager.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/project/paths.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/rules/README.md +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/rules/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/rules/catalog.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/rules/installer.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/rules/meta.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/rules/service.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/rules/staleness.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/rules/static/comandos.md +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/rules/static/errores.md +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/rules/static/modelo.md +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/schema.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/README.md +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/diff/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/diff/constraints.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/diff/indexes.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/diff/tables.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/diff/views.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/executor.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/plan/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/plan/planner.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/plan/statements.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/presenter.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/report.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/safety.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/state/__init__.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/state/local.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/state/model.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/state/normalize.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/state/remote.py +0 -0
- {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/synchronizer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: tai-sql
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.9
|
|
4
4
|
Summary: SQL database management and code generation tool
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -22,9 +22,12 @@ Provides-Extra: bigquery
|
|
|
22
22
|
Provides-Extra: deploy
|
|
23
23
|
Provides-Extra: diagrams
|
|
24
24
|
Provides-Extra: encryption
|
|
25
|
+
Provides-Extra: hashing
|
|
25
26
|
Provides-Extra: mysql
|
|
26
27
|
Provides-Extra: sqlserver
|
|
27
28
|
Provides-Extra: vectors
|
|
29
|
+
Requires-Dist: bcrypt (>=4.0.0,<6.0) ; extra == "all"
|
|
30
|
+
Requires-Dist: bcrypt (>=4.0.0,<6.0) ; extra == "hashing"
|
|
28
31
|
Requires-Dist: click (>=8.2.1,<9.0)
|
|
29
32
|
Requires-Dist: cryptography (>=45.0.4,<46.0) ; extra == "all"
|
|
30
33
|
Requires-Dist: cryptography (>=45.0.4,<46.0) ; extra == "encryption"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "tai-sql"
|
|
3
|
-
version = "0.7.
|
|
3
|
+
version = "0.7.9"
|
|
4
4
|
description = "SQL database management and code generation tool"
|
|
5
5
|
license = "MIT"
|
|
6
6
|
license-files = ["LICENSE"]
|
|
@@ -89,6 +89,9 @@ bigquery = ["sqlalchemy-bigquery>=1.11.0,<2.0"]
|
|
|
89
89
|
# Graphviz en el sistema (`apt install graphviz`), que pip no instala.
|
|
90
90
|
vectors = ["pgvector>=0.3.0,<1.0"]
|
|
91
91
|
encryption = ["cryptography>=45.0.4,<46.0"]
|
|
92
|
+
# `hashing`: alguna `column(hash=True)`. Lo necesitan `feed` —hashea lo que inserta— y
|
|
93
|
+
# `hash-migrate`, que reescribe una columna ya existente.
|
|
94
|
+
hashing = ["bcrypt>=4.0.0,<6.0"]
|
|
92
95
|
diagrams = ["graphviz>=0.20.3,<1.0"]
|
|
93
96
|
|
|
94
97
|
# ── Comandos concretos del CLI ────────────────────────────────────────────────
|
|
@@ -110,6 +113,7 @@ all = [
|
|
|
110
113
|
"sqlalchemy-bigquery>=1.11.0,<2.0",
|
|
111
114
|
"pgvector>=0.3.0,<1.0",
|
|
112
115
|
"cryptography>=45.0.4,<46.0",
|
|
116
|
+
"bcrypt>=4.0.0,<6.0",
|
|
113
117
|
"graphviz>=0.20.3,<1.0",
|
|
114
118
|
"tai-storage>=0.1.0",
|
|
115
119
|
"tai-alphi>=2.0.1,<3.0",
|
|
@@ -2,6 +2,7 @@ from .cmd_deploy import push
|
|
|
2
2
|
from .cmd_schema import new_schema, set_default_schema
|
|
3
3
|
from .cmd_init import init
|
|
4
4
|
from .cmd_generate import generate, run_generate
|
|
5
|
+
from .cmd_hash_migrate import hash_migrate
|
|
5
6
|
from .cmd_ping import ping
|
|
6
7
|
from .cmd_info import info
|
|
7
8
|
from .cmd_feed import feed
|
|
@@ -17,6 +18,7 @@ __all__ = [
|
|
|
17
18
|
'init',
|
|
18
19
|
'generate',
|
|
19
20
|
'run_generate',
|
|
21
|
+
'hash_migrate',
|
|
20
22
|
'ping',
|
|
21
23
|
'info',
|
|
22
24
|
'push',
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
`tai-sql hash-migrate`: pasa a hash una columna que hoy está cifrada o en claro.
|
|
4
|
+
|
|
5
|
+
Existe por una ventana que se cierra sola. Mientras lo almacenado siga siendo reversible
|
|
6
|
+
—`column(encrypt=True)` cifra con Fernet— se puede leer el valor original y volver a
|
|
7
|
+
escribirlo hasheado **sin que nadie tenga que volver a entrar**. En cuanto alguien cambie
|
|
8
|
+
la declaración a `hash=True` y empiece a escribir hashes encima, esa posibilidad
|
|
9
|
+
desaparece y solo queda el reseteo masivo de contraseñas.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import click
|
|
14
|
+
import sqlalchemy as sa
|
|
15
|
+
|
|
16
|
+
from tai_sql import pm
|
|
17
|
+
from tai_sql.crypto import build_cipher
|
|
18
|
+
from tai_sql.errors import ErrorCode, SchemaDefinitionError
|
|
19
|
+
|
|
20
|
+
from ...preamble import available_schemas, load_schema
|
|
21
|
+
from ..prompts import select_one
|
|
22
|
+
|
|
23
|
+
#: Prefijos con los que empieza un hash de bcrypt. Lo que ya los lleva está migrado.
|
|
24
|
+
PREFIJOS_BCRYPT = ('$2a$', '$2b$', '$2x$', '$2y$')
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def ya_esta_hasheado(valor: str) -> bool:
|
|
28
|
+
return isinstance(valor, str) and valor.startswith(PREFIJOS_BCRYPT)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@click.command('hash-migrate')
|
|
32
|
+
@click.option('--schema', '-s', help='Nombre del esquema')
|
|
33
|
+
@click.option('--table', '-t', required=True, help='Tabla que contiene la columna')
|
|
34
|
+
@click.option('--column', '-c', 'columna', required=True, help='Columna a migrar')
|
|
35
|
+
@click.option('--dry-run', '-d', is_flag=True, help='Cuenta lo que cambiaría, sin escribir')
|
|
36
|
+
def hash_migrate(schema: str, table: str, columna: str, dry_run: bool):
|
|
37
|
+
"""Convierte a hash los valores de una columna declarada con hash=True."""
|
|
38
|
+
if not schema:
|
|
39
|
+
schema = select_one('Selecciona el esquema', available_schemas())
|
|
40
|
+
load_schema(schema)
|
|
41
|
+
|
|
42
|
+
modelo = pm.db.schema_model
|
|
43
|
+
entidad = next(
|
|
44
|
+
(e for e in modelo.entities if e.tablename == table), None
|
|
45
|
+
)
|
|
46
|
+
if entidad is None:
|
|
47
|
+
raise SchemaDefinitionError(
|
|
48
|
+
message=f"La tabla '{table}' no existe en el schema '{schema}'",
|
|
49
|
+
solution=(
|
|
50
|
+
'Usa el nombre de tabla tal y como lo declara `__tablename__`: '
|
|
51
|
+
+ ', '.join(sorted(e.tablename for e in modelo.entities))
|
|
52
|
+
),
|
|
53
|
+
code=ErrorCode.MISSING_TABLE,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
col = entidad.columns.get(columna)
|
|
57
|
+
if col is None:
|
|
58
|
+
raise SchemaDefinitionError(
|
|
59
|
+
message=f"La columna '{columna}' no existe en '{table}'",
|
|
60
|
+
solution='Columnas disponibles: ' + ', '.join(sorted(entidad.columns)),
|
|
61
|
+
code=ErrorCode.INVALID_COLUMN,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
if not col.hash:
|
|
65
|
+
raise SchemaDefinitionError(
|
|
66
|
+
message=f"La columna '{table}.{columna}' no está declarada con hash=True",
|
|
67
|
+
solution=(
|
|
68
|
+
'Migrar una columna a hash es de ida: primero se declara '
|
|
69
|
+
f'`{columna}: col[str] = column(hash=True)` en el schema y se ejecuta '
|
|
70
|
+
'`tai-sql push`, y solo después se migran los valores'
|
|
71
|
+
),
|
|
72
|
+
code=ErrorCode.INVALID_COLUMN,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
claves = [c.name for c in entidad.columns.values() if c.is_primary_key]
|
|
76
|
+
if not claves:
|
|
77
|
+
raise SchemaDefinitionError(
|
|
78
|
+
message=f"La tabla '{table}' no tiene clave primaria",
|
|
79
|
+
solution='Sin ella no hay forma de reescribir cada fila por separado',
|
|
80
|
+
code=ErrorCode.INVALID_CONSTRAINT,
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
_migrar(schema, entidad.tablename, columna, claves, dry_run)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _migrar(schema: str, tabla: str, columna: str, claves: list[str], dry_run: bool) -> None:
|
|
87
|
+
import bcrypt
|
|
88
|
+
|
|
89
|
+
cipher = build_cipher(pm.db.secret_key_name)
|
|
90
|
+
if cipher is None:
|
|
91
|
+
# Sin cifrador no se puede revertir nada: lo que haya se tratará como valor en
|
|
92
|
+
# claro. Es correcto si la columna nunca estuvo cifrada, y una pérdida silenciosa
|
|
93
|
+
# si lo estuvo, así que se dice antes de tocar nada.
|
|
94
|
+
click.echo(click.style(
|
|
95
|
+
f"⚠️ No hay cifrador: falta cryptography o la variable "
|
|
96
|
+
f"'{pm.db.secret_key_name}'. Si la columna estaba cifrada, aborta y "
|
|
97
|
+
f"configúrala; si estaba en claro, puedes continuar.",
|
|
98
|
+
fg='yellow',
|
|
99
|
+
))
|
|
100
|
+
if not dry_run and not click.confirm('¿Continuar?', default=False):
|
|
101
|
+
raise click.Abort()
|
|
102
|
+
|
|
103
|
+
metadata = sa.MetaData()
|
|
104
|
+
tabla_sa = sa.Table(
|
|
105
|
+
tabla, metadata,
|
|
106
|
+
*[sa.Column(nombre, sa.String, primary_key=True) for nombre in claves],
|
|
107
|
+
sa.Column(columna, sa.String),
|
|
108
|
+
schema=pm.db.schema_name,
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
engine = pm.db.provider.engine
|
|
112
|
+
cifrados = planos = ya_hechos = nulos = 0
|
|
113
|
+
|
|
114
|
+
click.echo(f"🔐 Migrando {tabla}.{columna} a hash" + (' (dry-run)' if dry_run else ''))
|
|
115
|
+
|
|
116
|
+
with engine.begin() as conexion:
|
|
117
|
+
filas = conexion.execute(
|
|
118
|
+
sa.select(*[tabla_sa.c[n] for n in claves], tabla_sa.c[columna])
|
|
119
|
+
).all()
|
|
120
|
+
|
|
121
|
+
for fila in filas:
|
|
122
|
+
valor = fila[-1]
|
|
123
|
+
identidad = dict(zip(claves, fila[:-1]))
|
|
124
|
+
|
|
125
|
+
if valor is None:
|
|
126
|
+
nulos += 1
|
|
127
|
+
continue
|
|
128
|
+
if ya_esta_hasheado(valor):
|
|
129
|
+
ya_hechos += 1
|
|
130
|
+
continue
|
|
131
|
+
|
|
132
|
+
try:
|
|
133
|
+
if cipher is None:
|
|
134
|
+
raise ValueError('sin cifrador')
|
|
135
|
+
original = cipher.decrypt(valor.encode()).decode()
|
|
136
|
+
cifrados += 1
|
|
137
|
+
except Exception:
|
|
138
|
+
# Ni hash ni cifrado: es el valor en claro, y hashearlo es justo lo que
|
|
139
|
+
# esta migración viene a hacer.
|
|
140
|
+
original = valor
|
|
141
|
+
planos += 1
|
|
142
|
+
|
|
143
|
+
if not dry_run:
|
|
144
|
+
nuevo = bcrypt.hashpw(original.encode(), bcrypt.gensalt()).decode()
|
|
145
|
+
conexion.execute(
|
|
146
|
+
sa.update(tabla_sa)
|
|
147
|
+
.where(sa.and_(*[tabla_sa.c[n] == v for n, v in identidad.items()]))
|
|
148
|
+
.values(**{columna: nuevo})
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
click.echo()
|
|
152
|
+
click.echo(f" Descifradas y hasheadas : {cifrados}")
|
|
153
|
+
click.echo(f" En claro, hasheadas : {planos}")
|
|
154
|
+
click.echo(f" Ya estaban hasheadas : {ya_hechos}")
|
|
155
|
+
click.echo(f" Nulas, sin tocar : {nulos}")
|
|
156
|
+
click.echo()
|
|
157
|
+
|
|
158
|
+
if dry_run:
|
|
159
|
+
click.echo(click.style('Nada escrito: era una simulación.', fg='yellow'))
|
|
160
|
+
else:
|
|
161
|
+
click.echo(click.style(
|
|
162
|
+
f'✅ {cifrados + planos} filas migradas. Volver a ejecutarlo no cambia nada.',
|
|
163
|
+
fg='green',
|
|
164
|
+
))
|
|
@@ -52,6 +52,8 @@ PYPROJECT_TEMPLATE = dedent('''\
|
|
|
52
52
|
|
|
53
53
|
# Funcionalidades opcionales
|
|
54
54
|
encryption = ["cryptography (>=45.0.4,<46.0)"]
|
|
55
|
+
# column(hash=True): el cliente hashea al escribir y verifica al comprobar.
|
|
56
|
+
hashing = ["bcrypt (>=4.0.0,<6.0)"]
|
|
55
57
|
vectors = ["pgvector (>=0.4.0,<1.0)"]
|
|
56
58
|
# Encoders de columnas vectoriales. El cliente generado los implementa sobre
|
|
57
59
|
# langchain, así que estas son las librerías que importa de verdad; deja solo la
|
|
@@ -35,7 +35,7 @@ class AuthSchema(SchemaTemplate):
|
|
|
35
35
|
provider=env('MAIN_DATABASE_URL'),
|
|
36
36
|
schema='auth',
|
|
37
37
|
syntax='v2',
|
|
38
|
-
operative_fields=
|
|
38
|
+
operative_fields=True, # Añade created_at, updated_at, created_by, updated_by a todas las tablas
|
|
39
39
|
)
|
|
40
40
|
|
|
41
41
|
# Configurar los generadores
|
|
@@ -96,7 +96,7 @@ class AuthSchema(SchemaTemplate):
|
|
|
96
96
|
|
|
97
97
|
first_name: col[str | None] = column(description="Nombre del usuario")
|
|
98
98
|
last_name: col[str | None] = column(description="Apellido del usuario")
|
|
99
|
-
password: col[str] = column(
|
|
99
|
+
password: col[str] = column(hash=True, description="Contraseña hasheada: solo se puede verificar, no leer")
|
|
100
100
|
email: col[str | None] = column(description="Correo electrónico")
|
|
101
101
|
is_active: col[bool] = column(default=True, description="Usuario activo")
|
|
102
102
|
session_id: col[str | None] = column(description="ID de sesión actual (para control de concurrencia)")
|
|
@@ -120,8 +120,11 @@ class AuthSchema(SchemaTemplate):
|
|
|
120
120
|
)
|
|
121
121
|
]
|
|
122
122
|
|
|
123
|
+
# Opcional: nombre y apellidos lo son, y sin ninguno de los dos no hay nombre
|
|
124
|
+
# completo que calcular. Declarada `-> str`, la columna era NOT NULL y crear un
|
|
125
|
+
# usuario sin nombre fallaba con una violación de integridad.
|
|
123
126
|
@calculated_column
|
|
124
|
-
def full_name(self) -> str:
|
|
127
|
+
def full_name(self) -> Optional[str]:
|
|
125
128
|
return f"{self.first_name or ''} {self.last_name or ''}".strip() if self.first_name or self.last_name else None
|
|
126
129
|
|
|
127
130
|
|
|
@@ -187,6 +190,10 @@ class AuthSchema(SchemaTemplate):
|
|
|
187
190
|
class UserRole(Table):
|
|
188
191
|
"""Relación M2M entre User y Role"""
|
|
189
192
|
__tablename__ = "user_role"
|
|
193
|
+
# Un usuario tiene un rol una vez. Es además la clave natural por la que
|
|
194
|
+
# `tai-sql feed` reconoce la fila: sin ella se negaba a alimentar la tabla,
|
|
195
|
+
# y un proyecto nuevo se quedaba sin el usuario admin.
|
|
196
|
+
__constraints__ = unique_constraint("user_name", "role_name", "realm_name")
|
|
190
197
|
|
|
191
198
|
id: col[bigint] = column(primary_key=True, autoincrement=True)
|
|
192
199
|
user_name: col[str] = column(description="ID del usuario")
|
|
@@ -206,6 +213,8 @@ class AuthSchema(SchemaTemplate):
|
|
|
206
213
|
class RolePermission(Table):
|
|
207
214
|
"""Relación M2M entre Role y Permission"""
|
|
208
215
|
__tablename__ = "role_permission"
|
|
216
|
+
# Un rol tiene un permiso una vez; y es la clave natural del feed, como en UserRole.
|
|
217
|
+
__constraints__ = unique_constraint("role_name", "permission_name", "realm_name")
|
|
209
218
|
|
|
210
219
|
id: col[bigint] = column(primary_key=True, autoincrement=True)
|
|
211
220
|
role_name: col[str] = column(description="ID del rol")
|