Skip to content

Commit

Permalink
Configuration Wizard: Alter column size with Varchar data type
Browse files Browse the repository at this point in the history
This patch allows altering the size of column with Varchar data
type. To avoid truncating data, the alter function will prevent
reducing the size of a column if the entity with the column
contains data.
pgathogo committed Jan 27, 2025
1 parent 431b66a commit d8f504b
Showing 9 changed files with 140 additions and 103 deletions.
18 changes: 16 additions & 2 deletions stdm/data/configuration/column_updaters.py
Original file line number Diff line number Diff line change
@@ -43,7 +43,8 @@
metadata
)
from stdm.data.pg_utils import (
drop_cascade_column
drop_cascade_column,
run_query
)
from . import _bind_metadata

@@ -131,13 +132,17 @@ def _update_col(column, table, data_type, columns):
idx_name = None
if column.index:
idx_name = 'idx_{0}_{1}'.format(column.entity.name, column.name)

unique_name = None
if column.unique:
unique_name = 'unq_{0}_{1}'.format(column.entity.name, column.name)

if column.action == DbItem.CREATE:
# Ensure the column does not exist otherwise an exception will be thrown
if column.name not in columns:

print(f"* NEW COL: {column.name}")

alchemy_column.create(
table=table,
unique_name=unique_name
@@ -157,7 +162,9 @@ def _update_col(column, table, data_type, columns):
# Ensure the column exists before altering
if column.name in columns:
col_attrs = _base_col_attrs(column)

col_attrs['table'] = table

alchemy_column.alter(**col_attrs)

elif column.action == DbItem.DROP:
@@ -190,6 +197,7 @@ def _update_col(column, table, data_type, columns):
except DummyException:
pass


return alchemy_column


@@ -239,14 +247,20 @@ def serial_updater(column, table, columns):
return col


def varchar_updater(column, table, columns):
def varchar_updater(column, table: str, columns: list):
"""
Updater for a character varying column.
:param varchar_column: Character varying column.
:type varchar_column: VarCharColumn
:param columns: Existing column names in the database for the given table.
:type columns: list
"""

if column.action == DbItem.ALTER:
alter = f"ALTER TABLE {table} ALTER COLUMN {column.name} TYPE varchar({column.maximum});"
run_query(alter)
return

return _update_col(column, table, String(column.maximum), columns)


1 change: 1 addition & 0 deletions stdm/data/configuration/config_updater.py
Original file line number Diff line number Diff line change
@@ -93,6 +93,7 @@ def exec_(self):
for rp in self.config.removed_profiles:
self.remove_profile(rp)


# Iterate through profiles
for p in self.config.profiles.values():
self.update_profile(p)
2 changes: 1 addition & 1 deletion stdm/data/configuration/entity_updaters.py
Original file line number Diff line number Diff line change
@@ -155,7 +155,7 @@ def update_entity_columns(entity, table, columns):
if c.name != 'id':
LOGGER.debug('Updating %s column.', c.name)

c.update(table, col_names)
updater = c.update(table, col_names)

LOGGER.debug('Finished updating %s column.', c.name)

11 changes: 11 additions & 0 deletions stdm/data/configuration/stdm_configuration.py
Original file line number Diff line number Diff line change
@@ -194,3 +194,14 @@ def _clear(self):
"""
self.profiles = OrderedDict()
self.is_null = True


def print_profile_entities_columns(self):
print("-------------------------------------------")
for profile in self.profiles.values():
print(f'Profile: {profile.name}')
for entity in profile.entities.values():
if entity.short_name == 'Enumeration':
print(entity.short_name)
for column in entity.columns.values():
print(f'\t{column.name} : Action - {column.action}')
4 changes: 2 additions & 2 deletions stdm/data/pg_utils.py
Original file line number Diff line number Diff line change
@@ -815,7 +815,7 @@ def drop_cascade_table(table_name):
return False


def drop_cascade_column(table_name, column):
def drop_cascade_column(table_name: str, column_name: str) ->bool:
"""
Safely deletes the column contained in the given table using the CASCADE option.
:param table_name: Name of the database table.
@@ -827,7 +827,7 @@ def drop_cascade_column(table_name, column):
"""
del_com = 'ALTER TABLE {0} DROP COLUMN IF EXISTS {1} CASCADE;'.format(
table_name,
column
column_name
)
t = text(del_com)

29 changes: 14 additions & 15 deletions stdm/ui/wizard/column_editor.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""
/***************************************************************************
Name : column_editor
@@ -147,6 +146,7 @@ def __init__(self, **kwargs):
self.edtColName.textChanged.connect(self.validate_text)

self.notice_bar = NotificationBar(self.notif_bar)

self.init_controls()

def exclude_column_types(self, type_info):
@@ -381,6 +381,8 @@ def init_form_fields(self):
self.form_fields['srid'] = self.type_attribs.get('srid', "")
self.form_fields['geom_type'] = self.type_attribs.get('geom_type', 0)
self.form_fields['in_db'] = self.in_db
self.form_fields['entity_has_records'] = self.entity_has_records

self.form_fields['prefix_source'] = self.type_attribs.get(
'prefix_source', none
)
@@ -777,7 +779,7 @@ def create_layer(self):
proj_wkt=srid)
return layer

def create_column(self):
def create_column(self) -> BaseColumn:
"""
Creates a new BaseColumn.
"""
@@ -791,8 +793,8 @@ def create_column(self):
return column

if self.is_property_set(self.type_info):
column = BaseColumn.registered_types[self.type_info] \
(self.form_fields['colname'], self.entity,
column = BaseColumn.registered_types[self.type_info](
self.form_fields['colname'], self.entity,
self.form_fields['geom_type'],
self.entity, **self.form_fields)
else:
@@ -807,7 +809,7 @@ def property_set(self):
self.prop_set = True
self.type_attribs[self.current_type_info()]['prop_set'] = True

def is_property_set(self, ti):
def is_property_set(self, ti: 'TYPE_INFO')->bool:
"""
Checks if column property is set by reading the value of
attribute 'prop_set'
@@ -837,7 +839,7 @@ def populate_data_type_cbo(self):
if self.cboDataType.count() > 0:
self.cboDataType.setCurrentIndex(0)

def change_data_type(self, index):
def change_data_type(self, index: int):
"""
Called by type combobox when you select a different data type.
"""
@@ -859,7 +861,7 @@ def change_data_type(self, index):
self.set_optionals(opts)
self.set_min_max_defaults(ti)

def set_optionals(self, opts):
def set_optionals(self, opts: dict):
"""
Enable/disables form controls based on selected
column data type attributes
@@ -876,7 +878,7 @@ def set_optionals(self, opts):
self.cbUnique.setCheckState(self.bool_to_check(opts['unique']['check_state']))
self.cbIndex.setCheckState(self.bool_to_check(opts['index']['check_state']))

def set_min_max_defaults(self, type_info):
def set_min_max_defaults(self, type_info: str):
"""
sets the work area 'form_fields' default values (minimum/maximum)
from the column's type attribute dictionary
@@ -942,7 +944,7 @@ def accept(self):
return False

if self.column is None: # new column
if self.duplicate_check(col_name):
if self.column_exists(col_name):
self.show_message(self.tr("Column with the same name already "
"exist in this entity!"))
return False
@@ -962,7 +964,7 @@ def accept(self):
def cancel(self):
self.done(0)

def make_column(self):
def make_column(self)->BaseColumn:
"""
Returns a newly created column
:rtype: BaseColumn
@@ -971,18 +973,15 @@ def make_column(self):
col = self.create_column()
return col

def duplicate_check(self, name):
def column_exists(self, name: str) ->bool:
"""
Return True if we have a column in the current entity with same name
as our new column
:param col_name: column name
:type col_name: str
"""
# check if another column with the same name exist in the current entity
if name in self.entity.columns:
return True
else:
return False
return True if name in self.entity.columns.keys() else False

def rejectAct(self):
self.done(0)
152 changes: 78 additions & 74 deletions stdm/ui/wizard/ui_varchar_property.ui
Original file line number Diff line number Diff line change
@@ -1,78 +1,82 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>VarcharProperty</class>
<widget class="QDialog" name="VarcharProperty">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>246</width>
<height>67</height>
</rect>
</property>
<property name="windowTitle">
<string>Varchar property</string>
</property>
<layout class="QFormLayout" name="formLayout">
<item row="0" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Character length</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QLineEdit" name="edtCharLen">
<property name="placeholderText">
<string>Enter column length</string>
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
<class>VarcharProperty</class>
<widget class="QDialog" name="VarcharProperty">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>290</width>
<height>98</height>
</rect>
</property>
<property name="windowTitle">
<string>Varchar property</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="label">
<property name="text">
<string>Character length</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="edtCharLen">
<property name="buttonSymbols">
<enum>QAbstractSpinBox::NoButtons</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>VarcharProperty</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>VarcharProperty</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>VarcharProperty</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>VarcharProperty</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>
Loading

0 comments on commit d8f504b

Please sign in to comment.