| // Licensed to the Apache Software Foundation (ASF) under one or more | |
| // contributor license agreements. See the NOTICE file distributed with | |
| // this work for additional information regarding copyright ownership. | |
| // The ASF licenses this file to You under the Apache License, Version 2.0 | |
| // (the "License"); you may not use this file except in compliance with | |
| // the License. You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| use std::sync::{Arc, Mutex as SyncMutex}; | |
| use arrow::{ | |
| datatypes::{Field, FieldRef, Fields, Schema, SchemaRef}, | |
| record_batch::RecordBatch, | |
| row::{RowConverter, Rows, SortField}, | |
| }; | |
| use datafusion::{ | |
| common::Result, | |
| physical_expr::{PhysicalExprRef, PhysicalSortExpr}, | |
| }; | |
| use crate::window::WindowExpr; | |
| #[derive(Debug)] | |
| pub struct WindowContext { | |
| pub window_exprs: Vec<WindowExpr>, | |
| pub partition_spec: Vec<PhysicalExprRef>, | |
| pub order_spec: Vec<PhysicalSortExpr>, | |
| pub group_limit: Option<usize>, | |
| pub output_window_cols: bool, | |
| pub input_schema: SchemaRef, | |
| pub output_schema: SchemaRef, | |
| pub partition_schema: SchemaRef, | |
| pub order_schema: SchemaRef, | |
| pub partition_row_converter: Arc<SyncMutex<RowConverter>>, | |
| pub order_row_converter: Arc<SyncMutex<RowConverter>>, | |
| } | |
| impl WindowContext { | |
| pub fn try_new( | |
| input_schema: SchemaRef, | |
| window_exprs: Vec<WindowExpr>, | |
| partition_spec: Vec<PhysicalExprRef>, | |
| order_spec: Vec<PhysicalSortExpr>, | |
| group_limit: Option<usize>, | |
| output_window_cols: bool, | |
| ) -> Result<Self> { | |
| let output_schema = Arc::new(Schema::new( | |
| vec![ | |
| input_schema.fields().to_vec(), | |
| if output_window_cols { | |
| window_exprs | |
| .iter() | |
| .map(|expr: &WindowExpr| expr.field.clone()) | |
| .collect::<Vec<FieldRef>>() | |
| } else { | |
| vec![] | |
| }, | |
| ] | |
| .concat(), | |
| )); | |
| let partition_schema = Arc::new(Schema::new( | |
| partition_spec | |
| .iter() | |
| .map(|expr: &PhysicalExprRef| { | |
| Ok(Field::new( | |
| "__partition_col__", | |
| expr.data_type(&input_schema)?, | |
| expr.nullable(&input_schema)?, | |
| )) | |
| }) | |
| .collect::<Result<Fields>>()?, | |
| )); | |
| let order_schema = Arc::new(Schema::new( | |
| order_spec | |
| .iter() | |
| .map(|expr: &PhysicalSortExpr| { | |
| Ok(Field::new( | |
| "__order_col__", | |
| expr.expr.data_type(&input_schema)?, | |
| expr.expr.nullable(&input_schema)?, | |
| )) | |
| }) | |
| .collect::<Result<Fields>>()?, | |
| )); | |
| let partition_row_converter = Arc::new(SyncMutex::new(RowConverter::new( | |
| partition_schema | |
| .fields() | |
| .iter() | |
| .map(|field: &FieldRef| SortField::new(field.data_type().clone())) | |
| .collect::<_>(), | |
| )?)); | |
| let order_row_converter = Arc::new(SyncMutex::new(RowConverter::new( | |
| order_schema | |
| .fields() | |
| .iter() | |
| .zip(&order_spec) | |
| .map(|(field, order)| { | |
| SortField::new_with_options(field.data_type().clone(), order.options) | |
| }) | |
| .collect(), | |
| )?)); | |
| Ok(Self { | |
| window_exprs, | |
| partition_spec, | |
| order_spec, | |
| group_limit, | |
| output_window_cols, | |
| input_schema, | |
| output_schema, | |
| partition_schema, | |
| order_schema, | |
| partition_row_converter, | |
| order_row_converter, | |
| }) | |
| } | |
| pub fn has_partition(&self) -> bool { | |
| !self.partition_schema.fields().is_empty() | |
| } | |
| pub fn get_partition_rows(&self, batch: &RecordBatch) -> Result<Rows> { | |
| Ok(self | |
| .partition_row_converter | |
| .lock() | |
| .expect("partition_row_converter lock poisoned") | |
| .convert_columns( | |
| &self | |
| .partition_spec | |
| .iter() | |
| .map(|expr: &PhysicalExprRef| { | |
| expr.evaluate(batch) | |
| .and_then(|v| v.into_array(batch.num_rows())) | |
| }) | |
| .collect::<Result<Vec<_>>>()?, | |
| )?) | |
| } | |
| pub fn get_order_rows(&self, batch: &RecordBatch) -> Result<Rows> { | |
| Ok(self | |
| .order_row_converter | |
| .lock() | |
| .expect("order_row_converter lock poisoned") | |
| .convert_columns( | |
| &self | |
| .order_spec | |
| .iter() | |
| .map(|expr: &PhysicalSortExpr| { | |
| expr.expr | |
| .evaluate(batch) | |
| .and_then(|v| v.into_array(batch.num_rows())) | |
| }) | |
| .collect::<Result<Vec<_>>>()?, | |
| )?) | |
| } | |
| pub fn requires_full_partition(&self) -> bool { | |
| self.window_exprs | |
| .iter() | |
| .any(|expr| expr.requires_full_partition()) | |
| } | |
| } |