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.
Files changed (228) hide show
  1. {tai_sql-0.7.2 → tai_sql-0.7.9}/PKG-INFO +4 -1
  2. {tai_sql-0.7.2 → tai_sql-0.7.9}/pyproject.toml +5 -1
  3. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/__init__.py +2 -0
  4. tai_sql-0.7.9/tai_sql/cli/commands/cmd_hash_migrate/__init__.py +3 -0
  5. tai_sql-0.7.9/tai_sql/cli/commands/cmd_hash_migrate/main.py +164 -0
  6. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_init/model.py +2 -0
  7. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_schema/schemas/auth.py +12 -3
  8. tai_sql-0.7.9/tai_sql/cli/commands/cmd_schema/schemas/chatbot.py +637 -0
  9. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/main.py +2 -0
  10. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/feed/README.md +17 -1
  11. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/feed/executor.py +11 -2
  12. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/feed/feeder.py +24 -0
  13. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/feed/transform.py +49 -7
  14. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/__init__.py +9 -0
  15. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/context.py +1 -0
  16. tai_sql-0.7.9/tai_sql/generators/python_client/templates/base.py.jinja2 +90 -0
  17. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/domain/dao.py.jinja2 +25 -0
  18. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/domain/dtos.py.jinja2 +19 -3
  19. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/domain/model.py.jinja2 +35 -7
  20. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/macros/filters.jinja2 +7 -7
  21. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/macros/read.jinja2 +53 -0
  22. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/macros/upsert.jinja2 +1 -1
  23. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/macros/write.jinja2 +53 -4
  24. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/analysis/column_analyzer.py +70 -0
  25. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/columns.py +8 -2
  26. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/model/column.py +4 -1
  27. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/model/entity.py +5 -0
  28. tai_sql-0.7.2/tai_sql/cli/commands/cmd_schema/schemas/chatbot.py +0 -318
  29. tai_sql-0.7.2/tai_sql/generators/python_client/templates/base.py.jinja2 +0 -46
  30. {tai_sql-0.7.2 → tai_sql-0.7.9}/LICENSE +0 -0
  31. {tai_sql-0.7.2 → tai_sql-0.7.9}/README.md +0 -0
  32. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/__init__.py +0 -0
  33. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_backup/__init__.py +0 -0
  34. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_backup/main.py +0 -0
  35. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_deploy/__init__.py +0 -0
  36. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_deploy/config.py +0 -0
  37. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_deploy/local.py +0 -0
  38. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_deploy/main.py +0 -0
  39. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_deploy/workflow.py +0 -0
  40. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_feed/__init__.py +0 -0
  41. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_feed/main.py +0 -0
  42. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_generate/__init__.py +0 -0
  43. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_generate/funcs.py +0 -0
  44. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_generate/main.py +0 -0
  45. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_info/__init__.py +0 -0
  46. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_info/main.py +0 -0
  47. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_info/status.py +0 -0
  48. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_init/__init__.py +0 -0
  49. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_init/main.py +0 -0
  50. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_install/__init__.py +0 -0
  51. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_install/main.py +0 -0
  52. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_ping/__init__.py +0 -0
  53. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_ping/main.py +0 -0
  54. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_pull/__init__.py +0 -0
  55. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_pull/funcs.py +0 -0
  56. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_pull/main.py +0 -0
  57. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_restore/__init__.py +0 -0
  58. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_restore/main.py +0 -0
  59. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_rules/__init__.py +0 -0
  60. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_rules/main.py +0 -0
  61. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_schema/__init__.py +0 -0
  62. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_schema/model.py +0 -0
  63. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_schema/new.py +0 -0
  64. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_schema/schemas/__init__.py +0 -0
  65. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_schema/schemas/base.py +0 -0
  66. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_schema/schemas/default.py +0 -0
  67. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/cmd_schema/set_default.py +0 -0
  68. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/prompts.py +0 -0
  69. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/utils/__init__.py +0 -0
  70. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/utils/connectivity.py +0 -0
  71. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/utils/create.py +0 -0
  72. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/utils/github/__init__.py +0 -0
  73. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/utils/github/auth.py +0 -0
  74. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/utils/github/client.py +0 -0
  75. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/commands/utils/github/models.py +0 -0
  76. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/errors.py +0 -0
  77. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/preamble.py +0 -0
  78. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/services/__init__.py +0 -0
  79. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/cli/services/backup.py +0 -0
  80. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/connection/README.md +0 -0
  81. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/connection/__init__.py +0 -0
  82. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/connection/provider.py +0 -0
  83. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/connection/recipe.py +0 -0
  84. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/connection/session.py +0 -0
  85. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/connection/sources.py +0 -0
  86. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/crypto.py +0 -0
  87. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/drivers/__init__.py +0 -0
  88. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/drivers/base.py +0 -0
  89. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/drivers/bigquery.py +0 -0
  90. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/drivers/capabilities.py +0 -0
  91. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/drivers/mysql.py +0 -0
  92. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/drivers/postgresql.py +0 -0
  93. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/drivers/sqlserver.py +0 -0
  94. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/errors/README.md +0 -0
  95. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/errors/__init__.py +0 -0
  96. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/errors/collector.py +0 -0
  97. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/errors/render.py +0 -0
  98. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/errors/types.py +0 -0
  99. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/feed/__init__.py +0 -0
  100. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/feed/collect.py +0 -0
  101. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/feed/plan.py +0 -0
  102. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/feed/presenter.py +0 -0
  103. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/feed/records.py +0 -0
  104. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/__init__.py +0 -0
  105. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/base.py +0 -0
  106. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/README.md +0 -0
  107. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/__init__.py +0 -0
  108. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/assets/README.md +0 -0
  109. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/assets/cytoscape.min.js +0 -0
  110. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/builder.py +0 -0
  111. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/generator.py +0 -0
  112. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/graphviz_render.py +0 -0
  113. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/html_render.py +0 -0
  114. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/layout.py +0 -0
  115. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/model.py +0 -0
  116. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/templates/er.html.jinja2 +0 -0
  117. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/diagram/themes.py +0 -0
  118. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/calculated.py +0 -0
  119. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/domain/__init__.py.jinja2 +0 -0
  120. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/facade.py.jinja2 +0 -0
  121. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/macros/aggregation.jinja2 +0 -0
  122. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/macros/dataframe.jinja2 +0 -0
  123. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/macros/hierarchy.jinja2 +0 -0
  124. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/macros/triggers.jinja2 +0 -0
  125. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/macros/vector.jinja2 +0 -0
  126. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/session.py.jinja2 +0 -0
  127. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/shared/__init__.py.jinja2 +0 -0
  128. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/shared/aggregation.py.jinja2 +0 -0
  129. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/shared/context.py.jinja2 +0 -0
  130. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/shared/utils/__init__.py.jinja2 +0 -0
  131. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/shared/utils/encoders.py.jinja2 +0 -0
  132. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/shared/utils/funcs.py.jinja2 +0 -0
  133. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/shared/utils/hierarchy.py.jinja2 +0 -0
  134. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/shared/utils/rls.py.jinja2 +0 -0
  135. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/templates/shared/vector.py.jinja2 +0 -0
  136. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/triggers/__init__.py +0 -0
  137. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/triggers/analyzer.py +0 -0
  138. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/triggers/renderer.py +0 -0
  139. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/triggers/steps.py +0 -0
  140. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/python_client/triggers/transformer.py +0 -0
  141. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/rules/__init__.py +0 -0
  142. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/generators/rules/templates/schema.md.jinja2 +0 -0
  143. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/introspect/README.md +0 -0
  144. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/introspect/__init__.py +0 -0
  145. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/introspect/blueprint.py +0 -0
  146. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/introspect/build.py +0 -0
  147. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/introspect/defaults.py +0 -0
  148. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/introspect/naming.py +0 -0
  149. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/introspect/reader.py +0 -0
  150. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/introspect/templates/schema.py.jinja2 +0 -0
  151. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/introspect/types.py +0 -0
  152. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/introspect/writer.py +0 -0
  153. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/README.md +0 -0
  154. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/__init__.py +0 -0
  155. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/analysis/README.md +0 -0
  156. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/analysis/__init__.py +0 -0
  157. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/analysis/calculated.py +0 -0
  158. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/analysis/collector.py +0 -0
  159. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/analysis/constraints.py +0 -0
  160. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/analysis/hints.py +0 -0
  161. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/analysis/relation_analyzer.py +0 -0
  162. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/analysis/schema_analyzer.py +0 -0
  163. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/README.md +0 -0
  164. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/__init__.py +0 -0
  165. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/annotations.py +0 -0
  166. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/base.py +0 -0
  167. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/calculated.py +0 -0
  168. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/constraints.py +0 -0
  169. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/datasource.py +0 -0
  170. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/descriptors.py +0 -0
  171. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/encoders.py +0 -0
  172. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/enumerations.py +0 -0
  173. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/relations.py +0 -0
  174. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/table.py +0 -0
  175. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/trigger_api.py +0 -0
  176. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/triggers.py +0 -0
  177. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/declarative/view.py +0 -0
  178. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/mapping/README.md +0 -0
  179. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/mapping/__init__.py +0 -0
  180. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/mapping/sqlalchemy_mapper.py +0 -0
  181. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/model/README.md +0 -0
  182. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/model/__init__.py +0 -0
  183. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/model/constraints.py +0 -0
  184. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/model/enumeration.py +0 -0
  185. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/model/relation.py +0 -0
  186. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/model/schema.py +0 -0
  187. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/model/trigger.py +0 -0
  188. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/naming.py +0 -0
  189. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/ordering.py +0 -0
  190. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/registry.py +0 -0
  191. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/orm/types.py +0 -0
  192. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/project/README.md +0 -0
  193. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/project/__init__.py +0 -0
  194. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/project/config.py +0 -0
  195. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/project/context.py +0 -0
  196. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/project/manager.py +0 -0
  197. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/project/paths.py +0 -0
  198. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/rules/README.md +0 -0
  199. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/rules/__init__.py +0 -0
  200. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/rules/catalog.py +0 -0
  201. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/rules/installer.py +0 -0
  202. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/rules/meta.py +0 -0
  203. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/rules/service.py +0 -0
  204. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/rules/staleness.py +0 -0
  205. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/rules/static/comandos.md +0 -0
  206. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/rules/static/errores.md +0 -0
  207. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/rules/static/modelo.md +0 -0
  208. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/schema.py +0 -0
  209. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/README.md +0 -0
  210. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/__init__.py +0 -0
  211. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/diff/__init__.py +0 -0
  212. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/diff/constraints.py +0 -0
  213. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/diff/indexes.py +0 -0
  214. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/diff/tables.py +0 -0
  215. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/diff/views.py +0 -0
  216. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/executor.py +0 -0
  217. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/plan/__init__.py +0 -0
  218. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/plan/planner.py +0 -0
  219. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/plan/statements.py +0 -0
  220. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/presenter.py +0 -0
  221. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/report.py +0 -0
  222. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/safety.py +0 -0
  223. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/state/__init__.py +0 -0
  224. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/state/local.py +0 -0
  225. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/state/model.py +0 -0
  226. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/state/normalize.py +0 -0
  227. {tai_sql-0.7.2 → tai_sql-0.7.9}/tai_sql/sync/state/remote.py +0 -0
  228. {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.2
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.2"
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,3 @@
1
+ from .main import hash_migrate
2
+
3
+ __all__ = ['hash_migrate']
@@ -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=False,
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(encrypt=True, description="Contraseña encriptada")
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")