alchemy-kit 0.2.1__tar.gz → 0.2.2__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 (118) hide show
  1. alchemy_kit-0.2.2/.import_linter_cache/alchemy_kit.meta.json +1 -0
  2. alchemy_kit-0.2.2/.import_linter_cache/b436f6e8205b1418cbee6b7347a3c7b0d1be876b.data.json +1 -0
  3. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/PKG-INFO +1 -1
  4. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/builder/_base.py +18 -8
  5. alchemy_kit-0.2.2/alchemy_kit/builder/_insert.py +152 -0
  6. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/builder/_select.py +26 -7
  7. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/builder/_temp.py +8 -14
  8. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/connect/_engine_handler.py +12 -3
  9. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/connect/_info.py +1 -2
  10. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_builders/_build_py_file.py +1 -5
  11. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_builders/_build_stub_file.py +2 -0
  12. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_builders/py_template_file.txt +0 -5
  13. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_builders/stub_template_file.txt +2 -1
  14. alchemy_kit-0.2.2/alchemy_kit/model/_table.py +62 -0
  15. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/base_model.py +5 -10
  16. alchemy_kit-0.2.2/alchemy_kit/model/units/__init__.py +19 -0
  17. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/units/_column_unit.py +245 -56
  18. alchemy_kit-0.2.2/alchemy_kit/model/units/_object_unit.py +61 -0
  19. alchemy_kit-0.2.2/alchemy_kit/model/units/_operand_unit.py +102 -0
  20. alchemy_kit-0.2.2/alchemy_kit/resources/_compiler.py +41 -0
  21. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/_identifiers.py +1 -1
  22. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/_sql.py +3 -0
  23. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/dialect_map/_base.py +19 -3
  24. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/dialect_map/mssql.py +2 -0
  25. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/sql_types.py +6 -2
  26. alchemy_kit-0.2.2/docs/model/units.md +0 -0
  27. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/pyproject.toml +4 -3
  28. alchemy_kit-0.2.2/tests/builder/test_builders.py +588 -0
  29. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/tests/connect/test_connect.py +3 -4
  30. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/tests/model/test_builder.py +13 -1
  31. alchemy_kit-0.2.1/.import_linter_cache/alchemy_kit.meta.json +0 -1
  32. alchemy_kit-0.2.1/.import_linter_cache/b436f6e8205b1418cbee6b7347a3c7b0d1be876b.data.json +0 -1
  33. alchemy_kit-0.2.1/alchemy_kit/builder/_insert.py +0 -81
  34. alchemy_kit-0.2.1/alchemy_kit/model/units/__init__.py +0 -4
  35. alchemy_kit-0.2.1/alchemy_kit/model/units/_object_unit.py +0 -95
  36. alchemy_kit-0.2.1/tests/builder/test_builders.py +0 -219
  37. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/.claude/settings.local.json +0 -0
  38. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/.gitignore +0 -0
  39. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/.grimp_cache/.gitignore +0 -0
  40. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/.grimp_cache/CACHEDIR.TAG +0 -0
  41. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/.grimp_cache/alchemy_kit.meta.json +0 -0
  42. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/.grimp_cache/b436f6e8205b1418cbee6b7347a3c7b0d1be876b.data.json +0 -0
  43. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/.import_linter_cache/.gitignore +0 -0
  44. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/.import_linter_cache/CACHEDIR.TAG +0 -0
  45. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/.python-version +0 -0
  46. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/LICENSE +0 -0
  47. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/README.md +0 -0
  48. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/__init__.py +0 -0
  49. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/builder/__init__.py +0 -0
  50. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/connect/__init__.py +0 -0
  51. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/connect/_conn_builders/__init__.py +0 -0
  52. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/connect/_conn_builders/mssql.py +0 -0
  53. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/connect/_drivers.py +0 -0
  54. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/connect/_engine_manager.py +0 -0
  55. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/connect/info_builder.py +0 -0
  56. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/__init__.py +0 -0
  57. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_builder.py +0 -0
  58. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_builders/__init__.py +0 -0
  59. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_builders/_check_parser.py +0 -0
  60. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_builders/_column_metadata.py +0 -0
  61. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_builders/_model.py +0 -0
  62. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_builders/_svg.py +0 -0
  63. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model/__init__.py +0 -0
  64. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model/column_model.py +0 -0
  65. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model/constraint_model.py +0 -0
  66. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model/db_model.py +0 -0
  67. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model/object_model.py +0 -0
  68. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model/schema_model.py +0 -0
  69. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model_def/__init__.py +0 -0
  70. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model_def/_extractor.py +0 -0
  71. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model_def/_frames.py +0 -0
  72. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model_def/_sql/mssql_check_constraints.sql +0 -0
  73. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model_def/_sql/mssql_unique_constraints.sql +0 -0
  74. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_schema_config.py +0 -0
  75. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_utils.py +0 -0
  76. /alchemy_kit-0.2.1/docs/builder/base-builder.md → /alchemy_kit-0.2.2/alchemy_kit/py.typed +0 -0
  77. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/__init__.py +0 -0
  78. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/_better_logger.py +0 -0
  79. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/_settings.py +0 -0
  80. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/dialect_map/__init__.py +0 -0
  81. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/dialect_map/mariadb.py +0 -0
  82. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/dialect_map/mysql.py +0 -0
  83. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/dialect_map/oracle.py +0 -0
  84. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/dialect_map/postgresql.py +0 -0
  85. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/dialect_map/sqlite.py +0 -0
  86. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/dir_helpers.py +0 -0
  87. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/__init__.py +0 -0
  88. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/_driver_types.py +0 -0
  89. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/_sql_parameters.py +0 -0
  90. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/_sql_utilities.py +0 -0
  91. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/api_types.py +0 -0
  92. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/auth_types.py +0 -0
  93. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/dialect_types.py +0 -0
  94. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/errors/__init__.py +0 -0
  95. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/errors/_model_validation_error.py +0 -0
  96. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/generic_path.py +0 -0
  97. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/py_type_parameters.py +0 -0
  98. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/sql_type_parameters.py +0 -0
  99. /alchemy_kit-0.2.1/docs/builder/insert-builder.md → /alchemy_kit-0.2.2/docs/builder/base-builder.md +0 -0
  100. /alchemy_kit-0.2.1/docs/builder/select-builder.md → /alchemy_kit-0.2.2/docs/builder/insert-builder.md +0 -0
  101. /alchemy_kit-0.2.1/docs/builder/temp-builder.md → /alchemy_kit-0.2.2/docs/builder/select-builder.md +0 -0
  102. /alchemy_kit-0.2.1/docs/connect/connection-info.md → /alchemy_kit-0.2.2/docs/builder/temp-builder.md +0 -0
  103. /alchemy_kit-0.2.1/docs/connect/engine-handler.md → /alchemy_kit-0.2.2/docs/connect/connection-info.md +0 -0
  104. /alchemy_kit-0.2.1/docs/connect/engine-manager.md → /alchemy_kit-0.2.2/docs/connect/engine-handler.md +0 -0
  105. /alchemy_kit-0.2.1/docs/connect/info-builder.md → /alchemy_kit-0.2.2/docs/connect/engine-manager.md +0 -0
  106. /alchemy_kit-0.2.1/docs/model/base-model.md → /alchemy_kit-0.2.2/docs/connect/info-builder.md +0 -0
  107. /alchemy_kit-0.2.1/docs/model/build.md → /alchemy_kit-0.2.2/docs/model/base-model.md +0 -0
  108. /alchemy_kit-0.2.1/docs/model/schema-config.md → /alchemy_kit-0.2.2/docs/model/build.md +0 -0
  109. /alchemy_kit-0.2.1/docs/model/units.md → /alchemy_kit-0.2.2/docs/model/schema-config.md +0 -0
  110. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/tests/connect/test_engine_handler.py +0 -0
  111. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/tests/dotenv_test +0 -0
  112. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/tests/model/test_check_codegen.py +0 -0
  113. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/tests/model/test_check_parser.py +0 -0
  114. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/tests/model/test_fk_validation.py +0 -0
  115. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/tests/model/test_inspector_def.py +0 -0
  116. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/tests/resources/test_reflected_type_facts.py +0 -0
  117. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/tests/test_json.json +0 -0
  118. {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/uv.lock +0 -0
@@ -0,0 +1 @@
1
+ {"alchemy_kit.model._builders._column_metadata": 1783788006.4632034, "alchemy_kit.connect.info_builder": 1783988911.0389419, "alchemy_kit.model": 1783123761.4766204, "alchemy_kit.model._model.constraint_model": 1783792900.5904074, "alchemy_kit.connect._conn_builders": 1781477258.7415013, "alchemy_kit.types._sql_utilities": 1783181974.5998778, "alchemy_kit.types.api_types": 1781653146.483559, "alchemy_kit.resources.dialect_map.mariadb": 1783992205.4850442, "alchemy_kit.types.dialect_types": 1782685078.7530954, "alchemy_kit.connect._conn_builders.mssql": 1783988674.3429148, "alchemy_kit.model._builders._check_parser": 1783804394.2903757, "alchemy_kit.resources._sql": 1783134313.4017901, "alchemy_kit.model._builders._model": 1783305892.6582034, "alchemy_kit.model._model.schema_model": 1782863015.7352564, "alchemy_kit.model._model.column_model": 1783137431.929781, "alchemy_kit.resources._compiler": 1784156156.726372, "alchemy_kit.model._table": 1784166124.3054385, "alchemy_kit.model.units": 1784151164.3408992, "alchemy_kit.model._model_def._extractor": 1783983932.895483, "alchemy_kit.resources.dialect_map.oracle": 1783992222.8661819, "alchemy_kit.types._sql_parameters": 1783136113.744246, "alchemy_kit.types.generic_path": 1782619664.2724166, "alchemy_kit.types.py_type_parameters": 1783871618.5644596, "alchemy_kit.resources.dialect_map._base": 1784139216.637915, "alchemy_kit.resources.dialect_map.postgresql": 1783992260.2914782, "alchemy_kit.model._model.db_model": 1783134298.5988443, "alchemy_kit.model._model_def": 1783793306.3740585, "alchemy_kit.connect": 1783979242.2356217, "alchemy_kit.model.base_model": 1784166108.7544315, "alchemy_kit.model._model": 1782768187.8235765, "alchemy_kit.connect._engine_manager": 1783182851.0969985, "alchemy_kit.model._model_def._frames": 1783793240.1109478, "alchemy_kit.types.errors": 1782777339.3075414, "alchemy_kit.resources.dir_helpers": 1782766100.265875, "alchemy_kit.resources": 1782594201.0667772, "alchemy_kit.model._builders._build_stub_file": 1784166269.5865104, "alchemy_kit.resources.dialect_map.mssql": 1783992194.1459546, "alchemy_kit.builder._base": 1784166201.488475, "alchemy_kit.builder._select": 1784166228.843489, "alchemy_kit.connect._drivers": 1783810352.0290878, "alchemy_kit.types.errors._model_validation_error": 1782777264.4723163, "alchemy_kit.builder._temp": 1784166243.8624969, "alchemy_kit.resources._settings": 1783988624.634489, "alchemy_kit.connect._engine_handler": 1784166082.4634204, "alchemy_kit.builder._insert": 1784166236.0814927, "alchemy_kit.model._builder": 1783304246.27759, "alchemy_kit.types": 1783134286.447885, "alchemy_kit.model._builders": 1783030834.3739681, "alchemy_kit.types.sql_type_parameters": 1783823966.2638404, "alchemy_kit.model.units._operand_unit": 1784156176.032399, "alchemy_kit.model._model.object_model": 1783792902.916411, "alchemy_kit.resources._better_logger": 1781466641.7605743, "alchemy_kit.types.auth_types": 1781654455.9127147, "alchemy_kit.model._schema_config": 1782918118.3620167, "alchemy_kit.model.units._object_unit": 1784166140.9344459, "alchemy_kit.model.units._column_unit": 1784166182.7314658, "alchemy_kit.model._builders._svg": 1783283781.2235174, "alchemy_kit.model._builders._build_py_file": 1784166260.1425054, "alchemy_kit.connect._info": 1783993996.204647, "alchemy_kit.types.sql_types": 1784124232.398432, "alchemy_kit.types._driver_types": 1783987776.477921, "alchemy_kit.builder": 1783894730.0536327, "alchemy_kit.model._utils": 1783793791.5428987, "alchemy_kit.resources._identifiers": 1784156301.8850915, "alchemy_kit.resources.dialect_map.mysql": 1783992200.267003, "alchemy_kit": 1782516403.8490307, "alchemy_kit.resources.dialect_map": 1783894579.0565424, "alchemy_kit.resources.dialect_map.sqlite": 1783992228.453226}
@@ -0,0 +1 @@
1
+ {"alchemy_kit.resources._sql":[["alchemy_kit.resources._settings",9,"from ._settings import Settings"],["alchemy_kit.types.generic_path",8,"from ..types.generic_path import GenericPath"],["alchemy_kit.types._sql_parameters",10,"from ..types._sql_parameters import SqlParamters, SqlTextReplacement"]],"alchemy_kit.types._driver_types":[],"alchemy_kit.types.errors":[["alchemy_kit.types.errors._model_validation_error",1,"from ._model_validation_error import ModelValidationError"]],"alchemy_kit.resources.dialect_map.sqlite":[["alchemy_kit.resources.dialect_map._base",8,"from ._base import ColumnLike, DialectMap, ReflectedTypeFacts, SaTypeFactory"],["alchemy_kit.types.sql_type_parameters",10,"from ...types.sql_type_parameters import SqliteTypeParameters"],["alchemy_kit.types.dialect_types",3,"from alchemy_kit.types.dialect_types import DialectTypes"],["alchemy_kit.types.py_type_parameters",9,"from ...types.py_type_parameters import PyTypeParameters"]],"alchemy_kit.model._schema_config":[],"alchemy_kit.types.sql_type_parameters":[],"alchemy_kit.types.dialect_types":[],"alchemy_kit.model._builders._build_stub_file":[["alchemy_kit.resources.dialect_map",6,"from ...resources.dialect_map import get_codegen_imports, get_type"],["alchemy_kit.model._model.object_model",9,"from .._model.object_model import ObjectModel"],["alchemy_kit.model._model.column_model",8,"from .._model.column_model import ColumnModel"],["alchemy_kit.types.sql_type_parameters",7,"from ...types import DialectTypes, sql_type_parameters"],["alchemy_kit.connect._engine_handler",5,"from ...connect._engine_handler import EngineHandler"],["alchemy_kit.model.base_model",10,"from ..base_model import BaseModel"],["alchemy_kit.model.units",11,"from .. import units"],["alchemy_kit.types",7,"from ...types import DialectTypes, sql_type_parameters"]],"alchemy_kit.model._table":[["alchemy_kit.resources.dialect_map",5,"from ..resources.dialect_map import DialectMap, get_map"],["alchemy_kit.model.base_model",8,"from .base_model import BaseModel"],["alchemy_kit.model._builders._column_metadata",7,"from ._builders._column_metadata import ColumnMetadata"],["alchemy_kit.types.dialect_types",6,"from ..types.dialect_types import DialectTypes"]],"alchemy_kit.types.generic_path":[],"alchemy_kit.model._model.schema_model":[["alchemy_kit.model._model.object_model",1,"from .object_model import ObjectModel"]],"alchemy_kit.model.units._column_unit":[["alchemy_kit.resources.dialect_map",8,"from ...resources.dialect_map import get_map"],["alchemy_kit.model.base_model",10,"from ..base_model import BaseModel"],["alchemy_kit.types.sql_types",9,"from ...types.sql_types import SqlScalarType"],["alchemy_kit.connect._engine_handler",13,"from ...connect._engine_handler import EngineHandler"]],"alchemy_kit.model._builder":[["alchemy_kit.resources._better_logger",5,"from ..resources._better_logger import BetterLogger"],["alchemy_kit.model._utils",8,"from ._utils import clear_dir, parse_db"],["alchemy_kit.model._builders",6,"from . import _builders"],["alchemy_kit.model._schema_config",7,"from ._schema_config import SchemaConfig"],["alchemy_kit.connect._info",4,"from ..connect._info import ConnectionInfo"]],"alchemy_kit.resources._compiler":[["alchemy_kit.types.dialect_types",7,"from ..types.dialect_types import DialectTypes"]],"alchemy_kit.builder._temp":[["alchemy_kit.builder._base",9,"from ._base import SqlBuilder"],["alchemy_kit.model.units",7,"from ..model.units import ObjectUnit"],["alchemy_kit.model._table",6,"from ..model._table import clone_table"],["alchemy_kit.types.dialect_types",8,"from ..types.dialect_types import DialectTypes"]],"alchemy_kit.builder._insert":[["alchemy_kit.model.units",7,"from ..model.units import ColumnUnit, ObjectUnit"],["alchemy_kit.builder._select",10,"from ._select import SelectBuilder"],["alchemy_kit.types.sql_types",8,"from ..types.sql_types import SqlScalarType"],["alchemy_kit.builder._base",9,"from ._base import SqlBuilder"]],"alchemy_kit.model._model_def":[["alchemy_kit.model._model_def._extractor",1,"from ._extractor import MetadataExtractor"],["alchemy_kit.model._model_def._frames",2,"from ._frames import ("]],"alchemy_kit.resources._better_logger":[],"alchemy_kit.resources.dialect_map._base":[["alchemy_kit.types.dialect_types",6,"from ...types.dialect_types import DialectTypes"],["alchemy_kit.types.py_type_parameters",7,"from ...types.py_type_parameters import PyTypeParameters"]],"alchemy_kit.connect._engine_manager":[["alchemy_kit.connect._engine_handler",9,"from ._engine_handler import EngineHandler"],["alchemy_kit.resources._better_logger",8,"from ..resources._better_logger import BetterLogger"],["alchemy_kit.connect._info",10,"from ._info import ConnectionInfo"]],"alchemy_kit.model.units":[["alchemy_kit.model.units._operand_unit",9,"from ._operand_unit import OperandUnit"],["alchemy_kit.model.units._object_unit",8,"from ._object_unit import ObjectUnit"],["alchemy_kit.model.units._column_unit",1,"from ._column_unit import ("]],"alchemy_kit.connect._engine_handler":[["alchemy_kit.connect._info",9,"from ._info import ConnectionInfo"],["alchemy_kit.types._sql_parameters",7,"from ..types._sql_parameters import SqlParamMap"],["alchemy_kit.resources._sql",6,"from ..resources._sql import SQL"],["alchemy_kit.types.sql_types",8,"from ..types.sql_types import SQL_EXPAND_CLASSES"]],"alchemy_kit.model._builders._build_py_file":[["alchemy_kit.resources._identifiers",5,"from ...resources._identifiers import Identifiers"],["alchemy_kit.model.base_model",11,"from ..base_model import BaseModel"],["alchemy_kit.model._model.column_model",8,"from .._model.column_model import ColumnModel"],["alchemy_kit.types",7,"from ...types import DialectTypes, sql_type_parameters"],["alchemy_kit.model._builders._check_parser",12,"from ._check_parser import ColumnComparisonCheck, IndexFilterCondition, parse_column_check, parse_index_filter, parse_table_check"],["alchemy_kit.types.sql_type_parameters",7,"from ...types import DialectTypes, sql_type_parameters"],["alchemy_kit.model._model.constraint_model",9,"from .._model.constraint_model import CheckConstraintModel, FilteredUniqueIndexModel"],["alchemy_kit.model._builders._column_metadata",13,"from ._column_metadata import ColumnMetadata, ForeignKeyMeta"],["alchemy_kit.resources.dialect_map",6,"from ...resources.dialect_map import get_codegen_imports, get_str_length, get_type, render_reference"],["alchemy_kit.model._model.object_model",10,"from .._model.object_model import ObjectModel"]],"alchemy_kit.types._sql_utilities":[],"alchemy_kit.model.units._operand_unit":[["alchemy_kit.resources._compiler",6,"from ...resources._compiler import new_sql_type"],["alchemy_kit.model.units._column_unit",8,"from ._column_unit import AggregateColumnUnit, ColumnUnit, WindowFunctionUnit"],["alchemy_kit.types.dialect_types",7,"from ...types.dialect_types import DialectTypes"]],"alchemy_kit.connect._drivers":[],"alchemy_kit.builder._base":[["alchemy_kit.model.units._object_unit",11,"from ..model.units._object_unit import ObjectUnit"],["alchemy_kit.connect._engine_handler",16,"from ..connect._engine_handler import EngineHandler"],["alchemy_kit.resources.dialect_map",13,"from ..resources.dialect_map import get_sa_dialect"],["alchemy_kit.model.base_model",9,"from ..model.base_model import BaseModel"],["alchemy_kit.model.units._column_unit",10,"from ..model.units._column_unit import _ExpressionUnit"],["alchemy_kit.resources._sql",12,"from ..resources._sql import SQL"]],"alchemy_kit.model._builders._model":[["alchemy_kit.resources._identifiers",4,"from ...resources._identifiers import Identifiers"],["alchemy_kit.resources._better_logger",3,"from ...resources._better_logger import BetterLogger"],["alchemy_kit.model._builders._build_py_file",6,"from ._build_py_file import PyFileBuilder"],["alchemy_kit.model._builders._build_stub_file",7,"from ._build_stub_file import StubFileBuilder"],["alchemy_kit.model._model.db_model",5,"from .._model.db_model import DBModel"]],"alchemy_kit.types.api_types":[],"alchemy_kit.builder._select":[["alchemy_kit.builder._base",8,"from ._base import SqlBuilder"],["alchemy_kit.types.sql_types",7,"from ..types.sql_types import SqlJoinTypes"],["alchemy_kit.model.units",6,"from ..model.units import BooleanColumnUnit, ColumnUnit, ObjectUnit, OrderingColumnUnit"]],"alchemy_kit.model._model_def._frames":[["alchemy_kit.model.base_model",4,"from ..base_model import BaseModel"]],"alchemy_kit.connect._conn_builders.mssql":[["alchemy_kit.types.api_types",7,"from ...types.api_types import SqlServerApi"],["alchemy_kit.types._driver_types",6,"from ...types import _driver_types"]],"alchemy_kit.model._utils":[["alchemy_kit.connect._engine_manager",8,"from ..connect._engine_manager import EngineManager"],["alchemy_kit.model._model.schema_model",16,"from ._model.schema_model import SchemaModel"],["alchemy_kit.model._model_def",17,"from ._model_def import ("],["alchemy_kit.model._model.constraint_model",13,"from ._model.constraint_model import CheckConstraintModel, FilteredUniqueIndexModel, ForeignKeyConstraintModel"],["alchemy_kit.model._model.column_model",12,"from ._model.column_model import ColumnModel, ForeignKeyModel"],["alchemy_kit.model._model.object_model",15,"from ._model.object_model import ObjectModel"],["alchemy_kit.connect._info",9,"from ..connect._info import ConnectionInfo"],["alchemy_kit.types._sql_utilities",11,"from ..types._sql_utilities import ObjectType"],["alchemy_kit.model._model.db_model",14,"from ._model.db_model import DBModel"],["alchemy_kit.resources._better_logger",10,"from ..resources._better_logger import BetterLogger"],["alchemy_kit.model._schema_config",26,"from ._schema_config import SchemaConfig"]],"alchemy_kit.model._model.column_model":[],"alchemy_kit.resources.dialect_map.oracle":[["alchemy_kit.types.py_type_parameters",9,"from ...types.py_type_parameters import PyTypeParameters"],["alchemy_kit.types.sql_type_parameters",10,"from ...types.sql_type_parameters import OracleTypeParameters"],["alchemy_kit.resources.dialect_map._base",8,"from ._base import ColumnLike, DialectMap, SaTypeFactory"],["alchemy_kit.types.dialect_types",3,"from alchemy_kit.types.dialect_types import DialectTypes"]],"alchemy_kit.resources.dir_helpers":[["alchemy_kit.resources._settings",4,"from ._settings import Settings"]],"alchemy_kit.model._model.db_model":[["alchemy_kit.model._model.schema_model",2,"from .schema_model import SchemaModel"],["alchemy_kit.types.dialect_types",1,"from ...types.dialect_types import DialectTypes"]],"alchemy_kit.types.py_type_parameters":[],"alchemy_kit.model._model":[],"alchemy_kit.model._model.object_model":[["alchemy_kit.model._model.constraint_model",2,"from .constraint_model import CheckConstraintModel, FilteredUniqueIndexModel, ForeignKeyConstraintModel"],["alchemy_kit.model._model.column_model",1,"from .column_model import ColumnModel"],["alchemy_kit.types._sql_utilities",3,"from ...types._sql_utilities import ObjectType"]],"alchemy_kit.model._model.constraint_model":[],"alchemy_kit.model._builders._check_parser":[["alchemy_kit.types.dialect_types",8,"from ...types.dialect_types import DialectTypes"]],"alchemy_kit.model.units._object_unit":[["alchemy_kit.model.units._column_unit",7,"from ._column_unit import ColumnUnit"],["alchemy_kit.model._table",6,"from .._table import table_from_model"],["alchemy_kit.model.base_model",5,"from ..base_model import BaseModel, MetaData"],["alchemy_kit.connect._engine_handler",10,"from ...connect._engine_handler import EngineHandler"]],"alchemy_kit.model":[["alchemy_kit.model._schema_config",2,"from ._schema_config import SchemaConfig"],["alchemy_kit.model._builder",1,"from ._builder import build"]],"alchemy_kit.resources._settings":[],"alchemy_kit.model._builders._svg":[["alchemy_kit.resources._better_logger",3,"from ...resources._better_logger import BetterLogger"],["alchemy_kit.model._model.db_model",4,"from .._model.db_model import DBModel"]],"alchemy_kit":[],"alchemy_kit.resources.dialect_map.mysql":[["alchemy_kit.types.py_type_parameters",9,"from ...types.py_type_parameters import PyTypeParameters"],["alchemy_kit.resources.dialect_map._base",8,"from ._base import ColumnLike, DialectMap, SaTypeFactory"],["alchemy_kit.types.dialect_types",3,"from alchemy_kit.types.dialect_types import DialectTypes"],["alchemy_kit.types.sql_type_parameters",10,"from ...types.sql_type_parameters import MysqlTypeParameters"]],"alchemy_kit.resources.dialect_map":[["alchemy_kit.resources.dialect_map.mssql",15,"from .mssql import MssqlMap"],["alchemy_kit.resources.dialect_map.mysql",16,"from .mysql import MysqlMap"],["alchemy_kit.types.py_type_parameters",12,"from ...types.py_type_parameters import PyTypeParameters"],["alchemy_kit.resources.dialect_map.mariadb",14,"from .mariadb import MariadbMap"],["alchemy_kit.resources.dialect_map.postgresql",18,"from .postgresql import PostgresqlMap"],["alchemy_kit.resources.dialect_map.sqlite",19,"from .sqlite import SqliteMap"],["alchemy_kit.types.dialect_types",11,"from ...types.dialect_types import DialectTypes"],["alchemy_kit.resources.dialect_map.oracle",17,"from .oracle import OracleMap"],["alchemy_kit.resources.dialect_map._base",13,"from ._base import ColumnLike, DialectMap, ReflectedTypeFacts"]],"alchemy_kit.model._model_def._extractor":[["alchemy_kit.resources._sql",11,"from ...resources._sql import SQL"],["alchemy_kit.resources.dialect_map",12,"from ...resources.dialect_map import DialectMap, ReflectedTypeFacts, get_map"],["alchemy_kit.model._model_def._frames",15,"from ._frames import ("],["alchemy_kit.model._schema_config",14,"from .._schema_config import SchemaConfig"],["alchemy_kit.types.dialect_types",13,"from ...types.dialect_types import DialectTypes"],["alchemy_kit.connect._engine_handler",10,"from ...connect._engine_handler import EngineHandler"]],"alchemy_kit.resources.dialect_map.postgresql":[["alchemy_kit.types.dialect_types",3,"from alchemy_kit.types.dialect_types import DialectTypes"],["alchemy_kit.types.sql_type_parameters",10,"from ...types.sql_type_parameters import PostgresqlTypeParameters"],["alchemy_kit.types.py_type_parameters",9,"from ...types.py_type_parameters import PyTypeParameters"],["alchemy_kit.resources.dialect_map._base",8,"from ._base import ColumnLike, DialectMap, ReflectedTypeFacts, SaTypeFactory"]],"alchemy_kit.resources.dialect_map.mssql":[["alchemy_kit.types.sql_type_parameters",9,"from ...types.sql_type_parameters import MssqlTypeParameters"],["alchemy_kit.resources.dialect_map._base",7,"from ._base import ColumnLike, DialectMap, ReflectedTypeFacts, SaTypeFactory"],["alchemy_kit.types.dialect_types",3,"from alchemy_kit.types.dialect_types import DialectTypes"],["alchemy_kit.types.py_type_parameters",8,"from ...types.py_type_parameters import PyTypeParameters"]],"alchemy_kit.connect._conn_builders":[],"alchemy_kit.types.errors._model_validation_error":[],"alchemy_kit.connect._info":[["alchemy_kit.resources.dir_helpers",8,"from ..resources.dir_helpers import find_project_root"],["alchemy_kit.types.dialect_types",7,"from ..types.dialect_types import DialectTypes"]],"alchemy_kit.types._sql_parameters":[["alchemy_kit.types.sql_types",2,"from .sql_types import SqlParamType"]],"alchemy_kit.model.base_model":[["alchemy_kit.model.units._object_unit",176,"from .units._object_unit import ObjectUnit"],["alchemy_kit.model.units._object_unit",16,"from .units._object_unit import ObjectUnit"],["alchemy_kit.model._builders._column_metadata",128,"from ._builders._column_metadata import ColumnMetadata"],["alchemy_kit.connect._engine_handler",14,"from ..connect._engine_handler import EngineHandler"],["alchemy_kit.model._builders._column_metadata",15,"from ._builders._column_metadata import ForeignKeyMeta"],["alchemy_kit.types.errors._model_validation_error",11,"from ..types.errors._model_validation_error import ModelValidationError"],["alchemy_kit.resources._sql",10,"from ..resources._sql import SQL"]],"alchemy_kit.types.sql_types":[],"alchemy_kit.types":[["alchemy_kit.types.generic_path",4,"from .generic_path import GenericPath"],["alchemy_kit.types._driver_types",1,"from ._driver_types import SqlServerNative, SqlServerODBC"],["alchemy_kit.types._sql_parameters",2,"from ._sql_parameters import SqlParamters, SqlTextReplacement, SqlParamMap"],["alchemy_kit.types.dialect_types",3,"from .dialect_types import DialectTypes, DialectTypesInput"]],"alchemy_kit.connect":[["alchemy_kit.connect._info",4,"from ._info import ConnectionInfo"],["alchemy_kit.connect._engine_handler",2,"from ._engine_handler import EngineHandler"],["alchemy_kit.connect._engine_manager",3,"from ._engine_manager import EngineManager"],["alchemy_kit.connect.info_builder",1,"from . import info_builder"]],"alchemy_kit.resources._identifiers":[],"alchemy_kit.connect.info_builder":[["alchemy_kit.types.dialect_types",13,"from ..types.dialect_types import DialectTypes"],["alchemy_kit.types.generic_path",14,"from ..types.generic_path import GenericPath"],["alchemy_kit.connect._conn_builders.mssql",15,"from ._conn_builders import mssql"],["alchemy_kit.types._driver_types",10,"from ..types import _driver_types"],["alchemy_kit.connect._drivers",16,"from ._drivers import require_driver"],["alchemy_kit.types.api_types",11,"from ..types.api_types import SqlServerApi"],["alchemy_kit.types.auth_types",12,"from ..types.auth_types import AuthType"],["alchemy_kit.resources._settings",9,"from ..resources._settings import Settings"],["alchemy_kit.connect._info",17,"from ._info import ConnectionInfo"]],"alchemy_kit.types.auth_types":[],"alchemy_kit.model._builders":[["alchemy_kit.model._builders._svg",2,"from ._svg import build_svg"],["alchemy_kit.model._builders._model",1,"from ._model import build_model"]],"alchemy_kit.resources.dialect_map.mariadb":[["alchemy_kit.resources.dialect_map._base",10,"from ._base import ColumnLike, DialectMap, SaTypeFactory"],["alchemy_kit.types.sql_type_parameters",12,"from ...types.sql_type_parameters import MariadbTypeParameters"],["alchemy_kit.types.dialect_types",3,"from alchemy_kit.types.dialect_types import DialectTypes"],["alchemy_kit.types.py_type_parameters",11,"from ...types.py_type_parameters import PyTypeParameters"]],"alchemy_kit.model._builders._column_metadata":[],"alchemy_kit.builder":[["alchemy_kit.builder._base",1,"from ._base import SqlBuilder"],["alchemy_kit.builder._temp",4,"from ._temp import TempBuilder"],["alchemy_kit.builder._select",3,"from ._select import SelectBuilder"],["alchemy_kit.builder._insert",2,"from ._insert import InsertBuilder"]],"alchemy_kit.resources":[["alchemy_kit.resources._sql",3,"from ._sql import SQL"],["alchemy_kit.resources._settings",2,"from ._settings import Settings"],["alchemy_kit.resources._better_logger",1,"from ._better_logger import BetterLogger"]]}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: alchemy-kit
3
- Version: 0.2.1
3
+ Version: 0.2.2
4
4
  Summary: Add your description here
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.12
@@ -7,6 +7,8 @@ from sqlalchemy.engine import Dialect
7
7
  from sqlalchemy.sql.expression import ClauseElement
8
8
 
9
9
  from ..model.base_model import BaseModel
10
+ from ..model.units._column_unit import _ExpressionUnit
11
+ from ..model.units._object_unit import ObjectUnit
10
12
  from ..resources._sql import SQL
11
13
  from ..resources.dialect_map import get_sa_dialect
12
14
 
@@ -16,18 +18,25 @@ if TYPE_CHECKING:
16
18
 
17
19
  class SqlBuilder(ABC):
18
20
  """Base of every SQL statement builder: assembles a SQLAlchemy Core
19
- statement from units and compiles it against the model's dialect.
20
- Subclasses implement ``_statement``; builders are immutable."""
21
+ statement from units and compiles it against the dialect of the engine
22
+ handler the units came from. Subclasses implement ``_statement``;
23
+ builders are immutable."""
21
24
 
22
- def __init__(self, base: type[BaseModel[Any]]) -> None:
25
+ def __init__(self, base: type[BaseModel[Any]], handler: "EngineHandler") -> None:
23
26
  self._base = base
27
+ self._handler = handler
28
+
29
+ def _check_units(self, *units: "_ExpressionUnit[Any] | ObjectUnit[Any]") -> None:
30
+ for unit in units:
31
+ if unit._handler is not None and unit._handler is not self._handler:
32
+ raise ValueError("all units in a query must come from the same engine handler")
24
33
 
25
34
  @abstractmethod
26
35
  def _statement(self) -> ClauseElement:
27
36
  """Return the Core statement this builder currently describes."""
28
37
 
29
38
  def _sa_dialect(self) -> Dialect:
30
- return get_sa_dialect(self._base._dialect)
39
+ return get_sa_dialect(self._handler._con_info.dialect)
31
40
 
32
41
  def _compile(self) -> Compiled:
33
42
  return self._statement().compile(
@@ -50,7 +59,8 @@ class SqlBuilder(ABC):
50
59
  ``:name`` placeholders for bound values."""
51
60
  return str(self._compile())
52
61
 
53
- def run(self, handler: "EngineHandler", **parameters: Any) -> tuple[int | None, pd.DataFrame]:
54
- """Compile and execute through ``handler.run_sql``, returning its
55
- ``(row_count, DataFrame)`` pair."""
56
- return handler.run_sql(self.to_sql(**parameters))
62
+ def run(self, **parameters: Any) -> tuple[int | None, pd.DataFrame]:
63
+ """Compile and execute on the engine handler the units came from,
64
+ returning its ``(row_count, DataFrame)`` pair."""
65
+ sql = self.to_sql(**parameters)
66
+ return self._handler.run_sql(sql)
@@ -0,0 +1,152 @@
1
+ from typing import Any, Sequence, cast
2
+
3
+ import pandas as pd
4
+ import sqlalchemy as sa
5
+ from sqlalchemy.sql.expression import Insert
6
+
7
+ from ..model.units import ColumnUnit, ObjectUnit
8
+ from ..resources._sql import SQL
9
+ from ..resources.dialect_map import get_map
10
+ from ..types.sql_types import SqlScalarType
11
+ from ._base import SqlBuilder
12
+ from ._select import SelectBuilder
13
+
14
+
15
+ class InsertBuilder(SqlBuilder):
16
+ """Builds an ``INSERT`` into an object unit's table; rows come from
17
+ :meth:`from_values`, :meth:`from_dataframe` or :meth:`from_select`.
18
+
19
+ These methods layer onto the statement rather than reset it: repeated
20
+ :meth:`from_values` merge columns (last wins per column), repeated
21
+ :meth:`from_dataframe` append rows, and repeated :meth:`from_select`
22
+ replaces the prior SELECT. Mixing value sources (single vs multi VALUES,
23
+ or values vs SELECT) raises ``InvalidRequestError``."""
24
+
25
+ def __init__(self, target: ObjectUnit[Any]) -> None:
26
+ super().__init__(target._base, target._handler)
27
+
28
+ selectable = target._selectable
29
+ if not isinstance(selectable, sa.Table):
30
+ raise TypeError("insert target must be a plain object unit, not an aliased one")
31
+
32
+ self._table = selectable
33
+ self._stmt: Insert = sa.insert(selectable)
34
+
35
+ def _with(self, stmt: Insert) -> "InsertBuilder":
36
+ clone = InsertBuilder.__new__(InsertBuilder)
37
+ SqlBuilder.__init__(clone, self._base, self._handler)
38
+ clone._table = self._table
39
+ clone._stmt = stmt
40
+ return clone
41
+
42
+ def _statement(self) -> Insert:
43
+ return self._stmt
44
+
45
+ def _sql_name(self, name: str) -> str:
46
+ sql_name = getattr(self._base, name, name)
47
+ return sql_name if isinstance(sql_name, str) else name
48
+
49
+ def get_parameters(self) -> dict[str, SqlScalarType] | list[dict[str, SqlScalarType]]:
50
+ """Return the values to insert, keyed by SQL column name: a dict for
51
+ :meth:`from_values`, one dict per row for :meth:`from_dataframe`
52
+ (empty for select inserts)."""
53
+ if multi_values := self._stmt._multi_values:
54
+ rows = cast("tuple[Sequence[dict[str, SqlScalarType]], ...]", multi_values)
55
+ return [dict(row) for group in rows for row in group]
56
+
57
+ if not (values := self._stmt._values):
58
+ return {}
59
+
60
+ return {str(column): parameter.value for column, parameter in values.items()}
61
+
62
+ def from_values(self, **column_values: SqlScalarType) -> "InsertBuilder":
63
+ """Set the inserted row's values by model field (or SQL column)
64
+ name; plain values are bound as parameters."""
65
+ resolved = {
66
+ self._sql_name(name): ColumnUnit._value_operand(value)
67
+ for name, value in column_values.items()
68
+ }
69
+ return self._with(self._stmt.values(**resolved))
70
+
71
+ def from_dataframe(
72
+ self,
73
+ df: pd.DataFrame,
74
+ columns: Sequence[str] | None = None,
75
+ ) -> "InsertBuilder":
76
+ """Insert every row of ``df`` as one multi-row ``INSERT``;
77
+ ``columns`` selects and orders which columns (all when omitted)."""
78
+ source = list(df.columns) if columns is None else list(columns)
79
+ target = {name: self._sql_name(name) for name in source}
80
+
81
+ rows = [
82
+ {target[name]: ColumnUnit._value_operand(record[name]) for name in source}
83
+ for record in df.to_dict(orient="records")
84
+ ]
85
+ return self._with(self._stmt.values(rows))
86
+
87
+ def from_select(
88
+ self,
89
+ select: SelectBuilder,
90
+ columns: Sequence[str] | None = None,
91
+ ) -> "InsertBuilder":
92
+ """Insert the rows produced by a :class:`SelectBuilder`
93
+ (``INSERT INTO ... SELECT``); ``columns`` names the targets in the
94
+ select's output order (the select's own names when omitted)."""
95
+ if select._handler is not self._handler:
96
+ raise ValueError("all units in a query must come from the same engine handler")
97
+ stmt = select._statement()
98
+
99
+ if columns is not None:
100
+ names = [self._sql_name(name) for name in columns]
101
+ else:
102
+ names = list(stmt.selected_columns.keys())
103
+
104
+ return self._with(self._stmt.from_select(names, stmt))
105
+
106
+ def run(self, *, chunk_size: int | None = None, **parameters: Any) -> tuple[int | None, pd.DataFrame]:
107
+ """Execute the insert; a multi-row ``from_dataframe`` insert that would
108
+ exceed the dialect's per-statement row/parameter limits (or
109
+ ``chunk_size`` when given) is split into chunks run in one
110
+ all-or-nothing transaction."""
111
+ rows = [row for group in self._stmt._multi_values for row in group]
112
+ effective = self._effective_chunk_size(chunk_size, rows)
113
+
114
+ if effective is None or len(rows) <= effective:
115
+ return super().run(**parameters)
116
+
117
+ sqls = [
118
+ self._chunk_sql(rows[start:start + effective])
119
+ for start in range(0, len(rows), effective)
120
+ ]
121
+ counts = [count for count, _ in self._handler.run_sqls(sqls)]
122
+
123
+ if any(count is None for count in counts):
124
+ return None, pd.DataFrame()
125
+ return sum(cast("list[int]", counts)), pd.DataFrame()
126
+
127
+ def _effective_chunk_size(self, chunk_size: int | None, rows: Sequence[Any]) -> int | None:
128
+ if not rows:
129
+ return None
130
+
131
+ limits = get_map(self._handler._con_info.dialect)
132
+ caps: list[int] = []
133
+ if limits.max_insert_rows is not None:
134
+ caps.append(limits.max_insert_rows)
135
+ if limits.max_statement_params is not None:
136
+ caps.append(limits.max_statement_params // len(rows[0]))
137
+
138
+ safe_max = min(caps) if caps else None
139
+
140
+ if chunk_size is None:
141
+ return safe_max
142
+ if safe_max is None:
143
+ return chunk_size
144
+ return min(chunk_size, safe_max)
145
+
146
+ def _chunk_sql(self, chunk: Sequence[Any]) -> SQL:
147
+ compiled = sa.insert(self._table).values(list(chunk)).compile(
148
+ dialect=self._sa_dialect(),
149
+ compile_kwargs={"render_postcompile": True},
150
+ )
151
+ return SQL(raw_query=str(compiled), query_parameters={**(compiled.params or {})})
152
+
@@ -3,7 +3,7 @@ from typing import Any
3
3
  import sqlalchemy as sa
4
4
  from sqlalchemy.sql.expression import ColumnElement, FromClause, Join, Select
5
5
 
6
- from ..model.units import BooleanColumnUnit, ColumnUnit, ObjectUnit
6
+ from ..model.units import BooleanColumnUnit, ColumnUnit, ObjectUnit, OrderingColumnUnit
7
7
  from ..types.sql_types import SqlJoinTypes
8
8
  from ._base import SqlBuilder
9
9
 
@@ -16,14 +16,20 @@ _JOIN_FLAGS: dict[SqlJoinTypes, dict[str, bool]] = {
16
16
 
17
17
  class SelectBuilder(SqlBuilder):
18
18
  """Builds a ``SELECT`` statement from column and/or object units; the
19
- dialect comes from the first unit's model and every fluent method
20
- returns a new builder."""
19
+ dialect comes from the first bound unit's engine handler and every
20
+ fluent method returns a new builder."""
21
21
 
22
22
  def __init__(self, *columns: "ColumnUnit[Any] | ObjectUnit[Any]") -> None:
23
23
  if not columns:
24
24
  raise ValueError("SelectBuilder needs at least one column or object unit")
25
25
 
26
- super().__init__(columns[0]._base)
26
+ base = next((c._base for c in columns if c._base is not None), None)
27
+ handler = next((c._handler for c in columns if c._handler is not None), None)
28
+ if base is None or handler is None:
29
+ raise ValueError("SelectBuilder needs at least one unit bound to a model")
30
+
31
+ super().__init__(base, handler)
32
+ self._check_units(*columns)
27
33
  self._stmt: Select[Any] = sa.select(*(self._selected(c) for c in columns))
28
34
  self._from: FromClause | None = None
29
35
 
@@ -41,6 +47,12 @@ class SelectBuilder(SqlBuilder):
41
47
  def _column_element(column: ColumnUnit[Any]) -> ColumnElement[Any]:
42
48
  return column._element
43
49
 
50
+ @staticmethod
51
+ def _ordering_element(
52
+ column: "ColumnUnit[Any] | OrderingColumnUnit[Any]",
53
+ ) -> ColumnElement[Any]:
54
+ return column._element
55
+
44
56
  @staticmethod
45
57
  def _required_condition(
46
58
  how: SqlJoinTypes, on: "BooleanColumnUnit[Any] | None"
@@ -53,7 +65,7 @@ class SelectBuilder(SqlBuilder):
53
65
  self, stmt: Select[Any], joined: FromClause | None = None
54
66
  ) -> "SelectBuilder":
55
67
  clone = SelectBuilder.__new__(SelectBuilder)
56
- SqlBuilder.__init__(clone, self._base)
68
+ SqlBuilder.__init__(clone, self._base, self._handler)
57
69
  clone._stmt = stmt
58
70
  clone._from = joined if joined is not None else self._from
59
71
  return clone
@@ -65,6 +77,7 @@ class SelectBuilder(SqlBuilder):
65
77
 
66
78
  def where(self, *conditions: BooleanColumnUnit[Any]) -> "SelectBuilder":
67
79
  """Add ``WHERE`` conditions (multiple conditions are ``AND``-ed)."""
80
+ self._check_units(*conditions)
68
81
  return self._with(
69
82
  self._stmt.where(*(self._condition_element(c) for c in conditions))
70
83
  )
@@ -78,6 +91,7 @@ class SelectBuilder(SqlBuilder):
78
91
  """Add a join to ``other``; ``CROSS`` takes no ``on`` condition and
79
92
  ``RIGHT`` compiles as the equivalent operand-flipped ``LEFT OUTER
80
93
  JOIN``."""
94
+ self._check_units(*(other,) if on is None else (other, on))
81
95
  left = self._from if self._from is not None else self._stmt.get_final_froms()[0]
82
96
 
83
97
  joined: Join
@@ -102,21 +116,26 @@ class SelectBuilder(SqlBuilder):
102
116
 
103
117
  def group_by(self, *columns: ColumnUnit[Any]) -> "SelectBuilder":
104
118
  """Add ``GROUP BY`` expressions."""
119
+ self._check_units(*columns)
105
120
  return self._with(
106
121
  self._stmt.group_by(*(self._column_element(c) for c in columns))
107
122
  )
108
123
 
109
124
  def having(self, *conditions: BooleanColumnUnit[Any]) -> "SelectBuilder":
110
125
  """Add ``HAVING`` conditions (multiple conditions are ``AND``-ed)."""
126
+ self._check_units(*conditions)
111
127
  return self._with(
112
128
  self._stmt.having(*(self._condition_element(c) for c in conditions))
113
129
  )
114
130
 
115
- def order_by(self, *columns: ColumnUnit[Any]) -> "SelectBuilder":
131
+ def order_by(
132
+ self, *columns: "ColumnUnit[Any] | OrderingColumnUnit[Any]"
133
+ ) -> "SelectBuilder":
116
134
  """Add ``ORDER BY`` expressions (direction via the column's
117
135
  ``asc()``/``desc()``)."""
136
+ self._check_units(*columns)
118
137
  return self._with(
119
- self._stmt.order_by(*(self._column_element(c) for c in columns))
138
+ self._stmt.order_by(*(self._ordering_element(c) for c in columns))
120
139
  )
121
140
 
122
141
  def distinct(self) -> "SelectBuilder":
@@ -3,6 +3,7 @@ from typing import Any
3
3
  import sqlalchemy as sa
4
4
  from sqlalchemy.schema import CreateTable
5
5
 
6
+ from ..model._table import clone_table
6
7
  from ..model.units import ObjectUnit
7
8
  from ..types.dialect_types import DialectTypes
8
9
  from ._base import SqlBuilder
@@ -17,34 +18,27 @@ class TempBuilder(SqlBuilder):
17
18
  """
18
19
 
19
20
  def __init__(self, source: ObjectUnit[Any], global_temp: bool = False) -> None:
20
- super().__init__(source._base)
21
+ super().__init__(source._base, source._handler)
21
22
 
22
23
  table = source._selectable
23
24
  if not isinstance(table, sa.Table):
24
25
  raise TypeError("temp source must be a plain object unit, not an aliased one")
25
26
 
27
+ dialect = self._handler._con_info.dialect
26
28
  temp_name = f"TEMP_{table.name}"
27
29
  prefixes: list[str] = ["TEMPORARY"]
28
30
 
29
- if self._base._dialect is DialectTypes.MSSQL:
31
+ if dialect is DialectTypes.MSSQL:
30
32
  temp_name = f"{'##' if global_temp else '#'}{temp_name}"
31
33
  prefixes = []
32
- elif self._base._dialect is DialectTypes.ORACLE:
34
+ elif dialect is DialectTypes.ORACLE:
33
35
  prefixes = ["GLOBAL TEMPORARY"]
34
36
  elif global_temp:
35
37
  raise ValueError(
36
- f"{self._base._dialect} does not support global temporary tables"
38
+ f"{dialect} does not support global temporary tables"
37
39
  )
38
40
 
39
- self._table = sa.Table(
40
- temp_name,
41
- sa.MetaData(),
42
- *(
43
- sa.Column(c.name, c.type, nullable=c.nullable, primary_key=c.primary_key)
44
- for c in table.columns
45
- ),
46
- prefixes=prefixes,
47
- )
41
+ self._table = clone_table(table, temp_name, prefixes)
48
42
 
49
43
  def _statement(self) -> CreateTable:
50
44
  return CreateTable(self._table)
@@ -52,5 +46,5 @@ class TempBuilder(SqlBuilder):
52
46
  def unit(self) -> ObjectUnit[Any]:
53
47
  """Return an :class:`ObjectUnit` over the temporary table, for
54
48
  selecting from / inserting into it after :meth:`run`."""
55
- return ObjectUnit(self._base, self._table)
49
+ return ObjectUnit(self._base, self._handler, self._table)
56
50
 
@@ -1,14 +1,18 @@
1
- from typing import Literal, Sequence, cast
1
+ from typing import Literal, Protocol, Sequence, cast
2
2
 
3
3
  import pandas as pd
4
4
  import sqlalchemy
5
5
 
6
6
  from ..resources._sql import SQL
7
7
  from ..types._sql_parameters import SqlParamMap
8
- from ..types.sql_types import SqlExpandType
8
+ from ..types.sql_types import SQL_EXPAND_CLASSES
9
9
  from ._info import ConnectionInfo
10
10
 
11
11
 
12
+ class _UnitFactory[_UnitT](Protocol):
13
+ def _get_unit(self, handler: "EngineHandler") -> _UnitT: ...
14
+
15
+
12
16
  class EngineHandler:
13
17
  """Performs database operations against a single pooled engine.
14
18
 
@@ -25,6 +29,11 @@ class EngineHandler:
25
29
  self._engine = engine
26
30
  self._con_info = con_info
27
31
 
32
+ def get_unit[_UnitT](self, model: _UnitFactory[_UnitT]) -> _UnitT:
33
+ """Return a unit for ``model``, bound to this handler's connection and
34
+ compiled with its dialect."""
35
+ return model._get_unit(self)
36
+
28
37
  def get_inspector(self) -> sqlalchemy.Inspector:
29
38
  """Return a SQLAlchemy ``Inspector`` bound to this handler's engine."""
30
39
  return sqlalchemy.inspect(self._engine)
@@ -93,7 +102,7 @@ class EngineHandler:
93
102
 
94
103
  def _execute_with_binded_parameters(self, conn: sqlalchemy.Connection, query: sqlalchemy.TextClause, parameters: SqlParamMap) -> sqlalchemy.CursorResult:
95
104
  binds = [
96
- sqlalchemy.bindparam(key, value, expanding=isinstance(value, SqlExpandType))
105
+ sqlalchemy.bindparam(key, value, expanding=isinstance(value, SQL_EXPAND_CLASSES))
97
106
  for key, value in parameters.items()
98
107
  ]
99
108
 
@@ -1,7 +1,6 @@
1
1
  import random
2
2
  import string
3
3
  from pathlib import Path
4
- from typing import Optional
5
4
 
6
5
  import sqlalchemy
7
6
 
@@ -29,7 +28,7 @@ class ConnectionInfo:
29
28
  resolved or set.
30
29
  """
31
30
 
32
- def __init__(self, conn_url: sqlalchemy.URL, unique_id: Optional[str]) -> None:
31
+ def __init__(self, conn_url: sqlalchemy.URL, unique_id: str | None = None) -> None:
33
32
  self.script_dir: Path | None = None
34
33
  self.con_url = conn_url
35
34
  self.dialect = self._resolve_dialect(conn_url)
@@ -33,7 +33,7 @@ class PyFileBuilder:
33
33
  }
34
34
 
35
35
  def build(self) -> str:
36
- map_module, parameter_map, type_parameters = get_codegen_imports(self.db_dialect)
36
+ _, _, type_parameters = get_codegen_imports(self.db_dialect)
37
37
  check_methods, unparsed_table_checks = self._build_table_checks()
38
38
  index_methods, unparsed_indexes = self._build_index_checks()
39
39
  check_methods += index_methods
@@ -41,13 +41,9 @@ class PyFileBuilder:
41
41
  template = self.template.read_text("UTF-8")
42
42
  template = template.format(
43
43
  class_name=self.class_name,
44
- dialect=f"DialectTypes.{self.db_dialect.name}",
45
44
  base_model_module=BaseModel.__module__,
46
- dialect_types_module=DialectTypes.__module__,
47
45
  type_parameters_module=sql_type_parameters.__name__,
48
46
  type_parameters=type_parameters,
49
- parameter_map_module=map_module,
50
- parameter_map=parameter_map,
51
47
  unique=f"unique={self.object_model.get_unique_constrait()!r}",
52
48
  metadata=self._get_object_metadata(unparsed_table_checks, unparsed_indexes),
53
49
  )
@@ -2,6 +2,7 @@ from dataclasses import dataclass
2
2
  from pathlib import Path
3
3
  from typing import ClassVar
4
4
 
5
+ from ...connect._engine_handler import EngineHandler
5
6
  from ...resources.dialect_map import get_codegen_imports, get_type
6
7
  from ...types import DialectTypes, sql_type_parameters
7
8
  from .._model.column_model import ColumnModel
@@ -27,6 +28,7 @@ class StubFileBuilder:
27
28
  template = self.template.read_text("UTF-8").format(
28
29
  class_name=self.class_name,
29
30
  base_model_module=BaseModel.__module__,
31
+ engine_handler_module=EngineHandler.__module__,
30
32
  object_unit_module=units.__name__,
31
33
  column_unit_module=units.__name__,
32
34
  type_parameters_module=sql_type_parameters.__name__,
@@ -9,9 +9,7 @@
9
9
  # --------------------------------------------------------------------------------
10
10
 
11
11
  from {base_model_module} import BaseModel, MetaData
12
- from {dialect_types_module} import DialectTypes
13
12
  from {type_parameters_module} import {type_parameters}
14
- from {parameter_map_module} import {parameter_map}
15
13
  from datetime import datetime, date
16
14
  from pandera.typing import Series
17
15
  from typing import Optional, Any
@@ -19,9 +17,6 @@ import pandas as pd
19
17
  import pandera.pandas as pa
20
18
 
21
19
  class {class_name}(BaseModel[{type_parameters}]):
22
- _dialect = {dialect}
23
- _map = {parameter_map}
24
-
25
20
  :columns
26
21
 
27
22
  :checks
@@ -9,6 +9,7 @@
9
9
  # --------------------------------------------------------------------------------
10
10
 
11
11
  from {base_model_module} import BaseModel
12
+ from {engine_handler_module} import EngineHandler
12
13
  from {object_unit_module} import ObjectUnit
13
14
  from {column_unit_module} import ColumnUnit
14
15
  from {type_parameters_module} import {type_parameters}
@@ -20,7 +21,7 @@ class {class_name}(BaseModel[{type_parameters}]):
20
21
  :columns
21
22
 
22
23
  @classmethod
23
- def get_unit(cls) -> {class_name}_Unit: ...
24
+ def _get_unit(cls, handler: EngineHandler) -> {class_name}_Unit: ...
24
25
 
25
26
  class {class_name}_Unit(ObjectUnit[{type_parameters}]):
26
27
  :unit_columns
@@ -0,0 +1,62 @@
1
+ from typing import Any
2
+
3
+ import sqlalchemy as sa
4
+
5
+ from ..resources.dialect_map import DialectMap, get_map
6
+ from ..types.dialect_types import DialectTypes
7
+ from ._builders._column_metadata import ColumnMetadata
8
+ from .base_model import BaseModel
9
+
10
+ _TABLES: dict[tuple[type[BaseModel[Any]], DialectTypes], sa.Table] = {}
11
+
12
+
13
+ def _column_from_model(name: str, column: Any, dialect_map: type[DialectMap[Any]]) -> sa.Column[Any]:
14
+ metadata = ColumnMetadata.from_dict(column.metadata)
15
+ sa_type = dialect_map.get_sa_type(
16
+ metadata.original_type, metadata.precision, metadata.scale
17
+ )
18
+
19
+ return sa.Column(
20
+ name,
21
+ sa_type,
22
+ nullable=bool(column.nullable),
23
+ primary_key=metadata.primary_key,
24
+ )
25
+
26
+
27
+ def table_from_model(base: type[BaseModel[Any]], dialect: DialectTypes) -> sa.Table:
28
+ """Return the SQLAlchemy Core ``Table`` for a generated model, built in
29
+ memory from the model's metadata (no reflection) and cached per
30
+ ``(model, dialect)``.
31
+
32
+ Columns carry the dialect's SQLAlchemy type (parameterised with the
33
+ recorded precision/scale for numerics), nullability, and primary-key
34
+ flags, so the table serves both expression compilation and DDL rendering.
35
+ """
36
+ if (base, dialect) not in _TABLES:
37
+ metadata = base.Config.metadata
38
+ dialect_map = get_map(dialect)
39
+ _TABLES[base, dialect] = sa.Table(
40
+ metadata["obj_name"],
41
+ sa.MetaData(),
42
+ *(
43
+ _column_from_model(str(name), column, dialect_map)
44
+ for name, column in base.to_schema().columns.items()
45
+ ),
46
+ schema=metadata["schema_name"],
47
+ )
48
+ return _TABLES[base, dialect]
49
+
50
+
51
+ def clone_table(source: sa.Table, name: str, prefixes: list[str]) -> sa.Table:
52
+ """Return a new ``Table`` named ``name`` cloning ``source``'s columns, with
53
+ the given DDL ``prefixes`` (e.g. ``TEMPORARY``)."""
54
+ return sa.Table(
55
+ name,
56
+ sa.MetaData(),
57
+ *(
58
+ sa.Column(c.name, c.type, nullable=c.nullable, primary_key=c.primary_key)
59
+ for c in source.columns
60
+ ),
61
+ prefixes=prefixes,
62
+ )