Hi, Please find the attached patch for RM #2849: Allow editing of data on tables with OIDs but no primary key.
Thanks, Khushboo
diff --git a/docs/en_US/query_tool.rst b/docs/en_US/query_tool.rst index e6dec55..957dcf7 100644 --- a/docs/en_US/query_tool.rst +++ b/docs/en_US/query_tool.rst @@ -148,7 +148,7 @@ If the Query tool is opened through the *Query tool* menu option on the *Tools* All rowsets from previous queries or commands that are displayed in the *Data Output* panel will be discarded when you invoke another query; open another query tool browser tab to keep your previous results available. -If the Query Tool is opened using the *View Data* menu option and the data is updatable and has a primary key, then you can double-click on values on the *Data Output* tab and edit them: +If the Query Tool is opened using the *View Data* menu option and the data is updatable and has a primary key or oid, then you can double-click on values on the *Data Output* tab and edit them: * To enter a NULL, clear the value of the string. * To enter a blank set the value of the cell to ''. diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/nodes.sql index f3353d6..2c3e573 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/nodes.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/9.2_plus/nodes.sql @@ -15,9 +15,13 @@ WHERE {% if clid %} AND att.attnum = {{ clid|qtLiteral }} {% endif %} - {### To show system objects ###} - {% if not show_sys_objects %} +{### To show system objects ###} +{% if not show_sys_objects and not has_oids %} AND att.attnum > 0 - {% endif %} +{% endif %} +{### To show oids in view data ###} +{% if has_oids %} + AND (att.attnum > 0 OR (att.attname = 'oid' AND att.attnum < 0)) +{% endif %} AND att.attisdropped IS FALSE ORDER BY att.attnum diff --git a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/default/nodes.sql b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/default/nodes.sql index 4f1de2a..584f7b1 100644 --- a/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/default/nodes.sql +++ b/web/pgadmin/browser/server_groups/servers/databases/schemas/tables/templates/column/sql/default/nodes.sql @@ -16,8 +16,12 @@ WHERE AND att.attnum = {{ clid|qtLiteral }} {% endif %} {### To show system objects ###} -{% if not show_sys_objects %} +{% if not show_sys_objects and not has_oids %} AND att.attnum > 0 {% endif %} +{### To show oids in view data ###} +{% if has_oids %} + AND (att.attnum > 0 OR (att.attname = 'oid' AND att.attnum < 0)) +{% endif %} AND att.attisdropped IS FALSE ORDER BY att.attnum diff --git a/web/pgadmin/tools/sqleditor/__init__.py b/web/pgadmin/tools/sqleditor/__init__.py index d360d91..7bf6c44 100644 --- a/web/pgadmin/tools/sqleditor/__init__.py +++ b/web/pgadmin/tools/sqleditor/__init__.py @@ -413,6 +413,9 @@ def start_view_data(trans_id): sql = trans_obj.get_sql() pk_names, primary_keys = trans_obj.get_primary_keys(default_conn) + # Fetch OIDs status + has_oids = trans_obj.has_oids(default_conn) + # Fetch the applied filter. filter_applied = trans_obj.is_filter_applied() @@ -424,6 +427,10 @@ def start_view_data(trans_id): # Store the primary keys to the session object session_obj['primary_keys'] = primary_keys + + # Store the OIDs status into session object + session_obj['has_oids'] = has_oids + update_session_grid_transaction(trans_id, session_obj) # Execute sql asynchronously @@ -635,6 +642,8 @@ def poll(trans_id): types = {} client_primary_key = None rset = None + has_oids = False + oids = None # Check the transaction and connection status status, error_msg, conn, trans_obj, session_obj = check_transaction_status(trans_id) @@ -660,6 +669,11 @@ def poll(trans_id): if 'primary_keys' in session_obj: primary_keys = session_obj['primary_keys'] + if 'has_oids' in session_obj: + has_oids = session_obj['has_oids'] + if has_oids: + oids = {'oid': 'oid'} + # Fetch column information columns_info = conn.get_column_info() client_primary_key = generate_client_primary_key_name( @@ -678,7 +692,8 @@ def poll(trans_id): SQL = render_template("/".join([template_path, 'nodes.sql']), - tid=command_obj.obj_id) + tid=command_obj.obj_id, + has_oids=True) # rows with attribute not_null colst, rset = conn.execute_2darray(SQL) if not colst: @@ -768,7 +783,9 @@ def poll(trans_id): 'colinfo': columns_info, 'primary_keys': primary_keys, 'types': types, - 'client_primary_key': client_primary_key + 'client_primary_key': client_primary_key, + 'has_oids': has_oids, + 'oids': oids } ) @@ -902,7 +919,7 @@ def save(trans_id): and trans_obj is not None and session_obj is not None: # If there is no primary key found then return from the function. - if len(session_obj['primary_keys']) <= 0 or len(changed_data) <= 0: + if (len(session_obj['primary_keys']) <= 0 or len(changed_data) <= 0) and 'has_oids' not in session_obj: return make_json_response( data={ 'status': False, diff --git a/web/pgadmin/tools/sqleditor/command.py b/web/pgadmin/tools/sqleditor/command.py index d09bf2b..3fba3b5 100644 --- a/web/pgadmin/tools/sqleditor/command.py +++ b/web/pgadmin/tools/sqleditor/command.py @@ -364,16 +364,20 @@ class TableCommand(GridCommand): # Fetch the primary keys for the table pk_names, primary_keys = self.get_primary_keys(default_conn) + # Fetch OIDs status + has_oids = self.has_oids(default_conn) + sql_filter = self.get_filter() if sql_filter is None: sql = render_template("/".join([self.sql_path, 'objectquery.sql']), object_name=self.object_name, nsp_name=self.nsp_name, pk_names=pk_names, cmd_type=self.cmd_type, - limit=self.limit, primary_keys=primary_keys) + limit=self.limit, primary_keys=primary_keys, has_oids=has_oids) else: sql = render_template("/".join([self.sql_path, 'objectquery.sql']), object_name=self.object_name, nsp_name=self.nsp_name, pk_names=pk_names, cmd_type=self.cmd_type, - sql_filter=sql_filter, limit=self.limit, primary_keys=primary_keys) + sql_filter=sql_filter, limit=self.limit, primary_keys=primary_keys, + has_oids=has_oids) return sql @@ -418,6 +422,31 @@ class TableCommand(GridCommand): def can_filter(self): return True + def has_oids(self, default_conn=None): + """ + This function checks whether the table has oids or not. + """ + driver = get_driver(PG_DEFAULT_DRIVER) + if default_conn is None: + manager = driver.connection_manager(self.sid) + conn = manager.connection(did=self.did, conn_id=self.conn_id) + else: + conn = default_conn + + if conn.connected(): + + # Fetch the table oids status + query = render_template("/".join([self.sql_path, 'has_oids.sql']), obj_id=self.obj_id) + + status, has_oids = conn.execute_scalar(query) + if not status: + raise Exception(has_oids) + + else: + raise Exception(gettext('Not connected to server or connection with the server has been closed.')) + + return has_oids + def save(self, changed_data, columns_info, @@ -489,6 +518,7 @@ class TableCommand(GridCommand): # of not null which is set by default. column_data = {} pk_names, primary_keys = self.get_primary_keys() + has_oids = 'oid' in column_type for each_row in changed_data[of_type]: data = changed_data[of_type][each_row]['data'] @@ -507,8 +537,10 @@ class TableCommand(GridCommand): object_name=self.object_name, nsp_name=self.nsp_name, data_type=column_type, - pk_names=pk_names) - list_of_sql[of_type].append({'sql': sql, 'data': data}) + pk_names=pk_names, + has_oids=has_oids) + list_of_sql[of_type].append({'sql': sql, 'data': data, + 'has_oids': has_oids, 'client_row': each_row}) # Reset column data column_data = {} @@ -568,13 +600,16 @@ class TableCommand(GridCommand): for opr, sqls in list_of_sql.items(): for item in sqls: if item['sql']: - status, res = conn.execute_void( - item['sql'], item['data']) - rows_affected = conn.rows_affected() + oids = None + # Send oids if added row has oid + if 'has_oids' in item and item['has_oids']: + status, res = conn.execute_scalar( + item['sql'], item['data']) + oids = {item['client_row']: res} - # store the result of each query in dictionary - query_res[count] = {'status': status, 'result': res, - 'sql': sql, 'rows_affected': rows_affected} + else: + status, res = conn.execute_void( + item['sql'], item['data']) if not status: conn.execute_void('ROLLBACK;') @@ -591,6 +626,14 @@ class TableCommand(GridCommand): _rowid = 0 return status, res, query_res, _rowid + + rows_affected = conn.rows_affected() + + # store the result of each query in dictionary + query_res[count] = {'status': status, 'result': None if oids else res, + 'sql': sql, 'rows_affected': rows_affected, + 'oids': oids} + count += 1 # Commit the transaction if there is no error found diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js index ba0ce67..0e696ae 100644 --- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js +++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js @@ -596,7 +596,10 @@ define('tools.querytool', [ options['width'] = column_size[table_name][c.name]; } // If grid is editable then add editor else make it readonly - if (c.cell == 'Json') { + if (c.cell == 'oid') { + options['editor'] = null; + } + else if (c.cell == 'Json') { options['editor'] = is_editable ? Slick.Editors.JsonText : Slick.Editors.ReadOnlyJsonText; options['formatter'] = c.is_array ? Slick.Formatters.JsonStringArray : Slick.Formatters.JsonString; @@ -685,13 +688,14 @@ define('tools.querytool', [ grid.registerPlugin(gridSelector); var editor_data = { - keys: self.handler.primary_keys, + keys: (_.isEmpty(self.handler.primary_keys) && self.handler.has_oids) ? self.handler.oids : self.handler.primary_keys, vals: collection, columns: columns, grid: grid, selection: grid.getSelectionModel(), editor: self, - client_primary_key: self.client_primary_key + client_primary_key: self.client_primary_key, + has_oids: self.handler.has_oids }; self.handler.slickgrid = grid; @@ -817,7 +821,8 @@ define('tools.querytool', [ // self.handler.data_store.updated will holds all the updated data var changed_column = args.grid.getColumns()[args.cell].field, updated_data = args.item[changed_column], // New value for current field - _pk = args.item[self.client_primary_key] || null, // Unique key to identify row + _pk = args.item[self.client_primary_key] || null, // Unique key to identify row + has_oids = self.handler.has_oids || null, // Unique key to identify row column_data = {}, _type; @@ -849,6 +854,7 @@ define('tools.querytool', [ column_data[changed_column] = updated_data; + if (_pk) { // Check if it is in newly added row by user? if (_pk in self.handler.data_store.added) { @@ -1868,18 +1874,20 @@ define('tools.querytool', [ _render: function (data) { var self = this; self.colinfo = data.col_info; - self.primary_keys = data.primary_keys; + self.primary_keys = (_.isEmpty(data.primary_keys) && data.has_oids)? data.oids : data.primary_keys; self.client_primary_key = data.client_primary_key; self.cell_selected = false; self.selected_model = null; self.changedModels = []; + self.has_oids = data.has_oids; + self.oids = data.oids; $('.sql-editor-explain').empty(); /* If object don't have primary keys then set the * can_edit flag to false. */ - if (self.primary_keys === null || self.primary_keys === undefined - || _.size(self.primary_keys) === 0) + if ((self.primary_keys === null || self.primary_keys === undefined + || _.size(self.primary_keys) === 0) && self.has_oids == false) self.can_edit = false; else self.can_edit = true; @@ -2040,6 +2048,9 @@ define('tools.querytool', [ } // Identify cell type of column. switch (type) { + case "oid": + col_cell = 'oid'; + break; case "json": case "json[]": case "jsonb": @@ -2096,7 +2107,7 @@ define('tools.querytool', [ 'pos': c.pos, 'label': column_label, 'cell': col_cell, - 'can_edit': self.can_edit, + 'can_edit': (c.name == 'oid') ? false : self.can_edit, 'type': type, 'not_null': c.not_null, 'has_default_val': c.has_default_val, @@ -2348,6 +2359,18 @@ define('tools.querytool', [ data_length = dataView.getLength(), data = []; if (res.data.status) { + if(self.has_oids && is_added) { + // Update the oids in a grid after addition + dataView.beginUpdate(); + _.each(res.data.query_result, function (r) { + if(!_.isNull(r.oids)) { + var row_id = Object.keys(r.oids)[0]; + var item = grid.getDataItem(row_id); + item['oid'] = r.oids[row_id]; + } + }); + dataView.endUpdate(); + } // Remove flag is_row_copied from copied rows _.each(data, function (row, idx) { if (row.is_row_copied) { diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/has_oids.sql b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/has_oids.sql new file mode 100644 index 0000000..edeeb83 --- /dev/null +++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/has_oids.sql @@ -0,0 +1,6 @@ +{# ============= Check object has OIDs or not ============= #} +{% if obj_id %} +SELECT rel.relhasoids AS has_oids +FROM pg_class rel +WHERE rel.oid = {{ obj_id }}::oid +{% endif %} diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/insert.sql b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/insert.sql index 23ffcb4..e4bef13 100644 --- a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/insert.sql +++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/insert.sql @@ -5,4 +5,5 @@ INSERT INTO {{ conn|qtIdent(nsp_name, object_name) }} ( ) VALUES ( {% for col in data_to_be_saved %} {% if not loop.first %}, {% endif %}%({{ col }})s::{{ data_type[col] }}{% endfor %} -); +) +{% if has_oids %} returning oid{% endif %}; diff --git a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/objectquery.sql b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/objectquery.sql index 2c6ba58..1cb60d9 100644 --- a/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/objectquery.sql +++ b/web/pgadmin/tools/sqleditor/templates/sqleditor/sql/default/objectquery.sql @@ -1,5 +1,5 @@ {# SQL query for objects #} -SELECT * FROM {{ conn|qtIdent(nsp_name, object_name) }} +SELECT {% if has_oids %}oid, {% endif %}* FROM {{ conn|qtIdent(nsp_name, object_name) }} {% if sql_filter %} WHERE {{ sql_filter }} {% endif %}