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 %}

Reply via email to