I have developed a new module, inspired by the devel module but with some convenient features for logging all "action" queries -- that is, all insert/update/delete queries.

This becomes very useful when you are performing a configuration task on your "staging" or "development" site and then want to propagate those changes to the live site...

Please give it a try. I am currently trying to get a cvs account so I can upload this to drupal.org properly.

Code follows...

qrecorder.module

// $Id: qrecorder.module,v 0.1 2006/11/25 00:00:00 chud Exp $

/**
 * Implementation of hook_help().
 */
function qrecorder_help($section) {
  switch ($section) {
    case 'admin/modules#description':
      return t('Records all insert/update/delete queries... makes it easy to tranfer changes from one version of a site to another.');
    case 'admin/settings/qrecorder':
      return '<p>'. t('Settings for the Query Recorder.'). '</p>';
  }
}

/**
 * Implementation of hook_menu().
 */
function qrecorder_menu($may_cache) {
  $items = array();

  if ($may_cache) {
    $items[] = array('path' => 'qrecorder/start',
      'title' => t('start recording'),
      'callback' => 'qrecorder_start',
      'access' => user_access('use query recorder'),
      'type' => MENU_CALLBACK,
    );
    $items[] = array('path' => 'qrecorder/stop',
      'title' => t('stop recording'),
      'callback' => 'qrecorder_stop',
      'access' => user_access('use query recorder'),
      'type' => MENU_CALLBACK,
    );
    $items[] = array('path' => 'qrecorder/empty',
      'title' => t('empty query log'),
      'callback' => 'qrecorder_empty',
      'access' => user_access('use query recorder'),
      'type' => MENU_CALLBACK,
    );
    $items[] = array('path' => 'qrecorder/qview',
      'title' => t('view query log'),
      'callback' => 'qrecorder_qview',
      'access' => user_access('use query recorder'),
      'type' => MENU_CALLBACK,
    );
  }

  return $items;
}

function qrecorder_start() {
  global $base_url;
  variable_set("dev_query", 1);
  variable_set("qrecorder_state", 1);
  drupal_set_message('query recorder started').
  $referer = referer_uri();
  header('Location: '. ($referer ? $referer : $base_url));
  exit();
}

function qrecorder_stop() {
  global $base_url;
  variable_set("qrecorder_state", 0);
  drupal_set_message('query recorder stopped').
  $referer = referer_uri();
  header('Location: '. ($referer ? $referer : $base_url));
  exit();
}

function qrecorder_empty() {
  global $base_url;
  db_query('DELETE FROM {qrecorder_queries}');
  drupal_set_message('query log emptied').
  $referer = referer_uri();
  header('Location: '. ($referer ? $referer : $base_url));
  exit();
}

function qrecorder_qview() {
  $result = db_query("SELECT query FROM {qrecorder_queries} ORDER BY qid;");
  while ($row = db_fetch_object($result)) {
  	$output .= $row->query . "\n";
  }
  $form['qlog'] = array(
		'#type' => 'textarea',
    '#title' => t('queries'),
    '#attributes' => array('wrap' => 'off'),
    '#default_value' => $output,
    '#rows' => 15,
  );
  $output = drupal_get_form('qlog', $form);
  return $output;
}

/**
 * Implementation of hook_perm().
 */
function qrecorder_perm() {
  return array('use query recorder');
}

/**
 * Implementation of hook_block().
 */
function qrecorder_block($op = 'list', $delta = 0) {
  if ($op == 'list') {
    $blocks[0]['info'] = t('Query Recorder');
    
    // Auto-enable the qrecorder blocks for fresh installations.
    $blocks[0]['status'] = 1;

    return $blocks;
  }
  else if ($op == 'view') {
    switch ($delta) {
      case 0:
      	if (user_access('use query recorder')) {
      		$block['subject'] = t('query recorder');
      		$block['content'] = theme_qrecorder_block();
      	}
    }

    return $block;
  }
}


function theme_qrecorder_block() {
	$output = '<p>';
	$output .= l('start recording', 'qrecorder/start');
	$output .= " | ";
	$output .= l('stop recording', 'qrecorder/stop');
	$output .= '</p>';
	$output .= '<p>';
	$output .= l('empty query log', 'qrecorder/empty');
	$output .= " | ";
	$output .= l('view query log', 'qrecorder/qview');
	$output .= '</p>';
	$output .= '<p>';
	$output .= '<b>' . t('current status: ') . '</b>';
	$output .= variable_get('qrecorder_state',0) ? t('active') : t('inactive');
	$output .= '</p>';
	return $output;
}


function qrecorder_log_queries($queries) {
	$pos = array();
//	print_a($queries, "new");
//	die();
	foreach ($queries as $query) {
    $ar = explode("\n", $query[0]);
    $function=array_shift($ar);
    $pos[0] = strpos(strtolower($ar[0]), "insert into");
		$pos[1] = strpos(strtolower($ar[0]), "update");
		$pos[2] = strpos(strtolower($ar[0]), "delete");
		if (in_array(0, $pos, TRUE)) { // we found an action query
			db_query("INSERT INTO {qrecorder_queries} (query, function, created) VALUES ('%s', '%s', %d)", $ar[0], $function, time());
		}
  }

}

function qrecorder_exit($destination = NULL) {
  global $queries;	
	if ($queries) {
		// print_a($queries,"new2");
	  if (variable_get('qrecorder_state', 0)) {
			qrecorder_log_queries($queries);
	  }
	}
}

qrecorder.install

<?php
// $Id: qrecorder.install,v 0.1 2006/11/25 00:00:00 chud Exp $
/**
 * Implementation of hook_install()
 */
function qrecorder_install() {
  // New module weights in core: put qrecorder as the very last in the chain.
  db_query("UPDATE {system} SET weight = 10 WHERE name = 'qrecorder'");

  switch ($GLOBALS['db_type']) {
    case 'mysqli':
    case 'mysql':
      $sql = "CREATE TABLE {qrecorder_queries} (
        qid int(10) NOT NULL auto_increment,
        function varchar(255) NOT NULL default '',
        query text NOT NULL,
        created int(11) NOT NULL,
        PRIMARY KEY (`qid`)
        ) /*!40100 DEFAULT CHARACTER SET utf8 */;";
      db_query($sql);
      break;
  }

  // Enable the blocks automatically at install.
  _block_rehash();
}

Comments

lefnire’s picture

ooooo, you should update this for 5.x (or maybe 6.0 instead). this sounds quite useful, but doesn't work... and i can't find anything else like this elsewhere