Added Support for Fetch Modes#4
Conversation
769344359
left a comment
There was a problem hiding this comment.
why remove shutdown callback ?
| SEASCLICK_RES_NAME, | ||
| SeasClick_functions, | ||
| PHP_MINIT(SeasClick), | ||
| PHP_MSHUTDOWN(SeasClick), |
There was a problem hiding this comment.
why remove shutdown callback?
There was a problem hiding this comment.
It doesn't do anything, so recommended approach is to not invlude PHP_MSHUT / PHP_MINIT unless they do something
| }; | ||
|
|
||
| #define REGISTER_SC_CLASS_CONST_LONG(const_name, value) \ | ||
| zend_declare_class_constant_long(SeasClick_ce, const_name, sizeof(const_name)-1, (zend_long)value); |
There was a problem hiding this comment.
Please do some compatibility tests, such as zend_long type does not support PHP 5 version
| convertToZval(col2, block[1], row, "", 0, fetch_mode|SC_FETCH_ONE); | ||
|
|
||
| if (Z_TYPE_P(col1) == IS_LONG) { | ||
| zend_hash_index_update(Z_ARRVAL_P(return_value), Z_LVAL_P(col1), col2); |
There was a problem hiding this comment.
Compatibility
#define zend_hash_index_update(ht, h, pData, nDataSize, pDest) _zend_hash_index_update_or_next_insert(ht, h, pData, nDataSize, pDest, HASH_UPDATE ZEND_FILE_LINE_CC)
This is the definition of PHP 5 version
Upgraded clickhouse lib to v1.1.0 Added ability control connection, recv, timeouts & retry settings
|
@769344359 @Neeke Any chance to get it merged? |
it will have been merged when i finsh unit tests! |
|
@guba-odudkin could you please help me to fix the conflicts ? it change a lot of file, i can not merge it . |
Resyncs this PR with SeasX/SeasClick master after five years of drift. Brings in upstream's PHP 8 support, the writeStart/write/writeEnd block-insert methods, the GCC 10 compile fix, and the package.xml bumps. Keeps the PR's fetch-mode features (FETCH_ONE, FETCH_KEY_PAIR, FETCH_COLUMN, DATE_AS_STRINGS), the SeasClickException class, and the timeout/retry properties. Conflict resolutions: - SeasClick.cpp: merged the fetch-mode select() with upstream's write* block-insert methods. Standardized arg-info names on SeasClick_* (upstream's master shipped a SeasCilck_ typo), and routed every catch through sc_zend_throw_exception_tsrmls_cc against SeasClickException_ce so PHP 7 and PHP 8 builds both link. - client.cpp: kept upstream's split InsertData / InsertDataEnd API. The PR's PHP-side writeStart/write/writeEnd already calls them in that order, so the combined InsertData from the lib upgrade was wrong. - php7_wrapper.h: changed the ZEND_ACC_DTOR fallback from 0x4000 to 0. PHP 8 reused bit 14 for ZEND_ACC_VARIADIC, and the old fallback was crashing zend_register_functions on extension load. - nullable.cpp: dropped the duplicate <stdexcept> include the auto-merge introduced. Verified against PHP 7.1, 7.2, 7.4, 8.3 (debug+ASAN), 8.4 (debug+ASAN and release), and 8.5 (debug+ASAN). Extension loads, all nine methods and four fetch-mode constants register, and SeasClickException extends Exception.
|
@Rock-520 Not sure if this repo is still alive, but PR updated and merges cleanly |
The PR's package.xml has declared <min>7.0.0</min> for years and the upstream code path stopped compiling on PHP 5 several releases ago. Carrying the dual-PHP scaffolding only obscures the real PHP 7 vs PHP 8 distinctions, so strip it. - php7_wrapper.h: deleted the entire PHP_MAJOR_VERSION < 7 branch (PHP 5 alternates for hash/zval/exception calls). Removed TSRMLS_CC from PHP 7 wrapper bodies (the macro is empty on PHP 7+ and absent on PHP 8). Removed the ZEND_ACC_DTOR fallback now that the only callsite is gone. - SeasClick.cpp: collapsed the PHP_VERSION_ID >= 70000 / <= 70000 / < 70000 conditionals around class registration, property declarations, and write() argument fetching. Stripped TSRMLS_CC from every zend_parse_parameters call. Dropped ZEND_ACC_DTOR from the __destruct method entry; PHP 7+ recognizes destructors by name. Net: -155 lines. Verified against PHP 7.4 (Docker), PHP 8.3 (debug+ASAN), and PHP 8.4 (release). Extension loads, all nine methods and four constants register, SeasClickException extends Exception.
Replaces the artpaul-fork v1.x vendored library with the official ClickHouse/clickhouse-cpp v2.6.1. Brings in DateTime64, LowCardinality, Map, Geo, Time and Time64 column types from upstream, modern ClientOptions, optional ZSTD compression on the wire, and a current contrib (cityhash, lz4, abseil-int128, zstd). PHP-side surface changes: - DateTime64(N) now accepts epoch-seconds (int) or "Y-m-d H:i:s" strings on insert, and reads back either as int or as "Y-m-d H:i:s.ffffff" when DATE_AS_STRINGS is set. - LowCardinality(String) and LowCardinality(FixedString(N)) round-trip through the dictionary column. Read returns plain strings. - Map(K, V) columns can be created via factory; per-row read/write through the new ColumnMapT API is left as TODO. Library / build mechanics: - Bumped to C++17 (clickhouse-cpp v2 requires it). - Vendored contrib/zstd in full (compress, decompress, common). zstd is a hard dep of the new compressed.cpp -- there is no compile-time gate. Built with -DZSTD_DISABLE_ASM since we don't compile the .S optimization file. - Reworked config.m4 source list (~80 sources now) and added include paths for the new contrib trees. - writeStart/write/writeEnd were rewired from the v1.x InsertQuery/InsertData/InsertDataEnd API to v2.x BeginInsert/SendInsertBlock/EndInsert. - ClientOptions field renames: SetSocketReceiveTimeout -> SetConnectionRecvTimeout, SetSocketConnectTimeout -> SetConnectionConnectTimeout. - Fixed two ZPP / arg-info mismatches PHP 8.3 caught: select() and execute() now declare their first argument as the only required one. The previous values were inherited from the original PR and worked on PHP 8.4 by accident. Build artifacts now ignored via .gitignore (Makefile, configure, config.*, autom4te.cache/, build/, modules/, *.dep, *.loT, etc.). Verified end-to-end against ClickHouse 24.8.14 (docker clickhouse/clickhouse-server) on PHP 7.4 (docker), PHP 8.3 (debug+ASAN), and PHP 8.4 (release): connect, ping, execute, insert, select with FETCH_ONE / FETCH_COLUMN / FETCH_KEY_PAIR / DATE_AS_STRINGS, writeStart/write/writeEnd block insert, DateTime64(3)/(6) round-trip, LowCardinality(String) round-trip with correct cardinality. PHP 8.3 ASAN reports indirect leaks (~17KB) inside Client::Impl construction (NonSecureSocketFactory and friends) that are vendored- library-internal and only surface at process exit. Functional path is clean -- no SEGV, no use-after-free. README updated: dependency list now reflects v2.x types, install section mentions the C++17 / vendored-contrib model, and added a "Testing against a local ClickHouse server" section pointing at the clickhouse/clickhouse-server image with a one-liner docker run.
Both deliberately switch to de_DE to prove the extension formats floats
locale-independently; they only ever ran on CI when the runner shipped
de_DE (it doesn't), so two latent 7.4 issues stayed hidden until the
local 7.4 lane gained the locale.
064: the write path is correct (the {x:Float64} round-trip reaches the
server as "1.5" and succeeds), but the assertion echoed the returned
double. PHP < 8.0 renders a float through LC_NUMERIC ("1,5" under de_DE);
PHP 8.0 made float-to-string locale-independent. Compare the value
numerically instead so the test verifies the wire format, not the
engine's echo behavior.
070: a PHP array literal [1.5 => "a"] coerces the float key to int
(silent < 8.1, deprecated after), so the keys never reached the Float64
read decoder the test targets -- the EXPECTF never matched reality, which
is why it skipped on >= 8.1 and silently mis-ran below. Build the map
server-side via map(1.5,'a',0.1,'b'), drop the version skip, and match
the locale-independent string key php_gcvt emits (0.10000000000000001).
Verified on 7.4 and 8.4 (identical keys); full 8.4 suite 143 passed,
0 failed.
- Update vendored tree to 2.6.2 - Mark memcpy guard in string.cpp as obsoleted (upstreamed) - Re-apply/adapt remaining 5 local patches (BeginInsert query_id, cityhash guard, public Query overloads for BeginInsert+SelectWithExternalData, early insert state clear in ResetConnection) - Add new columns bool.cpp/json.cpp to config.m4 + config.w32 - Fix Map(Float) key lifetime: release ref on temporary map_zv after add, use std::string for float key strings instead of stack kbuf - Clear per-Query callbacks after Select to avoid closures holding refs to do_select_into stack - Add missing main/snprintf.h include for php_gcvt prototype - 070.phpt now strict (no trailing %a); test passes cleanly under ASAN build - Add minimal repro tests/070_minimal.php for the locale+Map(Float) case - Update docs and changelog for the bump
Removed the erroneous Z_DELREF_P after add_next_index_zval/sc_add_assoc_zval_ex for the map_zv (and the comment claiming the add does an inc). This was causing heap-use-after-free on the Map array (allocated at array_init in convertToZval) under ASAN when the result row / foreach / json_encode etc. later destroyed it. The add takes ownership of the refcount=1 from array_init; ZVAL_UNDEF is sufficient to detach the SC_MAKE pointer temp (consistent with emitNestedZval and other complex column paths in the same file). The std::string float-key copies (in the kkind==2 branches) and the 7 post-Select On* clears remain. Addresses the sanitizer (and ZTS) crashes on 019.phpt (and related Map tests) that made the 2.6.2 bump CI red. 086.phpt paths were inspected and untouched by this change.
…n to 2.6.2 The 5 local patches re-applied against vendored clickhouse-cpp v2.6.2 are now kept as applyable .patch files under patches/upstream/, so future lib bumps can git-apply them instead of hand-reconstructing from LOCAL_PATCHES.md prose. Bump .upstream/clickhouse-cpp.yml pin 2.6.1 -> 2.6.2 to match what is vendored (and what upstream ships); the empty string_view memcpy UB guard is now upstream and no longer carried locally.
clickhouse-cpp 2.6.2's VariantIndex trait built a std::variant in a constexpr non-type template argument, which MSVC 14.29 (VS2019, the PHP 8.3 Windows toolset) rejects with C2440. VS2022 (8.4/8.5) and GCC/Clang accept it, so the break only hit the 8.3 Windows lane -- and only in release-windows.yml, which runs after the tag. 0.8.6 shipped with no PHP 8.3 Windows DLLs as a result. Compute the index with a std::is_same_v fold instead (no std::variant constructed); identical result, portable across toolsets. Drop the now-unused VariantIndexTag. Recorded as local patch 0006 + LOCAL_PATCHES.md entry. Expand the pre-tag Windows gate (tests.yml) from 8.4 to 8.3/8.4/8.5 so a vs16-only break blocks the release instead of surfacing post-tag.
…to-discard clickhouse_free_obj() called ResetConnection() to "discard" an orphaned streaming insert (writeStart/write with no writeEnd). That goal is unreachable. ClickHouse inserts are not transactional, so streamed blocks may already be written to parts, and dropping the connection only discards inserts small enough to sit in the server squash buffer, making it size-dependent and silently partial. It also opened a fresh connection plus handshake at destruction just to drop it, and left a latent bug: when connect() threw, state_ stayed Inserting and ~Client committed the insert anyway on the old socket. Now free_obj just deletes the client and lets ~Client wind the insert down on the existing wire. The ResetConnection calls in the write()/insert() error paths stay; there they recover a wedged inserting_ handle rather than roll back, with the framing corrected in comments. Behavior change: an abandoned streaming insert now finalizes whatever the server accepted instead of attempting to discard it. Updated 091.phpt and CHANGELOG accordingly.
…ion discard flag write()'s error path keyed recovery off the session-level insert_blocks_sent flag: once any block had been sent in the session, a later write() that threw during row conversion (a healthy wire) went through ResetConnection() to "discard" the insert. Same illusory rollback the teardown path had: ClickHouse is not transactional, so earlier blocks may already be persisted, and the discard only worked for inserts small enough to sit in the server squash buffer. Distinguish a dirty wire from a healthy one with a per-call block_send_started local, set immediately before SendInsertBlock. A throw before that point (row conversion) leaves the wire healthy, so the catch finalizes with EndInsert() to match the teardown policy, falling back to ResetConnection() only if the finalize itself throws. A throw from SendInsertBlock leaves the wire dirty and still ResetConnection()s for handle recovery. The session-level insert_blocks_sent flag is now unused and removed. Updated 089.phpt to assert the finalize behavior and refreshed the stale comment in 142.phpt.
Wire the vendored clickhouse-cpp ColumnJSON through the PHP boundary on
both the read and write paths.
insert() auto-detects by PHP type: an array or object is json_encode'd, a
string is stored as raw JSON text (validated client-side so malformed
input fails fast instead of mid-stream), and null becomes the empty
object {}, matching the lib's Nullable(JSON) convention.
select() returns the raw JSON string by default. The new
ClickHouse::JSON_AS_ARRAY and JSON_AS_OBJECT fetch flags decode it to a
nested associative array or nested stdClass. Reads require
output_format_native_write_json_as_string=1 on the session; without it
the vendored client throws a protocol error on the column prefix.
selectStream() and selectStreamCallback() gain a trailing fetch_mode
argument, so the bitmask (the new JSON flags and the existing
DATE_AS_STRINGS) now applies on the streaming read paths too.
Tests cover the three read modes, array/object/raw-string insert,
Nullable(JSON), streaming decode, and insert validation, gated behind a
JSON-capability skip helper.
PHP 7.4's php_json_decode takes a non-const char* buffer, so the read-path decode passing std::string::c_str() (const char*) failed to convert there. Pass &str[0] instead, a mutable NUL-terminated pointer that PHP 8's const parameter accepts too.
Bool now reads as a PHP bool and writes from a bool or int. IPv4 and IPv6 gain write support (both were read-only): IPv4 accepts a dotted-quad string or an integer matching ClickHouse's toIPv4() convention (16909060 inserts as 1.2.3.4), IPv6 accepts a dotted/colon string. All three validate client-side and support Nullable; IPv4/IPv6 null rows write a masked sentinel because the column's inet_pton path rejects empty input. Also correct the README: top-level Tuple inserts work (the "read-only" note was stale), though Array(Tuple) writes remain unsupported. Tests cover the round-trips, integer-vs-toIPv4() parity, Nullable variants, and the bad-input rejection paths.
createColumn(Tuple) now builds an empty ColumnTuple from the field types instead of throwing, so a Tuple can serve as the element column of Array(Tuple) on the write path. The Array insert loop dropped its reusable-child + Clear() pattern in favor of appending each row's column directly. AppendAsColumn copies the row data into the array's flat column, so the indirection was unnecessary, and ColumnTuple::Clear() drops its sub-columns rather than emptying their rows, which made the old pattern unusable for tuples. Plain Array(scalar) inserts are unaffected. Also replace the three remaining em-dashes in README.md with commas or sentence breaks.
Security: - Decimal read: bound the server-declared scale at 38 before the in-place format. A scale beyond 38 (a Decimal256, or a hostile/MITM server) drove memset/memmove past a fixed 64-byte stack buffer on the first decoded row of a select(). Mirrors the DateTime64 precision guard. Correctness: - strict_zval_* now ZVAL_DEREF their input, so by-reference cells nested in Array/Tuple insert rows coerce instead of throwing a misleading "array/object/resource cannot be assigned" error. - ZStrGuard throws when a __toString() left EG(exception) pending, so an insert aborts cleanly and surfaces the original exception instead of committing a corrupted "" cell. - DateTime64/Time64 reject a float at precision >= 7 (a double cannot hold the tick count exactly) and bound the precision at 9 on insert, closing a signed-int64 overflow in the scale loop. - IPv4 inserts accept an integral float, consistent with integer columns. - ClickHouseStatement::__construct returns after throwing. Performance: - Array(scalar) inserts reuse one element column across rows again; the per-row allocation now applies only to Tuple elements, where Clear() drops sub-columns. Reverts a regression from Array(Tuple) support. - JSON insert string validation uses php_json_validate on 8.3+ (no value tree materialized); decode-and-discard on older PHP. Docs: README write-side coercion rules, Bool truthiness caveat, and a selectStream-buffering note. Findings from a 5-agent security/quality/perf/standards/correctness review. tests/149.phpt covers the security and correctness fixes.
…verflow The ASAN/UBSan lane caught a signed-integer-overflow at the DateTime64 insert: the integer path treats the value as whole seconds and multiplies by 10^precision, which overflows int64 for an out-of-range input (the new tests/149.phpt passed ticks instead of seconds and tripped it). The multiply is now bounded before it runs, and the misleading "integer ticks" wording is corrected to "whole seconds" (the value is scaled internally, matching DateTime). Same guard applied to Time64. The test now passes epoch seconds.
Casting an out-of-range double to int64_t is UB (C11 6.3.1.4p1); on x86-64 it silently saturates rather than trapping. The sub-precision-7 float path did (int64_t)(d * scale) unguarded, so a large epoch (1e300) was undefined. A new checked_double_to_int64 helper bounds the value against [-2^63, 2^63) before the cast and throws otherwise; both the DateTime64 and Time64 float paths use it. The extension's ASAN job builds with clang, whose -fsanitize=undefined includes float-cast-overflow, so tests/149.phpt's out-of-range case gates this class.
…g paths
A PHP reference (IS_REFERENCE) reaching a type/shape branch via Z_TYPE_P
was mishandled in every path that didn't go through the strict_zval_*
helpers (which already deref). The worst cases silently wrote a
placeholder instead of erroring:
- Array(Nullable(Int32)) with a by-ref null stored 0; the null bitmap
checked Z_TYPE_P without deref while the child build (under the
allow-null guard) wrote a 0 placeholder. Array(LowCardinality(
Nullable(String))) stored "" the same way.
- A by-ref settings value serialized to "" (formatScalarParam switched
without deref), and the server rejected it ("Cannot parse bool from
string ''").
- A by-ref constructor config value was mishandled: ['compression' =>
&"zstd"] silently fell back to LZ4; by-ref endpoints was rejected.
php_array_get_value now derefs the found value, covering every config
key.
Swept the remaining direct Z_TYPE_P(array_value) sites in insertColumn
and the geo/UUID helpers (Enum, Date, LowCardinality, Map, Int128,
UInt128, Time, Array element, Tuple row + field, Point/Ring/Polygon/
MultiPolygon). tests/150.phpt covers the composite, settings, and config
cases.
Also: config.m4 now validates openssl/ssl.h + ssl/crypto when
--enable-clickhouse-openssl is requested, failing with a clear message
instead of an opaque missing-header compile error (config.w32 already
did the equivalent CHECK_LIB/CHECK_HEADER).
Findings from an external review pass.
…HP 7.4/8.0 tests/150.phpt read the protected compression property via Reflection without setAccessible(true), which 8.1+ made implicit but 7.4/8.0 still require. Code path was fine; only the test was version-sensitive.
setAccessible(true) is required on PHP 7.4/8.0 but deprecated in 8.5 (emitting a notice that fails the --EXPECT-- match). A bound closure reads the protected compression property cleanly across 7.4-8.5.
Closure::bind can't scope to an internal class, and setAccessible is required on 7.4/8.0 but deprecated in 8.5. Call it only on PHP < 8.1, where it is needed and not deprecated; 8.1+ reads the protected property without it. Verified locally before push.
- __construct rejects a re-run on an already-constructed object before
mutating any property, instead of overwriting host/port/db/user and
only then throwing (fnd_e76c3771).
- A nested array element in a {placeholder} list is rejected rather than
stringified to the bogus identifier "Array" (fnd_020ad08b). Stringable
object elements still work (their __toString is honored).
- selectToStream resumes a short write instead of aborting the export;
a stream wrapper may accept fewer bytes than requested per call
(fnd_a0b74d40).
- selectStream / selectStreamCallback mask the fetch_mode to the
value-shaping flags (DATE_AS_STRINGS / JSON_AS_*) before the per-cell
convertToZval. A result-shape flag (FETCH_ONE / KEY_PAIR / COLUMN) is
meaningless per cell and made the iterator emit a scalar, fataling on
current()'s ?array return type (fnd_eb7a982d).
…_35ca0004) isExists() validated db/table with the strict identifier rule and rejected any name that wasn't a bare identifier, so a table legitimately named `my-table` couldn't be checked. But the values are compared as string VALUES against system.tables, not interpolated as identifiers, so the correct treatment is sqlStringLiteral escaping (the same as showTables()): it admits any name and neutralizes injection by escaping rather than rejecting. Verified the injection probes return false with no SQL executed. showCreateTable keeps strict identifier validation deliberately: there the name is interpolated as an identifier, so the validation is real defense-in-depth (fnd_272c7f8e left as won't-fix per maintainer). Updated tests/053.phpt: the isExists probes now assert safe-bool results instead of validator rejections.
Add the missing entries from the review rounds (construct re-run guard, nested-array placeholder, selectToStream short-write resume, selectStream shape-flag masking, isExists string-literal escaping, DateTime64/Time64 overflow and float-cast guards). Merge the duplicate Changed section, reorder to Added/Changed/Fixed/Security, and rewrite the Unreleased bullets terse and em-dash-free per the ~/ai NEWS-style rule. Released sections are left untouched.
Move the full usage reference into a self-contained docs/index.html for GitHub Pages: per-type read/write matrix, fetch modes, configuration, the grouped method list, JSON columns, CSV/TSV streaming, placeholders, settings, and observability. The type matrix is verified against typesToPhp.cpp and the .phpt suite; code blocks get inline PHP highlighting with no external dependency. README drops from 494 to a landing page: intro, a prominent docs link, fork rationale, install, one quick example, benchmarks, and license. Correct two stale clickhouse-cpp v2.6.1 references to v2.6.2.
Address validated findings from a multi-agent review (docs/review/2025-06-21/findings.md): - Map(UUID, …) keys and Map(…, UUID) values read back as bare 32-char hex, matching standalone UUID columns; they previously rendered hyphenated. New ClickHouse::UUID_WITH_DASHES fetch flag opts into the canonical dashed form across select/selectStream/selectStreamCallback (CR-003). - Time inserts reject values outside the int32 range instead of silently truncating (CR-005). - Decimal inserts reject a non-scalar value with a clear client-side exception instead of stringifying it to "Array" and failing server-side (CR-007). - FixedString inserts reject an over-width value client-side (CR-012). - Drop a dead column_names vector in insertFromStream (CR-008). - config.w32 fails hard when --enable-clickhouse-openssl is set but OpenSSL is missing, matching config.m4 (CR-018). - Bump PHP_CLICKHOUSE_VERSION to 0.8.9. Tests 152/153 added (the Decimal/FixedString cases assert the client-side message so a regression to a server-side error flips them); 052 updated for the raw-hex Map output.
The naming-consistency CI guard (scripts/check-no-seasclick.sh) rejects SeasClick references outside the documented compat surface; a comment added in the prior commit named it incidentally. Reword to "historical default".
str_contains() is PHP 8.0+; 152.phpt fataled on the 7.4 CI lane. Verified the full suite green on a local 7.4 build (152 pass, 1 skip).
Follow-up hardening from the consolidated audit review. - Route every server-supplied type-metadata cast (As<...Type>()) in the create / insert / read paths through a new type_as_or_throw<> helper, so a type whose concrete metadata doesn't match its declared code throws instead of dereferencing null. Reachable from a malicious or proxied server. - Wrap the full __construct config-assembly body in the existing try/catch so an allocation failure during option setup surfaces as a ClickHouseException rather than escaping the Zend dispatcher and aborting the process. - Add the ClickHouse::FIXEDSTRING_BINARY fetch flag: returns FixedString values at their full declared width (trailing NULs preserved) for binary payloads, instead of the default trailing-NUL trim. Covers the standalone and LowCardinality(FixedString) read paths. Pinned by tests/154.phpt. - emitVerbose only swallows an exception raised by encoding the trace payload; a query exception already pending when the trace fires is preserved. - Minor: generic SKIPIF messages (no host:port), drop an unused parameter, fix an inaccurate 2^64 comment. Remove the now-consolidated docs/review/2025-06-21/findings.md.
The linux job's "Rebuild with TLS" step only compiled the SSL build path and never ran a TLS test (CR-019); tests/027.phpt therefore never executed in CI (CR-026) — the plaintext services: container has no 9440, and GitHub service containers start before any step runs, so they can't mount a step-generated cert. Run the TLS server manually in that step instead: generate a self-signed cert, start clickhouse-server with a committed tls.xml drop-in (tcp_port_secure 9440, verificationMode none), rebuild the extension with --enable-clickhouse-openssl, wait on a plaintext HTTP readiness probe (host 8124), then run 027 against 9440. run-tests.php exits 0 on all-skip, so the step parses the summary and fails unless 027 actually ran (skipped=0) and passed (passed=1) — a regression that drops TLS support or breaks the handshake now blocks CI instead of skipping silently.

Couple of feature enhancements to simplify data retrieval
Also added SeasClickException class so that exceptions thrown are specific to extension as opposed to using generic Exception class.