I've encountered the following error when trying to force save a node using the rules module, so that I can then perform some access checks on a newly created node.

PDOException: SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '49-4-taxonomy_access_role' for key 'PRIMARY': INSERT INTO {node_access} (nid, realm, gid, grant_view, grant_update, grant_delete) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5); Array ( [:db_insert_placeholder_0] => 49 [:db_insert_placeholder_1] => taxonomy_access_role [:db_insert_placeholder_2] => 4 [:db_insert_placeholder_3] => 1 [:db_insert_placeholder_4] => 0 [:db_insert_placeholder_5] => 0 ) in node_access_write_grants() (line 3417 of /var/www/redacted/modules/node/node.module).

After tracking down a similar issue with the tac_lite module here http://drupal.org/node/1307114 I've started my investigation but am still having some trouble finding the fix.

Does anyone have any suggestions? It feels like somewhere in the node save process the grants are being calculated twice?

Comments

beauz’s picture

After much headache I think I've fixed the problem.

I've changed taxonomy_access.module arround line 262 from:

function taxonomy_access_node_access_records($node) {
  // Only write grants for published nodes.
  if ($node->status) {
    // Make sure to reset caches for accurate grant values.
    return _taxonomy_access_node_access_records($node->nid, TRUE);
  }
}

to

function taxonomy_access_node_access_records($node) {
  // Only write grants for published nodes.
  if ($node->status && isset($node->original)) {
    // Make sure to reset caches for accurate grant values.
    return _taxonomy_access_node_access_records($node->nid, TRUE);
  }
}

This seems to have fixed the problem.

beauz’s picture

I've now realised this is not the correct solution as the node_access table doesn't get written properly for instance when rebuild node access permissions is run.

I've come up with this rather hacky solution that checks the database for an existing row first and deletes it if there is a match, but there is probably a much better way to do this..

function _taxonomy_access_node_access_records($node_nid, $reset = FALSE) {

  // Build the base node grant query.
  $query = _taxonomy_access_grant_query(array('view', 'update', 'delete'));
  
  // Select grants for this node only and group by role.
  $query->join(
    'taxonomy_index', 'ti',
    'td.tid = ti.tid'
  );
  $query
    ->fields('tadg', array('rid'))
    ->condition('ti.nid', $node_nid)
    ->groupBy('tadg.rid')
    ->addTag('taxonomy_access_node_access')
    ->addTag('taxonomy_access_node')
    ;

  // Fetch and format all grant records for the node.
  $grants = array();
  $records = $query->execute()->fetchAll();
  // The node grant query returns no rows if the node has no tags.
  // In that scenario, use the global default.
  if (sizeof($records) == 0) {
    $records = taxonomy_access_global_defaults($reset);
  }
  foreach ($records as $record) {
    // check db for existing nid and gid ($record->rid)
    $result = db_query("SELECT n.nid, n.gid, n.realm FROM {node_access} n WHERE n.nid = :nid AND n.gid = :gid AND n.realm = 'taxonomy_access_role'", array(':nid' => $node_nid, ':gid' => $record->rid));
    
    if ($result->rowCount() > 0) {
      $delete = db_query("DELETE FROM node_access WHERE nid = :nid AND gid = :gid AND realm = 'taxonomy_access_role'", array(':nid' => $node_nid, ':gid' => $record->rid));
      $grants[] = _taxonomy_access_format_node_access_record($record);
    } else {
      $grants[] = _taxonomy_access_format_node_access_record($record);
    }
  }
  
  return $grants;
}
kenorb’s picture

Priority: Normal » Minor
Issue summary: View changes