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.
- alchemy_kit-0.2.2/.import_linter_cache/alchemy_kit.meta.json +1 -0
- alchemy_kit-0.2.2/.import_linter_cache/b436f6e8205b1418cbee6b7347a3c7b0d1be876b.data.json +1 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/PKG-INFO +1 -1
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/builder/_base.py +18 -8
- alchemy_kit-0.2.2/alchemy_kit/builder/_insert.py +152 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/builder/_select.py +26 -7
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/builder/_temp.py +8 -14
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/connect/_engine_handler.py +12 -3
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/connect/_info.py +1 -2
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_builders/_build_py_file.py +1 -5
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_builders/_build_stub_file.py +2 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_builders/py_template_file.txt +0 -5
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_builders/stub_template_file.txt +2 -1
- alchemy_kit-0.2.2/alchemy_kit/model/_table.py +62 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/base_model.py +5 -10
- alchemy_kit-0.2.2/alchemy_kit/model/units/__init__.py +19 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/units/_column_unit.py +245 -56
- alchemy_kit-0.2.2/alchemy_kit/model/units/_object_unit.py +61 -0
- alchemy_kit-0.2.2/alchemy_kit/model/units/_operand_unit.py +102 -0
- alchemy_kit-0.2.2/alchemy_kit/resources/_compiler.py +41 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/_identifiers.py +1 -1
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/_sql.py +3 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/dialect_map/_base.py +19 -3
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/dialect_map/mssql.py +2 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/sql_types.py +6 -2
- alchemy_kit-0.2.2/docs/model/units.md +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/pyproject.toml +4 -3
- alchemy_kit-0.2.2/tests/builder/test_builders.py +588 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/tests/connect/test_connect.py +3 -4
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/tests/model/test_builder.py +13 -1
- alchemy_kit-0.2.1/.import_linter_cache/alchemy_kit.meta.json +0 -1
- alchemy_kit-0.2.1/.import_linter_cache/b436f6e8205b1418cbee6b7347a3c7b0d1be876b.data.json +0 -1
- alchemy_kit-0.2.1/alchemy_kit/builder/_insert.py +0 -81
- alchemy_kit-0.2.1/alchemy_kit/model/units/__init__.py +0 -4
- alchemy_kit-0.2.1/alchemy_kit/model/units/_object_unit.py +0 -95
- alchemy_kit-0.2.1/tests/builder/test_builders.py +0 -219
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/.claude/settings.local.json +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/.gitignore +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/.grimp_cache/.gitignore +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/.grimp_cache/CACHEDIR.TAG +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/.grimp_cache/alchemy_kit.meta.json +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/.grimp_cache/b436f6e8205b1418cbee6b7347a3c7b0d1be876b.data.json +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/.import_linter_cache/.gitignore +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/.import_linter_cache/CACHEDIR.TAG +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/.python-version +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/LICENSE +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/README.md +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/__init__.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/builder/__init__.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/connect/__init__.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/connect/_conn_builders/__init__.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/connect/_conn_builders/mssql.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/connect/_drivers.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/connect/_engine_manager.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/connect/info_builder.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/__init__.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_builder.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_builders/__init__.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_builders/_check_parser.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_builders/_column_metadata.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_builders/_model.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_builders/_svg.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model/__init__.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model/column_model.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model/constraint_model.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model/db_model.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model/object_model.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model/schema_model.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model_def/__init__.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model_def/_extractor.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model_def/_frames.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model_def/_sql/mssql_check_constraints.sql +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_model_def/_sql/mssql_unique_constraints.sql +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_schema_config.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/model/_utils.py +0 -0
- /alchemy_kit-0.2.1/docs/builder/base-builder.md → /alchemy_kit-0.2.2/alchemy_kit/py.typed +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/__init__.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/_better_logger.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/_settings.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/dialect_map/__init__.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/dialect_map/mariadb.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/dialect_map/mysql.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/dialect_map/oracle.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/dialect_map/postgresql.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/dialect_map/sqlite.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/resources/dir_helpers.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/__init__.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/_driver_types.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/_sql_parameters.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/_sql_utilities.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/api_types.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/auth_types.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/dialect_types.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/errors/__init__.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/errors/_model_validation_error.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/generic_path.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/py_type_parameters.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/alchemy_kit/types/sql_type_parameters.py +0 -0
- /alchemy_kit-0.2.1/docs/builder/insert-builder.md → /alchemy_kit-0.2.2/docs/builder/base-builder.md +0 -0
- /alchemy_kit-0.2.1/docs/builder/select-builder.md → /alchemy_kit-0.2.2/docs/builder/insert-builder.md +0 -0
- /alchemy_kit-0.2.1/docs/builder/temp-builder.md → /alchemy_kit-0.2.2/docs/builder/select-builder.md +0 -0
- /alchemy_kit-0.2.1/docs/connect/connection-info.md → /alchemy_kit-0.2.2/docs/builder/temp-builder.md +0 -0
- /alchemy_kit-0.2.1/docs/connect/engine-handler.md → /alchemy_kit-0.2.2/docs/connect/connection-info.md +0 -0
- /alchemy_kit-0.2.1/docs/connect/engine-manager.md → /alchemy_kit-0.2.2/docs/connect/engine-handler.md +0 -0
- /alchemy_kit-0.2.1/docs/connect/info-builder.md → /alchemy_kit-0.2.2/docs/connect/engine-manager.md +0 -0
- /alchemy_kit-0.2.1/docs/model/base-model.md → /alchemy_kit-0.2.2/docs/connect/info-builder.md +0 -0
- /alchemy_kit-0.2.1/docs/model/build.md → /alchemy_kit-0.2.2/docs/model/base-model.md +0 -0
- /alchemy_kit-0.2.1/docs/model/schema-config.md → /alchemy_kit-0.2.2/docs/model/build.md +0 -0
- /alchemy_kit-0.2.1/docs/model/units.md → /alchemy_kit-0.2.2/docs/model/schema-config.md +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/tests/connect/test_engine_handler.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/tests/dotenv_test +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/tests/model/test_check_codegen.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/tests/model/test_check_parser.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/tests/model/test_fk_validation.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/tests/model/test_inspector_def.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/tests/resources/test_reflected_type_facts.py +0 -0
- {alchemy_kit-0.2.1 → alchemy_kit-0.2.2}/tests/test_json.json +0 -0
- {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"]]}
|
|
@@ -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
|
|
20
|
-
Subclasses implement ``_statement``;
|
|
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.
|
|
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,
|
|
54
|
-
"""Compile and execute
|
|
55
|
-
``(row_count, DataFrame)`` pair."""
|
|
56
|
-
|
|
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
|
|
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
|
-
|
|
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(
|
|
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.
|
|
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
|
|
31
|
+
if dialect is DialectTypes.MSSQL:
|
|
30
32
|
temp_name = f"{'##' if global_temp else '#'}{temp_name}"
|
|
31
33
|
prefixes = []
|
|
32
|
-
elif
|
|
34
|
+
elif dialect is DialectTypes.ORACLE:
|
|
33
35
|
prefixes = ["GLOBAL TEMPORARY"]
|
|
34
36
|
elif global_temp:
|
|
35
37
|
raise ValueError(
|
|
36
|
-
f"{
|
|
38
|
+
f"{dialect} does not support global temporary tables"
|
|
37
39
|
)
|
|
38
40
|
|
|
39
|
-
self._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
|
|
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,
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
+
)
|