diff --git a/web/pgadmin/tools/sqleditor/utils/save_changed_data.py b/web/pgadmin/tools/sqleditor/utils/save_changed_data.py
index 9bb68a398..769a2ba06 100644
--- a/web/pgadmin/tools/sqleditor/utils/save_changed_data.py
+++ b/web/pgadmin/tools/sqleditor/utils/save_changed_data.py
@@ -54,6 +54,66 @@ def save_changed_data(changed_data, columns_info, conn, command_obj,
if not status:
return status, res, query_results, None
+ def verify_column_and_data_size(row_data):
+ """
+ This function verifies the length of data type and
+ actual value of that row
+ :return: None
+ """
+ for val in row_data:
+ if val in columns_info:
+ column_size = columns_info[val]['internal_size']
+ value_size = len(row_data[val])
+ if value_size > column_size:
+ validate_rows.append(val)
+
+ def query_rollback():
+ """
+ This function rollback the transaction if any and update the message
+ for SQL query
+ :return: None
+ """
+ if is_savepoint:
+ sql = 'ROLLBACK TO SAVEPOINT save_data;'
+ msg = 'A ROLLBACK was done for the save operation only. ' \
+ 'The active transaction is not affected.'
+ else:
+ sql = 'ROLLBACK;'
+ msg = 'A ROLLBACK was done for the save transaction.'
+
+ rollback_status, rollback_result = \
+ execute_void_wrapper(conn, sql, query_results)
+ if not rollback_status:
+ return rollback_status, rollback_result, query_results, None
+
+ # If we roll backed every thing then update the
+ # message for each sql query.
+ for query in query_results:
+ if query['status']:
+ query['result'] = msg
+
+ def failure_handle(res, row_id):
+ """
+ This function handles the query failure also rollback the transaction
+ and update the message for each SQL query
+ :param res: query response
+ :param row_id: row id
+ :return: res, query_results, row_id
+ """
+ query_rollback()
+ mogrified_sql = conn.mogrify(item['sql'], item['data'])
+ mogrified_sql = mogrified_sql if mogrified_sql is not None \
+ else item['sql']
+ query_results.append({
+ 'status': False,
+ 'result': res,
+ 'sql': mogrified_sql,
+ 'rows_affected': 0,
+ 'row_added': None
+ })
+ return False, res, query_results, row_id
+
+ validate_rows = []
# Iterate total number of records to be updated/inserted
for of_type in changed_data:
# No need to go further if its not add/update/delete operation
@@ -104,6 +164,10 @@ def save_changed_data(changed_data, columns_info, conn, command_obj,
# dict key
tmp_row_index = added_index[each_row]
data = changed_data[of_type][tmp_row_index]['data']
+
+ # Verify length of row data and length of data type
+ verify_column_and_data_size(data)
+
# Remove our unique tracking key
data.pop(client_primary_key, None)
data.pop('is_row_copied', None)
@@ -156,6 +220,10 @@ def save_changed_data(changed_data, columns_info, conn, command_obj,
for pk, pk_val in
changed_data[of_type][each_row]['primary_keys'].items()
}
+
+ # Verify length of row data and length of data type
+ verify_column_and_data_size(data)
+
sql = render_template(
"/".join([command_obj.sql_path, 'update.sql']),
data_to_be_saved=data,
@@ -212,38 +280,10 @@ def save_changed_data(changed_data, columns_info, conn, command_obj,
)
list_of_sql[of_type].append({'sql': sql, 'data': {}})
- def failure_handle(res, row_id):
- mogrified_sql = conn.mogrify(item['sql'], item['data'])
- mogrified_sql = mogrified_sql if mogrified_sql is not None \
- else item['sql']
- query_results.append({
- 'status': False,
- 'result': res,
- 'sql': mogrified_sql,
- 'rows_affected': 0,
- 'row_added': None
- })
-
- if is_savepoint:
- sql = 'ROLLBACK TO SAVEPOINT save_data;'
- msg = 'A ROLLBACK was done for the save operation only. ' \
- 'The active transaction is not affected.'
- else:
- sql = 'ROLLBACK;'
- msg = 'A ROLLBACK was done for the save transaction.'
-
- rollback_status, rollback_result = \
- execute_void_wrapper(conn, sql, query_results)
- if not rollback_status:
- return rollback_status, rollback_result, query_results, None
-
- # If we roll backed every thing then update the
- # message for each sql query.
- for query in query_results:
- if query['status']:
- query['result'] = msg
-
- return False, res, query_results, row_id
+ if validate_rows:
+ query_rollback()
+ return False, "Values are larger than underlying field size",\
+ query_results, None
for opr, sqls in list_of_sql.items():
for item in sqls:
diff --git a/web/pgadmin/tools/sqleditor/utils/tests/test_save_changed_data.py b/web/pgadmin/tools/sqleditor/utils/tests/test_save_changed_data.py
index f959b2b4b..1e5c5a7a0 100644
--- a/web/pgadmin/tools/sqleditor/utils/tests/test_save_changed_data.py
+++ b/web/pgadmin/tools/sqleditor/utils/tests/test_save_changed_data.py
@@ -71,6 +71,55 @@ class TestSaveChangedData(BaseTestGenerator):
check_sql='SELECT * FROM %s WHERE pk_col = 3',
check_result=[[3, "three"]]
)),
+ ('When inserting row with long data', dict(
+ save_payload={
+ "updated": {},
+ "added": {
+ "2": {
+ "err": False,
+ "data": {
+ "pk_col": "3",
+ "__temp_PK": "2",
+ "normal_col": "invalid-log-string"
+ }
+ }
+ },
+ "staged_rows": {},
+ "deleted": {},
+ "updated_index": {},
+ "added_index": {"2": "2"},
+ "columns": [
+ {
+ "name": "pk_col",
+ "display_name": "pk_col",
+ "column_type": "[PK] integer",
+ "column_type_internal": "integer",
+ "pos": 0,
+ "label": "pk_col
[PK] integer",
+ "cell": "number",
+ "can_edit": True,
+ "type": "integer",
+ "not_null": True,
+ "has_default_val": False,
+ "is_array": False},
+ {"name": "normal_col",
+ "display_name": "normal_col",
+ "column_type": "character varying",
+ "column_type_internal": "character varying",
+ "pos": 1,
+ "label": "normal_col
character varying",
+ "cell": "string",
+ "can_edit": True,
+ "type": "character varying",
+ "not_null": False,
+ "has_default_val": False,
+ "is_array": False}
+ ]
+ },
+ save_status=False,
+ check_sql='SELECT * FROM %s WHERE pk_col = 3',
+ check_result='SELECT 0'
+ )),
('When inserting new invalid row', dict(
save_payload={
"updated": {},
@@ -168,6 +217,53 @@ class TestSaveChangedData(BaseTestGenerator):
check_sql='SELECT * FROM %s WHERE pk_col = 1',
check_result=[[1, "ONE"]]
)),
+ ('When updating a row in a invalid way', dict(
+ save_payload={
+ "updated": {
+ "1":
+ {"err": False,
+ "data": {"normal_col": "INVALID-COL-LENGTH"},
+ "primary_keys":
+ {"pk_col": 1}
+ }
+ },
+ "added": {},
+ "staged_rows": {},
+ "deleted": {},
+ "updated_index": {"1": "1"},
+ "added_index": {},
+ "columns": [
+ {
+ "name": "pk_col",
+ "display_name": "pk_col",
+ "column_type": "[PK] integer",
+ "column_type_internal": "integer",
+ "pos": 0,
+ "label": "pk_col
[PK] integer",
+ "cell": "number",
+ "can_edit": True,
+ "type": "integer",
+ "not_null": True,
+ "has_default_val": False,
+ "is_array": False},
+ {"name": "normal_col",
+ "display_name": "normal_col",
+ "column_type": "character varying",
+ "column_type_internal": "character varying",
+ "pos": 1,
+ "label": "normal_col
character varying",
+ "cell": "string",
+ "can_edit": True,
+ "type": "character varying",
+ "not_null": False,
+ "has_default_val": False,
+ "is_array": False}
+ ]
+ },
+ save_status=False,
+ check_sql='SELECT * FROM %s WHERE pk_col = 1',
+ check_result=[[1, "one"]]
+ )),
('When updating a row in an invalid way', dict(
save_payload={
"updated": {
@@ -343,7 +439,7 @@ class TestSaveChangedData(BaseTestGenerator):
CREATE TABLE "%s"(
pk_col INT PRIMARY KEY,
- normal_col VARCHAR);
+ normal_col VARCHAR(5));
INSERT INTO "%s" VALUES
(1, 'one'),