From afcefe90c346b66d411e4a2924e331e6c70b27ca Mon Sep 17 00:00:00 2001 From: nasc17 <69922333+nasc17@users.noreply.github.com> Date: Wed, 24 May 2023 14:34:24 -0400 Subject: [PATCH] Fix being able to edit tables (#411) * set metadata * fix edit table * fix breaking changes --- .../smo_edit_table_metadata_factory.py | 2 +- .../edit_data/update_management/row_edit.py | 72 ++++++++++++------- .../table_objects/column/9.2_plus/nodes.sql | 48 +++++++------ 3 files changed, 76 insertions(+), 46 deletions(-) diff --git a/ossdbtoolsservice/edit_data/smo_edit_table_metadata_factory.py b/ossdbtoolsservice/edit_data/smo_edit_table_metadata_factory.py index 001beff01..669bced43 100644 --- a/ossdbtoolsservice/edit_data/smo_edit_table_metadata_factory.py +++ b/ossdbtoolsservice/edit_data/smo_edit_table_metadata_factory.py @@ -21,7 +21,7 @@ def get(self, connection: ServerConnection, schema_name: str, object_name: str, server = Server(connection) result_object: Table = None - object_metadata = ObjectMetadata(server.urn_base, None, object_type, object_name, schema_name) + object_metadata = ObjectMetadata(server.urn_base, 0, object_type, object_name, schema_name) if object_type.lower() == 'table': result_object = server.find_table(object_metadata) diff --git a/ossdbtoolsservice/edit_data/update_management/row_edit.py b/ossdbtoolsservice/edit_data/update_management/row_edit.py index a2fef6404..e344a2caf 100644 --- a/ossdbtoolsservice/edit_data/update_management/row_edit.py +++ b/ossdbtoolsservice/edit_data/update_management/row_edit.py @@ -58,30 +58,52 @@ def validate_column_is_updatable(self, column_index: int): def build_where_clause(self): - if len(self.table_metadata.key_columns) == 0: - raise TypeError(f'Table {self.table_metadata.table_name} does not have a single column that can be trusted for uniqueness') + if len(self.table_metadata.key_columns) != 0: + + where_start = 'WHERE {0}' + column_name_template = '"{0}" {1}' + parameters = [] + where_clauses = [] + + row = self.result_set.get_row(self.row_id) + + for column in self.table_metadata.key_columns: + cell: DbCellValue = row[column.ordinal] + + cell_data_clause = '' + column_name = column.name + if cell.is_null is True: + cell_data_clause += 'IS NULL' + elif isinstance(cell.raw_object, bytearray) or column.db_column.data_type.lower() == 'text': + cell_data_clause += 'IS NOT NULL' + else: + cell_data_clause += '= %s' + parameters.append(cell.raw_object) + + where_clauses.append(column_name_template.format(column_name, cell_data_clause)) + query_template = where_start.format(' AND '.join(where_clauses)) + else: + where_start = 'WHERE CTID IN ( SELECT CTID FROM {0} {1} LIMIT 1)' + where_body = 'WHERE {0}' + column_name_template = '"{0}" {1}' + parameters = [] + where_clauses = [] + + row = self.result_set.get_row(self.row_id) + + for i, cell in enumerate(row): + cell_data_clause = '' + column_name = self.table_metadata.db_columns[i].column_name + if cell.is_null is True: + cell_data_clause += 'IS NULL' + elif isinstance(cell.raw_object, bytearray) or self.table_metadata.db_columns[i].data_type.lower() == 'text': + cell_data_clause += 'IS NOT NULL' + else: + cell_data_clause += '= %s' + parameters.append(cell.raw_object) + + where_clauses.append(column_name_template.format(column_name, cell_data_clause)) + body = where_body.format(' AND '.join(where_clauses)) + query_template = where_start.format(self.table_metadata.multipart_name, body) - where_start = 'WHERE {0}' - column_name_template = '"{0}" {1}' - parameters = [] - where_clauses = [] - - row = self.result_set.get_row(self.row_id) - - for column in self.table_metadata.key_columns: - cell: DbCellValue = row[column.ordinal] - - cell_data_clause = '' - column_name = column.name - if cell.is_null is True: - cell_data_clause += 'IS NULL' - elif isinstance(cell.raw_object, bytearray) or column.db_column.data_type.lower() == 'text': - cell_data_clause += 'IS NOT NULL' - else: - cell_data_clause += '= %s' - parameters.append(cell.raw_object) - - where_clauses.append(column_name_template.format(column_name, cell_data_clause)) - - query_template = where_start.format(' AND '.join(where_clauses)) return EditScript(query_template, parameters) diff --git a/pgsmo/objects/table_objects/column/9.2_plus/nodes.sql b/pgsmo/objects/table_objects/column/9.2_plus/nodes.sql index 6cbf8feb4..bad74bf65 100644 --- a/pgsmo/objects/table_objects/column/9.2_plus/nodes.sql +++ b/pgsmo/objects/table_objects/column/9.2_plus/nodes.sql @@ -5,30 +5,38 @@ # This software is released under the PostgreSQL Licence #} SELECT - attname as name, attnum as OID, typ.oid AS typoid, typ.typname AS datatype, attnotnull as not_null, attr.atthasdef as has_default_val - ,nspname, relname, attrelid, - CASE WHEN typ.typtype = 'd' THEN typ.typtypmod ELSE atttypmod END AS typmod, - CASE WHEN atthasdef THEN (SELECT pg_get_expr(adbin, cls.oid) FROM pg_attrdef WHERE adrelid = cls.oid AND adnum = attr.attnum) ELSE NULL END AS default, - TRUE AS is_updatable, /* Supported only since PG 8.2 */ - FALSE AS isprimarykey, /* Can't do ANY() on pg_index.indkey which is int2vector */ - FALSE AS isunique /* Can't do ANY() on pg_index.indkey which is int2vector */ + attname AS name, + attnum AS OID, + typ.oid AS typoid, + typ.typname AS datatype, + attnotnull AS not_null, + attr.atthasdef AS has_default_val, + nspname, + relname, + attrelid, + CASE WHEN typ.typtype = 'd' THEN typ.typtypmod ELSE atttypmod END AS typmod, + CASE WHEN atthasdef THEN (SELECT pg_get_expr(adbin, cls.oid) FROM pg_attrdef WHERE adrelid = cls.oid AND adnum = attr.attnum) ELSE NULL END AS default, + TRUE AS is_updatable, /* Supported only since PG 8.2 */ + -- Add this expression to show if each column is a primary key column. Can't do ANY() on pg_index.indkey which is int2vector + CASE WHEN EXISTS (SELECT * FROM information_schema.key_column_usage WHERE table_schema = nspname AND table_name = relname AND column_name = attname) THEN TRUE ELSE FALSE END AS isprimarykey, + CASE WHEN EXISTS (SELECT * FROM information_schema.table_constraints WHERE table_schema = nspname AND table_name = relname AND constraint_type = 'UNIQUE' AND constraint_name IN (SELECT constraint_name FROM information_schema.constraint_column_usage WHERE table_schema = nspname AND table_name = relname AND column_name = attname)) THEN TRUE ELSE FALSE END AS isunique FROM pg_attribute AS attr JOIN pg_type AS typ ON attr.atttypid = typ.oid JOIN pg_class AS cls ON cls.oid = attr.attrelid JOIN pg_namespace AS ns ON ns.oid = cls.relnamespace LEFT OUTER JOIN information_schema.columns AS col ON col.table_schema = nspname AND - col.table_name = relname AND - col.column_name = attname + col.table_name = relname AND + col.column_name = attname WHERE - attr.attrelid = {{ parent_id|qtLiteral }}::oid - {% if clid %} - AND attr.attnum = {{ clid|qtLiteral }} - {% endif %} - {### To show system objects ###} - {% if not show_sys_objects %} - AND attr.attnum > 0 - {% endif %} - AND atttypid <> 0 AND - relkind IN ('r', 'v', 'm') AND - NOT attisdropped + attr.attrelid = {{ parent_id|qtLiteral }}::oid + {% if clid %} + AND attr.attnum = {{ clid|qtLiteral }} + {% endif %} + {### To show system objects ###} + {% if not show_sys_objects %} + AND attr.attnum > 0 + {% endif %} + AND atttypid <> 0 AND + relkind IN ('r', 'v', 'm') AND + NOT attisdropped ORDER BY attnum \ No newline at end of file