@@ -271,11 +271,10 @@ StatusRecordOr<DSResults> ExecuteScript(
271271
272272#if (!defined(_WIN32) || defined(_WIN64)) && !defined(NO_ARROW)
273273
274- StatusRecordOr<std::shared_ptr<arrow::Schema>> GetArrowSchema (
275- ::google::cloud::bigquery::storage::v1::ArrowSchema const & schema_in,
276- RowSchema& row_schema) {
274+ StatusRecordOr<std::shared_ptr<arrow::Schema>> GetArrowSchemaFromBytes (
275+ std::string const & serialized_schema, RowSchema& row_schema) {
277276 std::shared_ptr<arrow::Buffer> buffer =
278- std::make_shared<arrow::Buffer>(schema_in. serialized_schema () );
277+ std::make_shared<arrow::Buffer>(serialized_schema);
279278 arrow::io::BufferReader buffer_reader (buffer);
280279 arrow::ipc::DictionaryMemo dictionary_memo;
281280 auto result = arrow::ipc::ReadSchema (&buffer_reader, &dictionary_memo);
@@ -349,12 +348,17 @@ StatusRecordOr<std::shared_ptr<arrow::Schema>> GetArrowSchema(
349348 return schema;
350349}
351350
352- StatusRecordOr<std::shared_ptr<arrow::RecordBatch>> GetArrowRecordBatch (
353- ::google::cloud::bigquery::storage::v1::ArrowRecordBatch const &
354- record_batch_in,
355- std::shared_ptr<arrow::Schema> schema) {
356- std::shared_ptr<arrow::Buffer> buffer = std::make_shared<arrow::Buffer>(
357- record_batch_in.serialized_record_batch ());
351+ StatusRecordOr<std::shared_ptr<arrow::Schema>> GetArrowSchema (
352+ ::google::cloud::bigquery::storage::v1::ArrowSchema const & schema_in,
353+ RowSchema& row_schema) {
354+ return GetArrowSchemaFromBytes (schema_in.serialized_schema (), row_schema);
355+ }
356+
357+ StatusRecordOr<std::shared_ptr<arrow::RecordBatch>>
358+ GetArrowRecordBatchFromBytes (std::string const & serialized_record_batch,
359+ std::shared_ptr<arrow::Schema> schema) {
360+ std::shared_ptr<arrow::Buffer> buffer =
361+ std::make_shared<arrow::Buffer>(serialized_record_batch);
358362 arrow::io::BufferReader buffer_reader (buffer);
359363 arrow::ipc::DictionaryMemo dictionary_memo;
360364 arrow::ipc::IpcReadOptions read_options;
@@ -368,6 +372,14 @@ StatusRecordOr<std::shared_ptr<arrow::RecordBatch>> GetArrowRecordBatch(
368372 return record_batch;
369373}
370374
375+ StatusRecordOr<std::shared_ptr<arrow::RecordBatch>> GetArrowRecordBatch (
376+ ::google::cloud::bigquery::storage::v1::ArrowRecordBatch const &
377+ record_batch_in,
378+ std::shared_ptr<arrow::Schema> schema) {
379+ return GetArrowRecordBatchFromBytes (
380+ record_batch_in.serialized_record_batch (), std::move (schema));
381+ }
382+
371383StatusRecord ProcessRecordBatch (
372384 std::shared_ptr<arrow::Schema> schema,
373385 std::shared_ptr<arrow::RecordBatch> record_batch, ResultSet& result_set) {
@@ -724,11 +736,26 @@ StatusRecord ReadNextResultsFromStream(StatementHandle& stmt_handle) {
724736 return StatusRecord::ConvertFrom (read_row_status.status ());
725737 }
726738 ReadRowsResponse row = *read_row_status;
727- if (row.has_arrow_record_batch ()) {
739+ if (row.has_arrow_schema () && !stmt_handle.GetArrowSchema ()) {
740+ ResultSet& result_set = stmt_handle.GetResultSet ();
741+ StatusRecordOr<std::shared_ptr<arrow::Schema>> schema_status =
742+ GetArrowSchema (row.arrow_schema (), result_set.row_schema );
743+ if (!schema_status) {
744+ return schema_status.GetStatusRecord ();
745+ }
746+ stmt_handle.SetArrowSchema (*schema_status);
747+ }
748+ if (row.has_arrow_record_batch () &&
749+ !row.arrow_record_batch ().serialized_record_batch ().empty ()) {
728750 // The schema is coming from ResultSet cached in the statement handle.
729751 // We don't want to generate the schema again for every batch since it
730752 // will remain the same.
731753 std::shared_ptr<arrow::Schema> schema = stmt_handle.GetArrowSchema ();
754+ if (!schema) {
755+ return StatusRecord{
756+ SQLStates::k_HY000 (),
757+ " Internal Error: Arrow schema missing for record batch" };
758+ }
732759 StatusRecordOr<std::shared_ptr<arrow::RecordBatch>> record_batch_status =
733760 GetArrowRecordBatch (row.arrow_record_batch (), schema);
734761 if (!record_batch_status) {
@@ -741,22 +768,47 @@ StatusRecord ReadNextResultsFromStream(StatementHandle& stmt_handle) {
741768 // cursor to default.
742769 result_set.cursor = -1 ;
743770 return ProcessRecordBatch (schema, *record_batch_status, result_set);
744- } else {
745- return StatusRecord{
746- SQLStates::k_HY000 (),
747- " Internal Error: cannot find arrow record batch to process!" };
748771 }
749- } else {
750- stmt_handle.ClearReadRowsStream ();
751- stmt_handle.ClearReadRowsIterator ();
752- // Empty result set.
753- stmt_handle.GetResultSet ().rows .clear ();
754- stmt_handle.GetResultSet ().cursor = -1 ;
755- LOG (INFO ) << " FetchBQDataReadArrow:: Read stream ended." ;
772+ ++(*optional_it);
773+ stmt_handle.SetReadRowsIterator (*optional_it);
756774 }
775+
776+ stmt_handle.ClearReadRowsStream ();
777+ stmt_handle.ClearReadRowsIterator ();
778+ // Empty result set.
779+ stmt_handle.GetResultSet ().rows .clear ();
780+ stmt_handle.GetResultSet ().cursor = -1 ;
781+ LOG (INFO ) << " FetchBQDataReadArrow:: Read stream ended." ;
757782 return StatusRecord::Ok ();
758783}
759784
785+ StatusRecord FetchBQDataReadJobArrow (StatementHandle& stmt_handle,
786+ std::string const & project_id,
787+ std::string const & location,
788+ std::string const & job_id,
789+ int64_t offset = 0 ) {
790+ std::string read_stream_name = " projects/" + project_id + " /locations/" +
791+ location + " /jobs/" + job_id +
792+ " /streams/_default" ;
793+
794+ ConnectionHandle& conn_handle = *(stmt_handle.GetConnectionHandle ());
795+ Options options;
796+ options.set <MaxRetriesOption>(conn_handle.GetDsn ().max_retries );
797+ auto bq_client = conn_handle.GetClient ();
798+
799+ ReadRowsRequest read_rows_request;
800+ read_rows_request.set_read_stream (read_stream_name);
801+ if (offset > 0 ) {
802+ read_rows_request.set_offset (offset);
803+ }
804+
805+ StreamRange<google::cloud::bigquery::storage::v1::ReadRowsResponse>
806+ read_rows_stream =
807+ bq_client->GetReadRowsStream (read_rows_request, options);
808+ stmt_handle.SetReadRowsStream (std::move (read_rows_stream));
809+ return ReadNextResultsFromStream (stmt_handle);
810+ }
811+
760812StatusRecord FetchBQDataReadArrow (StatementHandle& stmt_handle,
761813 TableReference& table_ref) {
762814 std::string project_id = table_ref.project_id ;
@@ -874,105 +926,91 @@ StatusRecord CreateLargeDatasetIfNeeded(std::shared_ptr<ODBCBQClient> bq_client,
874926
875927StatusRecord FetchBQDataRead (StatementHandle& stmt_handle,
876928 PostQueryRequest const & post_query_request) {
877- QueryRequest query_request = post_query_request.query_request ();
878- std::string query = query_request.query ();
879- Job job;
880- job.configuration .query .query = query;
881- job.configuration .query .use_query_cache = true ;
882- job.configuration .dry_run = false ;
883- job.configuration .query .allow_large_results = true ;
884- job.configuration .query .use_legacy_sql = false ;
885- job.configuration .query .create_disposition = " CREATE_IF_NEEDED" ;
886- job.configuration .query .write_disposition = " WRITE_TRUNCATE" ;
887- job.configuration .query .query_parameters = query_request.query_parameters ();
888-
889929 ConnectionHandle& conn_handle = *(stmt_handle.GetConnectionHandle ());
890930 auto dsn = conn_handle.GetDsn ();
891- std::string catalog_name = dsn.catalog ;
892- std::string default_dataset = dsn.default_dataset ;
893- if (!default_dataset.empty ()) {
894- job.configuration .query .default_dataset .project_id = catalog_name;
895- job.configuration .query .default_dataset .dataset_id = default_dataset;
896- }
897- job.configuration .query .destination_table .project_id = catalog_name;
898- job.configuration .query .destination_table .dataset_id =
899- dsn.use_default_large_results_dataset ? kDefaultDestDatasetId
900- : dsn.large_results_dataset_id ;
901- std::string table_id = GenerateTableId ();
902- job.configuration .query .destination_table .table_id = table_id;
903-
904- job.job_reference .job_id = " job_" + table_id;
905- job.job_reference .project_id = catalog_name;
906-
907- job.configuration .query .parameter_mode = " POSITIONAL" ;
908- job.configuration .query .allow_large_results = true ;
909-
910931 Options opt;
911932 opt.set <MaxRetriesOption>(dsn.max_retries );
912933 auto bq_client = conn_handle.GetClient ();
913- std::string dataset_location;
914- // Check if the destination dataset (LargeResultsDataSetId) exists.
915- // 1. If it exists, we execute the query job in the same region as the
916- // destination dataset.
917- // 2. If it does not exist (404), we create it in the region where the query
918- // would run
919- // (determined by the dry-run, which resolves to the DefaultDataset region,
920- // source tables region, or defaults to US). Subsequent runs will find the
921- // dataset and execute in its region.
922- auto response = bq_client->GetDataset (
923- catalog_name, job.configuration .query .destination_table .dataset_id , opt);
924- if (!response.Ok ()) {
925- StatusRecord err_status = response.GetStatusRecord ();
926- if (err_status.native_error_code == 404 ) {
927- dataset_location = query_request.location ();
928- // We need to first create large results dataset if it was not there
929- StatusRecord create_dataset_status = CreateLargeDatasetIfNeeded (
930- bq_client, dsn.catalog ,
931- job.configuration .query .destination_table .dataset_id ,
932- dataset_location, dsn.large_table_expiration_time , opt);
933- if (!create_dataset_status.ok ()) {
934- return create_dataset_status;
935- }
936- } else {
937- return err_status;
938- }
939- } else {
940- dataset_location = response->location ;
934+ if (!bq_client) {
935+ LOG (ERROR ) << " FetchBQDataRead:: Invalid or null BQ Client within the "
936+ " connection handle." ;
937+ return StatusRecord{
938+ SQLStates::k_HY000 (),
939+ " Invalid or null BQ Client within the connection handle" };
941940 }
942941
943- job.job_reference .location = dataset_location;
942+ auto pq_status = PostQueryWithoutResults (conn_handle, post_query_request);
943+ if (!pq_status.Ok ()) {
944+ return pq_status.GetStatusRecord ();
945+ }
944946
945- // Insert job
946- auto insert_response = bq_client->InsertJob (dsn.catalog , job, opt);
947- if (!insert_response.Ok ()) {
948- return insert_response.GetStatusRecord ();
947+ std::string project_id = pq_status->job_reference .project_id ;
948+ std::string job_id = pq_status->job_reference .job_id ;
949+ std::string location = pq_status->job_reference .location ;
950+ if (location.empty ()) {
951+ location = post_query_request.query_request ().location ();
949952 }
950- // Here we are replacing the dry run Job created during SQLPrepare.
951- // This should be safe since the same query is executed during HTAPI flow too.
952- stmt_handle.SetPreparedJob (*insert_response);
953-
954- // Wait for Job to complete
955- std::string job_status = insert_response->status .state ;
956- ExponentialBackoffPolicy backoff (chrono_ms (100 ), chrono_ms (200 ), 2 );
957- StatusRecordOr<Job> get_job_response = insert_response;
958- while (job_status != " DONE" ) {
959- std::this_thread::sleep_for (backoff.OnCompletion ());
960- get_job_response = bq_client->GetJob (
961- conn_handle.GetDsn ().catalog , insert_response->job_reference .job_id ,
962- insert_response->job_reference .location , opt);
963- if (!get_job_response.Ok ()) {
964- return get_job_response.GetStatusRecord ();
953+ if (location.empty ()) {
954+ location = " US" ;
955+ }
956+
957+ stmt_handle.GetPagingInfo ().job_id = job_id;
958+
959+ if (!pq_status->job_complete ) {
960+ ExponentialBackoffPolicy backoff (chrono_ms (100 ), chrono_ms (200 ), 2 );
961+ StatusRecordOr<Job> get_job_response;
962+ std::string job_state = " RUNNING" ;
963+ while (job_state != " DONE" ) {
964+ std::this_thread::sleep_for (backoff.OnCompletion ());
965+ get_job_response = bq_client->GetJob (project_id, job_id, location, opt);
966+ if (!get_job_response.Ok ()) {
967+ return get_job_response.GetStatusRecord ();
968+ }
969+ job_state = get_job_response->status .state ;
970+ }
971+ if (!get_job_response->status .error_result .message .empty ()) {
972+ LOG (ERROR ) << " FetchBQDataRead:: "
973+ << get_job_response->status .error_result .message ;
974+ return StatusRecord{SQLStates::k_HY000 (),
975+ get_job_response->status .error_result .message };
976+ }
977+ if (location.empty () && !get_job_response->job_reference .location .empty ()) {
978+ location = get_job_response->job_reference .location ;
965979 }
966- job_status = get_job_response->status .state ;
967980 }
968- std::string error_message = get_job_response->status .error_result .message ;
969- if (!error_message.empty ()) {
970- LOG (ERROR ) << " FetchBQDataRead:: " << error_message;
971- return StatusRecord{SQLStates::k_HY000 (), error_message};
981+
982+ int64_t fetched_rows = 0 ;
983+ if (!pq_status->arrow_schema .serialized_schema .empty () &&
984+ !pq_status->arrow_record_batch .serialized_record_batch .empty ()) {
985+ auto & result_set = stmt_handle.GetResultSet ();
986+ auto schema_status = GetArrowSchemaFromBytes (
987+ pq_status->arrow_schema .serialized_schema , result_set.row_schema );
988+ if (schema_status.Ok ()) {
989+ auto schema = *schema_status;
990+ stmt_handle.SetArrowSchema (schema);
991+ auto batch_status = GetArrowRecordBatchFromBytes (
992+ pq_status->arrow_record_batch .serialized_record_batch , schema);
993+ if (batch_status.Ok ()) {
994+ auto batch = *batch_status;
995+ fetched_rows = batch->num_rows ();
996+ result_set.cursor = -1 ;
997+ auto process_status = ProcessRecordBatch (schema, batch, result_set);
998+ if (!process_status.ok ()) {
999+ return process_status;
1000+ }
1001+ if (pq_status->job_complete && pq_status->page_token .empty () &&
1002+ static_cast <uint64_t >(result_set.rows .size ()) >=
1003+ pq_status->total_rows ) {
1004+ stmt_handle.ClearReadRowsStream ();
1005+ stmt_handle.ClearReadRowsIterator ();
1006+ return StatusRecord::Ok ();
1007+ }
1008+ }
1009+ }
9721010 }
9731011
974- return FetchBQDataReadArrow (
975- stmt_handle, insert_response-> configuration . query . destination_table );
1012+ return FetchBQDataReadJobArrow (stmt_handle, project_id, location, job_id,
1013+ /* offset= */ fetched_rows );
9761014}
9771015
9781016#endif // (!defined(_WIN32) || defined(_WIN64)) && !defined(NO_ARROW)
0 commit comments