|
3 | 3 | from argparse import ArgumentParser |
4 | 4 | from collections import defaultdict |
5 | 5 | from copy import deepcopy |
| 6 | +from textwrap import dedent |
6 | 7 | from typing import Any, cast |
7 | 8 |
|
8 | 9 | import simplejson as json |
@@ -980,6 +981,24 @@ def handle_edit_tree( |
980 | 981 | remove_empty(dc_edit_tree_dict, collection_name) |
981 | 982 | return sql |
982 | 983 |
|
| 984 | + @staticmethod |
| 985 | + def get_recreate_enum( |
| 986 | + enum_name: str, collection_name: str, field_name: str, values: list[str] |
| 987 | + ) -> str: |
| 988 | + result = "" |
| 989 | + result += ( |
| 990 | + AlterSchemaHelper.get_drop_enum_type_statement_from_collection_and_column( |
| 991 | + collection_name, field_name |
| 992 | + ) |
| 993 | + ) |
| 994 | + result += Helper.ENUM_DEFINITION_TEMPLATE.substitute( |
| 995 | + { |
| 996 | + "name": enum_name, |
| 997 | + "values": ", ".join([f"'{item}'" for item in values]), |
| 998 | + } |
| 999 | + ) |
| 1000 | + return result |
| 1001 | + |
983 | 1002 | @staticmethod |
984 | 1003 | def handle_edit_field_attributes( |
985 | 1004 | table_name: str, |
@@ -1037,6 +1056,13 @@ def handle_edit_field_attributes( |
1037 | 1056 | table_name, field_name |
1038 | 1057 | ), |
1039 | 1058 | ) |
| 1059 | + case "enum": |
| 1060 | + values_old = PREV_MODELS[collection_name]["fields"][field_name][ |
| 1061 | + constraint |
| 1062 | + ] |
| 1063 | + constraints_sql += EditHelper.edit_enum( |
| 1064 | + value, values_old, collection_name, field_name |
| 1065 | + ) |
1040 | 1066 | case "sql": |
1041 | 1067 | alter_views.add(collection_name) |
1042 | 1068 | case "reference" | "to": |
@@ -1066,10 +1092,107 @@ def handle_edit_field_attributes( |
1066 | 1092 | case value if value in FieldAttributes.skipped_in_schema: |
1067 | 1093 | pass |
1068 | 1094 | case _: |
| 1095 | + # Currently unhandled: |
| 1096 | + # "required" "unique" as they are only present with true and will be deleted if false. |
| 1097 | + # type changes are not yet supported |
1069 | 1098 | raise NotImplementedError(f"{constraint}: {value}") |
1070 | 1099 | del dc_field_def[0][constraint] |
1071 | 1100 | return constraints_sql |
1072 | 1101 |
|
| 1102 | + @classmethod |
| 1103 | + def edit_enum( |
| 1104 | + cls, |
| 1105 | + values_new: list[str], |
| 1106 | + values_old: list[str], |
| 1107 | + collection_name: str, |
| 1108 | + field_name: str, |
| 1109 | + ) -> str: |
| 1110 | + # not_found_streak = None |
| 1111 | + recreate_enum = False |
| 1112 | + add_attributes: list[str] = [] |
| 1113 | + rename_attributes = {} # old: new |
| 1114 | + concurrent_mismatches = {} # old: new |
| 1115 | + expected_idx = 0 |
| 1116 | + alter_enum_sql = "" |
| 1117 | + # Finding the stretches that differ by identifying the position in the old array |
| 1118 | + for nmbr, v_new in enumerate(values_new): |
| 1119 | + if recreate_enum: |
| 1120 | + break |
| 1121 | + expected_idx = nmbr - len(add_attributes) |
| 1122 | + try: |
| 1123 | + if field_name == "state": |
| 1124 | + pass |
| 1125 | + idx_old = values_old.index(v_new) |
| 1126 | + except ValueError: |
| 1127 | + # value not in list |
| 1128 | + # edge up NOT_FOUND_STREAK |
| 1129 | + # collect for change detection |
| 1130 | + if expected_idx < len(values_old): |
| 1131 | + concurrent_mismatches[values_old[expected_idx]] = v_new |
| 1132 | + elif not concurrent_mismatches: |
| 1133 | + add_attributes.append(v_new) |
| 1134 | + else: |
| 1135 | + raise Exception( |
| 1136 | + "It seems you are trying to rename and add at the end of the list at the same time." |
| 1137 | + ) |
| 1138 | + # not_found_streak = True |
| 1139 | + else: |
| 1140 | + # edge down for NOT_FOUND_STREAK |
| 1141 | + if idx_old == expected_idx: |
| 1142 | + # if gap matches rename attribute and give warning to check those lines in output |
| 1143 | + # else recreated enum |
| 1144 | + # if not_found_streak: |
| 1145 | + # not_found_streak = False |
| 1146 | + rename_attributes.update(concurrent_mismatches) |
| 1147 | + elif idx_old < expected_idx: |
| 1148 | + # Add inbetween of new entries. |
| 1149 | + add_attributes.extend(v for v in concurrent_mismatches.values()) |
| 1150 | + elif idx_old > expected_idx: |
| 1151 | + # Add inbetween of old entries means remove in new. |
| 1152 | + # We can't delete but can tolerate it being unused. So nothing to do here. |
| 1153 | + # TODO decide whether to do nothing, |
| 1154 | + recreate_enum = True |
| 1155 | + # or change type to string? |
| 1156 | + if concurrent_mismatches: |
| 1157 | + concurrent_mismatches.clear() |
| 1158 | + # Clean up remaining |
| 1159 | + if len(values_new) > len(values_old) + len(add_attributes): |
| 1160 | + add_attributes.extend(v for v in concurrent_mismatches.values()) |
| 1161 | + else: |
| 1162 | + rename_attributes.update(concurrent_mismatches) |
| 1163 | + if len(values_old) > len(values_new) - len(add_attributes): |
| 1164 | + recreate_enum = True |
| 1165 | + |
| 1166 | + # Do the stuff |
| 1167 | + enum_name = HelperGetNames.get_enum_name_for_column(collection_name, field_name) |
| 1168 | + if recreate_enum: |
| 1169 | + # recreate enum |
| 1170 | + alter_enum_sql += EditHelper.get_recreate_enum( |
| 1171 | + enum_name, collection_name, field_name, values_new |
| 1172 | + ) |
| 1173 | + else: |
| 1174 | + for attr in add_attributes: |
| 1175 | + alter_enum_sql += AlterSchemaHelper.get_add_value_to_enum( |
| 1176 | + enum_name, attr |
| 1177 | + ) |
| 1178 | + if rename_attributes: |
| 1179 | + print( |
| 1180 | + dedent(f""" |
| 1181 | + Renaming entries for '{enum_name}'. |
| 1182 | + {rename_attributes} |
| 1183 | + Make sure that this is intended. |
| 1184 | + Alternatively recreate it by replacing with: |
| 1185 | + """) |
| 1186 | + + EditHelper.get_recreate_enum( |
| 1187 | + enum_name, collection_name, field_name, values_new |
| 1188 | + ) |
| 1189 | + ) |
| 1190 | + for attr_old, attr_new in rename_attributes.items(): |
| 1191 | + alter_enum_sql += AlterSchemaHelper.get_rename_value_in_enum( |
| 1192 | + enum_name, attr_old, attr_new |
| 1193 | + ) |
| 1194 | + return alter_enum_sql |
| 1195 | + |
1073 | 1196 |
|
1074 | 1197 | def handle_rename(renames: Renames, dc_rename_dict: Renames) -> str: |
1075 | 1198 | result = "" |
|
0 commit comments