├── .coveralls.yml ├── .gitignore ├── .hhconfig ├── .scrutinizer.yml ├── .sensiolabs.yml ├── .styleci.yml ├── .travis.yml ├── LICENSE ├── README.md ├── composer.json ├── example └── client.php ├── nitpick.json ├── phpcs.xml ├── phpmd.xml ├── phpunit.xml ├── src ├── ClientSession.php ├── Column.php ├── Exception │ ├── QueryErrorException.php │ └── RequestFailedException.php ├── FixData.php ├── LoggerClient.php ├── PrestoHeaders.php ├── QueryError.php ├── QueryResult.php ├── ResultsSession.php ├── Session │ ├── AbstractKeyValueStorage.php │ ├── PreparedStatement.php │ └── Property.php ├── StatementClient.php └── StatementStats.php └── tests ├── ClientSessionTest.php ├── LoggerClientTest.php ├── MockClientTrait.php ├── ResultsSessionTest.php ├── StatementClientTest.php ├── TestReflectionTrait.php ├── build └── .gitignore └── data ├── error.json ├── fourth_response.json ├── next_response.json ├── success.json └── third_response.json /.coveralls.yml: -------------------------------------------------------------------------------- 1 | service_name: travis-ci 2 | coverage_clover: tests/build/clover.xml 3 | json_path: tests/build/coveralls-upload.json 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | composer.lock 2 | composer.phar 3 | vendor 4 | -------------------------------------------------------------------------------- /.hhconfig: -------------------------------------------------------------------------------- 1 | assume_php=false 2 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | tools: 2 | php_code_sniffer: true 3 | php_cpd: true 4 | php_loc: true 5 | php_mess_detector: true 6 | php_pdepend: true 7 | php_analyzer: true 8 | sensiolabs_security_checker: true 9 | filter: 10 | paths: [src/*] 11 | checks: 12 | php: 13 | code_rating: true 14 | duplication: true 15 | -------------------------------------------------------------------------------- /.sensiolabs.yml: -------------------------------------------------------------------------------- 1 | global_exclude_dirs: 2 | - vendor 3 | - tests 4 | -------------------------------------------------------------------------------- /.styleci.yml: -------------------------------------------------------------------------------- 1 | preset: psr2 2 | finder: 3 | exclude: 4 | - example 5 | - tests 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | sudo: false 3 | php: 4 | - 7.0 5 | - 7.1 6 | - 7.2 7 | - 7.3 8 | - nightly 9 | before_script: 10 | - composer self-update 11 | - composer install --prefer-dist --no-interaction 12 | script: 13 | - chmod -R 777 tests/build 14 | - ./vendor/bin/phpunit --coverage-clover tests/build/clover.xml 15 | after_script: 16 | - if [[ ${TRAVIS_PHP_VERSION:0:3} == "7.1" ]]; then php vendor/bin/coveralls -v; fi 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Yuuki Takezawa 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 12 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 13 | HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 14 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 15 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 16 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ytake\PrestoClient 2 | 3 | [![Build Status](http://img.shields.io/travis/ytake/php-presto-client/master.svg?style=flat-square)](https://travis-ci.org/ytake/php-presto-client) 4 | [![Coverage Status](http://img.shields.io/coveralls/ytake/php-presto-client/master.svg?style=flat-square)](https://coveralls.io/r/ytake/php-presto-client?branch=master) 5 | [![Scrutinizer Code Quality](http://img.shields.io/scrutinizer/g/ytake/php-presto-client.svg?style=flat-square)](https://scrutinizer-ci.com/g/ytake/php-presto-client/?branch=master) 6 | 7 | [![License](http://img.shields.io/packagist/l/ytake/php-presto-client.svg?style=flat-square)](https://packagist.org/packages/ytake/php-presto-client) 8 | [![Latest Version](http://img.shields.io/packagist/v/ytake/php-presto-client.svg?style=flat-square)](https://packagist.org/packages/ytake/php-presto-client) 9 | [![Total Downloads](http://img.shields.io/packagist/dt/ytake/php-presto-client.svg?style=flat-square)](https://packagist.org/packages/ytake/php-presto-client) 10 | [![StyleCI](https://styleci.io/repos/94699825/shield?branch=master)](https://styleci.io/repos/94699825) 11 | 12 | [![SensioLabsInsight](https://insight.sensiolabs.com/projects/9a13a5c0-7588-459f-835e-d73dabd22843/mini.png)](https://insight.sensiolabs.com/projects/9a13a5c0-7588-459f-835e-d73dabd22843) 13 | 14 | prestodb http protocol client for php 15 | 16 | [prestodb](https://prestodb.io/) 17 | 18 | ## What is Presto 19 | 20 | Presto is an open source distributed SQL query engine for running interactive analytic queries against data sources of all sizes ranging from gigabytes to petabytes. 21 | 22 | ## Install 23 | 24 | *required >= PHP 7.0* 25 | 26 | ```bash 27 | $ composer require ytake/php-presto-client 28 | ``` 29 | 30 | ## Usage 31 | 32 | ### Standard 33 | 34 | ```php 35 | execute(); 43 | // next call uri 44 | $client->advance(); 45 | 46 | /** @var \Ytake\PrestoClient\QueryResult $result */ 47 | // current result 48 | $result = $client->current(); 49 | 50 | // request cancel 51 | $client->cancelLeafStage(); 52 | ``` 53 | 54 | ### bulk operations 55 | 56 | ```php 57 | execute()->yieldResults(); 66 | 67 | // array 68 | $result = $resultSession->execute()->getResults(); 69 | ``` 70 | 71 | ## Fetch Styles 72 | 73 | ### FixData Object 74 | 75 | ```php 76 | execute()->yieldResults(); 84 | /** @var \Ytake\PrestoClient\QueryResult $row */ 85 | foreach ($result as $row) { 86 | foreach ($row->yieldData() as $yieldRow) { 87 | if ($yieldRow instanceof \Ytake\PrestoClient\FixData) { 88 | var_dump($yieldRow->offsetGet('column_name'), $yieldRow['column_name']); 89 | } 90 | } 91 | } 92 | ``` 93 | 94 | ### Array Keys 95 | 96 | ```php 97 | execute()->yieldResults(); 105 | /** @var \Ytake\PrestoClient\QueryResult $row */ 106 | foreach ($result as $row) { 107 | /** @var array $item */ 108 | foreach ($row->yieldDataArray() as $item) { 109 | if (!is_null($item)) { 110 | var_dump($item); 111 | } 112 | } 113 | } 114 | ``` 115 | 116 | ### Mapping Class 117 | 118 | ```php 119 | execute()->yieldResults(); 134 | /** @var \Ytake\PrestoClient\QueryResult $row */ 135 | foreach ($result as $row) { 136 | foreach($row->yieldObject(Testing::class) as $object) { 137 | if ($object instanceof Testing) { 138 | var_dump($object); 139 | } 140 | } 141 | } 142 | ``` 143 | 144 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ytake/php-presto-client", 3 | "description": "php prestodb client", 4 | "minimum-stability": "stable", 5 | "keywords": ["prestodb"], 6 | "license": "BSD-3-Clause", 7 | "authors": [ 8 | { 9 | "name": "yuuki takezawa", 10 | "email": "yuuki.takezawa@comnect.jp.net" 11 | } 12 | ], 13 | "require": { 14 | "php": ">=7.0.0", 15 | "ext-curl": "*", 16 | "psr/log": "~1.0", 17 | "ramsey/uuid": "^3.0", 18 | "guzzlehttp/guzzle": "^6.2", 19 | "fig/http-message-util": "^1.1" 20 | }, 21 | "require-dev": { 22 | "phpunit/phpunit": "~6.0", 23 | "satooshi/php-coveralls": "*", 24 | "phpmd/phpmd": "@stable", 25 | "squizlabs/php_codesniffer": "~2.7", 26 | "sebastian/phpcpd": "*", 27 | "phploc/phploc": "*", 28 | "pdepend/pdepend" : "^2.2.4", 29 | "sensiolabs/security-checker": "^4.0.0", 30 | "monolog/monolog": "^1.22" 31 | }, 32 | "autoload": { 33 | "psr-4": { 34 | "Ytake\\PrestoClient\\": "src/" 35 | } 36 | }, 37 | "autoload-dev": { 38 | "files": [ 39 | "tests/MockClientTrait.php", 40 | "tests/TestReflectionTrait.php" 41 | ] 42 | }, 43 | "scripts": { 44 | "ci": [ 45 | "./vendor/bin/phpunit", 46 | "./vendor/bin/phpcpd src/", 47 | "./vendor/bin/phploc src/ --log-xml=tests/build/phploc.xml" 48 | ], 49 | "phpcs": "./vendor/bin/phpcs src/ --report-full --report-source --standard=PSR2 --colors", 50 | "security-checker": "./vendor/bin/security-checker security:check composer.lock" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /example/client.php: -------------------------------------------------------------------------------- 1 | execute(); 9 | // next call uri 10 | $client->advance(); 11 | // request cancel 12 | $client->cancelLeafStage(); 13 | $client->advance(); 14 | $client->close(); 15 | -------------------------------------------------------------------------------- /nitpick.json: -------------------------------------------------------------------------------- 1 | { 2 | "ignore": [ 3 | "tests/*", 4 | "example/*" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /phpmd.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | ./tests/ 16 | 17 | 18 | 19 | 20 | ./src/ 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/ClientSession.php: -------------------------------------------------------------------------------- 1 | 28 | */ 29 | class ClientSession 30 | { 31 | /** @var string */ 32 | protected $host; 33 | 34 | /** @var string */ 35 | protected $catalog; 36 | 37 | /** @var UuidInterface */ 38 | protected $transactionId; 39 | 40 | /** @var string */ 41 | protected $schema = 'default'; 42 | 43 | /** @var string */ 44 | protected $header = []; 45 | 46 | /** @var string */ 47 | protected $user = 'presto'; 48 | 49 | /** @var string */ 50 | protected $source = PrestoHeaders::PRESTO_SOURCE_VALUE; 51 | 52 | /** @var Property[] */ 53 | protected $property = []; 54 | 55 | /** @var PreparedStatement[] */ 56 | protected $preparedStatement = []; 57 | 58 | /** 59 | * PrestoSession constructor. 60 | * 61 | * @param string $host 62 | * @param string $catalog 63 | */ 64 | public function __construct(string $host, string $catalog) 65 | { 66 | $this->host = $host; 67 | $this->catalog = $catalog; 68 | } 69 | 70 | /** 71 | * @param string $schema 72 | */ 73 | public function setSchema(string $schema) 74 | { 75 | $this->schema = $schema; 76 | } 77 | 78 | /** 79 | * @param array $header 80 | */ 81 | public function setHeader(array $header) 82 | { 83 | $this->header = $header; 84 | } 85 | 86 | /** 87 | * @param string $user 88 | */ 89 | public function setUser(string $user) 90 | { 91 | $this->user = $user; 92 | } 93 | 94 | /** 95 | * @param string $source 96 | */ 97 | public function setSource(string $source) 98 | { 99 | $this->source = $source; 100 | } 101 | 102 | /** 103 | * @param UuidInterface $transactionId 104 | */ 105 | public function setTransactionId(UuidInterface $transactionId) 106 | { 107 | $this->transactionId = $transactionId; 108 | } 109 | 110 | /** 111 | * @param Property $property 112 | */ 113 | public function setProperty(Property $property) 114 | { 115 | $this->property[] = $property; 116 | } 117 | 118 | /** 119 | * @param PreparedStatement $preparedStatement 120 | */ 121 | public function setPreparedStatement(PreparedStatement $preparedStatement) 122 | { 123 | $this->preparedStatement[] = $preparedStatement; 124 | } 125 | 126 | /** 127 | * @return Property[] 128 | */ 129 | public function getProperty(): array 130 | { 131 | return $this->property; 132 | } 133 | 134 | /** 135 | * @return PreparedStatement[] 136 | */ 137 | public function getPreparedStatement(): array 138 | { 139 | return $this->preparedStatement; 140 | } 141 | 142 | /** 143 | * @return string 144 | */ 145 | public function getHost(): string 146 | { 147 | return $this->host; 148 | } 149 | 150 | /** 151 | * @return string 152 | */ 153 | public function getCatalog(): string 154 | { 155 | return $this->catalog; 156 | } 157 | 158 | /** 159 | * @return array 160 | */ 161 | public function getHeader(): array 162 | { 163 | return $this->header; 164 | } 165 | 166 | /** 167 | * @return string 168 | */ 169 | public function getSchema(): string 170 | { 171 | return $this->schema; 172 | } 173 | 174 | /** 175 | * @return string 176 | */ 177 | public function getUser(): string 178 | { 179 | return $this->user; 180 | } 181 | 182 | /** 183 | * @return string 184 | */ 185 | public function getSource(): string 186 | { 187 | return $this->source; 188 | } 189 | 190 | /** 191 | * @return UuidInterface|null 192 | */ 193 | public function getTransactionId() 194 | { 195 | return $this->transactionId; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/Column.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | final class Column 26 | { 27 | /** @var string */ 28 | private $name = ''; 29 | 30 | /** @var string */ 31 | private $type = ''; 32 | 33 | /** @var \stdClass */ 34 | private $typeSignature; 35 | 36 | /** 37 | * Column constructor. 38 | * 39 | * @param \stdClass $jsonContent 40 | */ 41 | public function __construct(\stdClass $jsonContent) 42 | { 43 | $this->name = $jsonContent->name; 44 | $this->type = $jsonContent->type; 45 | $this->typeSignature = $jsonContent->typeSignature; 46 | } 47 | 48 | /** 49 | * @return string 50 | */ 51 | public function getName(): string 52 | { 53 | return $this->name; 54 | } 55 | 56 | /** 57 | * @return string 58 | */ 59 | public function getType(): string 60 | { 61 | return $this->type; 62 | } 63 | 64 | /** 65 | * @return mixed 66 | */ 67 | public function getTypeSignature() 68 | { 69 | return $this->typeSignature; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/Exception/QueryErrorException.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | final class QueryErrorException extends \Exception 26 | { 27 | } 28 | -------------------------------------------------------------------------------- /src/Exception/RequestFailedException.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | final class RequestFailedException extends \RuntimeException 26 | { 27 | } 28 | -------------------------------------------------------------------------------- /src/FixData.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | final class FixData implements \ArrayAccess 26 | { 27 | /** 28 | * @param string $column 29 | * @param $value 30 | */ 31 | public function add(string $column, $value) 32 | { 33 | $this->$column = $value; 34 | } 35 | 36 | /** 37 | * @param mixed $offset 38 | * 39 | * @return bool 40 | */ 41 | public function offsetExists($offset) 42 | { 43 | return isset($this->$offset); 44 | } 45 | 46 | /** 47 | * @param string $offset 48 | * 49 | * @return mixed|null 50 | */ 51 | public function offsetGet($offset) 52 | { 53 | return $this->$offset ?? null; 54 | } 55 | 56 | /** 57 | * @param mixed $offset 58 | * @param mixed $value 59 | */ 60 | public function offsetSet($offset, $value) 61 | { 62 | $this->$offset = $value; 63 | } 64 | 65 | /** 66 | * @param string $offset 67 | */ 68 | public function offsetUnset($offset) 69 | { 70 | unset($this->$offset); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/LoggerClient.php: -------------------------------------------------------------------------------- 1 | 31 | */ 32 | class LoggerClient 33 | { 34 | /** @var LoggerInterface */ 35 | protected $logger; 36 | 37 | /** @var string */ 38 | protected $template = '{date_common_log} {uri} {req_headers} {req_body} {res_headers}'; 39 | 40 | /** 41 | * LoggerClient constructor. 42 | * 43 | * @param LoggerInterface $logger 44 | */ 45 | public function __construct(LoggerInterface $logger) 46 | { 47 | $this->logger = $logger; 48 | } 49 | 50 | /** 51 | * @param callable|null $handler 52 | * 53 | * @return ClientInterface 54 | */ 55 | public function client(callable $handler = null): ClientInterface 56 | { 57 | $handlerStack = HandlerStack::create($handler); 58 | $handlerStack->push( 59 | Middleware::log($this->logger, new MessageFormatter($this->template)) 60 | ); 61 | return new Client([ 62 | 'handler' => $handlerStack, 63 | ]); 64 | } 65 | 66 | /** 67 | * @codeCoverageIgnore 68 | * @param string $template 69 | */ 70 | public function setTemplate(string $template) 71 | { 72 | $this->template = $template; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/PrestoHeaders.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | final class PrestoHeaders 26 | { 27 | const PRESTO_USER = "X-Presto-User"; 28 | const PRESTO_SOURCE = "X-Presto-Source"; 29 | const PRESTO_CATALOG = "X-Presto-Catalog"; 30 | const PRESTO_SCHEMA = "X-Presto-Schema"; 31 | const PRESTO_TIME_ZONE = "X-Presto-Time-Zone"; 32 | const PRESTO_LANGUAGE = "X-Presto-Language"; 33 | const PRESTO_SESSION = "X-Presto-Session"; 34 | const PRESTO_SET_SESSION = "X-Presto-Set-Session"; 35 | const PRESTO_CLEAR_SESSION = "X-Presto-Clear-Session"; 36 | const PRESTO_PREPARED_STATEMENT = "X-Presto-Prepared-Statement"; 37 | const PRESTO_ADDED_PREPARE = "X-Presto-Added-Prepare"; 38 | const PRESTO_DEALLOCATED_PREPARE = "X-Presto-Deallocated-Prepare"; 39 | const PRESTO_TRANSACTION_ID = "X-Presto-Transaction-Id"; 40 | const PRESTO_STARTED_TRANSACTION_ID = "X-Presto-Started-Transaction-Id"; 41 | const PRESTO_CLEAR_TRANSACTION_ID = "X-Presto-Clear-Transaction-Id"; 42 | const PRESTO_CLIENT_INFO = "X-Presto-Client-Info"; 43 | const PRESTO_CURRENT_STATE = "X-Presto-Current-State"; 44 | const PRESTO_MAX_WAIT = "X-Presto-Max-Wait"; 45 | const PRESTO_MAX_SIZE = "X-Presto-Max-Size"; 46 | const PRESTO_TASK_INSTANCE_ID = "X-Presto-Task-Instance-Id"; 47 | const PRESTO_PAGE_TOKEN = "X-Presto-Page-Sequence-Id"; 48 | const PRESTO_PAGE_NEXT_TOKEN = "X-Presto-Page-End-Sequence-Id"; 49 | const PRESTO_BUFFER_COMPLETE = "X-Presto-Buffer-Complete"; 50 | 51 | /** library version */ 52 | const VERSION = '0.1.0'; 53 | const PRESTO_SOURCE_VALUE = 'PrestoClient'; 54 | 55 | private function __construct() 56 | { 57 | // 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/QueryError.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | final class QueryError 26 | { 27 | /** @var string */ 28 | private $message = ''; 29 | 30 | /** @var string */ 31 | private $sqlState = ''; 32 | 33 | /** @var int */ 34 | private $errorCode; 35 | 36 | /** @var string */ 37 | private $errorName; 38 | 39 | /** @var string */ 40 | private $errorType; 41 | 42 | /** @var \stdClass */ 43 | private $failureInfo; 44 | 45 | /** 46 | * QueryError constructor. 47 | * 48 | * @param \stdClass $jsonContent 49 | */ 50 | public function __construct(\stdClass $jsonContent) 51 | { 52 | $this->message = strval($jsonContent->message); 53 | $this->sqlState = $jsonContent->sqlState ?? ''; 54 | $this->errorCode = intval($jsonContent->errorCode); 55 | $this->errorName = strval($jsonContent->errorName); 56 | $this->errorType = strval($jsonContent->errorType); 57 | $this->failureInfo = $jsonContent->failureInfo; 58 | } 59 | 60 | /** 61 | * @return string 62 | */ 63 | public function getMessage(): string 64 | { 65 | return $this->message; 66 | } 67 | 68 | /** 69 | * @return string 70 | */ 71 | public function getSqlState(): string 72 | { 73 | return $this->sqlState; 74 | } 75 | 76 | /** 77 | * @return int 78 | */ 79 | public function getErrorCode(): int 80 | { 81 | return $this->errorCode; 82 | } 83 | 84 | /** 85 | * @return string 86 | */ 87 | public function getErrorName(): string 88 | { 89 | return $this->errorName; 90 | } 91 | 92 | /** 93 | * @return string 94 | */ 95 | public function getErrorType(): string 96 | { 97 | return $this->errorType; 98 | } 99 | 100 | /** 101 | * @return \stdClass 102 | */ 103 | public function getFailureInfo(): \stdClass 104 | { 105 | return $this->failureInfo; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/QueryResult.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | final class QueryResult 26 | { 27 | /** @var string */ 28 | private $id; 29 | 30 | /** @var string */ 31 | private $infoUri; 32 | 33 | /** @var string */ 34 | private $partialCancelUri; 35 | 36 | /** @var string */ 37 | private $nextUri; 38 | 39 | /** @var \stdClass[] */ 40 | private $columns = []; 41 | 42 | /** @var array */ 43 | private $data = []; 44 | 45 | /** @var StatementStats|null */ 46 | private $stats; 47 | 48 | /** @var QueryError|null */ 49 | private $error; 50 | 51 | /** 52 | * QueryResult constructor. 53 | * 54 | * @param string $content 55 | */ 56 | public function set(string $content) 57 | { 58 | $parsed = $this->parseContent($content); 59 | $this->id = $parsed->id; 60 | $this->infoUri = $parsed->infoUri; 61 | $this->partialCancelUri = $parsed->partialCancelUri ?? null; 62 | $this->nextUri = $parsed->nextUri ?? null; 63 | $this->columns = []; 64 | if (isset($parsed->columns)) { 65 | $this->columnTransfer($parsed->columns); 66 | } 67 | $this->data = $parsed->data ?? []; 68 | $this->stats = isset($parsed->stats) ? $this->statsTransfer($parsed->stats) : null; 69 | $this->error = isset($parsed->error) ? $this->errorTransfer($parsed->error) : null; 70 | } 71 | 72 | /** 73 | * @return string|null 74 | */ 75 | public function getId() 76 | { 77 | return $this->id; 78 | } 79 | 80 | /** 81 | * @return string|null 82 | */ 83 | public function getInfoUri() 84 | { 85 | return $this->infoUri; 86 | } 87 | 88 | /** 89 | * @return string|null 90 | */ 91 | public function getNextUri() 92 | { 93 | return $this->nextUri; 94 | } 95 | 96 | /** 97 | * @return QueryError|null 98 | */ 99 | public function getError() 100 | { 101 | return $this->error; 102 | } 103 | 104 | /** 105 | * @return string|null 106 | */ 107 | public function getPartialCancelUri() 108 | { 109 | return $this->partialCancelUri; 110 | } 111 | 112 | /** 113 | * @return \Generator 114 | */ 115 | public function yieldData(): \Generator 116 | { 117 | if (!count($this->data)) { 118 | yield; 119 | } 120 | $column = $this->getColumns(); 121 | $columnCount = count($column); 122 | foreach ($this->data as $data) { 123 | $fixData = new FixData(); 124 | for ($i = 0; $i < $columnCount; $i++) { 125 | $fixData->add($column[$i]->getName(), $data[$i]); 126 | } 127 | yield $fixData; 128 | } 129 | } 130 | 131 | /** 132 | * @return \Generator 133 | */ 134 | public function yieldDataArray(): \Generator 135 | { 136 | if (!count($this->data)) { 137 | yield; 138 | } 139 | $columns = array_map(function (Column $item) { 140 | return $item->getName(); 141 | }, $this->getColumns()); 142 | foreach ($this->data as $data) { 143 | yield array_combine($columns, $data); 144 | } 145 | } 146 | 147 | /** 148 | * @param string $fetchClassName 149 | * 150 | * @return \Generator 151 | */ 152 | public function yieldObject(string $fetchClassName) 153 | { 154 | if (!count($this->data)) { 155 | yield; 156 | } 157 | $column = $this->getColumns(); 158 | $columnCount = count($column); 159 | foreach ($this->data as $data) { 160 | $reflectionClass = new \ReflectionClass($fetchClassName); 161 | $newInstance = $reflectionClass->newInstanceWithoutConstructor(); 162 | for ($i = 0; $i < $columnCount; $i++) { 163 | if ($reflectionClass->hasProperty($column[$i]->getName())) { 164 | $property = $reflectionClass->getProperty($column[$i]->getName()); 165 | $property->setAccessible(true); 166 | $property->setValue($newInstance, $data[$i]); 167 | } 168 | } 169 | yield $newInstance; 170 | } 171 | } 172 | 173 | /** 174 | * @param string $content 175 | * 176 | * @return \stdClass 177 | */ 178 | private function parseContent(string $content): \stdClass 179 | { 180 | $parsed = json_decode($content); 181 | if ($parsed === null && json_last_error() !== JSON_ERROR_NONE) { 182 | throw new \RuntimeException; 183 | } 184 | 185 | return $parsed; 186 | } 187 | 188 | /** 189 | * @param \stdClass $jsonContent 190 | * 191 | * @return StatementStats 192 | */ 193 | private function statsTransfer(\stdClass $jsonContent): StatementStats 194 | { 195 | return new StatementStats($jsonContent); 196 | } 197 | 198 | /** 199 | * @param \stdClass $jsonContent 200 | * 201 | * @return QueryError 202 | */ 203 | private function errorTransfer(\stdClass $jsonContent): QueryError 204 | { 205 | return new QueryError($jsonContent); 206 | } 207 | 208 | /** 209 | * @param array $columns 210 | */ 211 | private function columnTransfer(array $columns) 212 | { 213 | foreach ($columns as $column) { 214 | $this->columns[] = new Column($column); 215 | } 216 | } 217 | 218 | /** 219 | * @return StatementStats|null 220 | */ 221 | public function getStats() 222 | { 223 | return $this->stats; 224 | } 225 | 226 | /** 227 | * @return Column[] 228 | */ 229 | public function getColumns(): array 230 | { 231 | return $this->columns; 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /src/ResultsSession.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | class ResultsSession 26 | { 27 | /** @var QueryResult[] */ 28 | private $results = []; 29 | 30 | /** @var StatementClient */ 31 | private $prestoClient; 32 | 33 | /** @var int */ 34 | private $timeout = 500000; 35 | 36 | /** @var bool */ 37 | private $debug = false; 38 | 39 | /** 40 | * @param StatementClient $prestoClient 41 | * @param int $timeout 42 | * @param bool $debug 43 | */ 44 | public function __construct(StatementClient $prestoClient, int $timeout = 500000, bool $debug = false) 45 | { 46 | $this->prestoClient = $prestoClient; 47 | $this->timeout = $timeout; 48 | $this->debug = $debug; 49 | } 50 | 51 | /** 52 | * @return ResultsSession 53 | */ 54 | public function execute(): ResultsSession 55 | { 56 | $this->prestoClient->execute($this->timeout, $this->debug); 57 | 58 | return $this; 59 | } 60 | 61 | /** 62 | * @return \Generator 63 | */ 64 | public function yieldResults(): \Generator 65 | { 66 | while ($this->prestoClient->isValid()) { 67 | yield $this->prestoClient->current(); 68 | $this->prestoClient->advance(); 69 | } 70 | } 71 | 72 | /** 73 | * @return array 74 | */ 75 | public function getResults(): array 76 | { 77 | while ($this->prestoClient->isValid()) { 78 | $this->addResults($this->prestoClient->current()); 79 | $this->prestoClient->advance(); 80 | } 81 | 82 | return $this->results; 83 | } 84 | 85 | /** 86 | * @param QueryResult $queryResult 87 | */ 88 | private function addResults(QueryResult $queryResult) 89 | { 90 | $this->results[] = $queryResult; 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Session/AbstractKeyValueStorage.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | abstract class AbstractKeyValueStorage 26 | { 27 | /** @var string */ 28 | private $key; 29 | 30 | /** @var string */ 31 | private $value; 32 | 33 | /** 34 | * @param string $key 35 | * @param string $value 36 | */ 37 | public function __construct(string $key, string $value) 38 | { 39 | $this->key = $key; 40 | $this->value = $value; 41 | } 42 | 43 | /** 44 | * @return string 45 | */ 46 | public function getKey(): string 47 | { 48 | return $this->key; 49 | } 50 | 51 | /** 52 | * @return string 53 | */ 54 | public function getValue(): string 55 | { 56 | return $this->value; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/Session/PreparedStatement.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | final class PreparedStatement extends AbstractKeyValueStorage 26 | { 27 | } 28 | -------------------------------------------------------------------------------- /src/Session/Property.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | final class Property extends AbstractKeyValueStorage 26 | { 27 | } 28 | -------------------------------------------------------------------------------- /src/StatementClient.php: -------------------------------------------------------------------------------- 1 | 38 | */ 39 | class StatementClient 40 | { 41 | const STATEMENT_URI = '/v1/statement'; 42 | 43 | /** @var ClientInterface */ 44 | private $client; 45 | 46 | /** @var ClientSession */ 47 | private $session; 48 | 49 | /** @var QueryResult */ 50 | protected $queryResult; 51 | 52 | /** @var array */ 53 | protected $headers = []; 54 | 55 | /** @var string */ 56 | protected $query; 57 | 58 | /** @var string */ 59 | protected $nextUri; 60 | 61 | /** @var bool */ 62 | private $gone = false; 63 | 64 | /** @var bool */ 65 | private $valid = true; 66 | 67 | /** @var bool */ 68 | private $closed = false; 69 | 70 | /** @var bool */ 71 | private $fulfilled = false; 72 | 73 | /** @var int */ 74 | protected $nanoseconds = 5000000000; 75 | 76 | /** 77 | * PrestoClient constructor. 78 | * 79 | * @param ClientSession $session 80 | * @param string $query 81 | * @param ClientInterface|null $client 82 | */ 83 | public function __construct(ClientSession $session, string $query, ClientInterface $client = null) 84 | { 85 | $this->session = $session; 86 | $this->query = $query; 87 | $this->client = (is_null($client)) ? new Client : $client; 88 | $this->queryResult = new QueryResult(); 89 | $this->prepareRequest(); 90 | } 91 | 92 | private function prepareRequest() 93 | { 94 | $this->headers = array_merge( 95 | [ 96 | PrestoHeaders::PRESTO_USER => $this->session->getUser(), 97 | 'User-Agent' => $this->session->getSource() . '/' . PrestoHeaders::VERSION 98 | ], 99 | $this->session->getHeader() 100 | ); 101 | } 102 | 103 | /** 104 | * @param Request $request 105 | * 106 | * @return Request 107 | */ 108 | protected function buildQueryRequest(Request $request): Request 109 | { 110 | $sessionTransaction = $this->session->getTransactionId(); 111 | $transactionId = is_null($sessionTransaction) ? 'NONE' : $sessionTransaction->toString(); 112 | $request = $request->withAddedHeader(PrestoHeaders::PRESTO_CATALOG, $this->session->getCatalog()) 113 | ->withAddedHeader(PrestoHeaders::PRESTO_SCHEMA, $this->session->getSchema()) 114 | ->withAddedHeader(PrestoHeaders::PRESTO_SOURCE, $this->session->getSource()) 115 | ->withAddedHeader(PrestoHeaders::PRESTO_TRANSACTION_ID, $transactionId); 116 | $sessionProperty = $this->session->getProperty(); 117 | if (count($sessionProperty)) { 118 | $sessions = []; 119 | /** @var Property $property */ 120 | foreach ($sessionProperty as $property) { 121 | $sessions[] = $property->getKey() . '=' . $property->getValue(); 122 | } 123 | $request = $request->withAddedHeader( 124 | PrestoHeaders::PRESTO_SESSION, 125 | implode(',', $sessions) 126 | ); 127 | } 128 | $preparedStatements = $this->session->getPreparedStatement(); 129 | if (count($preparedStatements)) { 130 | $statements = []; 131 | foreach ($preparedStatements as $preparedStatement) { 132 | $statements[] = urlencode($preparedStatement->getKey()) 133 | . '=' . urlencode($preparedStatement->getValue()); 134 | } 135 | $request = $request->withAddedHeader( 136 | PrestoHeaders::PRESTO_PREPARED_STATEMENT, 137 | implode(',', $statements) 138 | ); 139 | } 140 | 141 | return $request; 142 | } 143 | 144 | /** 145 | * @param int $timeout 146 | * @param bool $debug 147 | * 148 | * @return void 149 | * @throws QueryErrorException 150 | */ 151 | public function execute(int $timeout = 500000, bool $debug = false) 152 | { 153 | $normalize = UriNormalizer::normalize( 154 | new Uri($this->session->getHost() . StatementClient::STATEMENT_URI), 155 | UriNormalizer::REMOVE_DUPLICATE_SLASHES 156 | ); 157 | $request = new Request(RequestMethodInterface::METHOD_POST, $normalize, $this->headers); 158 | try { 159 | $response = $this->client->send($this->buildQueryRequest($request), [ 160 | 'timeout' => $timeout, 161 | 'body' => $this->query, 162 | 'debug' => $debug, 163 | ]); 164 | if ($response->getStatusCode() === StatusCodeInterface::STATUS_OK) { 165 | $this->queryResult->set($response->getBody()->getContents()); 166 | } 167 | } catch (ClientException $e) { 168 | throw new QueryErrorException($e->getMessage(), $e->getCode(), $e); 169 | } 170 | } 171 | 172 | /** 173 | * @return QueryResult 174 | * @throws QueryErrorException 175 | */ 176 | public function current(): QueryResult 177 | { 178 | return $this->queryResult; 179 | } 180 | 181 | /** 182 | * @return bool 183 | */ 184 | public function advance(): bool 185 | { 186 | $nextUri = $this->current()->getNextUri(); 187 | if (is_null($nextUri) || $this->isClosed()) { 188 | $this->valid = false; 189 | 190 | return false; 191 | } 192 | $this->prepareRequest(); 193 | 194 | return $this->detectResponse($nextUri); 195 | } 196 | 197 | /** 198 | * @param int $timeout 199 | * @param bool $debug 200 | * 201 | * @return bool 202 | */ 203 | public function cancelLeafStage(int $timeout = 500000, bool $debug = false): bool 204 | { 205 | if (!$this->isClosed()) { 206 | $cancelUri = $this->current()->getPartialCancelUri(); 207 | if (is_null($cancelUri)) { 208 | return false; 209 | } 210 | $promise = $this->client->deleteAsync($cancelUri, [ 211 | 'timeout' => $timeout, 212 | 'debug' => $debug, 213 | ]); 214 | $promise->then(function (ResponseInterface $response) { 215 | $this->fulfilled = (StatusCodeInterface::STATUS_NO_CONTENT === $response->getStatusCode()); 216 | }, function (RequestException $e) { 217 | throw new RequestFailedException($e->getMessage(), $e->getCode(), $e); 218 | }); 219 | $promise->wait(); 220 | } 221 | 222 | return $this->fulfilled; 223 | } 224 | 225 | /** 226 | * @param int $nanoseconds 227 | */ 228 | public function setNanoseconds(int $nanoseconds) 229 | { 230 | $this->nanoseconds = $nanoseconds; 231 | } 232 | 233 | /** 234 | * @return string 235 | */ 236 | public function getQuery(): string 237 | { 238 | return $this->query; 239 | } 240 | 241 | /** 242 | * @return bool 243 | */ 244 | public function isFailed(): bool 245 | { 246 | return $this->queryResult->getError() !== null; 247 | } 248 | 249 | /** 250 | * @return bool 251 | */ 252 | public function isValid(): bool 253 | { 254 | return $this->valid && (!$this->isGone()) && (!$this->isClosed()); 255 | } 256 | 257 | /** 258 | * @return bool 259 | */ 260 | public function isGone(): bool 261 | { 262 | return $this->gone; 263 | } 264 | 265 | /** 266 | * @return bool 267 | */ 268 | public function isClosed(): bool 269 | { 270 | return $this->closed; 271 | } 272 | 273 | /** 274 | * close 275 | * HTTP method DELETE 276 | */ 277 | public function close() 278 | { 279 | $uri = $this->current()->getNextUri(); 280 | if (!is_null($uri)) { 281 | $this->client->deleteAsync($uri)->wait(); 282 | } 283 | $this->closed = true; 284 | } 285 | 286 | /** 287 | * @param string $message 288 | * @param string $uri 289 | * @param ResponseInterface|null $response 290 | * 291 | * @return RequestFailedException 292 | */ 293 | private function requestFailedException( 294 | string $message, 295 | string $uri, 296 | ResponseInterface $response = null 297 | ): RequestFailedException { 298 | $this->gone = true; 299 | if ($response) { 300 | if (!$response->getBody()->getSize()) { 301 | return new RequestFailedException( 302 | sprintf( 303 | "Error %s at %s returned an invalid response: %s [Error: %s]", 304 | $message, 305 | $uri, 306 | $response->getStatusCode(), 307 | $response->getBody()->getContents() 308 | ) 309 | ); 310 | } 311 | 312 | return new RequestFailedException( 313 | sprintf( 314 | "Error %s at %s returned %s: %s", 315 | $message, 316 | $uri, 317 | $response->getStatusCode(), 318 | $response->getBody()->getContents() 319 | ) 320 | ); 321 | } 322 | 323 | return new RequestFailedException('server error.'); 324 | } 325 | 326 | /** 327 | * @param string $nextUri 328 | * 329 | * @return bool 330 | */ 331 | private function detectResponse(string $nextUri): bool 332 | { 333 | $start = microtime(true); 334 | $cause = null; 335 | $attempts = 0; 336 | do { 337 | if ($attempts > 0) { 338 | usleep($attempts * 100); 339 | } 340 | $attempts++; 341 | try { 342 | $response = $this->client->get($nextUri, ['headers' => $this->headers]); 343 | if ($response->getStatusCode() === StatusCodeInterface::STATUS_OK) { 344 | $this->queryResult->set($response->getBody()->getContents()); 345 | 346 | return true; 347 | } 348 | } catch (ClientException $e) { 349 | $cause = $e; 350 | if ($e->getCode() != StatusCodeInterface::STATUS_SERVICE_UNAVAILABLE) { 351 | throw $this->requestFailedException("fetching next", $nextUri, $e->getResponse()); 352 | } 353 | } 354 | } while (((microtime(true) - $start) < $this->nanoseconds) && !$this->isClosed()); 355 | 356 | $this->gone = true; 357 | throw new \RuntimeException('Error fetching next', 0, $cause); 358 | } 359 | } 360 | -------------------------------------------------------------------------------- /src/StatementStats.php: -------------------------------------------------------------------------------- 1 | 24 | */ 25 | final class StatementStats 26 | { 27 | /** @var string */ 28 | private $state = ''; 29 | 30 | /** @var bool */ 31 | private $queued; 32 | 33 | /** @var bool */ 34 | private $scheduled; 35 | 36 | /** @var int */ 37 | private $nodes; 38 | 39 | /** @var int */ 40 | private $totalSplits; 41 | 42 | /** @var int */ 43 | private $queuedSplits; 44 | 45 | /** @var int */ 46 | private $runningSplits; 47 | 48 | /** @var int */ 49 | private $completedSplits; 50 | 51 | /** @var int */ 52 | private $userTimeMillis; 53 | 54 | /** @var int */ 55 | private $cpuTimeMillis; 56 | 57 | /** @var int */ 58 | private $wallTimeMillis; 59 | 60 | /** @var int */ 61 | private $processedRows; 62 | 63 | /** @var int */ 64 | private $processedBytes; 65 | 66 | /** @var \stdClass */ 67 | private $rootStage; 68 | 69 | /** @var string[] */ 70 | private $primitiveCasts = [ 71 | 'state' => 'strval', 72 | 'queued' => 'boolval', 73 | 'scheduled' => 'boolval', 74 | 'nodes' => 'intval', 75 | 'totalSplits' => 'intval', 76 | 'queuedSplits' => 'intval', 77 | 'runningSplits' => 'intval', 78 | 'completedSplits' => 'intval', 79 | 'userTimeMillis' => 'intval', 80 | 'cpuTimeMillis' => 'intval', 81 | 'wallTimeMillis' => 'intval', 82 | 'processedRows' => 'intval', 83 | 'processedBytes' => 'intval', 84 | ]; 85 | 86 | /** 87 | * StatementStats constructor. 88 | * 89 | * @param \stdClass $jsonContent 90 | */ 91 | public function __construct(\stdClass $jsonContent) 92 | { 93 | $arrayContent = (array)$jsonContent; 94 | foreach ($arrayContent as $element => $value) { 95 | if (property_exists($this, $element)) { 96 | if (isset($this->primitiveCasts[$element])) { 97 | $castFunction = $this->primitiveCasts[$element]; 98 | $this->$element = $castFunction($value); 99 | } 100 | } 101 | } 102 | if (isset($jsonContent->rootStage)) { 103 | $this->rootStage = $jsonContent->rootStage; 104 | } 105 | } 106 | 107 | /** 108 | * @return string 109 | */ 110 | public function getState(): string 111 | { 112 | return $this->state; 113 | } 114 | 115 | /** 116 | * @return bool 117 | */ 118 | public function isQueued(): bool 119 | { 120 | return $this->queued; 121 | } 122 | 123 | /** 124 | * @return bool 125 | */ 126 | public function isScheduled(): bool 127 | { 128 | return $this->scheduled; 129 | } 130 | 131 | /** 132 | * @return int 133 | */ 134 | public function getNodes(): int 135 | { 136 | return $this->nodes; 137 | } 138 | 139 | /** 140 | * @return int 141 | */ 142 | public function getTotalSplits(): int 143 | { 144 | return $this->totalSplits; 145 | } 146 | 147 | /** 148 | * @return int 149 | */ 150 | public function getQueuedSplits(): int 151 | { 152 | return $this->queuedSplits; 153 | } 154 | 155 | /** 156 | * @return int 157 | */ 158 | public function getRunningSplits(): int 159 | { 160 | return $this->runningSplits; 161 | } 162 | 163 | /** 164 | * @return int 165 | */ 166 | public function getCompletedSplits(): int 167 | { 168 | return $this->completedSplits; 169 | } 170 | 171 | /** 172 | * @return int 173 | */ 174 | public function getUserTimeMillis(): int 175 | { 176 | return $this->userTimeMillis; 177 | } 178 | 179 | /** 180 | * @return int 181 | */ 182 | public function getCpuTimeMillis(): int 183 | { 184 | return $this->cpuTimeMillis; 185 | } 186 | 187 | /** 188 | * @return int 189 | */ 190 | public function getWallTimeMillis(): int 191 | { 192 | return $this->wallTimeMillis; 193 | } 194 | 195 | /** 196 | * @return int 197 | */ 198 | public function getProcessedRows(): int 199 | { 200 | return $this->processedRows; 201 | } 202 | 203 | /** 204 | * @return int 205 | */ 206 | public function getProcessedBytes(): int 207 | { 208 | return $this->processedBytes; 209 | } 210 | 211 | /** 212 | * @return \stdClass|null 213 | */ 214 | public function getRootStage() 215 | { 216 | return $this->rootStage; 217 | } 218 | } 219 | -------------------------------------------------------------------------------- /tests/ClientSessionTest.php: -------------------------------------------------------------------------------- 1 | assertSame('testing', $session->getCatalog()); 18 | $this->assertSame('http://localhost', $session->getHost()); 19 | $this->assertSame('PrestoClient', $session->getSource()); 20 | $this->assertSame('default', $session->getSchema()); 21 | $this->assertSame('presto', $session->getUser()); 22 | $this->assertNull($session->getTransactionId()); 23 | $this->assertCount(0, $session->getProperty()); 24 | $this->assertCount(0, $session->getPreparedStatement()); 25 | } 26 | 27 | public function testShouldReturnChangedSession() 28 | { 29 | $session = new ClientSession('http://localhost', 'testing'); 30 | $session->setSchema('testing'); 31 | $this->assertSame('testing', $session->getSchema()); 32 | $session->setSource('testingPresto'); 33 | $this->assertSame('testingPresto', $session->getSource()); 34 | $uuid = \Ramsey\Uuid\Uuid::uuid4(); 35 | $session->setTransactionId($uuid); 36 | $this->assertSame($uuid, $session->getTransactionId()); 37 | $session->setUser('testing'); 38 | $this->assertSame('testing', $session->getUser()); 39 | $session->setProperty(new Property('testing', '1')); 40 | $this->assertCount(1, $session->getProperty()); 41 | $this->assertSame('testing', $session->getProperty()[0]->getKey()); 42 | $this->assertSame('1', $session->getProperty()[0]->getValue()); 43 | $session->setPreparedStatement(new PreparedStatement('1', '1')); 44 | $this->assertCount(1, $session->getPreparedStatement()); 45 | $this->assertSame('1', $session->getPreparedStatement()[0]->getKey()); 46 | $this->assertSame('1', $session->getPreparedStatement()[0]->getValue()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/LoggerClientTest.php: -------------------------------------------------------------------------------- 1 | pushHandler($testHandler); 25 | $loggerClient = new LoggerClient($logger); 26 | 27 | $client = new \Ytake\PrestoClient\StatementClient( 28 | $this->session(), 29 | 'SELECT * FROM example.hoge.fuga', 30 | $loggerClient->client($mock) 31 | ); 32 | $client->execute(); 33 | $records = $testHandler->getRecords(); 34 | $this->assertCount(1, $records); 35 | } 36 | 37 | /** 38 | * @return ClientSession 39 | */ 40 | private function session(): ClientSession 41 | { 42 | return new ClientSession('http://localhost', 'testing'); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /tests/MockClientTrait.php: -------------------------------------------------------------------------------- 1 | HandlerStack::create($mock)]); 30 | } 31 | 32 | /** 33 | * @return Client 34 | */ 35 | public function throwRequestExceptionClient(): Client 36 | { 37 | $mock = new MockHandler([ 38 | new RequestException("Error Communicating with Server", new Request('GET', 'test')), 39 | ]); 40 | 41 | return new Client(['handler' => HandlerStack::create($mock)]); 42 | } 43 | 44 | /** 45 | * @return Client 46 | */ 47 | public function throwClientExceptionClient(): Client 48 | { 49 | $mock = new MockHandler([ 50 | new \GuzzleHttp\Exception\ClientException("Error Communicating with Server", new Request('POST', 'test')), 51 | ]); 52 | 53 | return new Client(['handler' => HandlerStack::create($mock)]); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /tests/ResultsSessionTest.php: -------------------------------------------------------------------------------- 1 | session(), 27 | 'SELECT * FROM example.hoge.fuga', 28 | new Client(['handler' => HandlerStack::create($mock)]) 29 | ); 30 | $resultSession = new ResultsSession($client); 31 | $result = $resultSession->execute()->getResults(); 32 | $this->assertNotCount(0, $result); 33 | $this->assertContainsOnlyInstancesOf(QueryResult::class, $result); 34 | } 35 | 36 | public function testShouldReturnQueryResultGenerator() 37 | { 38 | $mock = new MockHandler([ 39 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/success.json'))), 40 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/next_response.json'))), 41 | new Response(204, [], file_get_contents(realpath(__DIR__ . '/data/third_response.json'))), 42 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/fourth_response.json'))), 43 | ]); 44 | $client = new \Ytake\PrestoClient\StatementClient( 45 | $this->session(), 46 | 'SELECT * FROM example.hoge.fuga', 47 | new Client(['handler' => HandlerStack::create($mock)]) 48 | ); 49 | $resultSession = new ResultsSession($client); 50 | $result = $resultSession->execute()->yieldResults(); 51 | $this->assertInstanceOf(\Generator::class, $result); 52 | /** @var \Ytake\PrestoClient\QueryResult $row */ 53 | foreach ($result as $row) { 54 | $this->assertInstanceOf(QueryResult::class, $row); 55 | $this->assertInstanceOf(\Generator::class, $row->yieldData()); 56 | foreach ($row->yieldData() as $item) { 57 | if (!is_null($item)) { 58 | $this->assertInstanceOf(\Ytake\PrestoClient\FixData::class, $item); 59 | } 60 | } 61 | } 62 | } 63 | 64 | public function testShouldReturnQueryResultWithDataArray() 65 | { 66 | $mock = new MockHandler([ 67 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/success.json'))), 68 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/next_response.json'))), 69 | new Response(204, [], file_get_contents(realpath(__DIR__ . '/data/third_response.json'))), 70 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/fourth_response.json'))), 71 | ]); 72 | $client = new \Ytake\PrestoClient\StatementClient( 73 | $this->session(), 74 | 'SELECT * FROM example.hoge.fuga', 75 | new Client(['handler' => HandlerStack::create($mock)]) 76 | ); 77 | $resultSession = new ResultsSession($client); 78 | $result = $resultSession->execute()->yieldResults(); 79 | foreach ($result as $row) { 80 | foreach ($row->yieldDataArray() as $item) { 81 | if (!is_null($item)) { 82 | $this->assertInternalType('array', $item); 83 | $this->assertArrayHasKey('test_id', $item); 84 | } 85 | } 86 | } 87 | } 88 | 89 | public function testShouldBeExpectInstance() 90 | { 91 | $mock = new MockHandler([ 92 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/success.json'))), 93 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/next_response.json'))), 94 | new Response(204, [], file_get_contents(realpath(__DIR__ . '/data/third_response.json'))), 95 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/fourth_response.json'))), 96 | ]); 97 | $client = new \Ytake\PrestoClient\StatementClient( 98 | $this->session(), 99 | 'SELECT * FROM example.hoge.fuga', 100 | new Client(['handler' => HandlerStack::create($mock)]) 101 | ); 102 | $resultSession = new ResultsSession($client); 103 | $result = $resultSession->execute()->yieldResults(); 104 | /** @var \Ytake\PrestoClient\QueryResult $row */ 105 | foreach ($result as $row) { 106 | foreach ($row->yieldObject(MockResultTest::class) as $item) { 107 | if (!is_null($item)) { 108 | $this->assertInstanceOf(MockResultTest::class, $item); 109 | $this->assertSame(1, $item->testId()); 110 | } 111 | } 112 | } 113 | } 114 | 115 | /** 116 | * @return ClientSession 117 | */ 118 | private function session(): ClientSession 119 | { 120 | return new ClientSession('http://localhost', 'testing'); 121 | } 122 | } 123 | 124 | class MockResultTest 125 | { 126 | /** @var int */ 127 | private $test_id; 128 | 129 | /** 130 | * @return int 131 | */ 132 | public function testId(): int 133 | { 134 | return $this->test_id; 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /tests/StatementClientTest.php: -------------------------------------------------------------------------------- 1 | session(), 30 | 'SELECT * FROM example.hoge.fuga', 31 | $this->mockClient(StatusCodeInterface::STATUS_OK) 32 | ); 33 | $this->assertSame('SELECT * FROM example.hoge.fuga', $client->getQuery()); 34 | $queryResult = $client->current(); 35 | $this->assertInstanceOf(QueryResult::class, $queryResult); 36 | $this->assertNull($queryResult->getId()); 37 | $this->assertNull($queryResult->getInfoUri()); 38 | $this->assertNull($queryResult->getPartialCancelUri()); 39 | $this->assertNull($queryResult->getNextUri()); 40 | $this->assertNull($queryResult->getStats()); 41 | $this->assertNull($queryResult->getError()); 42 | $this->assertCount(0, $queryResult->getColumns()); 43 | $this->assertInstanceOf(\Generator::class, $queryResult->yieldData()); 44 | $this->assertFalse($client->cancelLeafStage()); 45 | $this->assertFalse($client->isClosed()); 46 | $this->assertFalse($client->advance()); 47 | $this->assertFalse($client->isValid()); 48 | $this->assertFalse($client->isGone()); 49 | $this->assertFalse($client->isFailed()); 50 | $client->close(); 51 | $this->assertTrue($client->isClosed()); 52 | } 53 | 54 | public function testShouldBeErrorQueryResult() 55 | { 56 | $error = file_get_contents(realpath(__DIR__ . '/data/error.json')); 57 | $client = new \Ytake\PrestoClient\StatementClient( 58 | $this->session(), 59 | 'SELECT * FROM example.hoge.fuga', 60 | $this->mockClient(StatusCodeInterface::STATUS_OK, $error) 61 | ); 62 | $this->assertNull($client->execute()); 63 | $this->assertFalse($client->advance()); 64 | $queryResult = $client->current(); 65 | $this->assertInstanceOf(QueryResult::class, $queryResult); 66 | $this->assertNotNull($queryResult->getId()); 67 | $this->assertNotNull($queryResult->getInfoUri()); 68 | $this->assertNull($queryResult->getPartialCancelUri()); 69 | $this->assertNull($queryResult->getNextUri()); 70 | $stats = $queryResult->getStats(); 71 | $this->assertInstanceOf(StatementStats::class, $stats); 72 | $this->assertSame('FAILED', $stats->getState()); 73 | $this->assertFalse($stats->isQueued()); 74 | $this->assertFalse($stats->isScheduled()); 75 | $this->assertSame(0, $stats->getCompletedSplits()); 76 | $this->assertSame(0, $stats->getCpuTimeMillis()); 77 | $this->assertSame(0, $stats->getNodes()); 78 | $this->assertSame(0, $stats->getProcessedBytes()); 79 | $this->assertSame(0, $stats->getProcessedRows()); 80 | $this->assertSame(0, $stats->getCpuTimeMillis()); 81 | $this->assertSame(0, $stats->getQueuedSplits()); 82 | $this->assertSame(0, $stats->getRunningSplits()); 83 | $this->assertSame(0, $stats->getCompletedSplits()); 84 | $this->assertSame(0, $stats->getTotalSplits()); 85 | $this->assertSame(0, $stats->getUserTimeMillis()); 86 | $this->assertSame(0, $stats->getWallTimeMillis()); 87 | $error = $queryResult->getError(); 88 | $this->assertInstanceOf(QueryError::class, $error); 89 | $this->assertInternalType('string', $error->getMessage()); 90 | $this->assertInternalType('int', $error->getErrorCode()); 91 | $this->assertInternalType('string', $error->getErrorName()); 92 | $this->assertInternalType('string', $error->getErrorType()); 93 | $this->assertInstanceOf(\stdClass::class, $error->getFailureInfo()); 94 | $this->assertInternalType('string', $error->getSqlState()); 95 | } 96 | 97 | /** 98 | * @expectedException \GuzzleHttp\Exception\RequestException 99 | */ 100 | public function testShouldThrowRequestException() 101 | { 102 | $client = new \Ytake\PrestoClient\StatementClient( 103 | $this->session(), 104 | 'SELECT * FROM example.hoge.fuga', 105 | $this->throwRequestExceptionClient() 106 | ); 107 | $client->execute(); 108 | } 109 | 110 | /** 111 | * @expectedException \Ytake\PrestoClient\Exception\QueryErrorException 112 | */ 113 | public function testShouldThrowQueryErrorException() 114 | { 115 | $client = new \Ytake\PrestoClient\StatementClient( 116 | $this->session(), 117 | 'SELECT * FROM example.hoge.fuga', 118 | $this->throwClientExceptionClient() 119 | ); 120 | $client->execute(); 121 | } 122 | 123 | public function testFunctionalClientProperties() 124 | { 125 | $client = new \Ytake\PrestoClient\StatementClient( 126 | $this->session(), 127 | 'SELECT * FROM example.hoge.fuga', 128 | $this->mockClient(StatusCodeInterface::STATUS_OK) 129 | ); 130 | $property = $this->getProtectProperty($client, 'headers'); 131 | $defaultHeaders = $property->getValue($client); 132 | $this->assertArrayHasKey(PrestoHeaders::PRESTO_USER, $defaultHeaders); 133 | $this->assertArrayHasKey('User-Agent', $defaultHeaders); 134 | 135 | $session = $this->session(); 136 | $session->setPreparedStatement(new PreparedStatement('testing', '1')); 137 | $session->setProperty(new Property('testing', 'testing')); 138 | $body = file_get_contents(realpath(__DIR__ . '/data/success.json')); 139 | $client = new \Ytake\PrestoClient\StatementClient( 140 | $session, 141 | 'SELECT * FROM example.hoge.fuga', 142 | $this->mockClient(StatusCodeInterface::STATUS_OK, $body) 143 | ); 144 | $client->execute(); 145 | $queryResult = $client->current(); 146 | $this->assertInstanceOf(QueryResult::class, $queryResult); 147 | $this->assertNotNull($queryResult->getId()); 148 | $this->assertNotNull($queryResult->getInfoUri()); 149 | $this->assertNull($queryResult->getPartialCancelUri()); 150 | $this->assertNotNull($queryResult->getNextUri()); 151 | $stats = $queryResult->getStats(); 152 | $this->assertInstanceOf(StatementStats::class, $stats); 153 | $this->assertSame('QUEUED', $stats->getState()); 154 | $this->assertTrue($stats->isQueued()); 155 | $this->assertFalse($stats->isScheduled()); 156 | } 157 | 158 | public function testFunctionalStackTwo() 159 | { 160 | $mock = new MockHandler([ 161 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/success.json'))), 162 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/next_response.json'))), 163 | ]); 164 | $client = new \Ytake\PrestoClient\StatementClient( 165 | $this->session(), 166 | 'SELECT * FROM example.hoge.fuga', 167 | new Client(['handler' => HandlerStack::create($mock)]) 168 | ); 169 | $client->execute(); 170 | $this->assertTrue($client->advance()); 171 | $queryResult = $client->current(); 172 | $this->assertInstanceOf(\stdClass::class, $queryResult->getStats()->getRootStage()); 173 | $columns = $queryResult->getColumns(); 174 | $column = $columns[0]; 175 | $this->assertInstanceOf(Column::class, $column); 176 | $this->assertSame('test_id', $column->getName()); 177 | $this->assertSame('integer', $column->getType()); 178 | $this->assertInstanceOf(\stdClass::class, $column->getTypeSignature()); 179 | } 180 | 181 | public function testFunctionalStackFour() 182 | { 183 | $mock = new MockHandler([ 184 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/success.json'))), 185 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/next_response.json'))), 186 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/third_response.json'))), 187 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/fourth_response.json'))), 188 | ]); 189 | $client = new \Ytake\PrestoClient\StatementClient( 190 | $this->session(), 191 | 'SELECT * FROM example.hoge.fuga', 192 | new Client(['handler' => HandlerStack::create($mock)]) 193 | ); 194 | $client->execute(); 195 | $this->assertTrue($client->advance()); 196 | $queryResult = $client->current(); 197 | $this->assertInstanceOf(\stdClass::class, $queryResult->getStats()->getRootStage()); 198 | $columns = $queryResult->getColumns(); 199 | $column = $columns[0]; 200 | $this->assertInstanceOf(Column::class, $column); 201 | $this->assertSame('test_id', $column->getName()); 202 | $this->assertSame('integer', $column->getType()); 203 | $this->assertInstanceOf(\stdClass::class, $column->getTypeSignature()); 204 | $this->assertTrue($client->advance()); 205 | $this->assertTrue($client->advance()); 206 | $queryResult = $client->current(); 207 | $this->assertInstanceOf(\Generator::class, $queryResult->yieldData()); 208 | /** @var FixData[] $array */ 209 | $array = iterator_to_array($queryResult->yieldData()); 210 | $this->assertCount(1, $array); 211 | $this->assertContainsOnly(FixData::class, $array); 212 | $this->assertSame(1, $array[0]['test_id']); 213 | $this->assertSame(1, $array[0]->test_id); 214 | $this->assertSame(1, $array[0]->offsetGet('test_id')); 215 | $this->assertTrue($array[0]->offsetExists('test_id')); 216 | $array[0]->offsetUnset('test_id'); 217 | $this->assertNull($array[0]->offsetGet('test_id')); 218 | $array[0]->offsetSet('test_id', 12); 219 | $this->assertSame(12, $array[0]->offsetGet('test_id')); 220 | $this->assertFalse($client->advance()); 221 | } 222 | 223 | public function testShouldBeCancel() 224 | { 225 | $mock = new MockHandler([ 226 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/success.json'))), 227 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/next_response.json'))), 228 | new Response(204, [], file_get_contents(realpath(__DIR__ . '/data/third_response.json'))), 229 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/fourth_response.json'))), 230 | ]); 231 | $client = new \Ytake\PrestoClient\StatementClient( 232 | $this->session(), 233 | 'SELECT * FROM example.hoge.fuga', 234 | new Client(['handler' => HandlerStack::create($mock)]) 235 | ); 236 | $client->execute(); 237 | $this->assertTrue($client->advance()); 238 | $this->assertTrue($client->cancelLeafStage()); 239 | $this->assertTrue($client->advance()); 240 | $this->assertFalse($client->advance()); 241 | } 242 | 243 | /** 244 | * @expectedException \Ytake\PrestoClient\Exception\RequestFailedException 245 | */ 246 | public function testShouldThrowRequestFailedException() 247 | { 248 | $mock = new MockHandler([ 249 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/success.json'))), 250 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/success.json'))), 251 | new Response(404, [], file_get_contents(realpath(__DIR__ . '/data/next_response.json'))), 252 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/success.json'))), 253 | new Response(200, [], file_get_contents(realpath(__DIR__ . '/data/success.json'))), 254 | ]); 255 | $client = new \Ytake\PrestoClient\StatementClient( 256 | $this->session(), 257 | 'SELECT * FROM example.hoge.fuga', 258 | new Client(['handler' => HandlerStack::create($mock)]) 259 | ); 260 | $client->execute(); 261 | $client->advance(); 262 | $client->advance(); 263 | } 264 | 265 | /** 266 | * @return ClientSession 267 | */ 268 | private function session(): ClientSession 269 | { 270 | return new ClientSession('http://localhost', 'testing'); 271 | } 272 | } 273 | -------------------------------------------------------------------------------- /tests/TestReflectionTrait.php: -------------------------------------------------------------------------------- 1 | getProperty($name); 19 | $property->setAccessible(true); 20 | 21 | return $property; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /tests/build/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /tests/data/error.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "20170618_093618_00058_3ytfr", 3 | "infoUri": "http://localhost:8080/query.html?20170618_093618_00058_3ytfr", 4 | "stats": { 5 | "state": "FAILED", 6 | "queued": false, 7 | "scheduled": false, 8 | "nodes": 0, 9 | "totalSplits": 0, 10 | "queuedSplits": 0, 11 | "runningSplits": 0, 12 | "completedSplits": 0, 13 | "userTimeMillis": 0, 14 | "cpuTimeMillis": 0, 15 | "wallTimeMillis": 0, 16 | "processedRows": 0, 17 | "processedBytes": 0 18 | }, 19 | "error": { 20 | "message": "Unknown session property session_variable_2", 21 | "errorCode": 14, 22 | "errorName": "INVALID_SESSION_PROPERTY", 23 | "errorType": "USER_ERROR", 24 | "failureInfo": { 25 | "type": "com.facebook.presto.spi.PrestoException", 26 | "message": "Unknown session property session_variable_2", 27 | "suppressed": [], 28 | "stack": [ 29 | "com.facebook.presto.metadata.SessionPropertyManager.lambda$validateSystemSessionProperty$2(SessionPropertyManager.java:190)", 30 | "java.util.Optional.orElseThrow(Optional.java:290)", 31 | "com.facebook.presto.metadata.SessionPropertyManager.validateSystemSessionProperty(SessionPropertyManager.java:190)", 32 | "com.facebook.presto.Session.beginTransactionId(Session.java:250)", 33 | "com.facebook.presto.execution.QueryStateMachine.beginWithTicker(QueryStateMachine.java:188)", 34 | "com.facebook.presto.execution.QueryStateMachine.begin(QueryStateMachine.java:166)", 35 | "com.facebook.presto.execution.SqlQueryExecution.\u003Cinit\u003E(SqlQueryExecution.java:167)", 36 | "com.facebook.presto.execution.SqlQueryExecution$SqlQueryExecutionFactory.createQueryExecution(SqlQueryExecution.java:641)", 37 | "com.facebook.presto.execution.SqlQueryExecution$SqlQueryExecutionFactory.createQueryExecution(SqlQueryExecution.java:563)", 38 | "com.facebook.presto.execution.SqlQueryManager.createQuery(SqlQueryManager.java:366)", 39 | "com.facebook.presto.server.StatementResource$Query.\u003Cinit\u003E(StatementResource.java:329)", 40 | "com.facebook.presto.server.StatementResource.createQuery(StatementResource.java:173)", 41 | "sun.reflect.GeneratedMethodAccessor579.invoke(Unknown Source)", 42 | "sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)", 43 | "java.lang.reflect.Method.invoke(Method.java:498)", 44 | "org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81)", 45 | "org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:144)", 46 | "org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:161)", 47 | "org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:160)", 48 | "org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:99)", 49 | "org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:389)", 50 | "org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:347)", 51 | "org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:102)", 52 | "org.glassfish.jersey.server.ServerRuntime$2.run(ServerRuntime.java:326)", 53 | "org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)", 54 | "org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)", 55 | "org.glassfish.jersey.internal.Errors.process(Errors.java:315)", 56 | "org.glassfish.jersey.internal.Errors.process(Errors.java:297)", 57 | "org.glassfish.jersey.internal.Errors.process(Errors.java:267)", 58 | "org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:317)", 59 | "org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:305)", 60 | "org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1154)", 61 | "org.glassfish.jersey.servlet.WebComponent.serviceImpl(WebComponent.java:473)", 62 | "org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:427)", 63 | "org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:388)", 64 | "org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:341)", 65 | "org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:228)", 66 | "org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:845)", 67 | "org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1689)", 68 | "io.airlift.http.server.TraceTokenFilter.doFilter(TraceTokenFilter.java:63)", 69 | "org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1676)", 70 | "io.airlift.http.server.TimingFilter.doFilter(TimingFilter.java:52)", 71 | "org.eclipse.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1676)", 72 | "org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:581)", 73 | "org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143)", 74 | "org.eclipse.jetty.server.handler.gzip.GzipHandler.handle(GzipHandler.java:395)", 75 | "org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1182)", 76 | "org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:511)", 77 | "org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1112)", 78 | "org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141)", 79 | "org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:119)", 80 | "org.eclipse.jetty.server.handler.StatisticsHandler.handle(StatisticsHandler.java:169)", 81 | "org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:52)", 82 | "org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:134)", 83 | "org.eclipse.jetty.server.Server.handle(Server.java:523)", 84 | "org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:320)", 85 | "org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:251)", 86 | "org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:273)", 87 | "org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:95)", 88 | "org.eclipse.jetty.io.SelectChannelEndPoint$2.run(SelectChannelEndPoint.java:93)", 89 | "org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.executeProduceConsume(ExecuteProduceConsume.java:303)", 90 | "org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.produceConsume(ExecuteProduceConsume.java:148)", 91 | "org.eclipse.jetty.util.thread.strategy.ExecuteProduceConsume.run(ExecuteProduceConsume.java:136)", 92 | "org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:671)", 93 | "org.eclipse.jetty.util.thread.QueuedThreadPool$2.run(QueuedThreadPool.java:589)", 94 | "java.lang.Thread.run(Thread.java:748)" 95 | ] 96 | } 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /tests/data/fourth_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "20170618_155510_00077_3ytfr", 3 | "infoUri": "http://localhost:8080/query.html?20170618_155510_00077_3ytfr", 4 | "partialCancelUri": "http://localhost:8080/v1/stage/20170618_155510_00077_3ytfr.1", 5 | "columns": [ 6 | { 7 | "name": "test_id", 8 | "type": "integer", 9 | "typeSignature": { 10 | "rawType": "integer", 11 | "typeArguments": [], 12 | "literalArguments": [], 13 | "arguments": [] 14 | } 15 | } 16 | ], 17 | "data": [ 18 | [ 19 | 1 20 | ] 21 | ], 22 | "stats": { 23 | "state": "RUNNING", 24 | "queued": false, 25 | "scheduled": true, 26 | "nodes": 1, 27 | "totalSplits": 18, 28 | "queuedSplits": 16, 29 | "runningSplits": 1, 30 | "completedSplits": 0, 31 | "userTimeMillis": 0, 32 | "cpuTimeMillis": 0, 33 | "wallTimeMillis": 0, 34 | "processedRows": 0, 35 | "processedBytes": 0, 36 | "rootStage": { 37 | "stageId": "0", 38 | "state": "RUNNING", 39 | "done": false, 40 | "nodes": 1, 41 | "totalSplits": 17, 42 | "queuedSplits": 16, 43 | "runningSplits": 0, 44 | "completedSplits": 0, 45 | "userTimeMillis": 0, 46 | "cpuTimeMillis": 0, 47 | "wallTimeMillis": 0, 48 | "processedRows": 0, 49 | "processedBytes": 0, 50 | "subStages": [ 51 | { 52 | "stageId": "1", 53 | "state": "RUNNING", 54 | "done": false, 55 | "nodes": 1, 56 | "totalSplits": 1, 57 | "queuedSplits": 0, 58 | "runningSplits": 1, 59 | "completedSplits": 0, 60 | "userTimeMillis": 0, 61 | "cpuTimeMillis": 0, 62 | "wallTimeMillis": 0, 63 | "processedRows": 0, 64 | "processedBytes": 0, 65 | "subStages": [] 66 | } 67 | ] 68 | }, 69 | "progressPercentage": 0.0 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /tests/data/next_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "20170618_151811_00066_3ytfr", 3 | "infoUri": "http://localhost:8080/query.html?20170618_151811_00066_3ytfr", 4 | "partialCancelUri": "http://localhost:8080/v1/stage/20170618_151811_00066_3ytfr.1", 5 | "nextUri": "http://localhost:8080/v1/statement/20170618_151811_00066_3ytfr/2", 6 | "columns": [ 7 | { 8 | "name": "test_id", 9 | "type": "integer", 10 | "typeSignature": { 11 | "rawType": "integer", 12 | "typeArguments": [], 13 | "literalArguments": [], 14 | "arguments": [] 15 | } 16 | } 17 | ], 18 | "stats": { 19 | "state": "RUNNING", 20 | "queued": false, 21 | "scheduled": false, 22 | "nodes": 1, 23 | "totalSplits": 0, 24 | "queuedSplits": 0, 25 | "runningSplits": 0, 26 | "completedSplits": 0, 27 | "userTimeMillis": 0, 28 | "cpuTimeMillis": 0, 29 | "wallTimeMillis": 0, 30 | "processedRows": 0, 31 | "processedBytes": 0, 32 | "rootStage": { 33 | "stageId": "0", 34 | "state": "PLANNED", 35 | "done": false, 36 | "nodes": 0, 37 | "totalSplits": 0, 38 | "queuedSplits": 0, 39 | "runningSplits": 0, 40 | "completedSplits": 0, 41 | "userTimeMillis": 0, 42 | "cpuTimeMillis": 0, 43 | "wallTimeMillis": 0, 44 | "processedRows": 0, 45 | "processedBytes": 0, 46 | "subStages": [ 47 | { 48 | "stageId": "1", 49 | "state": "SCHEDULED", 50 | "done": false, 51 | "nodes": 1, 52 | "totalSplits": 0, 53 | "queuedSplits": 0, 54 | "runningSplits": 0, 55 | "completedSplits": 0, 56 | "userTimeMillis": 0, 57 | "cpuTimeMillis": 0, 58 | "wallTimeMillis": 0, 59 | "processedRows": 0, 60 | "processedBytes": 0, 61 | "subStages": [] 62 | } 63 | ] 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /tests/data/success.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "20170618_151959_00067_3ytfr", 3 | "infoUri": "http://localhost:8080/query.html?20170618_151959_00067_3ytfr", 4 | "nextUri": "http://localhost:8080/v1/statement/20170618_151959_00067_3ytfr/1", 5 | "stats": { 6 | "state": "QUEUED", 7 | "queued": true, 8 | "scheduled": false, 9 | "nodes": 0, 10 | "totalSplits": 0, 11 | "queuedSplits": 0, 12 | "runningSplits": 0, 13 | "completedSplits": 0, 14 | "userTimeMillis": 0, 15 | "cpuTimeMillis": 0, 16 | "wallTimeMillis": 0, 17 | "processedRows": 0, 18 | "processedBytes": 0 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/data/third_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "20170618_154732_00069_3ytfr", 3 | "infoUri": "http://localhost:8080/query.html?20170618_154732_00069_3ytfr", 4 | "partialCancelUri": "http://localhost:8080/v1/stage/20170618_154732_00069_3ytfr.1", 5 | "nextUri": "http://localhost:8080/v1/statement/20170618_154732_00069_3ytfr/3", 6 | "columns": [ 7 | { 8 | "name": "test_id", 9 | "type": "integer", 10 | "typeSignature": { 11 | "rawType": "integer", 12 | "typeArguments": [], 13 | "literalArguments": [], 14 | "arguments": [] 15 | } 16 | } 17 | ], 18 | "stats": { 19 | "state": "RUNNING", 20 | "queued": false, 21 | "scheduled": false, 22 | "nodes": 1, 23 | "totalSplits": 0, 24 | "queuedSplits": 0, 25 | "runningSplits": 0, 26 | "completedSplits": 0, 27 | "userTimeMillis": 0, 28 | "cpuTimeMillis": 0, 29 | "wallTimeMillis": 0, 30 | "processedRows": 0, 31 | "processedBytes": 0, 32 | "rootStage": { 33 | "stageId": "0", 34 | "state": "SCHEDULING", 35 | "done": false, 36 | "nodes": 1, 37 | "totalSplits": 0, 38 | "queuedSplits": 0, 39 | "runningSplits": 0, 40 | "completedSplits": 0, 41 | "userTimeMillis": 0, 42 | "cpuTimeMillis": 0, 43 | "wallTimeMillis": 0, 44 | "processedRows": 0, 45 | "processedBytes": 0, 46 | "subStages": [ 47 | { 48 | "stageId": "1", 49 | "state": "SCHEDULED", 50 | "done": false, 51 | "nodes": 1, 52 | "totalSplits": 0, 53 | "queuedSplits": 0, 54 | "runningSplits": 0, 55 | "completedSplits": 0, 56 | "userTimeMillis": 0, 57 | "cpuTimeMillis": 0, 58 | "wallTimeMillis": 0, 59 | "processedRows": 0, 60 | "processedBytes": 0, 61 | "subStages": [] 62 | } 63 | ] 64 | } 65 | } 66 | } 67 | --------------------------------------------------------------------------------