iceaxe 0.7.1__tar.gz → 0.8.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of iceaxe might be problematic. Click here for more details.
- {iceaxe-0.7.1/iceaxe.egg-info → iceaxe-0.8.0}/PKG-INFO +1 -1
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/schemas/test_actions.py +1 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/schemas/test_db_memory_serializer.py +46 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/test_queries.py +159 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/base.py +1 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/field.py +13 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/functions.py +527 -1
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/queries.py +5 -1
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/schemas/db_memory_serializer.py +6 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/schemas/db_stubs.py +4 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/session_optimized.c +38 -23
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/sql_types.py +1 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0/iceaxe.egg-info}/PKG-INFO +1 -1
- {iceaxe-0.7.1 → iceaxe-0.8.0}/pyproject.toml +1 -1
- {iceaxe-0.7.1 → iceaxe-0.8.0}/LICENSE +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/MANIFEST.in +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/README.md +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__init__.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/__init__.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/benchmarks/__init__.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/benchmarks/test_bulk_insert.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/benchmarks/test_select.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/conf_models.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/conftest.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/docker_helpers.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/helpers.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/migrations/__init__.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/migrations/conftest.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/migrations/test_action_sorter.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/migrations/test_generator.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/migrations/test_generics.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/mountaineer/__init__.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/mountaineer/dependencies/__init__.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/mountaineer/dependencies/test_core.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/schemas/__init__.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/schemas/test_cli.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/schemas/test_db_serializer.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/schemas/test_db_stubs.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/test_alias.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/test_base.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/test_comparison.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/test_field.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/test_helpers.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/test_modifications.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/test_queries_str.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/test_session.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/__tests__/test_text_search.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/alias_values.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/comparison.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/generics.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/io.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/logging.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/migrations/__init__.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/migrations/action_sorter.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/migrations/cli.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/migrations/client_io.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/migrations/generator.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/migrations/migration.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/migrations/migrator.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/modifications.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/mountaineer/__init__.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/mountaineer/cli.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/mountaineer/config.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/mountaineer/dependencies/__init__.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/mountaineer/dependencies/core.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/postgres.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/py.typed +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/queries_str.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/schemas/__init__.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/schemas/actions.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/schemas/cli.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/schemas/db_serializer.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/session.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/session_optimized.pyx +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe/typing.py +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe.egg-info/SOURCES.txt +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe.egg-info/dependency_links.txt +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe.egg-info/requires.txt +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/iceaxe.egg-info/top_level.txt +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/setup.cfg +0 -0
- {iceaxe-0.7.1 → iceaxe-0.8.0}/setup.py +0 -0
|
@@ -262,6 +262,7 @@ async def test_add_column_any_type(
|
|
|
262
262
|
(ColumnType.DECIMAL, ColumnType.NUMERIC),
|
|
263
263
|
(ColumnType.SERIAL, ColumnType.INTEGER),
|
|
264
264
|
(ColumnType.BIGSERIAL, ColumnType.BIGINT),
|
|
265
|
+
(ColumnType.SMALLSERIAL, ColumnType.SMALLINT),
|
|
265
266
|
(ColumnType.CHAR, "character"),
|
|
266
267
|
(ColumnType.TIME_WITHOUT_TIME_ZONE, "time without time zone"),
|
|
267
268
|
(ColumnType.TIMESTAMP_WITHOUT_TIME_ZONE, "timestamp without time zone"),
|
|
@@ -1523,3 +1523,49 @@ def test_multiple_primary_keys_warning():
|
|
|
1523
1523
|
assert "multiple fields marked as primary_key=True" in warning_message
|
|
1524
1524
|
assert "composite primary key constraint" in warning_message
|
|
1525
1525
|
assert "Consider using only one primary key field" in warning_message
|
|
1526
|
+
|
|
1527
|
+
|
|
1528
|
+
def test_explicit_type_override(clear_all_database_objects):
|
|
1529
|
+
"""
|
|
1530
|
+
Test that explicit_type parameter overrides automatic type inference.
|
|
1531
|
+
"""
|
|
1532
|
+
|
|
1533
|
+
class TestModel(TableBase):
|
|
1534
|
+
id: int = Field(primary_key=True)
|
|
1535
|
+
# This should be BIGINT instead of INTEGER due to explicit_type
|
|
1536
|
+
big_number: int = Field(explicit_type=ColumnType.BIGINT)
|
|
1537
|
+
# This should be TEXT instead of VARCHAR due to explicit_type
|
|
1538
|
+
long_text: str = Field(explicit_type=ColumnType.TEXT)
|
|
1539
|
+
# This should be JSONB instead of JSON due to explicit_type
|
|
1540
|
+
data: dict = Field(is_json=True, explicit_type=ColumnType.JSONB)
|
|
1541
|
+
# Normal field without explicit_type for comparison
|
|
1542
|
+
normal_field: str = Field()
|
|
1543
|
+
|
|
1544
|
+
migrator = DatabaseMemorySerializer()
|
|
1545
|
+
db_objects = list(migrator.delegate([TestModel]))
|
|
1546
|
+
|
|
1547
|
+
# Extract column definitions
|
|
1548
|
+
columns = [obj for obj, _ in db_objects if isinstance(obj, DBColumn)]
|
|
1549
|
+
|
|
1550
|
+
# Find each column and verify the type
|
|
1551
|
+
big_number_column = next(c for c in columns if c.column_name == "big_number")
|
|
1552
|
+
assert big_number_column.column_type == ColumnType.BIGINT
|
|
1553
|
+
assert not big_number_column.nullable
|
|
1554
|
+
|
|
1555
|
+
long_text_column = next(c for c in columns if c.column_name == "long_text")
|
|
1556
|
+
assert long_text_column.column_type == ColumnType.TEXT
|
|
1557
|
+
assert not long_text_column.nullable
|
|
1558
|
+
|
|
1559
|
+
data_column = next(c for c in columns if c.column_name == "data")
|
|
1560
|
+
assert data_column.column_type == ColumnType.JSONB
|
|
1561
|
+
assert not data_column.nullable
|
|
1562
|
+
|
|
1563
|
+
# Verify normal field still uses automatic inference
|
|
1564
|
+
normal_field_column = next(c for c in columns if c.column_name == "normal_field")
|
|
1565
|
+
assert normal_field_column.column_type == ColumnType.VARCHAR
|
|
1566
|
+
assert not normal_field_column.nullable
|
|
1567
|
+
|
|
1568
|
+
# Verify the id field uses automatic inference (INTEGER)
|
|
1569
|
+
id_column = next(c for c in columns if c.column_name == "id")
|
|
1570
|
+
assert id_column.column_type == ColumnType.INTEGER
|
|
1571
|
+
assert not id_column.nullable
|
|
@@ -5,6 +5,7 @@ import pytest
|
|
|
5
5
|
|
|
6
6
|
from iceaxe.__tests__.conf_models import (
|
|
7
7
|
ArtifactDemo,
|
|
8
|
+
ComplexDemo,
|
|
8
9
|
Employee,
|
|
9
10
|
FunctionDemoModel,
|
|
10
11
|
UserDemo,
|
|
@@ -316,6 +317,13 @@ def test_function_transformations():
|
|
|
316
317
|
[],
|
|
317
318
|
)
|
|
318
319
|
|
|
320
|
+
# Test unnest function
|
|
321
|
+
new_query = QueryBuilder().select(func.unnest(ComplexDemo.string_list))
|
|
322
|
+
assert new_query.build() == (
|
|
323
|
+
'SELECT unnest("complexdemo"."string_list") AS aggregate_0 FROM "complexdemo"',
|
|
324
|
+
[],
|
|
325
|
+
)
|
|
326
|
+
|
|
319
327
|
# Test type conversion functions
|
|
320
328
|
new_query = QueryBuilder().select(
|
|
321
329
|
(
|
|
@@ -337,6 +345,157 @@ def test_function_transformations():
|
|
|
337
345
|
)
|
|
338
346
|
|
|
339
347
|
|
|
348
|
+
def test_array_operators():
|
|
349
|
+
# Test ANY operator
|
|
350
|
+
new_query = (
|
|
351
|
+
QueryBuilder()
|
|
352
|
+
.select(ComplexDemo)
|
|
353
|
+
.where(func.any(ComplexDemo.string_list) == "python")
|
|
354
|
+
)
|
|
355
|
+
assert new_query.build() == (
|
|
356
|
+
'SELECT "complexdemo"."id" AS "complexdemo_id", "complexdemo"."string_list" AS "complexdemo_string_list", '
|
|
357
|
+
'"complexdemo"."json_data" AS "complexdemo_json_data" FROM "complexdemo" WHERE \'python\' = ANY("complexdemo"."string_list")',
|
|
358
|
+
[],
|
|
359
|
+
)
|
|
360
|
+
|
|
361
|
+
# Test ALL operator
|
|
362
|
+
new_query = (
|
|
363
|
+
QueryBuilder()
|
|
364
|
+
.select(ComplexDemo)
|
|
365
|
+
.where(func.all(ComplexDemo.string_list) == "active")
|
|
366
|
+
)
|
|
367
|
+
assert new_query.build() == (
|
|
368
|
+
'SELECT "complexdemo"."id" AS "complexdemo_id", "complexdemo"."string_list" AS "complexdemo_string_list", '
|
|
369
|
+
'"complexdemo"."json_data" AS "complexdemo_json_data" FROM "complexdemo" WHERE \'active\' = ALL("complexdemo"."string_list")',
|
|
370
|
+
[],
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
# Test array_contains operator (@>)
|
|
374
|
+
new_query = (
|
|
375
|
+
QueryBuilder()
|
|
376
|
+
.select(ComplexDemo)
|
|
377
|
+
.where(
|
|
378
|
+
func.array_contains(ComplexDemo.string_list, ["python", "django"]) == True # noqa: E712
|
|
379
|
+
)
|
|
380
|
+
)
|
|
381
|
+
assert new_query.build() == (
|
|
382
|
+
'SELECT "complexdemo"."id" AS "complexdemo_id", "complexdemo"."string_list" AS "complexdemo_string_list", '
|
|
383
|
+
'"complexdemo"."json_data" AS "complexdemo_json_data" FROM "complexdemo" WHERE "complexdemo"."string_list" @> ARRAY[\'python\',\'django\'] = $1',
|
|
384
|
+
[True],
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
# Test array_contained_by operator (<@)
|
|
388
|
+
new_query = (
|
|
389
|
+
QueryBuilder()
|
|
390
|
+
.select(ComplexDemo)
|
|
391
|
+
.where(
|
|
392
|
+
func.array_contained_by( # noqa: E712
|
|
393
|
+
ComplexDemo.string_list, ["python", "java", "go", "rust"]
|
|
394
|
+
)
|
|
395
|
+
== True
|
|
396
|
+
)
|
|
397
|
+
)
|
|
398
|
+
assert new_query.build() == (
|
|
399
|
+
'SELECT "complexdemo"."id" AS "complexdemo_id", "complexdemo"."string_list" AS "complexdemo_string_list", '
|
|
400
|
+
'"complexdemo"."json_data" AS "complexdemo_json_data" FROM "complexdemo" WHERE "complexdemo"."string_list" <@ ARRAY[\'python\',\'java\',\'go\',\'rust\'] = $1',
|
|
401
|
+
[True],
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
# Test array_overlaps operator (&&)
|
|
405
|
+
new_query = (
|
|
406
|
+
QueryBuilder()
|
|
407
|
+
.select(ComplexDemo)
|
|
408
|
+
.where(
|
|
409
|
+
func.array_overlaps( # noqa: E712
|
|
410
|
+
ComplexDemo.string_list, ["python", "data-science", "ml"]
|
|
411
|
+
)
|
|
412
|
+
== True
|
|
413
|
+
)
|
|
414
|
+
)
|
|
415
|
+
assert new_query.build() == (
|
|
416
|
+
'SELECT "complexdemo"."id" AS "complexdemo_id", "complexdemo"."string_list" AS "complexdemo_string_list", '
|
|
417
|
+
'"complexdemo"."json_data" AS "complexdemo_json_data" FROM "complexdemo" WHERE "complexdemo"."string_list" && ARRAY[\'python\',\'data-science\',\'ml\'] = $1',
|
|
418
|
+
[True],
|
|
419
|
+
)
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
def test_array_comparison_operators():
|
|
423
|
+
# Test ANY with different operators
|
|
424
|
+
new_query = (
|
|
425
|
+
QueryBuilder()
|
|
426
|
+
.select(ComplexDemo)
|
|
427
|
+
.where(func.any(ComplexDemo.string_list) != "inactive")
|
|
428
|
+
)
|
|
429
|
+
assert new_query.build() == (
|
|
430
|
+
'SELECT "complexdemo"."id" AS "complexdemo_id", "complexdemo"."string_list" AS "complexdemo_string_list", '
|
|
431
|
+
'"complexdemo"."json_data" AS "complexdemo_json_data" FROM "complexdemo" WHERE \'inactive\' != ANY("complexdemo"."string_list")',
|
|
432
|
+
[],
|
|
433
|
+
)
|
|
434
|
+
|
|
435
|
+
# Test ALL with >= operator
|
|
436
|
+
new_query = (
|
|
437
|
+
QueryBuilder()
|
|
438
|
+
.select(ComplexDemo)
|
|
439
|
+
.where(func.all(ComplexDemo.string_list) >= "a")
|
|
440
|
+
)
|
|
441
|
+
assert new_query.build() == (
|
|
442
|
+
'SELECT "complexdemo"."id" AS "complexdemo_id", "complexdemo"."string_list" AS "complexdemo_string_list", '
|
|
443
|
+
'"complexdemo"."json_data" AS "complexdemo_json_data" FROM "complexdemo" WHERE \'a\' >= ALL("complexdemo"."string_list")',
|
|
444
|
+
[],
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
|
|
448
|
+
def test_array_manipulation_functions():
|
|
449
|
+
# Test array_append
|
|
450
|
+
new_query = QueryBuilder().select(
|
|
451
|
+
func.array_append(ComplexDemo.string_list, "new-tag")
|
|
452
|
+
)
|
|
453
|
+
assert new_query.build() == (
|
|
454
|
+
'SELECT array_append("complexdemo"."string_list", \'new-tag\') AS aggregate_0 FROM "complexdemo"',
|
|
455
|
+
[],
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
# Test array_prepend
|
|
459
|
+
new_query = QueryBuilder().select(
|
|
460
|
+
func.array_prepend("featured", ComplexDemo.string_list)
|
|
461
|
+
)
|
|
462
|
+
assert new_query.build() == (
|
|
463
|
+
'SELECT array_prepend(\'featured\', "complexdemo"."string_list") AS aggregate_0 FROM "complexdemo"',
|
|
464
|
+
[],
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
# Test array_cat with field - this would require a join in practice
|
|
468
|
+
# For now, let's test with a simpler case using the same table
|
|
469
|
+
# or we could test array_cat with a literal array which is more common
|
|
470
|
+
|
|
471
|
+
# Test array_cat with literal array
|
|
472
|
+
new_query = QueryBuilder().select(
|
|
473
|
+
func.array_cat(ComplexDemo.string_list, ["admin", "superuser"])
|
|
474
|
+
)
|
|
475
|
+
assert new_query.build() == (
|
|
476
|
+
'SELECT array_cat("complexdemo"."string_list", ARRAY[\'admin\',\'superuser\']) AS aggregate_0 FROM "complexdemo"',
|
|
477
|
+
[],
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
# Test array_position
|
|
481
|
+
new_query = QueryBuilder().select(
|
|
482
|
+
func.array_position(ComplexDemo.string_list, "python")
|
|
483
|
+
)
|
|
484
|
+
assert new_query.build() == (
|
|
485
|
+
'SELECT array_position("complexdemo"."string_list", \'python\') AS aggregate_0 FROM "complexdemo"',
|
|
486
|
+
[],
|
|
487
|
+
)
|
|
488
|
+
|
|
489
|
+
# Test array_remove
|
|
490
|
+
new_query = QueryBuilder().select(
|
|
491
|
+
func.array_remove(ComplexDemo.string_list, "deprecated")
|
|
492
|
+
)
|
|
493
|
+
assert new_query.build() == (
|
|
494
|
+
'SELECT array_remove("complexdemo"."string_list", \'deprecated\') AS aggregate_0 FROM "complexdemo"',
|
|
495
|
+
[],
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
|
|
340
499
|
def test_invalid_where_condition():
|
|
341
500
|
with pytest.raises(ValueError):
|
|
342
501
|
QueryBuilder().select(UserDemo.id).where("invalid condition") # type: ignore
|
|
@@ -19,6 +19,7 @@ from pydantic_core import PydanticUndefined
|
|
|
19
19
|
from iceaxe.comparison import ComparisonBase
|
|
20
20
|
from iceaxe.postgres import PostgresFieldBase
|
|
21
21
|
from iceaxe.queries_str import QueryIdentifier, QueryLiteral
|
|
22
|
+
from iceaxe.sql_types import ColumnType
|
|
22
23
|
|
|
23
24
|
if TYPE_CHECKING:
|
|
24
25
|
from iceaxe.base import TableBase
|
|
@@ -37,6 +38,7 @@ class DBFieldInputs(_FieldInfoInputs, total=False):
|
|
|
37
38
|
index: bool
|
|
38
39
|
check_expression: str | None
|
|
39
40
|
is_json: bool
|
|
41
|
+
explicit_type: ColumnType | None
|
|
40
42
|
|
|
41
43
|
|
|
42
44
|
class DBFieldInfo(FieldInfo):
|
|
@@ -97,6 +99,12 @@ class DBFieldInfo(FieldInfo):
|
|
|
97
99
|
When True, the field's value will be JSON serialized before storage.
|
|
98
100
|
"""
|
|
99
101
|
|
|
102
|
+
explicit_type: ColumnType | None = None
|
|
103
|
+
"""
|
|
104
|
+
Explicitly specify the SQL column type for this field.
|
|
105
|
+
When set, this type takes precedence over automatic type inference.
|
|
106
|
+
"""
|
|
107
|
+
|
|
100
108
|
def __init__(self, **kwargs: Unpack[DBFieldInputs]):
|
|
101
109
|
"""
|
|
102
110
|
Initialize a new DBFieldInfo instance with the given field configuration.
|
|
@@ -119,6 +127,7 @@ class DBFieldInfo(FieldInfo):
|
|
|
119
127
|
self.index = kwargs.pop("index", False)
|
|
120
128
|
self.check_expression = kwargs.pop("check_expression", None)
|
|
121
129
|
self.is_json = kwargs.pop("is_json", False)
|
|
130
|
+
self.explicit_type = kwargs.pop("explicit_type", None)
|
|
122
131
|
|
|
123
132
|
@classmethod
|
|
124
133
|
def extend_field(
|
|
@@ -131,6 +140,7 @@ class DBFieldInfo(FieldInfo):
|
|
|
131
140
|
index: bool,
|
|
132
141
|
check_expression: str | None,
|
|
133
142
|
is_json: bool,
|
|
143
|
+
explicit_type: ColumnType | None,
|
|
134
144
|
):
|
|
135
145
|
"""
|
|
136
146
|
Helper function to extend a Pydantic FieldInfo with database-specific attributes.
|
|
@@ -144,6 +154,7 @@ class DBFieldInfo(FieldInfo):
|
|
|
144
154
|
index=index,
|
|
145
155
|
check_expression=check_expression,
|
|
146
156
|
is_json=is_json,
|
|
157
|
+
explicit_type=explicit_type,
|
|
147
158
|
**field._attributes_set, # type: ignore
|
|
148
159
|
)
|
|
149
160
|
|
|
@@ -168,6 +179,7 @@ def __get_db_field(_: Callable[Concatenate[Any, P], Any] = PydanticField): # ty
|
|
|
168
179
|
index: bool = False,
|
|
169
180
|
check_expression: str | None = None,
|
|
170
181
|
is_json: bool = False,
|
|
182
|
+
explicit_type: ColumnType | None = None,
|
|
171
183
|
default: Any = _Unset,
|
|
172
184
|
default_factory: (
|
|
173
185
|
Callable[[], Any] | Callable[[dict[str, Any]], Any] | None
|
|
@@ -192,6 +204,7 @@ def __get_db_field(_: Callable[Concatenate[Any, P], Any] = PydanticField): # ty
|
|
|
192
204
|
index=index,
|
|
193
205
|
check_expression=check_expression,
|
|
194
206
|
is_json=is_json,
|
|
207
|
+
explicit_type=explicit_type,
|
|
195
208
|
),
|
|
196
209
|
)
|
|
197
210
|
|