Skip to content

Commit dd7b75c

Browse files
committed
Handle transient schema create targets
1 parent ecebe50 commit dd7b75c

2 files changed

Lines changed: 67 additions & 9 deletions

File tree

‎src/openstatspec/sql/inplace_transform.py‎

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -484,10 +484,6 @@ def _apply_plan_on_connection(
484484
)
485485
]
486486
preflight(target_profile, output_variables)
487-
output_by_name = {
488-
variable.name.casefold(): variable
489-
for variable in bound.output_schema.variables
490-
}
491487
audit = apply_audit_catalog(MetaData())
492488
if not inspect(connection).has_table("transformation_apply"):
493489
raise TransformationError(
@@ -528,11 +524,24 @@ def _apply_plan_on_connection(
528524
"delete_variable_not_supported",
529525
"This SQLite runtime does not support ALTER TABLE DROP COLUMN.",
530526
)
531-
unsupported_targets = [
532-
operation.target for operation in create_operations
533-
if not isinstance(operation, CreateVariableOperation)
534-
and output_by_name[operation.target.casefold()].storage_kind != "numeric"
535-
]
527+
unsupported_targets: list[str] = []
528+
for operation_index, operation in enumerate(plan.operations):
529+
if not isinstance(operation, (RecodeOperation, AssignOperation)):
530+
continue
531+
if operation.target_mode != "create":
532+
continue
533+
prefix = TransformationPlan(
534+
plan.operations[:operation_index + 1],
535+
contract=plan.contract,
536+
input_alias=plan.input_alias,
537+
)
538+
prefix_output = bind_transformation_plan(prefix, schema).output_schema
539+
created = next(
540+
variable for variable in prefix_output.variables
541+
if variable.name.casefold() == operation.target.casefold()
542+
)
543+
if created.storage_kind != "numeric":
544+
unsupported_targets.append(operation.target)
536545
if unsupported_targets:
537546
raise TransformationError(
538547
"in_place_target_type_unsupported",

‎tests/test_inplace_transform.py‎

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,55 @@ def test_delete_then_recreate_same_name_resolves_operations_in_order(catalog) ->
374374
)["valid"] is True
375375

376376

377+
def test_temporary_created_target_can_be_deleted_before_final_schema(catalog) -> None:
378+
url, path, dataset_id, table_name = catalog
379+
380+
openstatspec.apply_spss_in_place(
381+
database_url=url,
382+
dataset_id=dataset_id,
383+
source_text="COMPUTE tmp = score. DELETE VARIABLES tmp.",
384+
actor="test-agent",
385+
)
386+
387+
connection = sqlite3.connect(path)
388+
assert connection.execute(
389+
"select source_name, source_ordinal from variable "
390+
"where dataset_id = ? order by source_ordinal",
391+
(dataset_id,),
392+
).fetchall() == [("score", 1)]
393+
assert [
394+
row[1] for row in connection.execute(f'PRAGMA table_info("{table_name}")')
395+
] == ["__case_ordinal", "score"]
396+
connection.close()
397+
398+
399+
def test_temporary_target_type_is_not_taken_from_same_name_recreation(catalog) -> None:
400+
url, path, dataset_id, table_name = catalog
401+
402+
openstatspec.apply_spss_in_place(
403+
database_url=url,
404+
dataset_id=dataset_id,
405+
source_text=(
406+
"COMPUTE tmp = score. DELETE VARIABLES tmp. STRING tmp (A4)."
407+
),
408+
actor="test-agent",
409+
)
410+
411+
connection = sqlite3.connect(path)
412+
assert connection.execute(
413+
"select source_name, storage_kind, declared_string_width "
414+
"from variable where dataset_id = ? order by source_ordinal",
415+
(dataset_id,),
416+
).fetchall() == [("score", "numeric", None), ("tmp", "string", 4)]
417+
assert connection.execute(
418+
f'SELECT score, tmp FROM "{table_name}" ORDER BY __case_ordinal'
419+
).fetchall() == [(1.0, ""), (2.0, ""), (3.0, "")]
420+
connection.close()
421+
assert validate_wide_dataset(
422+
database_url=url, dataset_id=dataset_id,
423+
)["valid"] is True
424+
425+
377426
def test_public_apply_supports_non_dolt_without_building_undo(catalog) -> None:
378427
url, path, dataset_id, table_name = catalog
379428
plan = _plan("RECODE score (1 = 0).")

0 commit comments

Comments
 (0)