├── rustfmt.toml ├── .gitignore ├── README.md ├── Cargo.toml ├── .pre-commit-config.yaml ├── src ├── lib.rs ├── log.rs ├── cache.rs └── aggregate.rs ├── examples └── demo.rs ├── LICENSE └── Cargo.lock /rustfmt.toml: -------------------------------------------------------------------------------- 1 | max_width = 120 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /debug.rs 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Datafusion Query Cache 2 | 3 | Project moved to [github.com/datafusion-contrib/datafusion-query-cache](https://github.com/datafusion-contrib/datafusion-query-cache). 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "datafusion-query-cache" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | async-trait = "0.1.83" 8 | #datafusion = "42.0.0" 9 | datafusion = {git = "https://github.com/apache/datafusion.git"} 10 | futures = "0.3.31" 11 | # TODo this hsould be feature flagged 12 | termcolor = "1.4.1" 13 | 14 | [dev-dependencies] 15 | chrono = "0.4.38" 16 | tokio = { version = "1.40.0", features = ["full"] } 17 | 18 | [lints.clippy] 19 | dbg_macro = "deny" 20 | pedantic = { level = "deny", priority = -1 } 21 | module_name_repetitions = "allow" 22 | must_use_candidate = "allow" 23 | return_self_not_must_use = "allow" 24 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | fail_fast: true 2 | 3 | repos: 4 | - repo: https://github.com/pre-commit/pre-commit-hooks 5 | rev: v4.0.1 6 | hooks: 7 | - id: check-yaml 8 | - id: check-toml 9 | - id: end-of-file-fixer 10 | - id: trailing-whitespace 11 | - id: check-added-large-files 12 | 13 | - repo: local 14 | hooks: 15 | - id: format 16 | name: Format 17 | entry: cargo fmt 18 | types: [rust] 19 | language: system 20 | pass_filenames: false 21 | - id: clippy 22 | name: Clippy 23 | entry: cargo clippy -- -D warnings 24 | types: [rust] 25 | language: system 26 | pass_filenames: false 27 | # - id: test 28 | # name: Test 29 | # entry: cargo test 30 | # types: [rust] 31 | # language: system 32 | # pass_filenames: false 33 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | mod aggregate; 2 | mod cache; 3 | mod log; 4 | 5 | use std::collections::HashSet; 6 | use std::sync::Arc; 7 | 8 | use async_trait::async_trait; 9 | use datafusion::common::{Column, Result as DataFusionResult}; 10 | use datafusion::execution::context::QueryPlanner; 11 | use datafusion::execution::{SessionState, SessionStateBuilder}; 12 | use datafusion::logical_expr::LogicalPlan; 13 | use datafusion::physical_plan::ExecutionPlan; 14 | use datafusion::physical_planner::{DefaultPhysicalPlanner, ExtensionPlanner, PhysicalPlanner}; 15 | 16 | use aggregate::{QCAggregateExecPlanner, QCAggregateOptimizerRule}; 17 | pub use cache::MemoryQueryCache; 18 | use cache::QueryCache; 19 | pub use log::{LogNoOp, LogStderrColors}; 20 | 21 | #[derive(Debug)] 22 | pub struct QueryCacheConfig { 23 | default_temporal_column: Column, 24 | temporal_columns: HashSet, 25 | group_by_functions: HashSet, 26 | override_now: Option, 27 | cache: Arc, 28 | } 29 | 30 | impl QueryCacheConfig { 31 | pub fn new(default_temporal_column: Column, cache: Arc) -> Self { 32 | let temporal_columns = HashSet::from([default_temporal_column.clone()]); 33 | Self { 34 | default_temporal_column, 35 | temporal_columns, 36 | override_now: None, 37 | group_by_functions: HashSet::new(), 38 | cache, 39 | } 40 | } 41 | 42 | pub fn with_temporal_column(mut self, column: Column) -> Self { 43 | self.temporal_columns.insert(column); 44 | self 45 | } 46 | 47 | pub fn with_override_now(mut self, timestamp: Option) -> Self { 48 | self.override_now = timestamp; 49 | self 50 | } 51 | 52 | pub fn with_group_by_function(mut self, function: impl Into) -> Self { 53 | self.group_by_functions.insert(function.into()); 54 | self 55 | } 56 | 57 | pub(crate) fn default_temporal_column(&self) -> &Column { 58 | &self.default_temporal_column 59 | } 60 | 61 | pub(crate) fn allow_group_by_function(&self, function: &str) -> bool { 62 | self.group_by_functions.contains(function) 63 | } 64 | 65 | pub(crate) fn allow_temporal_column(&self, column: &Column) -> bool { 66 | self.temporal_columns.contains(column) 67 | } 68 | 69 | pub fn cache(&self) -> &Arc { 70 | &self.cache 71 | } 72 | } 73 | 74 | pub fn with_query_cache(builder: SessionStateBuilder, config: QueryCacheConfig) -> SessionStateBuilder { 75 | with_query_cache_log(builder, config, LogNoOp) 76 | } 77 | 78 | pub fn with_query_cache_log( 79 | builder: SessionStateBuilder, 80 | config: QueryCacheConfig, 81 | log: Log, 82 | ) -> SessionStateBuilder { 83 | let config = Arc::new(config); 84 | builder 85 | .with_query_planner(Arc::new(QueryCacheQueryPlanner::new(log.clone(), config.clone()))) 86 | .with_optimizer_rule(Arc::new(QCAggregateOptimizerRule::new(log, config))) 87 | } 88 | 89 | #[derive(Debug)] 90 | struct QueryCacheQueryPlanner { 91 | log: Log, 92 | config: Arc, 93 | } 94 | 95 | impl QueryCacheQueryPlanner { 96 | pub fn new(log: Log, config: Arc) -> Self { 97 | Self { log, config } 98 | } 99 | } 100 | 101 | #[async_trait] 102 | impl QueryPlanner for QueryCacheQueryPlanner { 103 | async fn create_physical_plan( 104 | &self, 105 | logical_plan: &LogicalPlan, 106 | session_state: &SessionState, 107 | ) -> DataFusionResult> { 108 | let planners: Vec> = vec![Arc::new(QCAggregateExecPlanner::new( 109 | self.log.clone(), 110 | self.config.clone(), 111 | ))]; 112 | 113 | DefaultPhysicalPlanner::with_extension_planners(planners) 114 | .create_physical_plan(logical_plan, session_state) 115 | .await 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/log.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::io::{Result as IoResult, Write}; 3 | use std::sync::{Arc, Mutex}; 4 | 5 | use datafusion::common::{DataFusionError, Result as DataFusionResult}; 6 | 7 | /// Represents a log of query caching operations. 8 | /// 9 | /// Logs should be cheap to clone, e.g. they should contain an Arc to the actual log data if applicable. 10 | pub trait AbstractLog: Send + Sync + fmt::Debug + Clone + 'static { 11 | fn log(&self, level: Level, query_fingerprint: &str, message: impl fmt::Display) -> DataFusionResult<()>; 12 | 13 | fn info(&self, query_fingerprint: &str, message: impl fmt::Display) -> DataFusionResult<()> { 14 | self.log(Level::Info, query_fingerprint, message) 15 | } 16 | 17 | fn warn(&self, query_fingerprint: &str, message: impl fmt::Display) -> DataFusionResult<()> { 18 | self.log(Level::Warn, query_fingerprint, message) 19 | } 20 | 21 | /// Returns the log of query caching operations if one is recorded. 22 | fn history(&self) -> Option> { 23 | None 24 | } 25 | } 26 | 27 | #[derive(Debug, Clone, Copy)] 28 | pub enum Level { 29 | Info, 30 | Warn, 31 | } 32 | 33 | #[allow(dead_code)] 34 | #[derive(Debug)] 35 | pub struct QueryLog { 36 | query_fingerprint: String, 37 | log: Vec<(Level, String)>, 38 | } 39 | 40 | #[derive(Debug, Clone)] 41 | pub struct LogNoOp; 42 | 43 | impl AbstractLog for LogNoOp { 44 | fn log(&self, _level: Level, _query_fingerprint: &str, _message: impl fmt::Display) -> DataFusionResult<()> { 45 | // Do nothing 46 | Ok(()) 47 | } 48 | } 49 | 50 | #[derive(Debug, Clone, Default)] 51 | pub struct LogStderrColors { 52 | last_fingerprint: Arc>, 53 | } 54 | 55 | impl LogStderrColors { 56 | fn log_inner(&self, level: Level, query_fingerprint: &str, message: impl fmt::Display) -> IoResult<()> { 57 | // here so it works with features 58 | use termcolor::{BufferWriter, Color, ColorChoice, ColorSpec, WriteColor}; 59 | 60 | let mut last_fingerprint = self.last_fingerprint.lock().unwrap(); 61 | 62 | let buf_writer = BufferWriter::stderr(ColorChoice::Auto); 63 | let mut buffer = buf_writer.buffer(); 64 | if *last_fingerprint != query_fingerprint { 65 | *last_fingerprint = query_fingerprint.to_string(); 66 | buffer.set_color(ColorSpec::new().set_fg(Some(Color::Green)))?; 67 | writeln!(&mut buffer, "{query_fingerprint}")?; 68 | } 69 | 70 | match level { 71 | Level::Info => { 72 | buffer.set_color(ColorSpec::new().set_fg(Some(Color::Blue)))?; 73 | write!(&mut buffer, " {message}")?; 74 | } 75 | Level::Warn => { 76 | buffer.set_color(ColorSpec::new().set_fg(Some(Color::Yellow)))?; 77 | write!(&mut buffer, " [WARN] {message}")?; 78 | } 79 | } 80 | writeln!(&mut buffer)?; 81 | buffer.reset()?; 82 | buf_writer.print(&buffer) 83 | } 84 | } 85 | 86 | impl AbstractLog for LogStderrColors { 87 | fn log(&self, level: Level, query_fingerprint: &str, message: impl fmt::Display) -> DataFusionResult<()> { 88 | self.log_inner(level, query_fingerprint, message) 89 | .map_err(|e| DataFusionError::Internal(format!("Failed to write to stderr: {e}"))) 90 | } 91 | } 92 | 93 | macro_rules! log_info { 94 | ($log:expr, $query_fingerprint:expr, $message:expr) => { 95 | $log.info($query_fingerprint, $message)?; 96 | }; 97 | 98 | ($log:expr, $query_fingerprint:expr, $message:expr, $( $msg_args:expr ),+ ) => { 99 | $log.info($query_fingerprint, format!($message, $( $msg_args ),+))?; 100 | }; 101 | } 102 | pub(crate) use log_info; 103 | 104 | macro_rules! log_warn { 105 | ($log:expr, $query_fingerprint:expr, $message:expr) => { 106 | $log.warn($query_fingerprint, $message)?; 107 | }; 108 | 109 | ($log:expr, $query_fingerprint:expr, $message:expr, $( $msg_args:expr ),+ ) => { 110 | $log.warn($query_fingerprint, format!($message, $( $msg_args ),+))?; 111 | }; 112 | } 113 | pub(crate) use log_warn; 114 | -------------------------------------------------------------------------------- /examples/demo.rs: -------------------------------------------------------------------------------- 1 | use chrono::{DateTime, FixedOffset}; 2 | use datafusion::arrow::array::{Int64Array, RecordBatch, StringArray, TimestampNanosecondArray}; 3 | use datafusion::arrow::datatypes::{DataType, Field, Schema, TimeUnit}; 4 | use datafusion::arrow::util::pretty::print_batches; 5 | use datafusion::common::Column; 6 | use datafusion::datasource::MemTable; 7 | use datafusion::execution::runtime_env::RuntimeEnv; 8 | use datafusion::execution::SessionStateBuilder; 9 | use datafusion::prelude::{SessionConfig, SessionContext}; 10 | use datafusion_query_cache::{with_query_cache_log, LogStderrColors, MemoryQueryCache, QueryCacheConfig}; 11 | use std::sync::Arc; 12 | 13 | #[tokio::main] 14 | async fn main() { 15 | let cache = Arc::new(MemoryQueryCache::default()); 16 | 17 | let divide = DateTime::parse_from_rfc3339("2024-01-01T17:18:19Z").unwrap(); 18 | let batch1 = create_data(DateTime::parse_from_rfc3339("2024-01-01T00:00:00Z").unwrap(), divide); 19 | 20 | let ctx = session_ctx(cache.clone(), divide.timestamp_nanos_opt()).await; 21 | let table = MemTable::try_new(batch1.schema(), vec![vec![batch1.clone()]]).unwrap(); 22 | ctx.register_table("records", Arc::new(table)).unwrap(); 23 | 24 | // let sql = "SELECT date_trunc('hour', timestamp), round(avg(value), 2), count(*) from records where value>1 group by 1 order by 1 desc"; 25 | 26 | let sql = "SELECT round(avg(value), 2), count(*) from records where value>1"; 27 | 28 | let batches = ctx.sql(sql).await.unwrap().collect().await.unwrap(); 29 | println!("first run:"); 30 | print_batches(&batches).unwrap(); 31 | 32 | let batch2 = create_data(divide, DateTime::parse_from_rfc3339("2024-01-02T00:00:00Z").unwrap()); 33 | 34 | let ctx = session_ctx(cache.clone(), None).await; 35 | let partitions = vec![vec![batch1.clone(), batch2]]; 36 | let table = MemTable::try_new(batch1.schema(), partitions.clone()).unwrap(); 37 | ctx.register_table("records", Arc::new(table)).unwrap(); 38 | 39 | let batches = ctx.sql(sql).await.unwrap().collect().await.unwrap(); 40 | println!("second run (more data added):"); 41 | print_batches(&batches).unwrap(); 42 | 43 | let ctx_simple = SessionContext::new(); 44 | let table = MemTable::try_new(batch1.schema(), partitions.clone()).unwrap(); 45 | ctx_simple.register_table("records", Arc::new(table)).unwrap(); 46 | // dbg!(ctx_simple.sql(sql).await.unwrap().create_physical_plan().await.unwrap()); 47 | 48 | let batches = ctx_simple.sql(sql).await.unwrap().collect().await.unwrap(); 49 | println!("second run with no caching:"); 50 | print_batches(&batches).unwrap(); 51 | 52 | let sql = format!("EXPLAIN ANALYZE {sql}"); 53 | let df = ctx.sql(&sql).await.unwrap(); 54 | let batches = df.collect().await.unwrap(); 55 | 56 | // second column, first value is the plan 57 | let plan = batches[0] 58 | .column(1) 59 | .as_any() 60 | .downcast_ref::() 61 | .unwrap() 62 | .value(0); 63 | println!("\nEXPLAIN ANALYZE:\n{}", plan); 64 | 65 | // println!("{}", cache.display()); 66 | } 67 | 68 | async fn session_ctx(cache: Arc, override_now: Option) -> SessionContext { 69 | let config = SessionConfig::new().with_target_partitions(10); 70 | let runtime = Arc::new(RuntimeEnv::default()); 71 | let state_builder = SessionStateBuilder::new() 72 | .with_config(config) 73 | .with_runtime_env(runtime) 74 | .with_default_features(); 75 | 76 | let sort_col = Column::new(Some("records".to_string()), "timestamp".to_string()); 77 | let query_cache_config = QueryCacheConfig::new(sort_col, cache) 78 | .with_group_by_function("date_trunc") 79 | .with_override_now(override_now); 80 | 81 | let log = LogStderrColors::default(); 82 | let state_builder = with_query_cache_log(state_builder, query_cache_config, log); 83 | SessionContext::new_with_state(state_builder.build()) 84 | } 85 | 86 | fn create_data(start: DateTime, stop: DateTime) -> RecordBatch { 87 | let schema = Arc::new(Schema::new(vec![ 88 | Field::new("timestamp", DataType::Timestamp(TimeUnit::Nanosecond, None), false), 89 | Field::new("service", DataType::Utf8, true), 90 | Field::new("value", DataType::Int64, true), 91 | ])); 92 | 93 | let mut timestamp = start.timestamp_nanos_opt().unwrap(); 94 | let mut timestamps = Vec::new(); 95 | let mut service_names = Vec::new(); 96 | let mut values = Vec::new(); 97 | 98 | let end = stop.timestamp_nanos_opt().unwrap(); 99 | 100 | let mut seed = 0; 101 | loop { 102 | timestamps.push(timestamp); 103 | timestamp += 1_000_000_000; 104 | service_names.push(SERVICES[usize::try_from(seed).unwrap() % 5]); 105 | values.push(seed); 106 | if timestamp >= end { 107 | break; 108 | } 109 | seed += 1; 110 | } 111 | 112 | RecordBatch::try_new( 113 | schema, 114 | vec![ 115 | Arc::new(TimestampNanosecondArray::from(timestamps)), 116 | Arc::new(StringArray::from(service_names)), 117 | Arc::new(Int64Array::from(values)), 118 | ], 119 | ) 120 | .unwrap() 121 | } 122 | 123 | const SERVICES: [&str; 5] = ["foo", "bar", "baz", "qux", "quux"]; 124 | -------------------------------------------------------------------------------- /src/cache.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::fmt; 3 | use std::sync::{Arc, Mutex}; 4 | 5 | use async_trait::async_trait; 6 | use datafusion::arrow::array::RecordBatch; 7 | use datafusion::arrow::util::pretty::pretty_format_batches; 8 | use datafusion::common::Result as DataFusionResult; 9 | 10 | #[async_trait] 11 | pub trait QueryCache: Send + Sync + fmt::Debug { 12 | async fn entry(&self, query_fingerprint: &str) -> DataFusionResult; 13 | } 14 | 15 | #[derive(Debug, Clone)] 16 | pub enum CacheEntry { 17 | Occupied(Arc), 18 | Vacant(Arc), 19 | } 20 | 21 | impl CacheEntry { 22 | pub fn occupied(&self) -> bool { 23 | matches!(self, CacheEntry::Occupied(_)) 24 | } 25 | 26 | pub async fn put(&self, timestamp: i64, record_batch: &[RecordBatch]) -> DataFusionResult<()> { 27 | match self { 28 | CacheEntry::Occupied(entry) => entry.put(timestamp, record_batch).await, 29 | CacheEntry::Vacant(entry) => entry.put(timestamp, record_batch).await, 30 | } 31 | } 32 | } 33 | 34 | #[async_trait] 35 | pub trait OccupiedCacheEntry: Send + Sync + fmt::Debug { 36 | /// The timestamp of the cache entry - e.g. when data was written to the cache. 37 | fn timestamp(&self) -> i64; 38 | 39 | /// Returns the record batch stored in this cache entry. 40 | async fn get(&self) -> DataFusionResult<&[RecordBatch]>; 41 | 42 | /// Updates the record batch stored in this cache entry with a new timestamp. 43 | async fn put(&self, timestamp: i64, record_batch: &[RecordBatch]) -> DataFusionResult<()>; 44 | } 45 | 46 | #[async_trait] 47 | pub trait VacantCacheEntry: Send + Sync + fmt::Debug { 48 | /// Set the record batch stored in this cache entry. 49 | async fn put(&self, timestamp: i64, record_batch: &[RecordBatch]) -> DataFusionResult<()>; 50 | } 51 | 52 | #[derive(Clone, Default)] 53 | #[allow(clippy::type_complexity)] 54 | pub struct MemoryQueryCache { 55 | cache: Arc>)>>>, 56 | } 57 | 58 | impl fmt::Debug for MemoryQueryCache { 59 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 60 | #[derive(Debug)] 61 | #[allow(dead_code)] 62 | struct MemoryQueryCacheEntry { 63 | timestamp: i64, 64 | num_rows: usize, 65 | } 66 | struct Entries<'a>(&'a MemoryQueryCache); 67 | 68 | impl<'a> fmt::Debug for Entries<'a> { 69 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 70 | let binding = self.0.cache.lock().unwrap(); 71 | let entries = binding.iter().map(|(fingerprint, (ts, rb))| { 72 | ( 73 | fingerprint, 74 | MemoryQueryCacheEntry { 75 | timestamp: *ts, 76 | num_rows: rb.iter().map(RecordBatch::num_rows).sum(), 77 | }, 78 | ) 79 | }); 80 | f.debug_map().entries(entries).finish() 81 | } 82 | } 83 | 84 | f.debug_struct("MemoryQueryCache") 85 | .field("entries", &Entries(self)) 86 | .finish() 87 | } 88 | } 89 | 90 | impl MemoryQueryCache { 91 | pub fn display(&self) -> String { 92 | struct DisplayMemoryQueryCache<'a>(&'a MemoryQueryCache); 93 | 94 | impl fmt::Display for DisplayMemoryQueryCache<'_> { 95 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 96 | writeln!(f, "## MemoryQueryCache:")?; 97 | for (fingerprint, (timestamp, record_batch)) in self.0.cache.lock().unwrap().iter() { 98 | let table = pretty_format_batches(record_batch).map_err(|_| fmt::Error)?; 99 | writeln!(f, "{fingerprint}\ntimestamp: {timestamp} data:\n{table}")?; 100 | } 101 | Ok(()) 102 | } 103 | } 104 | 105 | DisplayMemoryQueryCache(self).to_string() 106 | } 107 | } 108 | 109 | impl MemoryQueryCache { 110 | fn put(&self, fingerprint: &str, timestamp: i64, record_batch: &[RecordBatch]) { 111 | let mut cache = self.cache.lock().unwrap(); 112 | cache.insert(fingerprint.to_owned(), (timestamp, Arc::new(record_batch.to_vec()))); 113 | } 114 | } 115 | 116 | #[async_trait] 117 | impl QueryCache for MemoryQueryCache { 118 | async fn entry(&self, query_fingerprint: &str) -> DataFusionResult { 119 | let cache = self.cache.lock().unwrap(); 120 | if let Some((timestamp, record_batch)) = cache.get(query_fingerprint).cloned() { 121 | let entry = OccupiedMemoryCacheEntry { 122 | fingerprint: query_fingerprint.to_string(), 123 | timestamp, 124 | record_batch, 125 | cache: self.clone(), 126 | }; 127 | Ok(CacheEntry::Occupied(Arc::new(entry))) 128 | } else { 129 | let entry = VacantMemoryCacheEntry { 130 | fingerprint: query_fingerprint.to_string(), 131 | cache: self.clone(), 132 | }; 133 | Ok(CacheEntry::Vacant(Arc::new(entry))) 134 | } 135 | } 136 | } 137 | 138 | #[derive(Debug)] 139 | struct OccupiedMemoryCacheEntry { 140 | fingerprint: String, 141 | timestamp: i64, 142 | record_batch: Arc>, 143 | cache: MemoryQueryCache, 144 | } 145 | 146 | #[async_trait] 147 | impl OccupiedCacheEntry for OccupiedMemoryCacheEntry { 148 | fn timestamp(&self) -> i64 { 149 | self.timestamp 150 | } 151 | 152 | async fn get(&self) -> DataFusionResult<&[RecordBatch]> { 153 | Ok(&self.record_batch) 154 | } 155 | 156 | async fn put(&self, timestamp: i64, record_batch: &[RecordBatch]) -> DataFusionResult<()> { 157 | self.cache.put(&self.fingerprint, timestamp, record_batch); 158 | Ok(()) 159 | } 160 | } 161 | 162 | #[derive(Debug)] 163 | struct VacantMemoryCacheEntry { 164 | fingerprint: String, 165 | cache: MemoryQueryCache, 166 | } 167 | 168 | #[async_trait] 169 | impl VacantCacheEntry for VacantMemoryCacheEntry { 170 | async fn put(&self, timestamp: i64, record_batch: &[RecordBatch]) -> DataFusionResult<()> { 171 | self.cache.put(&self.fingerprint, timestamp, record_batch); 172 | Ok(()) 173 | } 174 | } 175 | 176 | // TODO disk cache 177 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/aggregate.rs: -------------------------------------------------------------------------------- 1 | use std::any::Any; 2 | use std::borrow::Cow; 3 | use std::collections::HashSet; 4 | use std::fmt; 5 | use std::fmt::Formatter; 6 | use std::hash::Hash; 7 | use std::sync::Arc; 8 | 9 | use async_trait::async_trait; 10 | use datafusion::arrow::array::RecordBatch; 11 | use datafusion::arrow::datatypes::{DataType, SchemaRef, TimeUnit}; 12 | use datafusion::common::tree_node::Transformed; 13 | use datafusion::common::{internal_err, plan_err, Column, DFSchemaRef, Result as DataFusionResult, ScalarValue}; 14 | use datafusion::execution::{SendableRecordBatchStream, SessionState, TaskContext}; 15 | use datafusion::logical_expr::expr::ScalarFunction; 16 | use datafusion::logical_expr::{ 17 | Aggregate, Between, BinaryExpr, Expr, Extension, Filter, LogicalPlan, Operator, TableScan, UserDefinedLogicalNode, 18 | UserDefinedLogicalNodeCore, 19 | }; 20 | use datafusion::optimizer::optimizer::ApplyOrder; 21 | use datafusion::optimizer::{OptimizerConfig, OptimizerRule}; 22 | use datafusion::physical_expr::expressions::{ 23 | BinaryExpr as PhysicalBinaryExpr, Column as PhysicalColumn, Literal as PhysicalLiteral, 24 | }; 25 | use datafusion::physical_plan::aggregates::{AggregateExec, AggregateMode}; 26 | use datafusion::physical_plan::coalesce_partitions::CoalescePartitionsExec; 27 | use datafusion::physical_plan::filter::FilterExec; 28 | use datafusion::physical_plan::metrics::{BaselineMetrics, ExecutionPlanMetricsSet, MetricsSet}; 29 | use datafusion::physical_plan::stream::RecordBatchStreamAdapter; 30 | use datafusion::physical_plan::union::UnionExec; 31 | use datafusion::physical_plan::{collect, DisplayAs, DisplayFormatType, ExecutionPlan, PlanProperties}; 32 | use datafusion::physical_planner::{ExtensionPlanner, PhysicalPlanner}; 33 | use futures::TryFutureExt; 34 | 35 | use crate::cache::{CacheEntry, OccupiedCacheEntry}; 36 | use crate::log::{log_info, log_warn, AbstractLog}; 37 | use crate::QueryCacheConfig; 38 | 39 | #[derive(Debug)] 40 | pub(crate) struct QCAggregateOptimizerRule { 41 | log: Log, 42 | config: Arc, 43 | } 44 | 45 | impl QCAggregateOptimizerRule { 46 | pub fn new(log: Log, config: Arc) -> Self { 47 | Self { log, config } 48 | } 49 | 50 | /// Find a column used in a group by expression that matches one of our temporal columns 51 | fn find_temporal_group_by(&self, expr: &Expr) -> Option { 52 | let Expr::ScalarFunction(ScalarFunction { func, args }) = expr else { 53 | return None; 54 | }; 55 | if !self.config.allow_group_by_function(func.name()) { 56 | return None; 57 | } 58 | let second_arg = args.get(1)?; 59 | 60 | if let Expr::Column(column) = second_arg { 61 | if self.config.allow_temporal_column(column) { 62 | return Some(column.clone()); 63 | } 64 | } 65 | 66 | None 67 | } 68 | } 69 | impl OptimizerRule for QCAggregateOptimizerRule { 70 | fn name(&self) -> &str { 71 | "query-cache-agg-group-by" 72 | } 73 | 74 | fn apply_order(&self) -> Option { 75 | Some(ApplyOrder::BottomUp) 76 | } 77 | 78 | // Example rewrite pass to insert a user defined LogicalPlanNode 79 | fn rewrite( 80 | &self, 81 | mut plan: LogicalPlan, 82 | _config: &dyn OptimizerConfig, 83 | ) -> DataFusionResult> { 84 | // println!("rewrite -> {}", plan.display()); 85 | let LogicalPlan::Aggregate(agg) = &plan else { 86 | // not an aggregation, continue rewrite 87 | return Ok(Transformed::no(plan)); 88 | }; 89 | let mut fingerprint = plan.display_indent_schema().to_string(); 90 | 91 | let Aggregate { input, group_expr, .. } = agg; 92 | let agg_input = input.as_ref().clone(); 93 | let mut temporal_group_bys = group_expr.iter().filter_map(|e| self.find_temporal_group_by(e)); 94 | 95 | let temporal_group_by = temporal_group_bys.next(); 96 | 97 | if temporal_group_bys.next().is_some() { 98 | // I've no idea if this is even possible, and what we could do if it is, do nothing for now 99 | self.log.info( 100 | &fingerprint, 101 | "multiple group bys using temporal columns, caching not possible!", 102 | )?; 103 | return Ok(Transformed::no(plan)); 104 | } 105 | 106 | let (dynamic_lower_bound, input) = if let LogicalPlan::Filter(filter) = &agg_input { 107 | let needle_columns = if let Some(temporal_group_by) = &temporal_group_by { 108 | Cow::Owned(HashSet::from([temporal_group_by.clone()])) 109 | } else { 110 | Cow::Borrowed(&self.config.temporal_columns) 111 | }; 112 | 113 | let dlb = match DynamicLowerBound::find(&filter.predicate, &needle_columns) { 114 | DynamicLowerBound::Found(bin_expr) => Some(bin_expr), 115 | DynamicLowerBound::Stable => None, 116 | _ => { 117 | // we found an unstable expression, we can't rewrite the plan 118 | self.log 119 | .info(&fingerprint, "we found an unstable expression, caching not possible")?; 120 | return Ok(Transformed::no(plan)); 121 | } 122 | }; 123 | (dlb, filter.input.as_ref().clone()) 124 | } else { 125 | (None, agg_input.clone()) 126 | }; 127 | 128 | if temporal_group_by.is_none() { 129 | // if temporal_group_by is none, we need to make sure the sort column is in the projection 130 | let LogicalPlan::TableScan(scan) = input else { 131 | // TODO we need to support this, e.g. a subquery 132 | self.log 133 | .info(&fingerprint, "input not a table scan, caching not possible")?; 134 | return Ok(Transformed::no(plan)); 135 | }; 136 | // TODO check table name 137 | let field_name = self.config.default_temporal_column().name.clone(); 138 | if !scan.projected_schema.fields().iter().any(|f| f.name() == &field_name) { 139 | let new_col_id = scan 140 | .source 141 | .schema() 142 | .fields() 143 | .iter() 144 | .enumerate() 145 | .find_map(|(id, f)| (f.name() == &field_name).then_some(id)); 146 | 147 | let Some(new_col_id) = new_col_id else { 148 | log_info!( 149 | self.log, 150 | &fingerprint, 151 | "sort column '{}' not found in table, caching not possible", 152 | field_name 153 | ); 154 | return Ok(Transformed::no(plan)); 155 | }; 156 | 157 | let mut new_projection = scan.projection.expect("no projection found"); 158 | new_projection.push(new_col_id); 159 | new_projection.sort_unstable(); 160 | 161 | let new_table_scan = TableScan::try_new( 162 | scan.table_name, 163 | scan.source, 164 | Some(new_projection), 165 | scan.filters, 166 | scan.fetch, 167 | ) 168 | .map(LogicalPlan::TableScan)?; 169 | 170 | let inner_plan = if let LogicalPlan::Filter(filter) = &agg_input { 171 | LogicalPlan::Filter(Filter::try_new(filter.predicate.clone(), Arc::new(new_table_scan))?) 172 | } else { 173 | new_table_scan 174 | }; 175 | plan = LogicalPlan::Aggregate(Aggregate::try_new( 176 | Arc::new(inner_plan), 177 | agg.group_expr.clone(), 178 | agg.aggr_expr.clone(), 179 | )?); 180 | fingerprint = plan.display_indent_schema().to_string(); 181 | } 182 | } 183 | 184 | // if dynamic_lower_bound.is_some() && temporal_group_by.is_none() { 185 | // self.log.info( 186 | // &fingerprint, 187 | // "found a dynamic lower bound but no temporal group by, caching not possible", 188 | // )?; 189 | // return Ok(Transformed::no(plan)); 190 | // } 191 | if dynamic_lower_bound.is_some() { 192 | return plan_err!("dynamic lower bound not yet supported"); 193 | } 194 | 195 | let temporal_column = temporal_group_by.unwrap_or_else(|| self.config.default_temporal_column().clone()); 196 | 197 | // do we need to check the input is a table scan? 198 | log_info!( 199 | self.log, 200 | &fingerprint, 201 | "query valid for caching, sort column {}", 202 | temporal_column 203 | ); 204 | Ok(Transformed::yes(LogicalPlan::Extension(Extension { 205 | node: Arc::new(QCAggregatePlanNode::new( 206 | plan.clone(), 207 | temporal_column, 208 | dynamic_lower_bound, 209 | Some(fingerprint), 210 | )?), 211 | }))) 212 | } 213 | } 214 | 215 | #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd)] 216 | struct QCAggregatePlanNode { 217 | input: LogicalPlan, 218 | fingerprint: String, 219 | temporal_column: Column, 220 | dynamic_lower_bound: Option, 221 | } 222 | 223 | impl fmt::Display for QCAggregatePlanNode { 224 | fn fmt(&self, f: &mut Formatter) -> fmt::Result { 225 | UserDefinedLogicalNodeCore::fmt_for_explain(self, f) 226 | } 227 | } 228 | 229 | /// Placeholder for th cached aggregation in the logical plan, there's no logic here, just boilerplate 230 | impl QCAggregatePlanNode { 231 | fn new( 232 | input: LogicalPlan, 233 | temporal_column: Column, 234 | dynamic_lower_bound: Option, 235 | fingerprint: Option, 236 | ) -> DataFusionResult { 237 | if let LogicalPlan::Extension(e) = input { 238 | if let Some(node) = e.node.as_any().downcast_ref::() { 239 | // already a `QCAggregatePlanNode`, return it 240 | Ok(node.clone()) 241 | } else { 242 | plan_err!("unexpected extension node, expected QCAggregatePlanNode") 243 | } 244 | } else if matches!(input, LogicalPlan::Aggregate(..)) { 245 | let fingerprint = fingerprint.unwrap_or_else(|| input.display_indent_schema().to_string()); 246 | Ok(Self { 247 | input, 248 | fingerprint, 249 | temporal_column, 250 | dynamic_lower_bound, 251 | }) 252 | } else { 253 | plan_err!( 254 | "unexpected input to QCAggregatePlanNode, mut be Aggregate or Extension(QCAggregatePlanNode), got {}", 255 | input.display() 256 | ) 257 | } 258 | } 259 | } 260 | 261 | impl UserDefinedLogicalNodeCore for QCAggregatePlanNode { 262 | fn name(&self) -> &str { 263 | "QueryCacheAggregate" 264 | } 265 | 266 | fn inputs(&self) -> Vec<&LogicalPlan> { 267 | vec![&self.input] 268 | } 269 | 270 | fn schema(&self) -> &DFSchemaRef { 271 | self.input.schema() 272 | } 273 | 274 | fn expressions(&self) -> Vec { 275 | let mut expressions = vec![Expr::Column(self.temporal_column.clone())]; 276 | if let Some(expr) = &self.dynamic_lower_bound { 277 | expressions.push(Expr::BinaryExpr(expr.clone())); 278 | } 279 | expressions 280 | } 281 | 282 | fn fmt_for_explain(&self, f: &mut Formatter) -> fmt::Result { 283 | write!(f, "QueryCacheAggregate: {}", self.input.display()) 284 | } 285 | 286 | fn with_exprs_and_inputs(&self, exprs: Vec, inputs: Vec) -> DataFusionResult { 287 | let mut iter_exprs = exprs.into_iter(); 288 | let Some(Expr::Column(column)) = iter_exprs.next() else { 289 | return plan_err!("UserDefinedLogicalNodeCore expected temporal column as first expressoin"); 290 | }; 291 | let dynamic_lower_bound = if let Some(expr) = iter_exprs.next() { 292 | if iter_exprs.next().is_some() { 293 | return plan_err!("UserDefinedLogicalNodeCore expected one or two expressions"); 294 | } 295 | 296 | if let Expr::BinaryExpr(dlb) = expr { 297 | Some(dlb) 298 | } else { 299 | return plan_err!("UserDefinedLogicalNodeCore expected binary expression as second expression"); 300 | } 301 | } else { 302 | None 303 | }; 304 | 305 | let mut iter_inputs = inputs.into_iter(); 306 | let Some(input) = iter_inputs.next() else { 307 | return plan_err!("UserDefinedLogicalNodeCore expected one inputs"); 308 | }; 309 | if iter_inputs.next().is_some() { 310 | plan_err!("UserDefinedLogicalNodeCore expected one inputs") 311 | } else { 312 | Self::new(input, column, dynamic_lower_bound, None) 313 | } 314 | } 315 | } 316 | 317 | /// A physical planner that knows how to convert a `QCAggregatePlanNode` into physical plan, 318 | /// using `QCInnerAggregateExec`. 319 | #[derive(Debug)] 320 | pub(crate) struct QCAggregateExecPlanner { 321 | log: Log, 322 | config: Arc, 323 | } 324 | 325 | impl QCAggregateExecPlanner { 326 | pub fn new(log: Log, config: Arc) -> Self { 327 | Self { log, config } 328 | } 329 | } 330 | 331 | #[async_trait] 332 | impl ExtensionPlanner for QCAggregateExecPlanner { 333 | async fn plan_extension( 334 | &self, 335 | _planner: &dyn PhysicalPlanner, 336 | node: &dyn UserDefinedLogicalNode, 337 | _logical_inputs: &[&LogicalPlan], 338 | physical_inputs: &[Arc], 339 | session_state: &SessionState, 340 | ) -> DataFusionResult>> { 341 | let Some(agg_node) = node.as_any().downcast_ref::() else { 342 | return Ok(None); 343 | }; 344 | if physical_inputs.len() != 1 { 345 | // maybe Ok(None) is ok here? 346 | return plan_err!("QueryCacheGroupByExec expected one input"); 347 | } 348 | 349 | let exec = physical_inputs[0].clone(); 350 | 351 | if find_existing_inner_exec(&exec) { 352 | // already a `QCInnerAggregateExec` (or contains a `QCInnerAggregateExec`), return it 353 | return Ok(Some(exec)); 354 | } 355 | 356 | let Some(agg_exec): Option<&AggregateExec> = exec.as_any().downcast_ref() else { 357 | // should this be an error? 358 | log_warn!( 359 | self.log, 360 | &agg_node.fingerprint, 361 | "QueryCacheGroupByExec expected an AggregateExec input, found {}", 362 | exec.name() 363 | ); 364 | return Ok(Some(exec)); 365 | }; 366 | 367 | let cache_entry = self.config.cache().entry(&agg_node.fingerprint).await?; 368 | log_info!( 369 | self.log, 370 | &agg_node.fingerprint, 371 | "cache entry hit {:?}", 372 | cache_entry.occupied() 373 | ); 374 | 375 | let now = self.config.override_now.unwrap_or_else(|| { 376 | session_state 377 | .execution_props() 378 | .query_execution_start_time 379 | .timestamp_nanos_opt() 380 | // we'll be in trouble after 2262! 381 | .unwrap() 382 | }); 383 | 384 | let partial_agg_exec = agg_exec.input().clone(); 385 | 386 | let input_exec = match &cache_entry { 387 | CacheEntry::Occupied(entry) => { 388 | let cached_exec = CachedAggregateExec::new_exec_plan(entry.clone(), partial_agg_exec.properties()); 389 | let new_exec = with_lower_bound(&partial_agg_exec, &agg_node.temporal_column, entry.timestamp())?; 390 | 391 | let combined_input = Arc::new(UnionExec::new(vec![cached_exec, new_exec])); 392 | Arc::new(CoalescePartitionsExec::new(combined_input)) 393 | } 394 | CacheEntry::Vacant(_) => partial_agg_exec, 395 | }; 396 | 397 | // whether or not we had a cache hit, we wrap the input in a `CacheUpdateAggregateExec` 398 | // to store the complete (but partial) aggregation 399 | let input_exec = CacheUpdateAggregateExec::new_exec_plan(cache_entry, input_exec, now); 400 | let input_schema = input_exec.schema(); 401 | 402 | Ok(Some(Arc::new(AggregateExec::try_new( 403 | AggregateMode::Final, 404 | agg_exec.group_expr().clone(), 405 | agg_exec.aggr_expr().to_vec(), 406 | agg_exec.filter_expr().to_vec(), 407 | input_exec, 408 | input_schema, 409 | )?))) 410 | } 411 | } 412 | 413 | /// apply a lower bound to an `AggregateExec` 414 | fn with_lower_bound( 415 | partial_agg_exec: &Arc, 416 | bound_column: &Column, 417 | lower_bound_ns: i64, 418 | ) -> DataFusionResult> { 419 | let Some(agg_exec): Option<&AggregateExec> = partial_agg_exec.as_any().downcast_ref() else { 420 | return plan_err!("expected an AggregateExec input, found {}", partial_agg_exec.name()); 421 | }; 422 | 423 | let find_column = agg_exec 424 | .input() 425 | .schema() 426 | .fields() 427 | .iter() 428 | .enumerate() 429 | .find_map(|(id, f)| { 430 | if f.name() == &bound_column.name { 431 | if let DataType::Timestamp(time_unit, _) = f.data_type() { 432 | let lower_bound = match time_unit { 433 | TimeUnit::Nanosecond => ScalarValue::TimestampNanosecond(Some(lower_bound_ns), None), 434 | TimeUnit::Microsecond => ScalarValue::TimestampMicrosecond(Some(lower_bound_ns / 1000), None), 435 | TimeUnit::Millisecond => { 436 | ScalarValue::TimestampMillisecond(Some(lower_bound_ns / 1_000_000), None) 437 | } 438 | TimeUnit::Second => ScalarValue::TimestampSecond(Some(lower_bound_ns / 1_000_000_000), None), 439 | }; 440 | Some((id, lower_bound)) 441 | } else { 442 | None 443 | } 444 | } else { 445 | None 446 | } 447 | }); 448 | 449 | let Some((column_id, lower_bound_scalar)) = find_column else { 450 | return plan_err!("Timestamp column '{}' not found in input schema", bound_column.name); 451 | }; 452 | 453 | let lower_bound_predicate = Arc::new(PhysicalBinaryExpr::new( 454 | Arc::new(PhysicalColumn::new(&bound_column.name, column_id)), 455 | Operator::GtEq, 456 | Arc::new(PhysicalLiteral::new(lower_bound_scalar)), 457 | )); 458 | let filter_exec = if let Some(filter) = agg_exec.input().as_any().downcast_ref::() { 459 | let new_predicate = PhysicalBinaryExpr::new(filter.predicate().clone(), Operator::And, lower_bound_predicate); 460 | let new_filter = FilterExec::try_new(Arc::new(new_predicate), filter.input().clone())?; 461 | new_filter.with_projection(filter.projection().cloned())? 462 | } else { 463 | FilterExec::try_new(lower_bound_predicate, agg_exec.input().clone())? 464 | }; 465 | 466 | let input_schema = filter_exec.schema(); 467 | 468 | Ok(Arc::new(AggregateExec::try_new( 469 | *agg_exec.mode(), 470 | agg_exec.group_expr().clone(), 471 | agg_exec.aggr_expr().to_vec(), 472 | agg_exec.filter_expr().to_vec(), 473 | Arc::new(filter_exec), 474 | input_schema, 475 | )?)) 476 | } 477 | 478 | /// check for an existing `QCInnerAggregateExec` in the plan 479 | fn find_existing_inner_exec(plan: &Arc) -> bool { 480 | match plan.name() { 481 | "QueryCacheAggregateExec" => true, 482 | "CoalescePartitionsExec" => { 483 | let coalesce = plan.as_any().downcast_ref::().unwrap(); 484 | find_existing_inner_exec(coalesce.input()) 485 | } 486 | "AggregateExec" => { 487 | let agg = plan.as_any().downcast_ref::().unwrap(); 488 | find_existing_inner_exec(agg.input()) 489 | } 490 | _ => false, 491 | // name => { 492 | // dbg!(name); 493 | // false 494 | // } 495 | } 496 | } 497 | 498 | /// A wrapper for `AggregateExec` that caches the result of the aggregation 499 | #[derive(Debug)] 500 | struct CacheUpdateAggregateExec { 501 | cache_entry: CacheEntry, 502 | input: Arc, 503 | /// from `session_state.execution_props().query_execution_start_time.timestamp_nanos_opt()` 504 | now: i64, 505 | properties: PlanProperties, 506 | metrics: ExecutionPlanMetricsSet, 507 | } 508 | 509 | impl CacheUpdateAggregateExec { 510 | fn new_exec_plan(cache_entry: CacheEntry, input: Arc, now: i64) -> Arc { 511 | // we need one partition so we can store one result in the cache, use `CoalescePartitionsExec` 512 | // if input is not already one 513 | let input = if input.name() == "CoalescePartitionsExec" { 514 | input 515 | } else { 516 | Arc::new(CoalescePartitionsExec::new(input)) 517 | }; 518 | let properties = input.properties().clone(); 519 | 520 | Arc::new(Self { 521 | cache_entry, 522 | input, 523 | now, 524 | properties, 525 | metrics: ExecutionPlanMetricsSet::new(), 526 | }) 527 | } 528 | } 529 | 530 | impl DisplayAs for CacheUpdateAggregateExec { 531 | fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> fmt::Result { 532 | match t { 533 | DisplayFormatType::Default => write!(f, "{}({})", self.name(), self.input.name()), 534 | DisplayFormatType::Verbose => write!(f, "{self:?}"), 535 | } 536 | } 537 | } 538 | 539 | #[async_trait] 540 | impl ExecutionPlan for CacheUpdateAggregateExec { 541 | fn name(&self) -> &str { 542 | "CacheUpdateAggregateExec" 543 | } 544 | 545 | fn as_any(&self) -> &dyn Any { 546 | self 547 | } 548 | 549 | fn properties(&self) -> &PlanProperties { 550 | &self.properties 551 | } 552 | 553 | fn children(&self) -> Vec<&Arc> { 554 | vec![&self.input] 555 | } 556 | 557 | fn with_new_children( 558 | self: Arc, 559 | children: Vec>, 560 | ) -> DataFusionResult> { 561 | if children.len() == 1 { 562 | Ok(Self::new_exec_plan( 563 | self.cache_entry.clone(), // TODO is it safe to reuse the cache entry? 564 | children[0].clone(), 565 | self.now, 566 | )) 567 | } else { 568 | internal_err!("CacheUpdateAggregateExec expected one child") 569 | } 570 | } 571 | 572 | fn execute(&self, partition: usize, context: Arc) -> DataFusionResult { 573 | assert_eq!(partition, 0, "CacheUpdateAggregateExec does not support partitioning"); 574 | let metrics = BaselineMetrics::new(&self.metrics, partition); 575 | Ok(Box::pin(RecordBatchStreamAdapter::new( 576 | self.input.schema(), 577 | execute_store(self.input.clone(), self.cache_entry.clone(), self.now, context, metrics) 578 | .map_ok(|partitions| futures::stream::iter(partitions.into_iter().map(Ok))) 579 | .try_flatten_stream(), 580 | ))) 581 | } 582 | 583 | fn metrics(&self) -> Option { 584 | Some(self.metrics.clone_inner()) 585 | } 586 | } 587 | 588 | async fn execute_store( 589 | input: Arc, 590 | cache_entry: CacheEntry, 591 | now: i64, 592 | context: Arc, 593 | metrics: BaselineMetrics, 594 | ) -> DataFusionResult> { 595 | let batches = collect(input, context).await?; 596 | // store the result for future use 597 | cache_entry.put(now, &batches).await?; 598 | metrics.record_output(batches.iter().map(RecordBatch::num_rows).sum()); 599 | metrics.done(); 600 | Ok(batches) 601 | } 602 | 603 | /// A wrapper for `AggregateExec` that caches the result of the aggregation 604 | #[derive(Debug)] 605 | struct CachedAggregateExec { 606 | cache_entry: Arc, 607 | schema: SchemaRef, 608 | properties: PlanProperties, 609 | metrics: ExecutionPlanMetricsSet, 610 | } 611 | 612 | impl CachedAggregateExec { 613 | fn new_exec_plan( 614 | cache_entry: Arc, 615 | inner_properties: &PlanProperties, 616 | ) -> Arc { 617 | Arc::new(Self { 618 | cache_entry, 619 | schema: inner_properties.eq_properties.schema().clone(), 620 | properties: inner_properties.clone(), 621 | metrics: ExecutionPlanMetricsSet::new(), 622 | }) 623 | } 624 | } 625 | 626 | impl DisplayAs for CachedAggregateExec { 627 | fn fmt_as(&self, t: DisplayFormatType, f: &mut Formatter) -> fmt::Result { 628 | match t { 629 | DisplayFormatType::Default => write!(f, "{}", self.name()), 630 | DisplayFormatType::Verbose => write!(f, "{self:?}"), 631 | } 632 | } 633 | } 634 | 635 | #[async_trait] 636 | impl ExecutionPlan for CachedAggregateExec { 637 | fn name(&self) -> &str { 638 | "CachedAggregateExec" 639 | } 640 | 641 | fn as_any(&self) -> &dyn Any { 642 | self 643 | } 644 | 645 | fn properties(&self) -> &PlanProperties { 646 | &self.properties 647 | } 648 | 649 | fn children(&self) -> Vec<&Arc> { 650 | vec![] 651 | } 652 | 653 | fn with_new_children( 654 | self: Arc, 655 | children: Vec>, 656 | ) -> DataFusionResult> { 657 | if children.is_empty() { 658 | Ok(self) 659 | } else { 660 | internal_err!("Children cannot be replaced in {}", self.name()) 661 | } 662 | } 663 | 664 | fn execute(&self, partition: usize, _context: Arc) -> DataFusionResult { 665 | assert_eq!(partition, 0, "CachedAggregateExec does not support partitioning"); 666 | let metrics = BaselineMetrics::new(&self.metrics, partition); 667 | Ok(Box::pin(RecordBatchStreamAdapter::new( 668 | self.schema.clone(), 669 | execute_get(self.cache_entry.clone(), metrics) 670 | .map_ok(|partitions| futures::stream::iter(partitions.into_iter().map(Ok))) 671 | .try_flatten_stream(), 672 | ))) 673 | } 674 | 675 | fn metrics(&self) -> Option { 676 | Some(self.metrics.clone_inner()) 677 | } 678 | } 679 | 680 | async fn execute_get( 681 | cache_entry: Arc, 682 | metrics: BaselineMetrics, 683 | ) -> DataFusionResult> { 684 | let batches = cache_entry.get().await.map(<[RecordBatch]>::to_vec)?; 685 | metrics.record_output(batches.iter().map(RecordBatch::num_rows).sum()); 686 | metrics.done(); 687 | Ok(batches) 688 | } 689 | 690 | /// Find a binary expression which must have the form `{column} >(=) {something with now()}` represents a 691 | /// lower bound on `column` but changes over time. 692 | #[derive(Debug)] 693 | enum DynamicLowerBound { 694 | /// we found an unstable expression which means we can't rewrite the plan 695 | Abandon, 696 | /// we found a suitable lower bound 697 | Found(BinaryExpr), 698 | /// we found `now()` or similar function which is allowed if within an expression which sets the lower bound 699 | FoundNow, 700 | /// we did not find a suitable lower bound, but the expression is stable 701 | Stable, 702 | } 703 | 704 | impl DynamicLowerBound { 705 | fn find(expr: &Expr, columns: &HashSet) -> Self { 706 | match expr { 707 | Expr::BinaryExpr(bin_expr) => Self::find_bin_expr(bin_expr, columns), 708 | Expr::Between(between) => Self::find_between(between, columns), 709 | Expr::Literal(_) 710 | | Expr::Like(_) 711 | | Expr::IsNotNull(_) 712 | | Expr::IsNull(_) 713 | | Expr::IsTrue(_) 714 | | Expr::IsFalse(_) 715 | | Expr::IsNotTrue(_) 716 | | Expr::IsNotFalse(_) 717 | | Expr::Column(_) => Self::Stable, 718 | Expr::Not(e) | Expr::Negative(e) => match Self::find(e, columns) { 719 | Self::Stable => Self::Stable, 720 | _ => Self::Abandon, 721 | }, 722 | Expr::ScalarFunction(scalar) => Self::find_scalar_function(scalar), 723 | // TODO there are other allowed cases 724 | _ => Self::Abandon, 725 | } 726 | } 727 | 728 | fn find_bin_expr(bin_expr: &BinaryExpr, columns: &HashSet) -> Self { 729 | let BinaryExpr { left, op, right } = bin_expr; 730 | match op { 731 | Operator::Gt | Operator::GtEq => { 732 | // expression of the form `left >(=) right`, for this to be a lower bound: 733 | // `left` must be the column 734 | // and `right` must be a dynamic bound 735 | if let Expr::Column(col) = left.as_ref() { 736 | if columns.contains(col) { 737 | return match Self::find(right, columns) { 738 | Self::Stable => Self::Stable, 739 | Self::FoundNow => Self::Found(bin_expr.clone()), 740 | _ => Self::Abandon, 741 | }; 742 | } 743 | } 744 | } 745 | Operator::Lt | Operator::LtEq => { 746 | // expression of the form `left <(=) right`, for this to be a lower bound: 747 | // `left` must be a dynamic bound 748 | // and `right` must be the column 749 | if let Expr::Column(col) = right.as_ref() { 750 | if columns.contains(col) { 751 | return match Self::find(left, columns) { 752 | Self::Stable => Self::Stable, 753 | Self::FoundNow => { 754 | let op = match op { 755 | Operator::Lt => Operator::GtEq, 756 | Operator::LtEq => Operator::Gt, 757 | _ => unreachable!(), 758 | }; 759 | Self::Found(BinaryExpr { 760 | right: left.clone(), 761 | op, 762 | left: right.clone(), 763 | }) 764 | } 765 | _ => Self::Abandon, 766 | }; 767 | } 768 | } 769 | } 770 | // AND or a simple arithmetic operation, check both sides 771 | Operator::And 772 | | Operator::Eq 773 | | Operator::Plus 774 | | Operator::Minus 775 | | Operator::Multiply 776 | | Operator::Divide 777 | | Operator::Modulo => (), 778 | _ => return Self::Abandon, 779 | }; 780 | 781 | let left = Self::find(left, columns); 782 | let right = Self::find(right, columns); 783 | left.either(right) 784 | } 785 | 786 | fn find_between(_between: &Between, _columns: &HashSet) -> Self { 787 | todo!() 788 | } 789 | 790 | fn find_scalar_function(scalar: &ScalarFunction) -> Self { 791 | if matches!(scalar.name(), "now" | "current_timestamp" | "current_date") { 792 | Self::FoundNow 793 | } else { 794 | Self::Abandon 795 | } 796 | } 797 | 798 | /// Find the value which is more important 799 | #[allow(clippy::match_same_arms)] 800 | fn either(self, other: Self) -> Self { 801 | match (self, other) { 802 | (Self::Abandon, _) | (_, Self::Abandon) => Self::Abandon, 803 | // found on both sides, not sure what to do 804 | (Self::Found(..), Self::Found(..)) => Self::Abandon, 805 | (Self::FoundNow, _) | (_, Self::FoundNow) => Self::FoundNow, 806 | (Self::Stable, other) | (other, Self::Stable) => other, 807 | } 808 | } 809 | } 810 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.24.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler2" 16 | version = "2.0.0" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" 19 | 20 | [[package]] 21 | name = "ahash" 22 | version = "0.8.11" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" 25 | dependencies = [ 26 | "cfg-if", 27 | "const-random", 28 | "getrandom", 29 | "once_cell", 30 | "version_check", 31 | "zerocopy", 32 | ] 33 | 34 | [[package]] 35 | name = "aho-corasick" 36 | version = "1.1.3" 37 | source = "registry+https://github.com/rust-lang/crates.io-index" 38 | checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" 39 | dependencies = [ 40 | "memchr", 41 | ] 42 | 43 | [[package]] 44 | name = "alloc-no-stdlib" 45 | version = "2.0.4" 46 | source = "registry+https://github.com/rust-lang/crates.io-index" 47 | checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" 48 | 49 | [[package]] 50 | name = "alloc-stdlib" 51 | version = "0.2.2" 52 | source = "registry+https://github.com/rust-lang/crates.io-index" 53 | checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" 54 | dependencies = [ 55 | "alloc-no-stdlib", 56 | ] 57 | 58 | [[package]] 59 | name = "allocator-api2" 60 | version = "0.2.18" 61 | source = "registry+https://github.com/rust-lang/crates.io-index" 62 | checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" 63 | 64 | [[package]] 65 | name = "android-tzdata" 66 | version = "0.1.1" 67 | source = "registry+https://github.com/rust-lang/crates.io-index" 68 | checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" 69 | 70 | [[package]] 71 | name = "android_system_properties" 72 | version = "0.1.5" 73 | source = "registry+https://github.com/rust-lang/crates.io-index" 74 | checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" 75 | dependencies = [ 76 | "libc", 77 | ] 78 | 79 | [[package]] 80 | name = "arrayref" 81 | version = "0.3.9" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" 84 | 85 | [[package]] 86 | name = "arrayvec" 87 | version = "0.7.6" 88 | source = "registry+https://github.com/rust-lang/crates.io-index" 89 | checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" 90 | 91 | [[package]] 92 | name = "arrow" 93 | version = "53.0.0" 94 | source = "registry+https://github.com/rust-lang/crates.io-index" 95 | checksum = "45aef0d9cf9a039bf6cd1acc451b137aca819977b0928dece52bd92811b640ba" 96 | dependencies = [ 97 | "arrow-arith", 98 | "arrow-array", 99 | "arrow-buffer", 100 | "arrow-cast", 101 | "arrow-csv", 102 | "arrow-data", 103 | "arrow-ipc", 104 | "arrow-json", 105 | "arrow-ord", 106 | "arrow-row", 107 | "arrow-schema", 108 | "arrow-select", 109 | "arrow-string", 110 | ] 111 | 112 | [[package]] 113 | name = "arrow-arith" 114 | version = "53.0.0" 115 | source = "registry+https://github.com/rust-lang/crates.io-index" 116 | checksum = "03675e42d1560790f3524800e41403b40d0da1c793fe9528929fde06d8c7649a" 117 | dependencies = [ 118 | "arrow-array", 119 | "arrow-buffer", 120 | "arrow-data", 121 | "arrow-schema", 122 | "chrono", 123 | "half", 124 | "num", 125 | ] 126 | 127 | [[package]] 128 | name = "arrow-array" 129 | version = "53.0.0" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "cd2bf348cf9f02a5975c5962c7fa6dee107a2009a7b41ac5fb1a027e12dc033f" 132 | dependencies = [ 133 | "ahash", 134 | "arrow-buffer", 135 | "arrow-data", 136 | "arrow-schema", 137 | "chrono", 138 | "chrono-tz", 139 | "half", 140 | "hashbrown 0.14.5", 141 | "num", 142 | ] 143 | 144 | [[package]] 145 | name = "arrow-buffer" 146 | version = "53.0.0" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "3092e37715f168976012ce52273c3989b5793b0db5f06cbaa246be25e5f0924d" 149 | dependencies = [ 150 | "bytes", 151 | "half", 152 | "num", 153 | ] 154 | 155 | [[package]] 156 | name = "arrow-cast" 157 | version = "53.0.0" 158 | source = "registry+https://github.com/rust-lang/crates.io-index" 159 | checksum = "7ce1018bb710d502f9db06af026ed3561552e493e989a79d0d0f5d9cf267a785" 160 | dependencies = [ 161 | "arrow-array", 162 | "arrow-buffer", 163 | "arrow-data", 164 | "arrow-schema", 165 | "arrow-select", 166 | "atoi", 167 | "base64", 168 | "chrono", 169 | "comfy-table", 170 | "half", 171 | "lexical-core", 172 | "num", 173 | "ryu", 174 | ] 175 | 176 | [[package]] 177 | name = "arrow-csv" 178 | version = "53.0.0" 179 | source = "registry+https://github.com/rust-lang/crates.io-index" 180 | checksum = "fd178575f45624d045e4ebee714e246a05d9652e41363ee3f57ec18cca97f740" 181 | dependencies = [ 182 | "arrow-array", 183 | "arrow-buffer", 184 | "arrow-cast", 185 | "arrow-data", 186 | "arrow-schema", 187 | "chrono", 188 | "csv", 189 | "csv-core", 190 | "lazy_static", 191 | "lexical-core", 192 | "regex", 193 | ] 194 | 195 | [[package]] 196 | name = "arrow-data" 197 | version = "53.0.0" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "4e4ac0c4ee79150afe067dc4857154b3ee9c1cd52b5f40d59a77306d0ed18d65" 200 | dependencies = [ 201 | "arrow-buffer", 202 | "arrow-schema", 203 | "half", 204 | "num", 205 | ] 206 | 207 | [[package]] 208 | name = "arrow-ipc" 209 | version = "53.0.0" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "bb307482348a1267f91b0912e962cd53440e5de0f7fb24c5f7b10da70b38c94a" 212 | dependencies = [ 213 | "arrow-array", 214 | "arrow-buffer", 215 | "arrow-cast", 216 | "arrow-data", 217 | "arrow-schema", 218 | "flatbuffers", 219 | "lz4_flex", 220 | ] 221 | 222 | [[package]] 223 | name = "arrow-json" 224 | version = "53.0.0" 225 | source = "registry+https://github.com/rust-lang/crates.io-index" 226 | checksum = "d24805ba326758effdd6f2cbdd482fcfab749544f21b134701add25b33f474e6" 227 | dependencies = [ 228 | "arrow-array", 229 | "arrow-buffer", 230 | "arrow-cast", 231 | "arrow-data", 232 | "arrow-schema", 233 | "chrono", 234 | "half", 235 | "indexmap", 236 | "lexical-core", 237 | "num", 238 | "serde", 239 | "serde_json", 240 | ] 241 | 242 | [[package]] 243 | name = "arrow-ord" 244 | version = "53.0.0" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "644046c479d80ae8ed02a7f1e1399072ea344ca6a7b0e293ab2d5d9ed924aa3b" 247 | dependencies = [ 248 | "arrow-array", 249 | "arrow-buffer", 250 | "arrow-data", 251 | "arrow-schema", 252 | "arrow-select", 253 | "half", 254 | "num", 255 | ] 256 | 257 | [[package]] 258 | name = "arrow-row" 259 | version = "53.0.0" 260 | source = "registry+https://github.com/rust-lang/crates.io-index" 261 | checksum = "a29791f8eb13b340ce35525b723f5f0df17ecb955599e11f65c2a94ab34e2efb" 262 | dependencies = [ 263 | "ahash", 264 | "arrow-array", 265 | "arrow-buffer", 266 | "arrow-data", 267 | "arrow-schema", 268 | "half", 269 | ] 270 | 271 | [[package]] 272 | name = "arrow-schema" 273 | version = "53.0.0" 274 | source = "registry+https://github.com/rust-lang/crates.io-index" 275 | checksum = "c85320a3a2facf2b2822b57aa9d6d9d55edb8aee0b6b5d3b8df158e503d10858" 276 | 277 | [[package]] 278 | name = "arrow-select" 279 | version = "53.0.0" 280 | source = "registry+https://github.com/rust-lang/crates.io-index" 281 | checksum = "9cc7e6b582e23855fd1625ce46e51647aa440c20ea2e71b1d748e0839dd73cba" 282 | dependencies = [ 283 | "ahash", 284 | "arrow-array", 285 | "arrow-buffer", 286 | "arrow-data", 287 | "arrow-schema", 288 | "num", 289 | ] 290 | 291 | [[package]] 292 | name = "arrow-string" 293 | version = "53.0.0" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "0775b6567c66e56ded19b87a954b6b1beffbdd784ef95a3a2b03f59570c1d230" 296 | dependencies = [ 297 | "arrow-array", 298 | "arrow-buffer", 299 | "arrow-data", 300 | "arrow-schema", 301 | "arrow-select", 302 | "memchr", 303 | "num", 304 | "regex", 305 | "regex-syntax", 306 | ] 307 | 308 | [[package]] 309 | name = "async-compression" 310 | version = "0.4.13" 311 | source = "registry+https://github.com/rust-lang/crates.io-index" 312 | checksum = "7e614738943d3f68c628ae3dbce7c3daffb196665f82f8c8ea6b65de73c79429" 313 | dependencies = [ 314 | "bzip2", 315 | "flate2", 316 | "futures-core", 317 | "futures-io", 318 | "memchr", 319 | "pin-project-lite", 320 | "tokio", 321 | "xz2", 322 | "zstd", 323 | "zstd-safe", 324 | ] 325 | 326 | [[package]] 327 | name = "async-trait" 328 | version = "0.1.83" 329 | source = "registry+https://github.com/rust-lang/crates.io-index" 330 | checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" 331 | dependencies = [ 332 | "proc-macro2", 333 | "quote", 334 | "syn", 335 | ] 336 | 337 | [[package]] 338 | name = "atoi" 339 | version = "2.0.0" 340 | source = "registry+https://github.com/rust-lang/crates.io-index" 341 | checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" 342 | dependencies = [ 343 | "num-traits", 344 | ] 345 | 346 | [[package]] 347 | name = "autocfg" 348 | version = "1.4.0" 349 | source = "registry+https://github.com/rust-lang/crates.io-index" 350 | checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" 351 | 352 | [[package]] 353 | name = "backtrace" 354 | version = "0.3.74" 355 | source = "registry+https://github.com/rust-lang/crates.io-index" 356 | checksum = "8d82cb332cdfaed17ae235a638438ac4d4839913cc2af585c3c6746e8f8bee1a" 357 | dependencies = [ 358 | "addr2line", 359 | "cfg-if", 360 | "libc", 361 | "miniz_oxide", 362 | "object", 363 | "rustc-demangle", 364 | "windows-targets", 365 | ] 366 | 367 | [[package]] 368 | name = "base64" 369 | version = "0.22.1" 370 | source = "registry+https://github.com/rust-lang/crates.io-index" 371 | checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" 372 | 373 | [[package]] 374 | name = "bitflags" 375 | version = "1.3.2" 376 | source = "registry+https://github.com/rust-lang/crates.io-index" 377 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" 378 | 379 | [[package]] 380 | name = "bitflags" 381 | version = "2.6.0" 382 | source = "registry+https://github.com/rust-lang/crates.io-index" 383 | checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" 384 | 385 | [[package]] 386 | name = "blake2" 387 | version = "0.10.6" 388 | source = "registry+https://github.com/rust-lang/crates.io-index" 389 | checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe" 390 | dependencies = [ 391 | "digest", 392 | ] 393 | 394 | [[package]] 395 | name = "blake3" 396 | version = "1.5.4" 397 | source = "registry+https://github.com/rust-lang/crates.io-index" 398 | checksum = "d82033247fd8e890df8f740e407ad4d038debb9eb1f40533fffb32e7d17dc6f7" 399 | dependencies = [ 400 | "arrayref", 401 | "arrayvec", 402 | "cc", 403 | "cfg-if", 404 | "constant_time_eq", 405 | ] 406 | 407 | [[package]] 408 | name = "block-buffer" 409 | version = "0.10.4" 410 | source = "registry+https://github.com/rust-lang/crates.io-index" 411 | checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" 412 | dependencies = [ 413 | "generic-array", 414 | ] 415 | 416 | [[package]] 417 | name = "brotli" 418 | version = "6.0.0" 419 | source = "registry+https://github.com/rust-lang/crates.io-index" 420 | checksum = "74f7971dbd9326d58187408ab83117d8ac1bb9c17b085fdacd1cf2f598719b6b" 421 | dependencies = [ 422 | "alloc-no-stdlib", 423 | "alloc-stdlib", 424 | "brotli-decompressor", 425 | ] 426 | 427 | [[package]] 428 | name = "brotli-decompressor" 429 | version = "4.0.1" 430 | source = "registry+https://github.com/rust-lang/crates.io-index" 431 | checksum = "9a45bd2e4095a8b518033b128020dd4a55aab1c0a381ba4404a472630f4bc362" 432 | dependencies = [ 433 | "alloc-no-stdlib", 434 | "alloc-stdlib", 435 | ] 436 | 437 | [[package]] 438 | name = "bumpalo" 439 | version = "3.16.0" 440 | source = "registry+https://github.com/rust-lang/crates.io-index" 441 | checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" 442 | 443 | [[package]] 444 | name = "byteorder" 445 | version = "1.5.0" 446 | source = "registry+https://github.com/rust-lang/crates.io-index" 447 | checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" 448 | 449 | [[package]] 450 | name = "bytes" 451 | version = "1.7.2" 452 | source = "registry+https://github.com/rust-lang/crates.io-index" 453 | checksum = "428d9aa8fbc0670b7b8d6030a7fadd0f86151cae55e4dbbece15f3780a3dfaf3" 454 | 455 | [[package]] 456 | name = "bzip2" 457 | version = "0.4.4" 458 | source = "registry+https://github.com/rust-lang/crates.io-index" 459 | checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" 460 | dependencies = [ 461 | "bzip2-sys", 462 | "libc", 463 | ] 464 | 465 | [[package]] 466 | name = "bzip2-sys" 467 | version = "0.1.11+1.0.8" 468 | source = "registry+https://github.com/rust-lang/crates.io-index" 469 | checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" 470 | dependencies = [ 471 | "cc", 472 | "libc", 473 | "pkg-config", 474 | ] 475 | 476 | [[package]] 477 | name = "cc" 478 | version = "1.1.25" 479 | source = "registry+https://github.com/rust-lang/crates.io-index" 480 | checksum = "e8d9e0b4957f635b8d3da819d0db5603620467ecf1f692d22a8c2717ce27e6d8" 481 | dependencies = [ 482 | "jobserver", 483 | "libc", 484 | "shlex", 485 | ] 486 | 487 | [[package]] 488 | name = "cfg-if" 489 | version = "1.0.0" 490 | source = "registry+https://github.com/rust-lang/crates.io-index" 491 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 492 | 493 | [[package]] 494 | name = "chrono" 495 | version = "0.4.38" 496 | source = "registry+https://github.com/rust-lang/crates.io-index" 497 | checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" 498 | dependencies = [ 499 | "android-tzdata", 500 | "iana-time-zone", 501 | "js-sys", 502 | "num-traits", 503 | "wasm-bindgen", 504 | "windows-targets", 505 | ] 506 | 507 | [[package]] 508 | name = "chrono-tz" 509 | version = "0.9.0" 510 | source = "registry+https://github.com/rust-lang/crates.io-index" 511 | checksum = "93698b29de5e97ad0ae26447b344c482a7284c737d9ddc5f9e52b74a336671bb" 512 | dependencies = [ 513 | "chrono", 514 | "chrono-tz-build", 515 | "phf", 516 | ] 517 | 518 | [[package]] 519 | name = "chrono-tz-build" 520 | version = "0.3.0" 521 | source = "registry+https://github.com/rust-lang/crates.io-index" 522 | checksum = "0c088aee841df9c3041febbb73934cfc39708749bf96dc827e3359cd39ef11b1" 523 | dependencies = [ 524 | "parse-zoneinfo", 525 | "phf", 526 | "phf_codegen", 527 | ] 528 | 529 | [[package]] 530 | name = "comfy-table" 531 | version = "7.1.1" 532 | source = "registry+https://github.com/rust-lang/crates.io-index" 533 | checksum = "b34115915337defe99b2aff5c2ce6771e5fbc4079f4b506301f5cf394c8452f7" 534 | dependencies = [ 535 | "strum", 536 | "strum_macros", 537 | "unicode-width", 538 | ] 539 | 540 | [[package]] 541 | name = "const-random" 542 | version = "0.1.18" 543 | source = "registry+https://github.com/rust-lang/crates.io-index" 544 | checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" 545 | dependencies = [ 546 | "const-random-macro", 547 | ] 548 | 549 | [[package]] 550 | name = "const-random-macro" 551 | version = "0.1.16" 552 | source = "registry+https://github.com/rust-lang/crates.io-index" 553 | checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" 554 | dependencies = [ 555 | "getrandom", 556 | "once_cell", 557 | "tiny-keccak", 558 | ] 559 | 560 | [[package]] 561 | name = "constant_time_eq" 562 | version = "0.3.1" 563 | source = "registry+https://github.com/rust-lang/crates.io-index" 564 | checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" 565 | 566 | [[package]] 567 | name = "core-foundation-sys" 568 | version = "0.8.7" 569 | source = "registry+https://github.com/rust-lang/crates.io-index" 570 | checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" 571 | 572 | [[package]] 573 | name = "cpufeatures" 574 | version = "0.2.14" 575 | source = "registry+https://github.com/rust-lang/crates.io-index" 576 | checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0" 577 | dependencies = [ 578 | "libc", 579 | ] 580 | 581 | [[package]] 582 | name = "crc32fast" 583 | version = "1.4.2" 584 | source = "registry+https://github.com/rust-lang/crates.io-index" 585 | checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" 586 | dependencies = [ 587 | "cfg-if", 588 | ] 589 | 590 | [[package]] 591 | name = "crossbeam-utils" 592 | version = "0.8.20" 593 | source = "registry+https://github.com/rust-lang/crates.io-index" 594 | checksum = "22ec99545bb0ed0ea7bb9b8e1e9122ea386ff8a48c0922e43f36d45ab09e0e80" 595 | 596 | [[package]] 597 | name = "crunchy" 598 | version = "0.2.2" 599 | source = "registry+https://github.com/rust-lang/crates.io-index" 600 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 601 | 602 | [[package]] 603 | name = "crypto-common" 604 | version = "0.1.6" 605 | source = "registry+https://github.com/rust-lang/crates.io-index" 606 | checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" 607 | dependencies = [ 608 | "generic-array", 609 | "typenum", 610 | ] 611 | 612 | [[package]] 613 | name = "csv" 614 | version = "1.3.0" 615 | source = "registry+https://github.com/rust-lang/crates.io-index" 616 | checksum = "ac574ff4d437a7b5ad237ef331c17ccca63c46479e5b5453eb8e10bb99a759fe" 617 | dependencies = [ 618 | "csv-core", 619 | "itoa", 620 | "ryu", 621 | "serde", 622 | ] 623 | 624 | [[package]] 625 | name = "csv-core" 626 | version = "0.1.11" 627 | source = "registry+https://github.com/rust-lang/crates.io-index" 628 | checksum = "5efa2b3d7902f4b634a20cae3c9c4e6209dc4779feb6863329607560143efa70" 629 | dependencies = [ 630 | "memchr", 631 | ] 632 | 633 | [[package]] 634 | name = "dashmap" 635 | version = "6.1.0" 636 | source = "registry+https://github.com/rust-lang/crates.io-index" 637 | checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" 638 | dependencies = [ 639 | "cfg-if", 640 | "crossbeam-utils", 641 | "hashbrown 0.14.5", 642 | "lock_api", 643 | "once_cell", 644 | "parking_lot_core", 645 | ] 646 | 647 | [[package]] 648 | name = "datafusion" 649 | version = "42.0.0" 650 | source = "git+https://github.com/apache/datafusion.git#6f8c74ca873fe015b2e7ff4eeb2f76bf21ae9d0e" 651 | dependencies = [ 652 | "ahash", 653 | "arrow", 654 | "arrow-array", 655 | "arrow-ipc", 656 | "arrow-schema", 657 | "async-compression", 658 | "async-trait", 659 | "bytes", 660 | "bzip2", 661 | "chrono", 662 | "dashmap", 663 | "datafusion-catalog", 664 | "datafusion-common", 665 | "datafusion-common-runtime", 666 | "datafusion-execution", 667 | "datafusion-expr", 668 | "datafusion-functions", 669 | "datafusion-functions-aggregate", 670 | "datafusion-functions-nested", 671 | "datafusion-functions-window", 672 | "datafusion-optimizer", 673 | "datafusion-physical-expr", 674 | "datafusion-physical-expr-common", 675 | "datafusion-physical-optimizer", 676 | "datafusion-physical-plan", 677 | "datafusion-sql", 678 | "flate2", 679 | "futures", 680 | "glob", 681 | "half", 682 | "hashbrown 0.14.5", 683 | "indexmap", 684 | "itertools", 685 | "log", 686 | "num_cpus", 687 | "object_store", 688 | "parking_lot", 689 | "parquet", 690 | "paste", 691 | "pin-project-lite", 692 | "rand", 693 | "sqlparser", 694 | "tempfile", 695 | "tokio", 696 | "tokio-util", 697 | "url", 698 | "uuid", 699 | "xz2", 700 | "zstd", 701 | ] 702 | 703 | [[package]] 704 | name = "datafusion-catalog" 705 | version = "42.0.0" 706 | source = "git+https://github.com/apache/datafusion.git#6f8c74ca873fe015b2e7ff4eeb2f76bf21ae9d0e" 707 | dependencies = [ 708 | "arrow-schema", 709 | "async-trait", 710 | "datafusion-common", 711 | "datafusion-execution", 712 | "datafusion-expr", 713 | "datafusion-physical-plan", 714 | "parking_lot", 715 | ] 716 | 717 | [[package]] 718 | name = "datafusion-common" 719 | version = "42.0.0" 720 | source = "git+https://github.com/apache/datafusion.git#6f8c74ca873fe015b2e7ff4eeb2f76bf21ae9d0e" 721 | dependencies = [ 722 | "ahash", 723 | "arrow", 724 | "arrow-array", 725 | "arrow-buffer", 726 | "arrow-schema", 727 | "chrono", 728 | "half", 729 | "hashbrown 0.14.5", 730 | "instant", 731 | "libc", 732 | "num_cpus", 733 | "object_store", 734 | "parquet", 735 | "paste", 736 | "sqlparser", 737 | "tokio", 738 | ] 739 | 740 | [[package]] 741 | name = "datafusion-common-runtime" 742 | version = "42.0.0" 743 | source = "git+https://github.com/apache/datafusion.git#6f8c74ca873fe015b2e7ff4eeb2f76bf21ae9d0e" 744 | dependencies = [ 745 | "log", 746 | "tokio", 747 | ] 748 | 749 | [[package]] 750 | name = "datafusion-execution" 751 | version = "42.0.0" 752 | source = "git+https://github.com/apache/datafusion.git#6f8c74ca873fe015b2e7ff4eeb2f76bf21ae9d0e" 753 | dependencies = [ 754 | "arrow", 755 | "chrono", 756 | "dashmap", 757 | "datafusion-common", 758 | "datafusion-expr", 759 | "futures", 760 | "hashbrown 0.14.5", 761 | "log", 762 | "object_store", 763 | "parking_lot", 764 | "rand", 765 | "tempfile", 766 | "url", 767 | ] 768 | 769 | [[package]] 770 | name = "datafusion-expr" 771 | version = "42.0.0" 772 | source = "git+https://github.com/apache/datafusion.git#6f8c74ca873fe015b2e7ff4eeb2f76bf21ae9d0e" 773 | dependencies = [ 774 | "ahash", 775 | "arrow", 776 | "arrow-array", 777 | "arrow-buffer", 778 | "chrono", 779 | "datafusion-common", 780 | "datafusion-expr-common", 781 | "datafusion-functions-aggregate-common", 782 | "datafusion-functions-window-common", 783 | "datafusion-physical-expr-common", 784 | "indexmap", 785 | "paste", 786 | "serde_json", 787 | "sqlparser", 788 | "strum", 789 | "strum_macros", 790 | ] 791 | 792 | [[package]] 793 | name = "datafusion-expr-common" 794 | version = "42.0.0" 795 | source = "git+https://github.com/apache/datafusion.git#6f8c74ca873fe015b2e7ff4eeb2f76bf21ae9d0e" 796 | dependencies = [ 797 | "arrow", 798 | "datafusion-common", 799 | "paste", 800 | ] 801 | 802 | [[package]] 803 | name = "datafusion-functions" 804 | version = "42.0.0" 805 | source = "git+https://github.com/apache/datafusion.git#6f8c74ca873fe015b2e7ff4eeb2f76bf21ae9d0e" 806 | dependencies = [ 807 | "arrow", 808 | "arrow-buffer", 809 | "base64", 810 | "blake2", 811 | "blake3", 812 | "chrono", 813 | "datafusion-common", 814 | "datafusion-execution", 815 | "datafusion-expr", 816 | "hashbrown 0.14.5", 817 | "hex", 818 | "itertools", 819 | "log", 820 | "md-5", 821 | "rand", 822 | "regex", 823 | "sha2", 824 | "unicode-segmentation", 825 | "uuid", 826 | ] 827 | 828 | [[package]] 829 | name = "datafusion-functions-aggregate" 830 | version = "42.0.0" 831 | source = "git+https://github.com/apache/datafusion.git#6f8c74ca873fe015b2e7ff4eeb2f76bf21ae9d0e" 832 | dependencies = [ 833 | "ahash", 834 | "arrow", 835 | "arrow-schema", 836 | "datafusion-common", 837 | "datafusion-execution", 838 | "datafusion-expr", 839 | "datafusion-functions-aggregate-common", 840 | "datafusion-physical-expr", 841 | "datafusion-physical-expr-common", 842 | "half", 843 | "indexmap", 844 | "log", 845 | "paste", 846 | ] 847 | 848 | [[package]] 849 | name = "datafusion-functions-aggregate-common" 850 | version = "42.0.0" 851 | source = "git+https://github.com/apache/datafusion.git#6f8c74ca873fe015b2e7ff4eeb2f76bf21ae9d0e" 852 | dependencies = [ 853 | "ahash", 854 | "arrow", 855 | "datafusion-common", 856 | "datafusion-expr-common", 857 | "datafusion-physical-expr-common", 858 | "rand", 859 | ] 860 | 861 | [[package]] 862 | name = "datafusion-functions-nested" 863 | version = "42.0.0" 864 | source = "git+https://github.com/apache/datafusion.git#6f8c74ca873fe015b2e7ff4eeb2f76bf21ae9d0e" 865 | dependencies = [ 866 | "arrow", 867 | "arrow-array", 868 | "arrow-buffer", 869 | "arrow-ord", 870 | "arrow-schema", 871 | "datafusion-common", 872 | "datafusion-execution", 873 | "datafusion-expr", 874 | "datafusion-functions", 875 | "datafusion-functions-aggregate", 876 | "datafusion-physical-expr-common", 877 | "itertools", 878 | "log", 879 | "paste", 880 | "rand", 881 | ] 882 | 883 | [[package]] 884 | name = "datafusion-functions-window" 885 | version = "42.0.0" 886 | source = "git+https://github.com/apache/datafusion.git#6f8c74ca873fe015b2e7ff4eeb2f76bf21ae9d0e" 887 | dependencies = [ 888 | "datafusion-common", 889 | "datafusion-expr", 890 | "datafusion-functions-window-common", 891 | "datafusion-physical-expr-common", 892 | "log", 893 | "paste", 894 | ] 895 | 896 | [[package]] 897 | name = "datafusion-functions-window-common" 898 | version = "42.0.0" 899 | source = "git+https://github.com/apache/datafusion.git#6f8c74ca873fe015b2e7ff4eeb2f76bf21ae9d0e" 900 | dependencies = [ 901 | "datafusion-common", 902 | ] 903 | 904 | [[package]] 905 | name = "datafusion-optimizer" 906 | version = "42.0.0" 907 | source = "git+https://github.com/apache/datafusion.git#6f8c74ca873fe015b2e7ff4eeb2f76bf21ae9d0e" 908 | dependencies = [ 909 | "arrow", 910 | "async-trait", 911 | "chrono", 912 | "datafusion-common", 913 | "datafusion-expr", 914 | "datafusion-physical-expr", 915 | "hashbrown 0.14.5", 916 | "indexmap", 917 | "itertools", 918 | "log", 919 | "paste", 920 | "regex-syntax", 921 | ] 922 | 923 | [[package]] 924 | name = "datafusion-physical-expr" 925 | version = "42.0.0" 926 | source = "git+https://github.com/apache/datafusion.git#6f8c74ca873fe015b2e7ff4eeb2f76bf21ae9d0e" 927 | dependencies = [ 928 | "ahash", 929 | "arrow", 930 | "arrow-array", 931 | "arrow-buffer", 932 | "arrow-ord", 933 | "arrow-schema", 934 | "arrow-string", 935 | "base64", 936 | "chrono", 937 | "datafusion-common", 938 | "datafusion-execution", 939 | "datafusion-expr", 940 | "datafusion-expr-common", 941 | "datafusion-functions-aggregate-common", 942 | "datafusion-physical-expr-common", 943 | "half", 944 | "hashbrown 0.14.5", 945 | "hex", 946 | "indexmap", 947 | "itertools", 948 | "log", 949 | "paste", 950 | "petgraph", 951 | "regex", 952 | ] 953 | 954 | [[package]] 955 | name = "datafusion-physical-expr-common" 956 | version = "42.0.0" 957 | source = "git+https://github.com/apache/datafusion.git#6f8c74ca873fe015b2e7ff4eeb2f76bf21ae9d0e" 958 | dependencies = [ 959 | "ahash", 960 | "arrow", 961 | "datafusion-common", 962 | "datafusion-expr-common", 963 | "hashbrown 0.14.5", 964 | "rand", 965 | ] 966 | 967 | [[package]] 968 | name = "datafusion-physical-optimizer" 969 | version = "42.0.0" 970 | source = "git+https://github.com/apache/datafusion.git#6f8c74ca873fe015b2e7ff4eeb2f76bf21ae9d0e" 971 | dependencies = [ 972 | "arrow-schema", 973 | "datafusion-common", 974 | "datafusion-execution", 975 | "datafusion-physical-expr", 976 | "datafusion-physical-plan", 977 | "itertools", 978 | ] 979 | 980 | [[package]] 981 | name = "datafusion-physical-plan" 982 | version = "42.0.0" 983 | source = "git+https://github.com/apache/datafusion.git#6f8c74ca873fe015b2e7ff4eeb2f76bf21ae9d0e" 984 | dependencies = [ 985 | "ahash", 986 | "arrow", 987 | "arrow-array", 988 | "arrow-buffer", 989 | "arrow-ord", 990 | "arrow-schema", 991 | "async-trait", 992 | "chrono", 993 | "datafusion-common", 994 | "datafusion-common-runtime", 995 | "datafusion-execution", 996 | "datafusion-expr", 997 | "datafusion-functions-aggregate", 998 | "datafusion-functions-aggregate-common", 999 | "datafusion-functions-window-common", 1000 | "datafusion-physical-expr", 1001 | "datafusion-physical-expr-common", 1002 | "futures", 1003 | "half", 1004 | "hashbrown 0.14.5", 1005 | "indexmap", 1006 | "itertools", 1007 | "log", 1008 | "once_cell", 1009 | "parking_lot", 1010 | "pin-project-lite", 1011 | "rand", 1012 | "tokio", 1013 | ] 1014 | 1015 | [[package]] 1016 | name = "datafusion-query-cache" 1017 | version = "0.1.0" 1018 | dependencies = [ 1019 | "async-trait", 1020 | "chrono", 1021 | "datafusion", 1022 | "futures", 1023 | "termcolor", 1024 | "tokio", 1025 | ] 1026 | 1027 | [[package]] 1028 | name = "datafusion-sql" 1029 | version = "42.0.0" 1030 | source = "git+https://github.com/apache/datafusion.git#6f8c74ca873fe015b2e7ff4eeb2f76bf21ae9d0e" 1031 | dependencies = [ 1032 | "arrow", 1033 | "arrow-array", 1034 | "arrow-schema", 1035 | "datafusion-common", 1036 | "datafusion-expr", 1037 | "log", 1038 | "regex", 1039 | "sqlparser", 1040 | "strum", 1041 | ] 1042 | 1043 | [[package]] 1044 | name = "digest" 1045 | version = "0.10.7" 1046 | source = "registry+https://github.com/rust-lang/crates.io-index" 1047 | checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" 1048 | dependencies = [ 1049 | "block-buffer", 1050 | "crypto-common", 1051 | "subtle", 1052 | ] 1053 | 1054 | [[package]] 1055 | name = "either" 1056 | version = "1.13.0" 1057 | source = "registry+https://github.com/rust-lang/crates.io-index" 1058 | checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" 1059 | 1060 | [[package]] 1061 | name = "equivalent" 1062 | version = "1.0.1" 1063 | source = "registry+https://github.com/rust-lang/crates.io-index" 1064 | checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" 1065 | 1066 | [[package]] 1067 | name = "errno" 1068 | version = "0.3.9" 1069 | source = "registry+https://github.com/rust-lang/crates.io-index" 1070 | checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" 1071 | dependencies = [ 1072 | "libc", 1073 | "windows-sys 0.52.0", 1074 | ] 1075 | 1076 | [[package]] 1077 | name = "fastrand" 1078 | version = "2.1.1" 1079 | source = "registry+https://github.com/rust-lang/crates.io-index" 1080 | checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6" 1081 | 1082 | [[package]] 1083 | name = "fixedbitset" 1084 | version = "0.4.2" 1085 | source = "registry+https://github.com/rust-lang/crates.io-index" 1086 | checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" 1087 | 1088 | [[package]] 1089 | name = "flatbuffers" 1090 | version = "24.3.25" 1091 | source = "registry+https://github.com/rust-lang/crates.io-index" 1092 | checksum = "8add37afff2d4ffa83bc748a70b4b1370984f6980768554182424ef71447c35f" 1093 | dependencies = [ 1094 | "bitflags 1.3.2", 1095 | "rustc_version", 1096 | ] 1097 | 1098 | [[package]] 1099 | name = "flate2" 1100 | version = "1.0.34" 1101 | source = "registry+https://github.com/rust-lang/crates.io-index" 1102 | checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0" 1103 | dependencies = [ 1104 | "crc32fast", 1105 | "miniz_oxide", 1106 | ] 1107 | 1108 | [[package]] 1109 | name = "form_urlencoded" 1110 | version = "1.2.1" 1111 | source = "registry+https://github.com/rust-lang/crates.io-index" 1112 | checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" 1113 | dependencies = [ 1114 | "percent-encoding", 1115 | ] 1116 | 1117 | [[package]] 1118 | name = "futures" 1119 | version = "0.3.31" 1120 | source = "registry+https://github.com/rust-lang/crates.io-index" 1121 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 1122 | dependencies = [ 1123 | "futures-channel", 1124 | "futures-core", 1125 | "futures-executor", 1126 | "futures-io", 1127 | "futures-sink", 1128 | "futures-task", 1129 | "futures-util", 1130 | ] 1131 | 1132 | [[package]] 1133 | name = "futures-channel" 1134 | version = "0.3.31" 1135 | source = "registry+https://github.com/rust-lang/crates.io-index" 1136 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 1137 | dependencies = [ 1138 | "futures-core", 1139 | "futures-sink", 1140 | ] 1141 | 1142 | [[package]] 1143 | name = "futures-core" 1144 | version = "0.3.31" 1145 | source = "registry+https://github.com/rust-lang/crates.io-index" 1146 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 1147 | 1148 | [[package]] 1149 | name = "futures-executor" 1150 | version = "0.3.31" 1151 | source = "registry+https://github.com/rust-lang/crates.io-index" 1152 | checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 1153 | dependencies = [ 1154 | "futures-core", 1155 | "futures-task", 1156 | "futures-util", 1157 | ] 1158 | 1159 | [[package]] 1160 | name = "futures-io" 1161 | version = "0.3.31" 1162 | source = "registry+https://github.com/rust-lang/crates.io-index" 1163 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 1164 | 1165 | [[package]] 1166 | name = "futures-macro" 1167 | version = "0.3.31" 1168 | source = "registry+https://github.com/rust-lang/crates.io-index" 1169 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 1170 | dependencies = [ 1171 | "proc-macro2", 1172 | "quote", 1173 | "syn", 1174 | ] 1175 | 1176 | [[package]] 1177 | name = "futures-sink" 1178 | version = "0.3.31" 1179 | source = "registry+https://github.com/rust-lang/crates.io-index" 1180 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 1181 | 1182 | [[package]] 1183 | name = "futures-task" 1184 | version = "0.3.31" 1185 | source = "registry+https://github.com/rust-lang/crates.io-index" 1186 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 1187 | 1188 | [[package]] 1189 | name = "futures-util" 1190 | version = "0.3.31" 1191 | source = "registry+https://github.com/rust-lang/crates.io-index" 1192 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 1193 | dependencies = [ 1194 | "futures-channel", 1195 | "futures-core", 1196 | "futures-io", 1197 | "futures-macro", 1198 | "futures-sink", 1199 | "futures-task", 1200 | "memchr", 1201 | "pin-project-lite", 1202 | "pin-utils", 1203 | "slab", 1204 | ] 1205 | 1206 | [[package]] 1207 | name = "generic-array" 1208 | version = "0.14.7" 1209 | source = "registry+https://github.com/rust-lang/crates.io-index" 1210 | checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" 1211 | dependencies = [ 1212 | "typenum", 1213 | "version_check", 1214 | ] 1215 | 1216 | [[package]] 1217 | name = "getrandom" 1218 | version = "0.2.15" 1219 | source = "registry+https://github.com/rust-lang/crates.io-index" 1220 | checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" 1221 | dependencies = [ 1222 | "cfg-if", 1223 | "libc", 1224 | "wasi", 1225 | ] 1226 | 1227 | [[package]] 1228 | name = "gimli" 1229 | version = "0.31.1" 1230 | source = "registry+https://github.com/rust-lang/crates.io-index" 1231 | checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" 1232 | 1233 | [[package]] 1234 | name = "glob" 1235 | version = "0.3.1" 1236 | source = "registry+https://github.com/rust-lang/crates.io-index" 1237 | checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" 1238 | 1239 | [[package]] 1240 | name = "half" 1241 | version = "2.4.1" 1242 | source = "registry+https://github.com/rust-lang/crates.io-index" 1243 | checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" 1244 | dependencies = [ 1245 | "cfg-if", 1246 | "crunchy", 1247 | "num-traits", 1248 | ] 1249 | 1250 | [[package]] 1251 | name = "hashbrown" 1252 | version = "0.14.5" 1253 | source = "registry+https://github.com/rust-lang/crates.io-index" 1254 | checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" 1255 | dependencies = [ 1256 | "ahash", 1257 | "allocator-api2", 1258 | ] 1259 | 1260 | [[package]] 1261 | name = "hashbrown" 1262 | version = "0.15.0" 1263 | source = "registry+https://github.com/rust-lang/crates.io-index" 1264 | checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" 1265 | 1266 | [[package]] 1267 | name = "heck" 1268 | version = "0.5.0" 1269 | source = "registry+https://github.com/rust-lang/crates.io-index" 1270 | checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" 1271 | 1272 | [[package]] 1273 | name = "hermit-abi" 1274 | version = "0.3.9" 1275 | source = "registry+https://github.com/rust-lang/crates.io-index" 1276 | checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" 1277 | 1278 | [[package]] 1279 | name = "hex" 1280 | version = "0.4.3" 1281 | source = "registry+https://github.com/rust-lang/crates.io-index" 1282 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" 1283 | 1284 | [[package]] 1285 | name = "humantime" 1286 | version = "2.1.0" 1287 | source = "registry+https://github.com/rust-lang/crates.io-index" 1288 | checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" 1289 | 1290 | [[package]] 1291 | name = "iana-time-zone" 1292 | version = "0.1.61" 1293 | source = "registry+https://github.com/rust-lang/crates.io-index" 1294 | checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" 1295 | dependencies = [ 1296 | "android_system_properties", 1297 | "core-foundation-sys", 1298 | "iana-time-zone-haiku", 1299 | "js-sys", 1300 | "wasm-bindgen", 1301 | "windows-core", 1302 | ] 1303 | 1304 | [[package]] 1305 | name = "iana-time-zone-haiku" 1306 | version = "0.1.2" 1307 | source = "registry+https://github.com/rust-lang/crates.io-index" 1308 | checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" 1309 | dependencies = [ 1310 | "cc", 1311 | ] 1312 | 1313 | [[package]] 1314 | name = "idna" 1315 | version = "0.5.0" 1316 | source = "registry+https://github.com/rust-lang/crates.io-index" 1317 | checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" 1318 | dependencies = [ 1319 | "unicode-bidi", 1320 | "unicode-normalization", 1321 | ] 1322 | 1323 | [[package]] 1324 | name = "indexmap" 1325 | version = "2.6.0" 1326 | source = "registry+https://github.com/rust-lang/crates.io-index" 1327 | checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" 1328 | dependencies = [ 1329 | "equivalent", 1330 | "hashbrown 0.15.0", 1331 | ] 1332 | 1333 | [[package]] 1334 | name = "instant" 1335 | version = "0.1.13" 1336 | source = "registry+https://github.com/rust-lang/crates.io-index" 1337 | checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" 1338 | dependencies = [ 1339 | "cfg-if", 1340 | "js-sys", 1341 | "wasm-bindgen", 1342 | "web-sys", 1343 | ] 1344 | 1345 | [[package]] 1346 | name = "integer-encoding" 1347 | version = "3.0.4" 1348 | source = "registry+https://github.com/rust-lang/crates.io-index" 1349 | checksum = "8bb03732005da905c88227371639bf1ad885cc712789c011c31c5fb3ab3ccf02" 1350 | 1351 | [[package]] 1352 | name = "itertools" 1353 | version = "0.13.0" 1354 | source = "registry+https://github.com/rust-lang/crates.io-index" 1355 | checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" 1356 | dependencies = [ 1357 | "either", 1358 | ] 1359 | 1360 | [[package]] 1361 | name = "itoa" 1362 | version = "1.0.11" 1363 | source = "registry+https://github.com/rust-lang/crates.io-index" 1364 | checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" 1365 | 1366 | [[package]] 1367 | name = "jobserver" 1368 | version = "0.1.32" 1369 | source = "registry+https://github.com/rust-lang/crates.io-index" 1370 | checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" 1371 | dependencies = [ 1372 | "libc", 1373 | ] 1374 | 1375 | [[package]] 1376 | name = "js-sys" 1377 | version = "0.3.70" 1378 | source = "registry+https://github.com/rust-lang/crates.io-index" 1379 | checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a" 1380 | dependencies = [ 1381 | "wasm-bindgen", 1382 | ] 1383 | 1384 | [[package]] 1385 | name = "lazy_static" 1386 | version = "1.5.0" 1387 | source = "registry+https://github.com/rust-lang/crates.io-index" 1388 | checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" 1389 | 1390 | [[package]] 1391 | name = "lexical-core" 1392 | version = "0.8.5" 1393 | source = "registry+https://github.com/rust-lang/crates.io-index" 1394 | checksum = "2cde5de06e8d4c2faabc400238f9ae1c74d5412d03a7bd067645ccbc47070e46" 1395 | dependencies = [ 1396 | "lexical-parse-float", 1397 | "lexical-parse-integer", 1398 | "lexical-util", 1399 | "lexical-write-float", 1400 | "lexical-write-integer", 1401 | ] 1402 | 1403 | [[package]] 1404 | name = "lexical-parse-float" 1405 | version = "0.8.5" 1406 | source = "registry+https://github.com/rust-lang/crates.io-index" 1407 | checksum = "683b3a5ebd0130b8fb52ba0bdc718cc56815b6a097e28ae5a6997d0ad17dc05f" 1408 | dependencies = [ 1409 | "lexical-parse-integer", 1410 | "lexical-util", 1411 | "static_assertions", 1412 | ] 1413 | 1414 | [[package]] 1415 | name = "lexical-parse-integer" 1416 | version = "0.8.6" 1417 | source = "registry+https://github.com/rust-lang/crates.io-index" 1418 | checksum = "6d0994485ed0c312f6d965766754ea177d07f9c00c9b82a5ee62ed5b47945ee9" 1419 | dependencies = [ 1420 | "lexical-util", 1421 | "static_assertions", 1422 | ] 1423 | 1424 | [[package]] 1425 | name = "lexical-util" 1426 | version = "0.8.5" 1427 | source = "registry+https://github.com/rust-lang/crates.io-index" 1428 | checksum = "5255b9ff16ff898710eb9eb63cb39248ea8a5bb036bea8085b1a767ff6c4e3fc" 1429 | dependencies = [ 1430 | "static_assertions", 1431 | ] 1432 | 1433 | [[package]] 1434 | name = "lexical-write-float" 1435 | version = "0.8.5" 1436 | source = "registry+https://github.com/rust-lang/crates.io-index" 1437 | checksum = "accabaa1c4581f05a3923d1b4cfd124c329352288b7b9da09e766b0668116862" 1438 | dependencies = [ 1439 | "lexical-util", 1440 | "lexical-write-integer", 1441 | "static_assertions", 1442 | ] 1443 | 1444 | [[package]] 1445 | name = "lexical-write-integer" 1446 | version = "0.8.5" 1447 | source = "registry+https://github.com/rust-lang/crates.io-index" 1448 | checksum = "e1b6f3d1f4422866b68192d62f77bc5c700bee84f3069f2469d7bc8c77852446" 1449 | dependencies = [ 1450 | "lexical-util", 1451 | "static_assertions", 1452 | ] 1453 | 1454 | [[package]] 1455 | name = "libc" 1456 | version = "0.2.159" 1457 | source = "registry+https://github.com/rust-lang/crates.io-index" 1458 | checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5" 1459 | 1460 | [[package]] 1461 | name = "libm" 1462 | version = "0.2.8" 1463 | source = "registry+https://github.com/rust-lang/crates.io-index" 1464 | checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" 1465 | 1466 | [[package]] 1467 | name = "linux-raw-sys" 1468 | version = "0.4.14" 1469 | source = "registry+https://github.com/rust-lang/crates.io-index" 1470 | checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" 1471 | 1472 | [[package]] 1473 | name = "lock_api" 1474 | version = "0.4.12" 1475 | source = "registry+https://github.com/rust-lang/crates.io-index" 1476 | checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" 1477 | dependencies = [ 1478 | "autocfg", 1479 | "scopeguard", 1480 | ] 1481 | 1482 | [[package]] 1483 | name = "log" 1484 | version = "0.4.22" 1485 | source = "registry+https://github.com/rust-lang/crates.io-index" 1486 | checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" 1487 | 1488 | [[package]] 1489 | name = "lz4_flex" 1490 | version = "0.11.3" 1491 | source = "registry+https://github.com/rust-lang/crates.io-index" 1492 | checksum = "75761162ae2b0e580d7e7c390558127e5f01b4194debd6221fd8c207fc80e3f5" 1493 | dependencies = [ 1494 | "twox-hash", 1495 | ] 1496 | 1497 | [[package]] 1498 | name = "lzma-sys" 1499 | version = "0.1.20" 1500 | source = "registry+https://github.com/rust-lang/crates.io-index" 1501 | checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27" 1502 | dependencies = [ 1503 | "cc", 1504 | "libc", 1505 | "pkg-config", 1506 | ] 1507 | 1508 | [[package]] 1509 | name = "md-5" 1510 | version = "0.10.6" 1511 | source = "registry+https://github.com/rust-lang/crates.io-index" 1512 | checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" 1513 | dependencies = [ 1514 | "cfg-if", 1515 | "digest", 1516 | ] 1517 | 1518 | [[package]] 1519 | name = "memchr" 1520 | version = "2.7.4" 1521 | source = "registry+https://github.com/rust-lang/crates.io-index" 1522 | checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" 1523 | 1524 | [[package]] 1525 | name = "miniz_oxide" 1526 | version = "0.8.0" 1527 | source = "registry+https://github.com/rust-lang/crates.io-index" 1528 | checksum = "e2d80299ef12ff69b16a84bb182e3b9df68b5a91574d3d4fa6e41b65deec4df1" 1529 | dependencies = [ 1530 | "adler2", 1531 | ] 1532 | 1533 | [[package]] 1534 | name = "mio" 1535 | version = "1.0.2" 1536 | source = "registry+https://github.com/rust-lang/crates.io-index" 1537 | checksum = "80e04d1dcff3aae0704555fe5fee3bcfaf3d1fdf8a7e521d5b9d2b42acb52cec" 1538 | dependencies = [ 1539 | "hermit-abi", 1540 | "libc", 1541 | "wasi", 1542 | "windows-sys 0.52.0", 1543 | ] 1544 | 1545 | [[package]] 1546 | name = "num" 1547 | version = "0.4.3" 1548 | source = "registry+https://github.com/rust-lang/crates.io-index" 1549 | checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" 1550 | dependencies = [ 1551 | "num-bigint", 1552 | "num-complex", 1553 | "num-integer", 1554 | "num-iter", 1555 | "num-rational", 1556 | "num-traits", 1557 | ] 1558 | 1559 | [[package]] 1560 | name = "num-bigint" 1561 | version = "0.4.6" 1562 | source = "registry+https://github.com/rust-lang/crates.io-index" 1563 | checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" 1564 | dependencies = [ 1565 | "num-integer", 1566 | "num-traits", 1567 | ] 1568 | 1569 | [[package]] 1570 | name = "num-complex" 1571 | version = "0.4.6" 1572 | source = "registry+https://github.com/rust-lang/crates.io-index" 1573 | checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" 1574 | dependencies = [ 1575 | "num-traits", 1576 | ] 1577 | 1578 | [[package]] 1579 | name = "num-integer" 1580 | version = "0.1.46" 1581 | source = "registry+https://github.com/rust-lang/crates.io-index" 1582 | checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" 1583 | dependencies = [ 1584 | "num-traits", 1585 | ] 1586 | 1587 | [[package]] 1588 | name = "num-iter" 1589 | version = "0.1.45" 1590 | source = "registry+https://github.com/rust-lang/crates.io-index" 1591 | checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" 1592 | dependencies = [ 1593 | "autocfg", 1594 | "num-integer", 1595 | "num-traits", 1596 | ] 1597 | 1598 | [[package]] 1599 | name = "num-rational" 1600 | version = "0.4.2" 1601 | source = "registry+https://github.com/rust-lang/crates.io-index" 1602 | checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" 1603 | dependencies = [ 1604 | "num-bigint", 1605 | "num-integer", 1606 | "num-traits", 1607 | ] 1608 | 1609 | [[package]] 1610 | name = "num-traits" 1611 | version = "0.2.19" 1612 | source = "registry+https://github.com/rust-lang/crates.io-index" 1613 | checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" 1614 | dependencies = [ 1615 | "autocfg", 1616 | "libm", 1617 | ] 1618 | 1619 | [[package]] 1620 | name = "num_cpus" 1621 | version = "1.16.0" 1622 | source = "registry+https://github.com/rust-lang/crates.io-index" 1623 | checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" 1624 | dependencies = [ 1625 | "hermit-abi", 1626 | "libc", 1627 | ] 1628 | 1629 | [[package]] 1630 | name = "object" 1631 | version = "0.36.5" 1632 | source = "registry+https://github.com/rust-lang/crates.io-index" 1633 | checksum = "aedf0a2d09c573ed1d8d85b30c119153926a2b36dce0ab28322c09a117a4683e" 1634 | dependencies = [ 1635 | "memchr", 1636 | ] 1637 | 1638 | [[package]] 1639 | name = "object_store" 1640 | version = "0.11.0" 1641 | source = "registry+https://github.com/rust-lang/crates.io-index" 1642 | checksum = "25a0c4b3a0e31f8b66f71ad8064521efa773910196e2cde791436f13409f3b45" 1643 | dependencies = [ 1644 | "async-trait", 1645 | "bytes", 1646 | "chrono", 1647 | "futures", 1648 | "humantime", 1649 | "itertools", 1650 | "parking_lot", 1651 | "percent-encoding", 1652 | "snafu", 1653 | "tokio", 1654 | "tracing", 1655 | "url", 1656 | "walkdir", 1657 | ] 1658 | 1659 | [[package]] 1660 | name = "once_cell" 1661 | version = "1.20.1" 1662 | source = "registry+https://github.com/rust-lang/crates.io-index" 1663 | checksum = "82881c4be219ab5faaf2ad5e5e5ecdff8c66bd7402ca3160975c93b24961afd1" 1664 | dependencies = [ 1665 | "portable-atomic", 1666 | ] 1667 | 1668 | [[package]] 1669 | name = "ordered-float" 1670 | version = "2.10.1" 1671 | source = "registry+https://github.com/rust-lang/crates.io-index" 1672 | checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" 1673 | dependencies = [ 1674 | "num-traits", 1675 | ] 1676 | 1677 | [[package]] 1678 | name = "parking_lot" 1679 | version = "0.12.3" 1680 | source = "registry+https://github.com/rust-lang/crates.io-index" 1681 | checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" 1682 | dependencies = [ 1683 | "lock_api", 1684 | "parking_lot_core", 1685 | ] 1686 | 1687 | [[package]] 1688 | name = "parking_lot_core" 1689 | version = "0.9.10" 1690 | source = "registry+https://github.com/rust-lang/crates.io-index" 1691 | checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" 1692 | dependencies = [ 1693 | "cfg-if", 1694 | "libc", 1695 | "redox_syscall", 1696 | "smallvec", 1697 | "windows-targets", 1698 | ] 1699 | 1700 | [[package]] 1701 | name = "parquet" 1702 | version = "53.0.0" 1703 | source = "registry+https://github.com/rust-lang/crates.io-index" 1704 | checksum = "f0fbf928021131daaa57d334ca8e3904fe9ae22f73c56244fc7db9b04eedc3d8" 1705 | dependencies = [ 1706 | "ahash", 1707 | "arrow-array", 1708 | "arrow-buffer", 1709 | "arrow-cast", 1710 | "arrow-data", 1711 | "arrow-ipc", 1712 | "arrow-schema", 1713 | "arrow-select", 1714 | "base64", 1715 | "brotli", 1716 | "bytes", 1717 | "chrono", 1718 | "flate2", 1719 | "futures", 1720 | "half", 1721 | "hashbrown 0.14.5", 1722 | "lz4_flex", 1723 | "num", 1724 | "num-bigint", 1725 | "object_store", 1726 | "paste", 1727 | "seq-macro", 1728 | "snap", 1729 | "thrift", 1730 | "tokio", 1731 | "twox-hash", 1732 | "zstd", 1733 | "zstd-sys", 1734 | ] 1735 | 1736 | [[package]] 1737 | name = "parse-zoneinfo" 1738 | version = "0.3.1" 1739 | source = "registry+https://github.com/rust-lang/crates.io-index" 1740 | checksum = "1f2a05b18d44e2957b88f96ba460715e295bc1d7510468a2f3d3b44535d26c24" 1741 | dependencies = [ 1742 | "regex", 1743 | ] 1744 | 1745 | [[package]] 1746 | name = "paste" 1747 | version = "1.0.15" 1748 | source = "registry+https://github.com/rust-lang/crates.io-index" 1749 | checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" 1750 | 1751 | [[package]] 1752 | name = "percent-encoding" 1753 | version = "2.3.1" 1754 | source = "registry+https://github.com/rust-lang/crates.io-index" 1755 | checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" 1756 | 1757 | [[package]] 1758 | name = "petgraph" 1759 | version = "0.6.5" 1760 | source = "registry+https://github.com/rust-lang/crates.io-index" 1761 | checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" 1762 | dependencies = [ 1763 | "fixedbitset", 1764 | "indexmap", 1765 | ] 1766 | 1767 | [[package]] 1768 | name = "phf" 1769 | version = "0.11.2" 1770 | source = "registry+https://github.com/rust-lang/crates.io-index" 1771 | checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" 1772 | dependencies = [ 1773 | "phf_shared", 1774 | ] 1775 | 1776 | [[package]] 1777 | name = "phf_codegen" 1778 | version = "0.11.2" 1779 | source = "registry+https://github.com/rust-lang/crates.io-index" 1780 | checksum = "e8d39688d359e6b34654d328e262234662d16cc0f60ec8dcbe5e718709342a5a" 1781 | dependencies = [ 1782 | "phf_generator", 1783 | "phf_shared", 1784 | ] 1785 | 1786 | [[package]] 1787 | name = "phf_generator" 1788 | version = "0.11.2" 1789 | source = "registry+https://github.com/rust-lang/crates.io-index" 1790 | checksum = "48e4cc64c2ad9ebe670cb8fd69dd50ae301650392e81c05f9bfcb2d5bdbc24b0" 1791 | dependencies = [ 1792 | "phf_shared", 1793 | "rand", 1794 | ] 1795 | 1796 | [[package]] 1797 | name = "phf_shared" 1798 | version = "0.11.2" 1799 | source = "registry+https://github.com/rust-lang/crates.io-index" 1800 | checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" 1801 | dependencies = [ 1802 | "siphasher", 1803 | ] 1804 | 1805 | [[package]] 1806 | name = "pin-project-lite" 1807 | version = "0.2.14" 1808 | source = "registry+https://github.com/rust-lang/crates.io-index" 1809 | checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" 1810 | 1811 | [[package]] 1812 | name = "pin-utils" 1813 | version = "0.1.0" 1814 | source = "registry+https://github.com/rust-lang/crates.io-index" 1815 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 1816 | 1817 | [[package]] 1818 | name = "pkg-config" 1819 | version = "0.3.31" 1820 | source = "registry+https://github.com/rust-lang/crates.io-index" 1821 | checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" 1822 | 1823 | [[package]] 1824 | name = "portable-atomic" 1825 | version = "1.9.0" 1826 | source = "registry+https://github.com/rust-lang/crates.io-index" 1827 | checksum = "cc9c68a3f6da06753e9335d63e27f6b9754dd1920d941135b7ea8224f141adb2" 1828 | 1829 | [[package]] 1830 | name = "ppv-lite86" 1831 | version = "0.2.20" 1832 | source = "registry+https://github.com/rust-lang/crates.io-index" 1833 | checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" 1834 | dependencies = [ 1835 | "zerocopy", 1836 | ] 1837 | 1838 | [[package]] 1839 | name = "proc-macro2" 1840 | version = "1.0.86" 1841 | source = "registry+https://github.com/rust-lang/crates.io-index" 1842 | checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" 1843 | dependencies = [ 1844 | "unicode-ident", 1845 | ] 1846 | 1847 | [[package]] 1848 | name = "quote" 1849 | version = "1.0.37" 1850 | source = "registry+https://github.com/rust-lang/crates.io-index" 1851 | checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af" 1852 | dependencies = [ 1853 | "proc-macro2", 1854 | ] 1855 | 1856 | [[package]] 1857 | name = "rand" 1858 | version = "0.8.5" 1859 | source = "registry+https://github.com/rust-lang/crates.io-index" 1860 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" 1861 | dependencies = [ 1862 | "libc", 1863 | "rand_chacha", 1864 | "rand_core", 1865 | ] 1866 | 1867 | [[package]] 1868 | name = "rand_chacha" 1869 | version = "0.3.1" 1870 | source = "registry+https://github.com/rust-lang/crates.io-index" 1871 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" 1872 | dependencies = [ 1873 | "ppv-lite86", 1874 | "rand_core", 1875 | ] 1876 | 1877 | [[package]] 1878 | name = "rand_core" 1879 | version = "0.6.4" 1880 | source = "registry+https://github.com/rust-lang/crates.io-index" 1881 | checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" 1882 | dependencies = [ 1883 | "getrandom", 1884 | ] 1885 | 1886 | [[package]] 1887 | name = "redox_syscall" 1888 | version = "0.5.7" 1889 | source = "registry+https://github.com/rust-lang/crates.io-index" 1890 | checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f" 1891 | dependencies = [ 1892 | "bitflags 2.6.0", 1893 | ] 1894 | 1895 | [[package]] 1896 | name = "regex" 1897 | version = "1.11.0" 1898 | source = "registry+https://github.com/rust-lang/crates.io-index" 1899 | checksum = "38200e5ee88914975b69f657f0801b6f6dccafd44fd9326302a4aaeecfacb1d8" 1900 | dependencies = [ 1901 | "aho-corasick", 1902 | "memchr", 1903 | "regex-automata", 1904 | "regex-syntax", 1905 | ] 1906 | 1907 | [[package]] 1908 | name = "regex-automata" 1909 | version = "0.4.8" 1910 | source = "registry+https://github.com/rust-lang/crates.io-index" 1911 | checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3" 1912 | dependencies = [ 1913 | "aho-corasick", 1914 | "memchr", 1915 | "regex-syntax", 1916 | ] 1917 | 1918 | [[package]] 1919 | name = "regex-syntax" 1920 | version = "0.8.5" 1921 | source = "registry+https://github.com/rust-lang/crates.io-index" 1922 | checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" 1923 | 1924 | [[package]] 1925 | name = "rustc-demangle" 1926 | version = "0.1.24" 1927 | source = "registry+https://github.com/rust-lang/crates.io-index" 1928 | checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" 1929 | 1930 | [[package]] 1931 | name = "rustc_version" 1932 | version = "0.4.1" 1933 | source = "registry+https://github.com/rust-lang/crates.io-index" 1934 | checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 1935 | dependencies = [ 1936 | "semver", 1937 | ] 1938 | 1939 | [[package]] 1940 | name = "rustix" 1941 | version = "0.38.37" 1942 | source = "registry+https://github.com/rust-lang/crates.io-index" 1943 | checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" 1944 | dependencies = [ 1945 | "bitflags 2.6.0", 1946 | "errno", 1947 | "libc", 1948 | "linux-raw-sys", 1949 | "windows-sys 0.52.0", 1950 | ] 1951 | 1952 | [[package]] 1953 | name = "rustversion" 1954 | version = "1.0.17" 1955 | source = "registry+https://github.com/rust-lang/crates.io-index" 1956 | checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" 1957 | 1958 | [[package]] 1959 | name = "ryu" 1960 | version = "1.0.18" 1961 | source = "registry+https://github.com/rust-lang/crates.io-index" 1962 | checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" 1963 | 1964 | [[package]] 1965 | name = "same-file" 1966 | version = "1.0.6" 1967 | source = "registry+https://github.com/rust-lang/crates.io-index" 1968 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 1969 | dependencies = [ 1970 | "winapi-util", 1971 | ] 1972 | 1973 | [[package]] 1974 | name = "scopeguard" 1975 | version = "1.2.0" 1976 | source = "registry+https://github.com/rust-lang/crates.io-index" 1977 | checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" 1978 | 1979 | [[package]] 1980 | name = "semver" 1981 | version = "1.0.23" 1982 | source = "registry+https://github.com/rust-lang/crates.io-index" 1983 | checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" 1984 | 1985 | [[package]] 1986 | name = "seq-macro" 1987 | version = "0.3.5" 1988 | source = "registry+https://github.com/rust-lang/crates.io-index" 1989 | checksum = "a3f0bf26fd526d2a95683cd0f87bf103b8539e2ca1ef48ce002d67aad59aa0b4" 1990 | 1991 | [[package]] 1992 | name = "serde" 1993 | version = "1.0.210" 1994 | source = "registry+https://github.com/rust-lang/crates.io-index" 1995 | checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a" 1996 | dependencies = [ 1997 | "serde_derive", 1998 | ] 1999 | 2000 | [[package]] 2001 | name = "serde_derive" 2002 | version = "1.0.210" 2003 | source = "registry+https://github.com/rust-lang/crates.io-index" 2004 | checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f" 2005 | dependencies = [ 2006 | "proc-macro2", 2007 | "quote", 2008 | "syn", 2009 | ] 2010 | 2011 | [[package]] 2012 | name = "serde_json" 2013 | version = "1.0.128" 2014 | source = "registry+https://github.com/rust-lang/crates.io-index" 2015 | checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8" 2016 | dependencies = [ 2017 | "itoa", 2018 | "memchr", 2019 | "ryu", 2020 | "serde", 2021 | ] 2022 | 2023 | [[package]] 2024 | name = "sha2" 2025 | version = "0.10.8" 2026 | source = "registry+https://github.com/rust-lang/crates.io-index" 2027 | checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" 2028 | dependencies = [ 2029 | "cfg-if", 2030 | "cpufeatures", 2031 | "digest", 2032 | ] 2033 | 2034 | [[package]] 2035 | name = "shlex" 2036 | version = "1.3.0" 2037 | source = "registry+https://github.com/rust-lang/crates.io-index" 2038 | checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" 2039 | 2040 | [[package]] 2041 | name = "signal-hook-registry" 2042 | version = "1.4.2" 2043 | source = "registry+https://github.com/rust-lang/crates.io-index" 2044 | checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" 2045 | dependencies = [ 2046 | "libc", 2047 | ] 2048 | 2049 | [[package]] 2050 | name = "siphasher" 2051 | version = "0.3.11" 2052 | source = "registry+https://github.com/rust-lang/crates.io-index" 2053 | checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" 2054 | 2055 | [[package]] 2056 | name = "slab" 2057 | version = "0.4.9" 2058 | source = "registry+https://github.com/rust-lang/crates.io-index" 2059 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 2060 | dependencies = [ 2061 | "autocfg", 2062 | ] 2063 | 2064 | [[package]] 2065 | name = "smallvec" 2066 | version = "1.13.2" 2067 | source = "registry+https://github.com/rust-lang/crates.io-index" 2068 | checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" 2069 | 2070 | [[package]] 2071 | name = "snafu" 2072 | version = "0.8.5" 2073 | source = "registry+https://github.com/rust-lang/crates.io-index" 2074 | checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" 2075 | dependencies = [ 2076 | "snafu-derive", 2077 | ] 2078 | 2079 | [[package]] 2080 | name = "snafu-derive" 2081 | version = "0.8.5" 2082 | source = "registry+https://github.com/rust-lang/crates.io-index" 2083 | checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" 2084 | dependencies = [ 2085 | "heck", 2086 | "proc-macro2", 2087 | "quote", 2088 | "syn", 2089 | ] 2090 | 2091 | [[package]] 2092 | name = "snap" 2093 | version = "1.1.1" 2094 | source = "registry+https://github.com/rust-lang/crates.io-index" 2095 | checksum = "1b6b67fb9a61334225b5b790716f609cd58395f895b3fe8b328786812a40bc3b" 2096 | 2097 | [[package]] 2098 | name = "socket2" 2099 | version = "0.5.7" 2100 | source = "registry+https://github.com/rust-lang/crates.io-index" 2101 | checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" 2102 | dependencies = [ 2103 | "libc", 2104 | "windows-sys 0.52.0", 2105 | ] 2106 | 2107 | [[package]] 2108 | name = "sqlparser" 2109 | version = "0.51.0" 2110 | source = "registry+https://github.com/rust-lang/crates.io-index" 2111 | checksum = "5fe11944a61da0da3f592e19a45ebe5ab92dc14a779907ff1f08fbb797bfefc7" 2112 | dependencies = [ 2113 | "log", 2114 | "sqlparser_derive", 2115 | ] 2116 | 2117 | [[package]] 2118 | name = "sqlparser_derive" 2119 | version = "0.2.2" 2120 | source = "registry+https://github.com/rust-lang/crates.io-index" 2121 | checksum = "01b2e185515564f15375f593fb966b5718bc624ba77fe49fa4616ad619690554" 2122 | dependencies = [ 2123 | "proc-macro2", 2124 | "quote", 2125 | "syn", 2126 | ] 2127 | 2128 | [[package]] 2129 | name = "static_assertions" 2130 | version = "1.1.0" 2131 | source = "registry+https://github.com/rust-lang/crates.io-index" 2132 | checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" 2133 | 2134 | [[package]] 2135 | name = "strum" 2136 | version = "0.26.3" 2137 | source = "registry+https://github.com/rust-lang/crates.io-index" 2138 | checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" 2139 | dependencies = [ 2140 | "strum_macros", 2141 | ] 2142 | 2143 | [[package]] 2144 | name = "strum_macros" 2145 | version = "0.26.4" 2146 | source = "registry+https://github.com/rust-lang/crates.io-index" 2147 | checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" 2148 | dependencies = [ 2149 | "heck", 2150 | "proc-macro2", 2151 | "quote", 2152 | "rustversion", 2153 | "syn", 2154 | ] 2155 | 2156 | [[package]] 2157 | name = "subtle" 2158 | version = "2.6.1" 2159 | source = "registry+https://github.com/rust-lang/crates.io-index" 2160 | checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" 2161 | 2162 | [[package]] 2163 | name = "syn" 2164 | version = "2.0.79" 2165 | source = "registry+https://github.com/rust-lang/crates.io-index" 2166 | checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590" 2167 | dependencies = [ 2168 | "proc-macro2", 2169 | "quote", 2170 | "unicode-ident", 2171 | ] 2172 | 2173 | [[package]] 2174 | name = "tempfile" 2175 | version = "3.13.0" 2176 | source = "registry+https://github.com/rust-lang/crates.io-index" 2177 | checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" 2178 | dependencies = [ 2179 | "cfg-if", 2180 | "fastrand", 2181 | "once_cell", 2182 | "rustix", 2183 | "windows-sys 0.59.0", 2184 | ] 2185 | 2186 | [[package]] 2187 | name = "termcolor" 2188 | version = "1.4.1" 2189 | source = "registry+https://github.com/rust-lang/crates.io-index" 2190 | checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" 2191 | dependencies = [ 2192 | "winapi-util", 2193 | ] 2194 | 2195 | [[package]] 2196 | name = "thrift" 2197 | version = "0.17.0" 2198 | source = "registry+https://github.com/rust-lang/crates.io-index" 2199 | checksum = "7e54bc85fc7faa8bc175c4bab5b92ba8d9a3ce893d0e9f42cc455c8ab16a9e09" 2200 | dependencies = [ 2201 | "byteorder", 2202 | "integer-encoding", 2203 | "ordered-float", 2204 | ] 2205 | 2206 | [[package]] 2207 | name = "tiny-keccak" 2208 | version = "2.0.2" 2209 | source = "registry+https://github.com/rust-lang/crates.io-index" 2210 | checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" 2211 | dependencies = [ 2212 | "crunchy", 2213 | ] 2214 | 2215 | [[package]] 2216 | name = "tinyvec" 2217 | version = "1.8.0" 2218 | source = "registry+https://github.com/rust-lang/crates.io-index" 2219 | checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938" 2220 | dependencies = [ 2221 | "tinyvec_macros", 2222 | ] 2223 | 2224 | [[package]] 2225 | name = "tinyvec_macros" 2226 | version = "0.1.1" 2227 | source = "registry+https://github.com/rust-lang/crates.io-index" 2228 | checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" 2229 | 2230 | [[package]] 2231 | name = "tokio" 2232 | version = "1.40.0" 2233 | source = "registry+https://github.com/rust-lang/crates.io-index" 2234 | checksum = "e2b070231665d27ad9ec9b8df639893f46727666c6767db40317fbe920a5d998" 2235 | dependencies = [ 2236 | "backtrace", 2237 | "bytes", 2238 | "libc", 2239 | "mio", 2240 | "parking_lot", 2241 | "pin-project-lite", 2242 | "signal-hook-registry", 2243 | "socket2", 2244 | "tokio-macros", 2245 | "windows-sys 0.52.0", 2246 | ] 2247 | 2248 | [[package]] 2249 | name = "tokio-macros" 2250 | version = "2.4.0" 2251 | source = "registry+https://github.com/rust-lang/crates.io-index" 2252 | checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" 2253 | dependencies = [ 2254 | "proc-macro2", 2255 | "quote", 2256 | "syn", 2257 | ] 2258 | 2259 | [[package]] 2260 | name = "tokio-util" 2261 | version = "0.7.12" 2262 | source = "registry+https://github.com/rust-lang/crates.io-index" 2263 | checksum = "61e7c3654c13bcd040d4a03abee2c75b1d14a37b423cf5a813ceae1cc903ec6a" 2264 | dependencies = [ 2265 | "bytes", 2266 | "futures-core", 2267 | "futures-sink", 2268 | "pin-project-lite", 2269 | "tokio", 2270 | ] 2271 | 2272 | [[package]] 2273 | name = "tracing" 2274 | version = "0.1.40" 2275 | source = "registry+https://github.com/rust-lang/crates.io-index" 2276 | checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" 2277 | dependencies = [ 2278 | "pin-project-lite", 2279 | "tracing-attributes", 2280 | "tracing-core", 2281 | ] 2282 | 2283 | [[package]] 2284 | name = "tracing-attributes" 2285 | version = "0.1.27" 2286 | source = "registry+https://github.com/rust-lang/crates.io-index" 2287 | checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" 2288 | dependencies = [ 2289 | "proc-macro2", 2290 | "quote", 2291 | "syn", 2292 | ] 2293 | 2294 | [[package]] 2295 | name = "tracing-core" 2296 | version = "0.1.32" 2297 | source = "registry+https://github.com/rust-lang/crates.io-index" 2298 | checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" 2299 | dependencies = [ 2300 | "once_cell", 2301 | ] 2302 | 2303 | [[package]] 2304 | name = "twox-hash" 2305 | version = "1.6.3" 2306 | source = "registry+https://github.com/rust-lang/crates.io-index" 2307 | checksum = "97fee6b57c6a41524a810daee9286c02d7752c4253064d0b05472833a438f675" 2308 | dependencies = [ 2309 | "cfg-if", 2310 | "static_assertions", 2311 | ] 2312 | 2313 | [[package]] 2314 | name = "typenum" 2315 | version = "1.17.0" 2316 | source = "registry+https://github.com/rust-lang/crates.io-index" 2317 | checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" 2318 | 2319 | [[package]] 2320 | name = "unicode-bidi" 2321 | version = "0.3.17" 2322 | source = "registry+https://github.com/rust-lang/crates.io-index" 2323 | checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893" 2324 | 2325 | [[package]] 2326 | name = "unicode-ident" 2327 | version = "1.0.13" 2328 | source = "registry+https://github.com/rust-lang/crates.io-index" 2329 | checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" 2330 | 2331 | [[package]] 2332 | name = "unicode-normalization" 2333 | version = "0.1.24" 2334 | source = "registry+https://github.com/rust-lang/crates.io-index" 2335 | checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" 2336 | dependencies = [ 2337 | "tinyvec", 2338 | ] 2339 | 2340 | [[package]] 2341 | name = "unicode-segmentation" 2342 | version = "1.12.0" 2343 | source = "registry+https://github.com/rust-lang/crates.io-index" 2344 | checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" 2345 | 2346 | [[package]] 2347 | name = "unicode-width" 2348 | version = "0.1.14" 2349 | source = "registry+https://github.com/rust-lang/crates.io-index" 2350 | checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" 2351 | 2352 | [[package]] 2353 | name = "url" 2354 | version = "2.5.2" 2355 | source = "registry+https://github.com/rust-lang/crates.io-index" 2356 | checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c" 2357 | dependencies = [ 2358 | "form_urlencoded", 2359 | "idna", 2360 | "percent-encoding", 2361 | ] 2362 | 2363 | [[package]] 2364 | name = "uuid" 2365 | version = "1.10.0" 2366 | source = "registry+https://github.com/rust-lang/crates.io-index" 2367 | checksum = "81dfa00651efa65069b0b6b651f4aaa31ba9e3c3ce0137aaad053604ee7e0314" 2368 | dependencies = [ 2369 | "getrandom", 2370 | ] 2371 | 2372 | [[package]] 2373 | name = "version_check" 2374 | version = "0.9.5" 2375 | source = "registry+https://github.com/rust-lang/crates.io-index" 2376 | checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" 2377 | 2378 | [[package]] 2379 | name = "walkdir" 2380 | version = "2.5.0" 2381 | source = "registry+https://github.com/rust-lang/crates.io-index" 2382 | checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" 2383 | dependencies = [ 2384 | "same-file", 2385 | "winapi-util", 2386 | ] 2387 | 2388 | [[package]] 2389 | name = "wasi" 2390 | version = "0.11.0+wasi-snapshot-preview1" 2391 | source = "registry+https://github.com/rust-lang/crates.io-index" 2392 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" 2393 | 2394 | [[package]] 2395 | name = "wasm-bindgen" 2396 | version = "0.2.93" 2397 | source = "registry+https://github.com/rust-lang/crates.io-index" 2398 | checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5" 2399 | dependencies = [ 2400 | "cfg-if", 2401 | "once_cell", 2402 | "wasm-bindgen-macro", 2403 | ] 2404 | 2405 | [[package]] 2406 | name = "wasm-bindgen-backend" 2407 | version = "0.2.93" 2408 | source = "registry+https://github.com/rust-lang/crates.io-index" 2409 | checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b" 2410 | dependencies = [ 2411 | "bumpalo", 2412 | "log", 2413 | "once_cell", 2414 | "proc-macro2", 2415 | "quote", 2416 | "syn", 2417 | "wasm-bindgen-shared", 2418 | ] 2419 | 2420 | [[package]] 2421 | name = "wasm-bindgen-macro" 2422 | version = "0.2.93" 2423 | source = "registry+https://github.com/rust-lang/crates.io-index" 2424 | checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf" 2425 | dependencies = [ 2426 | "quote", 2427 | "wasm-bindgen-macro-support", 2428 | ] 2429 | 2430 | [[package]] 2431 | name = "wasm-bindgen-macro-support" 2432 | version = "0.2.93" 2433 | source = "registry+https://github.com/rust-lang/crates.io-index" 2434 | checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836" 2435 | dependencies = [ 2436 | "proc-macro2", 2437 | "quote", 2438 | "syn", 2439 | "wasm-bindgen-backend", 2440 | "wasm-bindgen-shared", 2441 | ] 2442 | 2443 | [[package]] 2444 | name = "wasm-bindgen-shared" 2445 | version = "0.2.93" 2446 | source = "registry+https://github.com/rust-lang/crates.io-index" 2447 | checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484" 2448 | 2449 | [[package]] 2450 | name = "web-sys" 2451 | version = "0.3.70" 2452 | source = "registry+https://github.com/rust-lang/crates.io-index" 2453 | checksum = "26fdeaafd9bd129f65e7c031593c24d62186301e0c72c8978fa1678be7d532c0" 2454 | dependencies = [ 2455 | "js-sys", 2456 | "wasm-bindgen", 2457 | ] 2458 | 2459 | [[package]] 2460 | name = "winapi-util" 2461 | version = "0.1.9" 2462 | source = "registry+https://github.com/rust-lang/crates.io-index" 2463 | checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" 2464 | dependencies = [ 2465 | "windows-sys 0.59.0", 2466 | ] 2467 | 2468 | [[package]] 2469 | name = "windows-core" 2470 | version = "0.52.0" 2471 | source = "registry+https://github.com/rust-lang/crates.io-index" 2472 | checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" 2473 | dependencies = [ 2474 | "windows-targets", 2475 | ] 2476 | 2477 | [[package]] 2478 | name = "windows-sys" 2479 | version = "0.52.0" 2480 | source = "registry+https://github.com/rust-lang/crates.io-index" 2481 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 2482 | dependencies = [ 2483 | "windows-targets", 2484 | ] 2485 | 2486 | [[package]] 2487 | name = "windows-sys" 2488 | version = "0.59.0" 2489 | source = "registry+https://github.com/rust-lang/crates.io-index" 2490 | checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" 2491 | dependencies = [ 2492 | "windows-targets", 2493 | ] 2494 | 2495 | [[package]] 2496 | name = "windows-targets" 2497 | version = "0.52.6" 2498 | source = "registry+https://github.com/rust-lang/crates.io-index" 2499 | checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" 2500 | dependencies = [ 2501 | "windows_aarch64_gnullvm", 2502 | "windows_aarch64_msvc", 2503 | "windows_i686_gnu", 2504 | "windows_i686_gnullvm", 2505 | "windows_i686_msvc", 2506 | "windows_x86_64_gnu", 2507 | "windows_x86_64_gnullvm", 2508 | "windows_x86_64_msvc", 2509 | ] 2510 | 2511 | [[package]] 2512 | name = "windows_aarch64_gnullvm" 2513 | version = "0.52.6" 2514 | source = "registry+https://github.com/rust-lang/crates.io-index" 2515 | checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" 2516 | 2517 | [[package]] 2518 | name = "windows_aarch64_msvc" 2519 | version = "0.52.6" 2520 | source = "registry+https://github.com/rust-lang/crates.io-index" 2521 | checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" 2522 | 2523 | [[package]] 2524 | name = "windows_i686_gnu" 2525 | version = "0.52.6" 2526 | source = "registry+https://github.com/rust-lang/crates.io-index" 2527 | checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" 2528 | 2529 | [[package]] 2530 | name = "windows_i686_gnullvm" 2531 | version = "0.52.6" 2532 | source = "registry+https://github.com/rust-lang/crates.io-index" 2533 | checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" 2534 | 2535 | [[package]] 2536 | name = "windows_i686_msvc" 2537 | version = "0.52.6" 2538 | source = "registry+https://github.com/rust-lang/crates.io-index" 2539 | checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" 2540 | 2541 | [[package]] 2542 | name = "windows_x86_64_gnu" 2543 | version = "0.52.6" 2544 | source = "registry+https://github.com/rust-lang/crates.io-index" 2545 | checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" 2546 | 2547 | [[package]] 2548 | name = "windows_x86_64_gnullvm" 2549 | version = "0.52.6" 2550 | source = "registry+https://github.com/rust-lang/crates.io-index" 2551 | checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" 2552 | 2553 | [[package]] 2554 | name = "windows_x86_64_msvc" 2555 | version = "0.52.6" 2556 | source = "registry+https://github.com/rust-lang/crates.io-index" 2557 | checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" 2558 | 2559 | [[package]] 2560 | name = "xz2" 2561 | version = "0.1.7" 2562 | source = "registry+https://github.com/rust-lang/crates.io-index" 2563 | checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2" 2564 | dependencies = [ 2565 | "lzma-sys", 2566 | ] 2567 | 2568 | [[package]] 2569 | name = "zerocopy" 2570 | version = "0.7.35" 2571 | source = "registry+https://github.com/rust-lang/crates.io-index" 2572 | checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" 2573 | dependencies = [ 2574 | "byteorder", 2575 | "zerocopy-derive", 2576 | ] 2577 | 2578 | [[package]] 2579 | name = "zerocopy-derive" 2580 | version = "0.7.35" 2581 | source = "registry+https://github.com/rust-lang/crates.io-index" 2582 | checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" 2583 | dependencies = [ 2584 | "proc-macro2", 2585 | "quote", 2586 | "syn", 2587 | ] 2588 | 2589 | [[package]] 2590 | name = "zstd" 2591 | version = "0.13.2" 2592 | source = "registry+https://github.com/rust-lang/crates.io-index" 2593 | checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" 2594 | dependencies = [ 2595 | "zstd-safe", 2596 | ] 2597 | 2598 | [[package]] 2599 | name = "zstd-safe" 2600 | version = "7.2.1" 2601 | source = "registry+https://github.com/rust-lang/crates.io-index" 2602 | checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" 2603 | dependencies = [ 2604 | "zstd-sys", 2605 | ] 2606 | 2607 | [[package]] 2608 | name = "zstd-sys" 2609 | version = "2.0.13+zstd.1.5.6" 2610 | source = "registry+https://github.com/rust-lang/crates.io-index" 2611 | checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" 2612 | dependencies = [ 2613 | "cc", 2614 | "pkg-config", 2615 | ] 2616 | --------------------------------------------------------------------------------