Skip to content

Commit 45097ac

Browse files
committed
chore: make arrow to report dynamic version
1 parent fd9633c commit 45097ac

8 files changed

Lines changed: 206 additions & 11 deletions

File tree

.github/workflows/job-arrow-extension.yml

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -82,11 +82,3 @@ jobs:
8282

8383
- name: Run Parquet Integration Tests with Arrow
8484
run: composer test:lib:parquet
85-
86-
- name: Upload to Codecov
87-
if: ${{ !cancelled() && matrix.php == '8.3' }}
88-
uses: codecov/codecov-action@v5
89-
with:
90-
token: ${{ secrets.CODECOV_TOKEN }}
91-
directory: ./var/phpunit/coverage/clover
92-
flags: arrow-extension

.nix/pkgs/php-arrow-ext/package.nix

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
rustPlatform,
66
clang,
77
llvmPackages,
8+
arrow-ext-version ? "dev",
89
}:
910

1011
let
@@ -25,7 +26,7 @@ let
2526
};
2627
pkg = rustPlatform.buildRustPackage {
2728
pname = "php-arrow-ext";
28-
version = "0.1.0";
29+
version = arrow-ext-version;
2930

3031
src = extSrc;
3132

@@ -42,6 +43,7 @@ let
4243
LIBCLANG_PATH = "${llvmPackages.libclang.lib}/lib";
4344
PHP_CONFIG = "${php.unwrapped.dev}/bin/php-config";
4445
PHP = "${php.unwrapped}/bin/php";
46+
ARROW_VERSION = arrow-ext-version;
4547
};
4648

4749
installPhase = let

src/extension/arrow-ext/build.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
fn main() {
2+
let version = std::env::var("ARROW_VERSION")
3+
.ok()
4+
.filter(|s| !s.is_empty())
5+
.or_else(|| {
6+
std::process::Command::new("git")
7+
.args(["describe", "--tags", "--always"])
8+
.output()
9+
.ok()
10+
.filter(|o| o.status.success())
11+
.and_then(|o| String::from_utf8(o.stdout).ok())
12+
.map(|s| s.trim().to_string())
13+
.filter(|s| !s.is_empty())
14+
})
15+
.unwrap_or_else(|| env!("CARGO_PKG_VERSION").to_string());
16+
17+
println!("cargo:rustc-env=ARROW_VERSION={version}");
18+
19+
let arrow_version = resolve_dep_version("arrow-schema").unwrap_or_else(|| "unknown".to_string());
20+
let parquet_version = resolve_dep_version("parquet").unwrap_or_else(|| "unknown".to_string());
21+
22+
println!("cargo:rustc-env=ARROW_LIB_VERSION={arrow_version}");
23+
println!("cargo:rustc-env=PARQUET_LIB_VERSION={parquet_version}");
24+
}
25+
26+
fn resolve_dep_version(crate_name: &str) -> Option<String> {
27+
let lock_contents = std::fs::read_to_string("Cargo.lock").ok()?;
28+
let needle = format!("name = \"{crate_name}\"");
29+
30+
for chunk in lock_contents.split("[[package]]") {
31+
if chunk.contains(&needle) {
32+
for line in chunk.lines() {
33+
let line = line.trim();
34+
if line.starts_with("version = ") {
35+
return Some(line.trim_start_matches("version = ").trim_matches('"').to_string());
36+
}
37+
}
38+
}
39+
}
40+
41+
None
42+
}

src/extension/arrow-ext/src/lib.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@ static GLOBAL: System = System;
1414

