<?php

use Vn\Rest;
use Vn\Db;

class RestMod extends Rest\Module
{
	function run ()
	{
		if (!isset ($_REQUEST['sql']) || $_REQUEST['sql'] == '')
			throw new Rest\Exception  ('Db', 'emptyQuery', s('EmptyQuery'));

		$results = [];
		$conn = $this->conn;

		try {
			$conn->multiQuery ($_REQUEST['sql']);

			do {
				$result = $conn->storeResult ();

				if ($result !== FALSE)
				{
					$tableMap = [];
					$columns = $result->fetch_fields ();

					$resultMap =
					[
						'data'    => [],
						'columns' => [],
						'tables'  => []
					];
					
					for ($i = 0; $i < $result->field_count; $i++)
					{
						$column = $columns[$i];
						
						switch ($column->type)
						{
							case MYSQLI_TYPE_BIT:
								$type = TYPE_BOOLEAN;
								break;
							case MYSQLI_TYPE_TINY:
							case MYSQLI_TYPE_SHORT:
							case MYSQLI_TYPE_LONG:
							case MYSQLI_TYPE_LONGLONG:
							case MYSQLI_TYPE_INT24:
							case MYSQLI_TYPE_YEAR:
								$type = TYPE_INTEGER;
								break;
							case MYSQLI_TYPE_FLOAT:
							case MYSQLI_TYPE_DOUBLE:
							case MYSQLI_TYPE_DECIMAL:	
							case MYSQLI_TYPE_NEWDECIMAL:
								$type = TYPE_DOUBLE;
								break;
							case MYSQLI_TYPE_DATE:	
								$type = TYPE_DATE;
								break;
							case MYSQLI_TYPE_DATETIME:
							case MYSQLI_TYPE_TIMESTAMP:
								$type = TYPE_DATE_TIME;
								break;
							default;
								$type = TYPE_STRING;
						}

						if (!isset ($tableMap[$column->table]))
						{
							$resultMap['tables'][] =
							[
								'name'     => $column->table,
								'orgname'  => $column->orgtable,
								'schema'   => $column->db,
								'pks'      => []
							];
							$tableIndex = count ($resultMap['tables']) - 1;
							$tableMap[$column->table] = $tableIndex;	 
						}
						else
							$tableIndex = $tableMap[$column->table];
						
						if ($column->flags & MYSQLI_PRI_KEY_FLAG)
							$resultMap['tables'][$tableIndex]['pks'][] = $i;
						
						$default = $this->castValue ($column->def, $type);

						$resultMap['columns'][] =
						[
							'type'     => $type,
							'flags'    => $column->flags,
							'def'      => $default,
							'name'     => $column->name,
							'orgname'  => $column->orgname,
							'table'    => $tableIndex
						];
					}
					
					$columns = $resultMap['columns'];

					while ($row = $result->fetch_row ())
					{
						for ($j = 0; $j < $result->field_count; $j++)
							$this->castValue ($row[$j], $columns[$j]['type']);

						$resultMap['data'][] = $row;
					}

					$results[] = $resultMap;
					$result->free ();
				}
				else
					$results[] = TRUE;
			}
			while ($conn->moreResults () && $conn->nextResult ());
			
			// Checks for warnings
			
			if ($conn->checkWarnings ()
			&& ($result = $conn->query ('SHOW WARNINGS')))
			{
				$sql = 'SELECT description, @warn code '.
					'FROM sql_message WHERE code = @warn';

				while ($row = $result->fetch_assoc ())
				{
					if ($row['Code'] == 1265
					&& ($warning = $conn->getRow ($sql)))
						Rest\Service::addWarning ('User', $warning['code'], $warning['description']);
					else
						trigger_error ("Db\\Conn: ${row['Code']}: ${row['Message']}", E_USER_WARNING);
				}
			}
			
			// Checks for errors

			$conn->checkError ();
		}
		catch (Db\Exception $e)
		{
			$row = NULL;
			$code = $e->getCode ();
			$message = $e->getMessage ();

			switch ($code)
			{
				case 1644: // ER_SIGNAL_EXCEPTION
				{
					$sql = 'SELECT description, #code code '.
						'FROM sql_message WHERE code = #code';
					$row = $conn->getRow ($sql, ['code' => $message]);
					break;
				}
				case 1305: // ER_SP_DOES_NOT_EXIST
				{
					if (strpos ($message, 'EXCEPTION') === FALSE)
						break;

					$sql = 'SELECT description, @err code '.
						'FROM sql_message WHERE code = @err';
					$row = $conn->getRow ($sql);
					break;
				}
			}

			if ($row)
				throw new Rest\Exception ('User', $row['code'], $row['description']);
			else
				throw $e;
		}

		return $results;
	}
	
	function castValue (&$value, $type)
	{
		if ($value !== NULL)
		switch ($type)
		{
			case TYPE_BOOLEAN:
				$value = (bool) $value;
				break;
			case TYPE_INTEGER:
				$value = (int) $value;
				break;	
			case TYPE_DOUBLE:
				$value = (float) $value;	
				break;
			case TYPE_DATE:	
			case TYPE_DATE_TIME:				
				$value = mktime
				(
					 substr ($value, 11 , 2)
					,substr ($value, 14 , 2)
					,substr ($value, 17 , 2)
					,substr ($value, 5 , 2)
					,substr ($value, 8 , 2)
					,substr ($value, 0 , 4)
				);
				break;
		}
	}
}

?>