arrayanalyze.php

application/x-httpd-php

Filename: arrayanalyze.php
Type: application/x-httpd-php
Part: 2
Message: Collect frequency statistics for arrays
<?php
/*
 * CREATE TABLE test_result (
 *   tablename      text,
 *   value          int[],
 *   operator       text,
 *   estimated_count int,
 *   actual_count   int
 * );
 */

/*
 * Description of testing tables.
 * table_name => table_desription
 *   count      - count of rows in table
 *   maxElement - parameter of distribution
 *   pow        - parameter of distribution
 * Numbers are assumed to be generated as random()^pow * maxElement
 */
$testTables = array(
  'array_test1' => array(
    'count'      => 10000,
    'maxElement' => 100,
    'pow'        => 1
  ),
  'array_test2' => array(
    'count'      => 10000,
    'maxElement' => 1500,
    'pow'        => 2
  ),
  'array_test3' => array(
    'count'      => 10000,
    'maxElement' => 5000,
    'pow'        => 3
  )
);

$testsCount = 100;
$operators = array('<@', '@>', '&&');

/*
 * Database connection options.
 */
$dbConnString = 'pgsql:host=localhost;dbname=test;port=5431';
$dbUsername = 'smagen';
$dbPassword = '';

/*
 * Generate test array.
 */
function genArray($maxN, $count, $pow)
{
  $range = 1.0;
  $result = array();
  for ($i = 0; $i < $count; $i++)
  {
    $value = rand() / getrandmax() * $range;
    $value = intval(pow($value, $pow) * $maxN);
    for ($j = 0; $j < $i; $j++)
    {
      if ($result[$j] <= $value)
        $value++;
    }
    $result []= $value;
    $range -= pow(($value + 1) / $maxN, 1 / $pow) - pow($value / $maxN, 1 / $pow);
    sort($result);
  }
  return $result;
}

/**
 * Returns number of extimated and actual numbers of rows.
 * 
 * @global PDO $con Соединение с БД
 * @param string $query Запрос
 * @return integer
 */
function getRowsCount($query)
{
  global $con;
  /* Get plan of query execution in JSON format */
  $result = $con->query('EXPLAIN (ANALYZE, FORMAT JSON) ' . $query);
  if (!$result)
  {
    echo $query;
    exit(1);
  }
  $row = $result->fetch(PDO::FETCH_NUM);
  
  /* Decoding JSON */
  $explain = json_decode($row[0], true);
  $plan = $explain[0]['Plan'];
  
  return array(
    'estimate' => $plan['Plan Rows'],
    'actual'   => $plan['Actual Rows']
  );
}

/*
 * Connect to the test database.
 */
$con = new PDO($dbConnString, $dbUsername);

/*
 * Prepared statement for new test information insertion.
 */
$testInsert = $con->prepare('
  INSERT INTO test_result
  (
    tablename,
    value,
    operator,
    estimated_count,
    actual_count
  )
  VALUES
  (
    :tablename,
    :value,
    :operator,
    :estimated_count,
    :actual_count
  );
');

/*
 * Interate over all test tables.
 */
foreach ($testTables as $tableName => $tableInfo)
{
  $maxElement = $tableInfo['maxElement'];
  $pow = $tableInfo['pow'];
  /*
   * Generate $testCount tests for each table.
   */
  for ($i = 1; $i <= $testsCount; $i++)
  {
    /*
     * Interate over supported operators.
     */
    foreach ($operators as $operator)
    {
      $testValue = genArray($maxElement, $i, $pow);

      /*
       * Measure estimated and actual count.
       */
      $query = '
        SELECT
          *
        FROM 
          ' . $tableName . '
        WHERE
          value ' . $operator . ' ARRAY[' . implode(',', $testValue) . ']';
      $count = getRowsCount($query);

      /*
       * Insert test result.
       */
      $testInsert->execute(array(
        'tablename'      => $tableName,
        'value'          => '{' . implode(',', $testValue) . '}',
        'operator'       => $operator,
        'estimated_count' => $count['estimate'],
        'actual_count'   => $count['actual']
      ));
    }
  }
}