<commit_before><?php
/**
 * @copyright	Copyright 2010-2013, The Titon Project
 * @license		http://opensource.org/licenses/bsd-license.php
 * @link		http://titon.io
 */

namespace Titon\Model\Driver;

use Titon\Model\Driver\AbstractDriver;
use Titon\Model\Driver\Type\AbstractType;
use Titon\Model\Exception\InvalidQueryException;
use Titon\Model\Exception\UnsupportedQueryStatementException;
use Titon\Model\Query;
use Titon\Model\Query\Expr;
use Titon\Model\Query\Predicate;
use Titon\Model\Query\Result\PdoResult;
use Titon\Model\Query\SubQuery;
use Titon\Utility\String;
use \PDO;

/**
 * Implements PDO based driver functionality.
 *
 * @link http://php.net/manual/en/pdo.drivers.php
 *
 * @package Titon\Model\Driver
 * @method \PDO getConnection()
 */
abstract class AbstractPdoDriver extends AbstractDriver {

	/**
	 * Configuration.
	 *
	 * @type array {
	 * 		@type string $dsn		Custom DSN that would take precedence
	 * }
	 */
	protected $_config = [
		'dsn' => '',
	];

	/**
	 * Current nested transaction depth.
	 *
	 * @type int
	 */
	protected $_transactions = 0;

	/**
	 * {@inheritdoc}
	 *
	 * @throws \Titon\Model\Exception\UnsupportedQueryStatementException
	 */
	public function buildStatement(Query $query) {
		$type = $query->getType();
		$method = 'build' . ucfirst($type);
		$dialect = $this->getDialect();

		if (!method_exists($dialect, $method)) {
			throw new UnsupportedQueryStatementException(sprintf('Query statement %s does not exist or has not been implemented', $type));
		}

		return $this->getConnection()->prepare(call_user_func([$dialect, $method], $query));
	}

	/**
	 * Connect to the database using PDO.
	 *
	 * @return bool
	 */
	public function connect() {
		if ($this->isConnected()) {
			return true;
		}

		$this->_connection = new PDO($this->getDsn(), $this->getUser(), $this->getPassword(), $this->config->flags + [
			PDO::ATTR_PERSISTENT => $this->isPersistent(),
			PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
		]);

		$this->_connected = true;

		return true;
	}

	/**
	 * {@inheritdoc}
	 */
	public function commitTransaction() {
		if ($this->_transactions === 1) {
			$status = $this->getConnection()->commit();
		} else {
			$status = true;
		}

		$this->_transactions--;

		return $status;
	}

	/**
	 * {@inheritdoc}
	 */
	public function describeDatabase($database = null) {
		$tables = $this->query('SELECT * FROM information_schema.tables WHERE table_schema = ?;', [$database ?: $this->getDatabase()])->fetchAll(false);
		$schema = [];

		if (!$tables) {
			return $schema;
		}

		foreach ($tables as $table) {
			$name = $table['TABLE_NAME'];

			$schema[$name] = [
				'table' => $name,
				'engine' => $table['ENGINE'],
				'format' => $table['ROW_FORMAT'],
				'rows' => $table['TABLE_ROWS'],
				'autoIncrement' => $table['AUTO_INCREMENT'],
				'collate' => $table['TABLE_COLLATION'],
				'comment' => $table['TABLE_COMMENT'],
				'avgRowLength' => $table['AVG_ROW_LENGTH'],
				'dataLength' => $table['DATA_LENGTH'],
				'dataFree' => $table['DATA_FREE'],
				'maxDataLength' => $table['MAX_DATA_LENGTH'],
				'indexLength' => $table['INDEX_LENGTH'],
				'created' => $table['CREATE_TIME'],
				'updated' => $table['UPDATE_TIME']
			];
		}

		return $schema;
	}