1515
pub extern "C" fn php_module_info(_module: *mut ModuleEntry) {
1616
info_table_start!();
17-
info_table_row!("arrow", "enabled");
18-
info_table_row!("version", env!("CARGO_PKG_VERSION"));
17+
info_table_row!("arrow.enabled", "true");
18+
info_table_row!("arrow.extension_version", env!("ARROW_VERSION"));
19+
info_table_row!("arrow.library_version", env!("ARROW_LIB_VERSION"));
20+
info_table_row!("arrow.parquet_library_version", env!("PARQUET_LIB_VERSION"));
1921
info_table_end!();
2022
}
2123

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Flow\Parquet\Tests\Unit\Engine;
6+
7+
use Flow\Parquet\Engine\{AdaptiveParquetEngine, ArrowParquetEngine, PhpParquetEngine};
8+
use PHPUnit\Framework\TestCase;
9+
10+
final class AdaptiveParquetEngineTest extends TestCase
11+
{
12+
public function test_creates_arrow_engine_when_arrow_extension_loaded() : void
13+
{
14+
if (!\extension_loaded('arrow')) {
15+
self::markTestSkipped('This test requires the arrow extension to be loaded');
16+
}
17+
18+
$engine = new AdaptiveParquetEngine();
19+
20+
$reflection = new \ReflectionClass($engine);
21+
$delegate = $reflection->getProperty('delegate')->getValue($engine);
22+
23+
self::assertInstanceOf(ArrowParquetEngine::class, $delegate);
24+
}
25+
26+
public function test_creates_php_engine_when_arrow_extension_not_loaded() : void
27+
{
28+
if (\extension_loaded('arrow')) {
29+
self::markTestSkipped('This test requires the arrow extension to NOT be loaded');
30+
}
31+
32+
$engine = new AdaptiveParquetEngine();
33+
34+
$reflection = new \ReflectionClass($engine);
35+
$delegate = $reflection->getProperty('delegate')->getValue($engine);
36+
37+
self::assertInstanceOf(PhpParquetEngine::class, $delegate);
38+
}
39+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Flow\Parquet\Tests\Unit\Engine\Arrow;
6+
7+
use Flow\Filesystem\Local\Memory\MemoryStream;
8+
use Flow\Filesystem\Path;
9+
use Flow\Parquet\Engine\Arrow\DestinationStreamAdapter;
10+
use PHPUnit\Framework\TestCase;
11+
12+
final class DestinationStreamAdapterTest extends TestCase
13+
{
14+
public function test_append_delegates_to_destination_stream() : void
15+
{
16+
$handle = \fopen('php://memory', 'r+b');
17+
$stream = new MemoryStream($handle, Path::realpath('/tmp/test'));
18+
19+
$adapter = new DestinationStreamAdapter($stream);
20+
$adapter->append('hello ');
21+
$adapter->append('world');
22+
23+
self::assertSame('hello world', $stream->content());
24+
}
25+
26+
public function test_append_returns_self() : void
27+
{
28+
$handle = \fopen('php://memory', 'r+b');
29+
$stream = new MemoryStream($handle, Path::realpath('/tmp/test'));
30+
31+
$adapter = new DestinationStreamAdapter($stream);
32+
33+
self::assertSame($adapter, $adapter->append('data'));
34+
}
35+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Flow\Parquet\Tests\Unit\Engine\Arrow;
6+
7+
use Flow\Filesystem\Local\Memory\MemoryStream;
8+
use Flow\Filesystem\Path;
9+
use Flow\Parquet\Engine\Arrow\SourceStreamAdapter;
10+
use PHPUnit\Framework\TestCase;
11+
12+
final class SourceStreamAdapterTest extends TestCase
13+
{
14+
public function test_read_delegates_to_source_stream() : void
15+
{
16+
$handle = \fopen('php://memory', 'r+b');
17+
\fwrite($handle, 'hello world');
18+
$stream = new MemoryStream($handle, Path::realpath('/tmp/test'));
19+
20+
$adapter = new SourceStreamAdapter($stream);
21+
22+
self::assertSame('hello', $adapter->read(5, 0));
23+
self::assertSame('world', $adapter->read(5, 6));
24+
self::assertSame(' ', $adapter->read(1, 5));
25+
}
26+
27+
public function test_size_delegates_to_source_stream() : void
28+
{
29+
$handle = \fopen('php://memory', 'r+b');
30+
\fwrite($handle, 'hello world');
31+
$stream = new MemoryStream($handle, Path::realpath('/tmp/test'));
32+
33+
$adapter = new SourceStreamAdapter($stream);
34+
35+
self::assertSame(11, $adapter->size());
36+
}
37+
}
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Flow\Parquet\Tests\Unit\Engine;
6+
7+
use Flow\Parquet\Engine\ArrowParquetEngine;
8+
use Flow\Parquet\Exception\RuntimeException;
9+
use Flow\Parquet\ParquetFile\Compressions;
10+
use PHPUnit\Framework\Attributes\TestWith;
11+
use PHPUnit\Framework\TestCase;
12+
13+
final class ArrowParquetEngineTest extends TestCase
14+
{
15+
public function test_constructor_throws_when_extension_not_loaded() : void
16+
{
17+
if (\extension_loaded('arrow')) {
18+
self::markTestSkipped('This test requires the arrow extension to NOT be loaded');
19+
}
20+
21+
$this->expectException(RuntimeException::class);
22+
$this->expectExceptionMessage('arrow extension is required');
23+
24+
new ArrowParquetEngine();
25+
}
26+
27+
#[TestWith([Compressions::UNCOMPRESSED, 'UNCOMPRESSED'])]
28+
#[TestWith([Compressions::SNAPPY, 'SNAPPY'])]
29+
#[TestWith([Compressions::GZIP, 'GZIP'])]
30+
#[TestWith([Compressions::BROTLI, 'BROTLI'])]
31+
#[TestWith([Compressions::LZ4, 'LZ4_RAW'])]
32+
#[TestWith([Compressions::LZ4_RAW, 'LZ4_RAW'])]
33+
#[TestWith([Compressions::ZSTD, 'ZSTD'])]
34+
public function test_map_compression(Compressions $input, string $expected) : void
35+
{
36+
self::assertSame($expected, ArrowParquetEngine::mapCompression($input));
37+
}
38+
39+
public function test_map_compression_throws_for_lzo() : void
40+
{
41+
$this->expectException(RuntimeException::class);
42+
$this->expectExceptionMessage('LZO compression is not supported');
43+
44+
ArrowParquetEngine::mapCompression(Compressions::LZO);
45+
}
46+
}

0 commit comments

Comments
 (0)