From 82a79e696d86ea3a189b4d9611a99eb0a8b53a0e Mon Sep 17 00:00:00 2001 From: hectorhammett Date: Tue, 19 May 2026 18:25:47 +0000 Subject: [PATCH 1/3] Fix the pagination bug for stateless queries --- BigQuery/src/BigQueryClient.php | 5 ++- BigQuery/tests/Unit/BigQueryClientTest.php | 50 ++++++++++++++++++++++ 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/BigQuery/src/BigQueryClient.php b/BigQuery/src/BigQueryClient.php index c8713355fb53..ea1fd44a37ab 100644 --- a/BigQuery/src/BigQueryClient.php +++ b/BigQuery/src/BigQueryClient.php @@ -438,13 +438,14 @@ public function runQuery(JobConfigurationInterface $query, array $options = []) $response = $this->connection->query($statelessArgs); if ($response['jobComplete'] ?? false) { + $jobId = $response['jobReference']['jobId'] ?? ''; return new QueryResults( $this->connection, - '', + $jobId, $this->projectId, $response, $this->mapper, - $this->createJob([], ''), // create an empty job + $this->createJob($response, $jobId), $queryResultsOptions ); } diff --git a/BigQuery/tests/Unit/BigQueryClientTest.php b/BigQuery/tests/Unit/BigQueryClientTest.php index 6ffa79b16b67..1a2153cb19ea 100644 --- a/BigQuery/tests/Unit/BigQueryClientTest.php +++ b/BigQuery/tests/Unit/BigQueryClientTest.php @@ -182,6 +182,56 @@ public function testRunQueryStateless() $this->assertTrue($queryResults->isComplete()); } + public function testRunQueryStatelessWithPagination() + { + $client = $this->getClient(); + $query = $client->query(self::QUERY_STRING); + + $this->connection->query(Argument::allOf( + Argument::withEntry('projectId', self::PROJECT_ID), + Argument::withEntry('query', self::QUERY_STRING), + Argument::withEntry('jobCreationMode', 'JOB_CREATION_OPTIONAL') + )) + ->willReturn([ + 'jobComplete' => true, + 'jobReference' => [ + 'jobId' => self::JOB_ID, + 'projectId' => self::PROJECT_ID + ], + 'schema' => [ + 'fields' => [ + ['name' => 'col1', 'type' => 'STRING'] + ] + ], + 'rows' => [ + ['f' => [['v' => 'val1']]] + ], + 'pageToken' => 'next-page-token' + ]) + ->shouldBeCalledTimes(1); + + $this->connection->getQueryResults(Argument::allOf( + Argument::withEntry('projectId', self::PROJECT_ID), + Argument::withEntry('jobId', self::JOB_ID), + Argument::withEntry('pageToken', 'next-page-token') + )) + ->willReturn([ + 'jobComplete' => true, + 'rows' => [ + ['f' => [['v' => 'val2']]] + ] + ]) + ->shouldBeCalledTimes(1); + + $client->___setProperty('connection', $this->connection->reveal()); + $results = $client->runQuery($query); + $rows = iterator_to_array($results); + + $this->assertCount(2, $rows); + $this->assertEquals('val1', $rows[0]['col1']); + $this->assertEquals('val2', $rows[1]['col1']); + } + public function testRunQueryJobQueryEndpointReturnsAJob() { $client = $this->getClient(); From 5cab8e3b6b4a0ac88065e6a1813315963cac26fd Mon Sep 17 00:00:00 2001 From: hectorhammett Date: Tue, 19 May 2026 20:41:45 +0000 Subject: [PATCH 2/3] Modified the createJob parameter to contain just job related information --- BigQuery/src/BigQueryClient.php | 13 ++++++++++--- BigQuery/src/Job.php | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/BigQuery/src/BigQueryClient.php b/BigQuery/src/BigQueryClient.php index ea1fd44a37ab..ed81caf7529a 100644 --- a/BigQuery/src/BigQueryClient.php +++ b/BigQuery/src/BigQueryClient.php @@ -437,20 +437,27 @@ public function runQuery(JobConfigurationInterface $query, array $options = []) $response = $this->connection->query($statelessArgs); + $jobId = $response['jobReference']['jobId'] ?? ''; + $jobInfo = ['jobReference' => $response['jobReference'] ?? []]; + if (isset($response['statistics'])) { + $jobInfo['statistics'] = $response['statistics']; + } + if ($response['jobComplete'] ?? false) { - $jobId = $response['jobReference']['jobId'] ?? ''; + // This is to keep it consistent with the response from a non stateless query + $jobInfo['status']['state'] = Job::DONE; return new QueryResults( $this->connection, $jobId, $this->projectId, $response, $this->mapper, - $this->createJob($response, $jobId), + $this->createJob($jobInfo, $jobId), $queryResultsOptions ); } - $job = $this->createJob($response, $response['jobReference']['jobId']); + $job = $this->createJob($jobInfo, $jobId); } else { $job = $this->startQuery($query, $options); } diff --git a/BigQuery/src/Job.php b/BigQuery/src/Job.php index 23b0baddd072..d07de6d249a2 100644 --- a/BigQuery/src/Job.php +++ b/BigQuery/src/Job.php @@ -33,6 +33,7 @@ class Job use JobWaitTrait; const MAX_RETRIES = PHP_INT_MAX; + const DONE = 'DONE'; /** * @var ConnectionInterface Represents a connection to BigQuery. From 16a093990886b3efe3edd1a95aa4c55e5e43ddce Mon Sep 17 00:00:00 2001 From: hectorhammett Date: Tue, 19 May 2026 21:47:31 +0000 Subject: [PATCH 3/3] Update job creating to have a valid state on non stateless result --- BigQuery/src/BigQueryClient.php | 1 + BigQuery/src/Job.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/BigQuery/src/BigQueryClient.php b/BigQuery/src/BigQueryClient.php index ed81caf7529a..87ca2ca00fb4 100644 --- a/BigQuery/src/BigQueryClient.php +++ b/BigQuery/src/BigQueryClient.php @@ -457,6 +457,7 @@ public function runQuery(JobConfigurationInterface $query, array $options = []) ); } + $jobInfo['status']['state'] = null; $job = $this->createJob($jobInfo, $jobId); } else { $job = $this->startQuery($query, $options); diff --git a/BigQuery/src/Job.php b/BigQuery/src/Job.php index d07de6d249a2..c07b6f1b45e8 100644 --- a/BigQuery/src/Job.php +++ b/BigQuery/src/Job.php @@ -267,7 +267,7 @@ function () use ($options) { */ public function isComplete(array $options = []) { - return $this->info($options)['status']['state'] === 'DONE'; + return $this->info($options)['status']['state'] === self::DONE; } /**