	/**
	 * {@inheritdoc}
	 *
	 * @uses Titon\Model\Type\AbstractType
	 */
	public function describeTable($table) {
		$columns = $this->query('SELECT * FROM information_schema.columns WHERE table_schema = ? AND table_name = ?;', [$this->getDatabase(), $table])->fetchAll(false);
		$schema = [];

		if (!$columns) {
			return $schema;
		}

		foreach ($columns as $column) {
			$field = $column['COLUMN_NAME'];
			$type = strtolower($column['COLUMN_TYPE']);
			$length = '';

			// Determine type and length
			if (preg_match('/([a-z]+)(?:\(([0-9,]+)\))?/is', $type, $matches)) {
				$type = $matches[1];

				if (isset($matches[2])) {
					$length = $matches[2];
				}
			}

			// Inherit type defaults
			$data = AbstractType::factory($type, $this)->getDefaultOptions();

			// Overwrite with custom
			$data = [
				'field' => $field,
				'type' => $type,
				'length' => $length,
				'null' => ($column['IS_NULLABLE'] === 'YES'),
			] + $data;

			foreach ([
				'default' => 'COLUMN_DEFAULT',
				'charset' => 'CHARACTER_SET_NAME',
				'collate' => 'COLLATION_NAME',
				'comment' => 'COLUMN_COMMENT'
			] as $key => $search) {
				if (!empty($column[$search])) {
					$data[$key] = $column[$search];
				}
			}

			switch (strtoupper($column['COLUMN_KEY'])) {
				case 'PRI': $data['primary'] = true; break;
				case 'UNI': $data['unique'] = true; break;
				case 'MUL': $data['index'] = true; break;
			}

			if ($column['EXTRA'] === 'auto_increment') {
				$data['ai'] = true;
			}

			$schema[$field] = $data;
		}

		return $schema;
	}

	/**
	 * {@inheritdoc}
	 */
	public function escape($value) {
		if ($value === null) {
			return 'NULL';
		}

		return $this->getConnection()->quote($value, $this->resolveType($value));
	}

	/**
	 * Return the PDO driver name.
	 *
	 * @return string
	 */
	abstract public function getDriver();

