msphpsql icon indicating copy to clipboard operation
msphpsql copied to clipboard

Binary stream returned from a statement becomes invalid if the statement gets implicitly closed

Open morozov opened this issue 1 year ago • 3 comments

PHP version 8.2.3

PHP SQLSRV or PDO_SQLSRV version 5.10.1

Microsoft ODBC Driver version
2.3.11

SQL Server version
15.00.4236

Client operating system
macOS

Problem description
If a function prepares and executes a statement that returns an sqlsrv_stream, then the stream becomes invalid outside of the function.

Expected behavior The stream is valid as long as it has at least one reference.

Actual behavior The stream is invalid. Passing it to any stream function results in the following error message:

TypeError: supplied resource is not a valid stream resource

Additionally, instead of "sqlsrv_stream", Xdebug displays the stream type as "Unknown".

Repro code or steps to reproduce

function get_stream($conn) {
    $stmt = sqlsrv_query($conn, "SELECT CONVERT(VARBINARY, '0x41')");

    sqlsrv_fetch($stmt);

    return sqlsrv_get_field($stmt, 0, SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY));
}

$conn = sqlsrv_connect('127.0.0.1', [
    'UID' => 'sa',
    'PWD' => 'Passw0rd',
]);

$stream = get_stream($conn);

var_dump(stream_get_meta_data($stream));

Inlining the code of the get_stream function mitigates the problem.

morozov avatar Feb 27 '23 00:02 morozov

Hi, we're looking into it.

absci avatar Mar 02 '23 18:03 absci

Seems the driver is designed to free the stream when the statement closes. But you could pass the $stmt to the function, and unset($stmt) when you're done.

<?php
function get_stream($stmt) {
    sqlsrv_fetch($stmt);
    return sqlsrv_get_field($stmt, 0, SQLSRV_PHPTYPE_STREAM(SQLSRV_ENC_BINARY));
}

$conn = sqlsrv_connect('127.0.0.1', [
    'UID' => 'sa',
    'PWD' => '',
]);


$stmt = sqlsrv_query($conn, "SELECT CONVERT(VARBINARY, '0x41')");
$stream = get_stream($stmt);

var_dump(stream_get_meta_data($stream));
unset($stmt);
?>

absci avatar Mar 15 '23 22:03 absci

This is still a workaround. The intent is to have a function that accepts a connection and returns the stream. The statement is an implementation detail of the function, not an external dependency.

Seems the driver is designed to free the stream when the statement closes.

This doesn't look like a valid design. If the stream depends on the statement, then the statement should be automatically closed only when it's no longer referenced by the userland code and any internal resources (e.g. streams).

morozov avatar Mar 16 '23 00:03 morozov