	/**
	 * Format and build the DSN based on the current configuration.
	 *
	 * @return string
	 */
	public function getDsn() {
		if ($dsn = $this->config->dsn) {
			return $dsn;
		}

		$params = ['dbname=' . $this->getDatabase()];<commit_msg>Cache the describe results<commit_after><?php
/**
 * @copyright	Copyright 2010-2013, The Titon Project
 * @license		http://opensource.org/licenses/bsd-license.php
 * @link		http://titon.io
 */

namespace Titon\Model\Driver;

use Titon\Model\Driver\AbstractDriver;
use Titon\Model\Driver\Type\AbstractType;
use Titon\Model\Exception\InvalidQueryException;
use Titon\Model\Exception\UnsupportedQueryStatementException;
use Titon\Model\Query;
use Titon\Model\Query\Expr;
use Titon\Model\Query\Predicate;
use Titon\Model\Query\Result\PdoResult;
use Titon\Model\Query\SubQuery;
use Titon\Utility\String;
use \PDO;

/**
 * Implements PDO based driver functionality.
 *
 * @link http://php.net/manual/en/pdo.drivers.php
 *
 * @package Titon\Model\Driver
 * @method \PDO getConnection()
 */
abstract class AbstractPdoDriver extends AbstractDriver {

	/**
	 * Configuration.
	 *
	 * @type array {
	 * 		@type string $dsn		Custom DSN that would take precedence
	 * }
	 */
	protected $_config = [
		'dsn' => '',
	];

	/**
	 * Current nested transaction depth.
	 *
	 * @type int
	 */
	protected $_transactions = 0;

	/**
	 * {@inheritdoc}
	 *
	 * @throws \Titon\Model\Exception\UnsupportedQueryStatementException
	 */
	public function buildStatement(Query $query) {
		$type = $query->getType();
		$method = 'build' . ucfirst($type);
		$dialect = $this->getDialect();

		if (!method_exists($dialect, $method)) {
			throw new UnsupportedQueryStatementException(sprintf('Query statement %s does not exist or has not been implemented', $type));
		}

		return $this->getConnection()->prepare(call_user_func([$dialect, $method], $query));
	}

	/**
	 * Connect to the database using PDO.
	 *
	 * @return bool
	 */
	public function connect() {
		if ($this->isConnected()) {
			return true;
		}

		$this->_connection = new PDO($this->getDsn(), $this->getUser(), $this->getPassword(), $this->config->flags + [
			PDO::ATTR_PERSISTENT => $this->isPersistent(),
			PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
		]);

		$this->_connected = true;

		return true;
	}

	/**
	 * {@inheritdoc}
	 */
	public function commitTransaction() {
		if ($this->_transactions === 1) {
			$status = $this->getConnection()->commit();
		} else {
			$status = true;
		}

		$this->_transactions--;

		return $status;
	}

	/**
	 * {@inheritdoc}
	 */
	public function describeDatabase($database = null) {
		$database = $database ?: $this->getDatabase();

		return $this->cache([__METHOD__, $database], function() use ($database) {
			$tables = $this->query('SELECT * FROM information_schema.tables WHERE table_schema = ?;', [$database])->fetchAll(false);
			$schema = [];

			if (!$tables) {
				return $schema;
			}

			foreach ($tables as $table) {
				$name = $table['TABLE_NAME'];

				$schema[$name] = [
					'table' => $name,
					'engine' => $table['ENGINE'],
					'format' => $table['ROW_FORMAT'],
					'rows' => $table['TABLE_ROWS'],
					'autoIncrement' => $table['AUTO_INCREMENT'],
					'collate' => $table['TABLE_COLLATION'],
					'comment' => $table['TABLE_COMMENT'],
					'avgRowLength' => $table['AVG_ROW_LENGTH'],
					'dataLength' => $table['DATA_LENGTH'],
					'dataFree' => $table['DATA_FREE'],
					'maxDataLength' => $table['MAX_DATA_LENGTH'],
					'indexLength' => $table['INDEX_LENGTH'],
					'created' => $table['CREATE_TIME'],
					'updated' => $table['UPDATE_TIME']
				];
			}

			return $schema;
		});
	}

	/**
	 * {@inheritdoc}
	 *
	 * @uses Titon\Model\Type\AbstractType
	 */
	public function describeTable($table) {
		return $this->cache([__METHOD__, $table], function() use ($table) {
			$columns = $this->query('SELECT * FROM information_schema.columns WHERE table_schema = ? AND table_name = ?;', [$this->getDatabase(), $table])->fetchAll(false);
			$schema = [];

			if (!$columns) {
				return $schema;
			}

			foreach ($columns as $column) {
				$field = $column['COLUMN_NAME'];
				$type = strtolower($column['COLUMN_TYPE']);
				$length = '';

				// Determine type and length
				if (preg_match('/([a-z]+)(?:\(([0-9,]+)\))?/is', $type, $matches)) {
					$type = $matches[1];

					if (isset($matches[2])) {
						$length = $matches[2];
					}
				}

				// Inherit type defaults
				$data = AbstractType::factory($type, $this)->getDefaultOptions();

				// Overwrite with custom
				$data = [
					'field' => $field,
					'type' => $type,
					'length' => $length,
					'null' => ($column['IS_NULLABLE'] === 'YES'),
				] + $data;

				foreach ([
					'default' => 'COLUMN_DEFAULT',
					'charset' => 'CHARACTER_SET_NAME',
					'collate' => 'COLLATION_NAME',
					'comment' => 'COLUMN_COMMENT'
				] as $key => $search) {
					if (!empty($column[$search])) {
						$data[$key] = $column[$search];
					}
				}

				switch (strtoupper($column['COLUMN_KEY'])) {
					case 'PRI': $data['primary'] = true; break;
					case 'UNI': $data['unique'] = true; break;
					case 'MUL': $data['index'] = true; break;
				}

				if ($column['EXTRA'] === 'auto_increment') {
					$data['ai'] = true;
				}

				$schema[$field] = $data;
			}

			return $schema;
		});
	}

	/**
	 * {@inheritdoc}
	 */
	public function escape($value) {
		if ($value === null) {
			return 'NULL';
		}

		return $this->getConnection()->quote($value, $this->resolveType($value));
	}

	/**
	 * Return the PDO driver name.
	 *
	 * @return string
	 */
	abstract public function getDriver();

	/**
	 * Format and build the DSN based on the current configuration.
	 *
	 * @return string
	 */
	public function getDsn() {
		if ($dsn = $this->config->dsn) {
			return $dsn;
		}

		$params = ['dbname=' . $this->getDatabase()];
{'fraction_non_alphanumeric': 0.19032732622287607, 'fraction_numerical': 0.002758367046708349, 'mean_word_length': 3.4927715819909126, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 117, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2357609', 'n_tokens_mistral': 4280, 'n_tokens_neox': 3636, 'n_words': 740}