diff --git a/config/config.md b/config/config.md
index 930608aa4f5..b5aa8488929 100644
--- a/config/config.md
+++ b/config/config.md
@@ -140,7 +140,7 @@
| `procedure.retry_delay` | String | `500ms` | Initial retry delay of procedures, increases exponentially |
| `procedure.max_running_procedures` | Integer | `128` | Max running procedures.
The maximum number of procedures that can be running at the same time.
If the number of running procedures exceeds this limit, the procedure will be rejected. |
| `query` | -- | -- | The query engine options. |
-| `query.experimental_native_promql` | Bool | `false` | Whether to enable experimental native PromQL execution.
Default: false |
+| `query.experimental_native_promql` | Bool | `false` | Enable experimental native PromQL execution for compatible queries.
Requires the per-request query.experimental_native_promql=true extension.
Default: false |
| `query.parallelism` | Integer | `0` | Parallelism of the query engine.
Default to 0, which means the number of CPU cores. |
| `query.memory_pool_size` | String | 0 | Memory pool size for query execution operators (aggregation, sorting, join).
Supports absolute size (e.g., "2GB", "4GB") or percentage of system memory (e.g., "20%").
Setting it to 0 disables the limit. Defaults to 0 (unlimited); this example sets it to `50%`.
When this limit is reached, queries will fail with ResourceExhausted error.
NOTE: This does NOT limit memory used by table scans. |
| `query.experimental_memory_pool_policy` | String | `greedy` | Experimental memory pool allocation policy:
- "greedy" (default): first-come-first-served allocation; preserves current behavior.
- "fair": divides available memory among spillable operators and may spill earlier.
Only effective when `memory_pool_size` is bounded (>0). |
@@ -385,7 +385,7 @@
| `meta_client.metadata_cache_ttl` | String | `10m` | TTL of the metadata cache. |
| `meta_client.metadata_cache_tti` | String | `5m` | -- |
| `query` | -- | -- | The query engine options. |
-| `query.experimental_native_promql` | Bool | `false` | Whether to enable experimental native PromQL execution.
Default: false |
+| `query.experimental_native_promql` | Bool | `false` | Enable experimental native PromQL execution for compatible standalone queries.
Requires in-process region transport and the per-request
query.experimental_native_promql=true extension. Default: false |
| `query.parallelism` | Integer | `0` | Parallelism of the query engine.
Default to 0, which means the number of CPU cores. |
| `query.allow_query_fallback` | Bool | `false` | Whether to allow query fallback when push down optimize fails.
Default to false, meaning when push down optimize failed, return error msg |
| `query.memory_pool_size` | String | 0 | Memory pool size for query execution operators (aggregation, sorting, join).
Supports absolute size (e.g., "4GB", "8GB") or percentage of system memory (e.g., "30%").
Setting it to 0 disables the limit. Defaults to 0 (unlimited); this example sets it to `50%`.
When this limit is reached, queries will fail with ResourceExhausted error.
NOTE: This does NOT limit memory used by table scans (only applies to datanodes). |
@@ -616,7 +616,6 @@
| `wal.max_unpersisted_age` | String | `8s` | The age of the oldest unpersisted entry at which new appends stall until an upload completes, in `enqueued` mode.
**It's only used when the provider is `experimental_object_store` and `ack_mode` is `enqueued`**. |
| `wal.on_corrupted_segment` | String | `skip` | What a read does with a segment that still does not decode after a second fetch, because its checksum does not match or its content disagrees with its footer entry.
- `skip`: the segment is skipped and recorded as a WAL hole of its region, a metric is incremented and a warning is logged; the other regions of the object are unaffected (the default).
- `fail`: the read fails, so the region does not open.
**It's only used when the provider is `experimental_object_store`**. |
| `query` | -- | -- | The query engine options. |
-| `query.experimental_native_promql` | Bool | `false` | Whether to enable experimental native PromQL execution.
Default: false |
| `query.parallelism` | Integer | `0` | Parallelism of the query engine.
Default to 0, which means the number of CPU cores. |
| `query.memory_pool_size` | String | 0 | Memory pool size for query execution operators (aggregation, sorting, join).
Supports absolute size (e.g., "2GB", "4GB") or percentage of system memory (e.g., "20%").
Setting it to 0 disables the limit. Defaults to 0 (unlimited); this example sets it to `50%`.
When this limit is reached, queries will fail with ResourceExhausted error.
NOTE: This does NOT limit memory used by table scans. |
| `query.experimental_memory_pool_policy` | String | `greedy` | Experimental memory pool allocation policy:
- "greedy" (default): first-come-first-served allocation; preserves current behavior.
- "fair": divides available memory among spillable operators and may spill earlier.
Only effective when `memory_pool_size` is bounded (>0). |
@@ -804,7 +803,6 @@
| `tracing` | -- | -- | The tracing options. Only effect when compiled with `tokio-console` feature. |
| `tracing.tokio_console_addr` | String | Unset | The tokio console address. |
| `query` | -- | -- | -- |
-| `query.experimental_native_promql` | Bool | `false` | Whether to enable experimental native PromQL execution.
Default: false |
| `query.parallelism` | Integer | `1` | Parallelism of the query engine for query sent by flownode.
Default to 1, so it won't use too much cpu or memory |
| `query.memory_pool_size` | String | 0 | Memory pool size for query execution operators (aggregation, sorting, join).
Supports absolute size (e.g., "1GB", "2GB") or percentage of system memory (e.g., "20%").
Setting it to 0 disables the limit. Defaults to 0 (unlimited); this example sets it to `50%`.
When this limit is reached, queries will fail with ResourceExhausted error.
NOTE: This does NOT limit memory used by table scans. |
| `query.experimental_memory_pool_policy` | String | `greedy` | Experimental memory pool allocation policy:
- "greedy" (default): first-come-first-served allocation; preserves current behavior.
- "fair": divides available memory among spillable operators and may spill earlier.
Only effective when `memory_pool_size` is bounded (>0). |
diff --git a/config/datanode.example.toml b/config/datanode.example.toml
index 2af83991638..0a01fa31973 100644
--- a/config/datanode.example.toml
+++ b/config/datanode.example.toml
@@ -327,9 +327,6 @@ overwrite_entry_start_id = false
## The query engine options.
[query]
-## Whether to enable experimental native PromQL execution.
-## Default: false
-#+ experimental_native_promql = false
## Parallelism of the query engine.
## Default to 0, which means the number of CPU cores.
parallelism = 0
diff --git a/config/flownode.example.toml b/config/flownode.example.toml
index 4827e576258..fcd5282cb7c 100644
--- a/config/flownode.example.toml
+++ b/config/flownode.example.toml
@@ -152,9 +152,6 @@ default_ratio = 1.0
[query]
-## Whether to enable experimental native PromQL execution.
-## Default: false
-#+ experimental_native_promql = false
## Parallelism of the query engine for query sent by flownode.
## Default to 1, so it won't use too much cpu or memory
parallelism = 1
diff --git a/config/frontend.example.toml b/config/frontend.example.toml
index 8f0122a04ff..d798f67e56e 100644
--- a/config/frontend.example.toml
+++ b/config/frontend.example.toml
@@ -360,8 +360,9 @@ metadata_cache_tti = "5m"
## The query engine options.
[query]
-## Whether to enable experimental native PromQL execution.
-## Default: false
+## Enable experimental native PromQL execution for compatible standalone queries.
+## Requires in-process region transport and the per-request
+## query.experimental_native_promql=true extension. Default: false
#+ experimental_native_promql = false
## Parallelism of the query engine.
## Default to 0, which means the number of CPU cores.
diff --git a/config/standalone.example.toml b/config/standalone.example.toml
index 4346b4daeb8..ea39a2e5a6b 100644
--- a/config/standalone.example.toml
+++ b/config/standalone.example.toml
@@ -538,7 +538,8 @@ max_running_procedures = 128
## The query engine options.
[query]
-## Whether to enable experimental native PromQL execution.
+## Enable experimental native PromQL execution for compatible queries.
+## Requires the per-request query.experimental_native_promql=true extension.
## Default: false
#+ experimental_native_promql = false
## Parallelism of the query engine.
diff --git a/src/common/query/src/request.rs b/src/common/query/src/request.rs
index 77971440447..86c6758aa25 100644
--- a/src/common/query/src/request.rs
+++ b/src/common/query/src/request.rs
@@ -14,9 +14,6 @@
mod base64_serde;
mod initial_remote_dyn_filter_reg;
-mod raw_series_request;
-
-pub use self::raw_series_request::{RawSeriesRequest, RawSeriesTag};
use std::cell::Cell;
use std::sync::Arc;
diff --git a/src/common/query/src/request/raw_series_request.rs b/src/common/query/src/request/raw_series_request.rs
deleted file mode 100644
index 0dd02b5290d..00000000000
--- a/src/common/query/src/request/raw_series_request.rs
+++ /dev/null
@@ -1,249 +0,0 @@
-// Copyright 2023 Greptime Team
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use std::collections::HashSet;
-use std::sync::Arc;
-
-use api::v1::SemanticType;
-use datafusion_common::{DataFusionError, Result as DataFusionResult};
-use datatypes::series::samples::{FieldSpec, SeriesSchema};
-use serde::{Deserialize, Serialize};
-use store_api::metadata::{RegionMetadata, RegionMetadataRef};
-use store_api::region_engine::{BoundTag, RawLabelSource};
-use store_api::storage::ColumnId;
-
-/// A logical label mapped to its physical region column.
-#[derive(Clone, Debug, Serialize, Deserialize)]
-#[serde(deny_unknown_fields)]
-pub struct RawSeriesTag {
- /// Physical region column identifier.
- pub column_id: ColumnId,
- /// Logical tag name.
- pub name: String,
-}
-
-/// Explicit versioned binding for a single-region raw-series request.
-#[derive(Clone, Debug, Serialize, Deserialize)]
-#[serde(deny_unknown_fields)]
-pub struct RawSeriesRequest {
- /// Control format version (currently 1).
- pub version: u32,
- /// Expected physical metadata snapshot.
- pub physical_metadata: RegionMetadata,
- /// Logical metric name.
- pub metric_name: String,
- /// Logical tags in output order.
- pub logical_tags: Vec,
- /// Physical fields in requested output order.
- pub field_ids: Vec,
-}
-
-impl RawSeriesRequest {
- /// Binds expected output fields and labels to the actual opened scanner metadata.
- pub fn bind(
- &self,
- actual: RegionMetadataRef,
- ) -> DataFusionResult<(Arc, RawLabelSource)> {
- self.validate()?;
- if !same_encoding_identity(&self.physical_metadata, &actual) {
- return Err(DataFusionError::Plan("Raw-series metadata mismatch".into()));
- }
- let fields = self
- .field_ids
- .iter()
- .map(|id| {
- actual
- .column_by_id(*id)
- .filter(|column| column.semantic_type == SemanticType::Field)
- .map(|column| FieldSpec {
- name: Arc::from(column.column_schema.name.as_str()),
- })
- .ok_or_else(|| DataFusionError::Plan("Invalid raw-series fields".into()))
- })
- .collect::>>()?;
- let schema = Arc::new(SeriesSchema {
- fields: fields.into(),
- eval_grid: None,
- });
- let tags = self
- .logical_tags
- .iter()
- .map(|tag| BoundTag {
- column_id: tag.column_id,
- name: Arc::from(tag.name.as_str()),
- })
- .collect::>();
- let source = RawLabelSource::PrimaryKey {
- metric_name: Arc::from(self.metric_name.as_str()),
- physical_metadata: actual,
- logical_tags: tags.into(),
- };
- Ok((schema, source))
- }
-
- fn validate(&self) -> DataFusionResult<()> {
- if self.version != 1 || self.field_ids.is_empty() {
- return Err(DataFusionError::Plan("Invalid raw-series request".into()));
- }
- let mut ids = HashSet::new();
- for id in &self.field_ids {
- let Some(column) = self.physical_metadata.column_by_id(*id) else {
- return Err(DataFusionError::Plan("Invalid raw-series fields".into()));
- };
- if column.semantic_type != SemanticType::Field || !ids.insert(*id) {
- return Err(DataFusionError::Plan("Invalid raw-series fields".into()));
- }
- }
- Ok(())
- }
-}
-
-fn same_encoding_identity(left: &RegionMetadata, right: &RegionMetadata) -> bool {
- left.region_id == right.region_id
- && left.schema_version == right.schema_version
- && left.column_metadatas == right.column_metadatas
- && left.primary_key == right.primary_key
- && left.primary_key_encoding == right.primary_key_encoding
- && left.time_index_column().column_id == right.time_index_column().column_id
- && left.time_index_type() == right.time_index_type()
-}
-
-#[cfg(test)]
-mod tests {
- use datatypes::data_type::ConcreteDataType;
- use datatypes::schema::ColumnSchema;
- use store_api::metadata::{ColumnMetadata, RegionMetadataBuilder};
- use store_api::storage::RegionId;
-
- use super::*;
-
- /// Builds the physical metadata of a one-tag, two-field region.
- fn fixture_metadata() -> RegionMetadata {
- let mut builder = RegionMetadataBuilder::new(RegionId::new(1, 1));
- builder
- .push_column_metadata(ColumnMetadata {
- column_schema: ColumnSchema::new(
- "host",
- ConcreteDataType::string_datatype(),
- false,
- ),
- semantic_type: SemanticType::Tag,
- column_id: 1,
- })
- .push_column_metadata(ColumnMetadata {
- column_schema: ColumnSchema::new(
- "value",
- ConcreteDataType::int32_datatype(),
- false,
- ),
- semantic_type: SemanticType::Field,
- column_id: 2,
- })
- .push_column_metadata(ColumnMetadata {
- column_schema: ColumnSchema::new("aux", ConcreteDataType::int64_datatype(), false),
- semantic_type: SemanticType::Field,
- column_id: 3,
- })
- .push_column_metadata(ColumnMetadata {
- column_schema: ColumnSchema::new(
- "ts",
- ConcreteDataType::timestamp_millisecond_datatype(),
- false,
- ),
- semantic_type: SemanticType::Timestamp,
- column_id: 4,
- })
- .primary_key(vec![1]);
- builder.build().unwrap()
- }
-
- /// Builds a request asking for fields in descending column order.
- fn fixture_request() -> RawSeriesRequest {
- RawSeriesRequest {
- version: 1,
- physical_metadata: fixture_metadata(),
- metric_name: "cpu".to_string(),
- logical_tags: vec![RawSeriesTag {
- column_id: 1,
- name: "host".to_string(),
- }],
- field_ids: vec![3, 2],
- }
- }
-
- #[test]
- fn rejects_unsupported_version() {
- let mut request = fixture_request();
- request.version = 2;
- assert!(request.validate().is_err());
- }
-
- #[test]
- fn rejects_empty_duplicate_and_non_field_ids() {
- let mut request = fixture_request();
- request.field_ids.clear();
- assert!(request.validate().is_err());
- let mut request = fixture_request();
- request.field_ids.push(3);
- assert!(request.validate().is_err());
- let mut request = fixture_request();
- request.field_ids = vec![1];
- assert!(request.validate().is_err());
- }
-
- #[test]
- fn binds_schema_order_and_actual_metadata_arc() {
- let request = fixture_request();
- let mut reordered = request.clone();
- reordered.field_ids.reverse();
- let actual = Arc::new(reordered.physical_metadata.clone());
- let (schema, source) = reordered.bind(actual.clone()).unwrap();
- assert_eq!(
- schema
- .fields
- .iter()
- .map(|field| field.name.as_ref())
- .collect::>(),
- ["value", "aux"]
- );
- let RawLabelSource::PrimaryKey {
- physical_metadata, ..
- } = source
- else {
- panic!("expected key labels")
- };
- assert!(Arc::ptr_eq(&actual, &physical_metadata));
- let mut mismatch = request.clone();
- mismatch.physical_metadata.schema_version += 1;
- assert!(mismatch.bind(actual.clone()).is_err());
- let mut mismatch = request.clone();
- mismatch.physical_metadata.region_id = RegionId::new(1, 2);
- assert!(mismatch.bind(actual.clone()).is_err());
- let mut mismatch = request.clone();
- mismatch.physical_metadata.primary_key.push(2);
- assert!(mismatch.bind(actual.clone()).is_err());
- let mut mismatch = request.clone();
- mismatch.physical_metadata.column_metadatas.swap(0, 1);
- assert!(mismatch.bind(actual.clone()).is_err());
- let mut mismatch = request.clone();
- mismatch.physical_metadata.column_metadatas[1].column_schema =
- ColumnSchema::new("value", ConcreteDataType::int64_datatype(), false);
- assert!(mismatch.bind(actual.clone()).is_err());
- let mut partition_only = request;
- partition_only
- .physical_metadata
- .set_partition_expr(Some("ignored".into()));
- assert!(partition_only.bind(actual).is_ok());
- }
-}
diff --git a/src/common/query/src/series_frame.rs b/src/common/query/src/series_frame.rs
index 04096c2b903..94022e4eb3a 100644
--- a/src/common/query/src/series_frame.rs
+++ b/src/common/query/src/series_frame.rs
@@ -57,10 +57,6 @@ use store_api::region_engine::{
NO_BOUND_LABEL_SOURCE, RawLabelSource, RawSeriesStream, SendableRawSeriesStream,
};
-mod record_batches;
-
-pub use record_batches::{InputMapping, record_batches_to_raw_series};
-
#[cfg(test)]
mod tests;
diff --git a/src/common/query/src/series_frame/record_batches.rs b/src/common/query/src/series_frame/record_batches.rs
deleted file mode 100644
index fa16f16deb3..00000000000
--- a/src/common/query/src/series_frame/record_batches.rs
+++ /dev/null
@@ -1,393 +0,0 @@
-// Copyright 2023 Greptime Team
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#[cfg(test)]
-mod tests;
-
-use std::collections::HashSet;
-use std::pin::Pin;
-use std::sync::Arc;
-use std::task::{Context, Poll};
-
-use common_error::ext::BoxedError;
-use common_recordbatch::{DfRecordBatch, DfSendableRecordBatchStream};
-use datafusion::arrow::array::{
- Array, ArrayRef, Float64Array, StringArray, StructArray, TimestampMillisecondArray,
-};
-use datafusion::arrow::buffer::Buffer;
-use datafusion::arrow::datatypes::{DataType, SchemaRef, TimeUnit};
-use datafusion::arrow::row::{RowConverter, Rows, SortField};
-use datafusion_common::{DataFusionError, Result as DfResult};
-use datatypes::arrow_array::string_array_value_at_index;
-use datatypes::series::raw::{RawSeriesChunk, RawTimestamps};
-use datatypes::series::samples::{FieldSpec, Samples, SeriesFields, SeriesSchema};
-use datatypes::series::{Label, SeriesLabels, SeriesOrdering};
-use futures_util::Stream;
-use snafu::Location;
-use store_api::region_engine::{RawLabelSource, RawSeriesStream, SendableRawSeriesStream};
-
-use crate::error::Error;
-use crate::native_histogram::{histogram_samples_from_arrow, native_histogram_arrow_type};
-use crate::series_frame::{codec_error, invalid};
-
-/// Explicit mapping from selected record-batch columns to raw-series identity and payload.
-///
-/// `time_index` selects a timezone-naive millisecond timestamp column. `metric_name` is an
-/// optional constant logical metric name, not a source column. `tags` binds all logical tag
-/// names to their selected source columns; supported tag columns are Utf8, LargeUtf8, Utf8View,
-/// or integer-key string dictionaries. `fields` binds selected source columns in output field
-/// order; supported field columns are Float64 and the native-histogram struct type.
-#[derive(Clone, Debug)]
-pub struct InputMapping {
- /// Source column containing timezone-naive millisecond timestamps.
- pub time_index: usize,
- /// Optional constant metric name; `None` omits the metric name.
- pub metric_name: Option>,
- /// Complete logical tag mapping, as `(tag_name, source_column_index)` pairs.
- pub tags: Vec<(Arc, usize)>,
- /// Selected payload columns in output order, paired with their logical field specs.
- pub fields: Vec<(FieldSpec, usize)>,
-}
-
-/// Converts ordinary record batches to contiguous raw-series chunks.
-///
-/// The already-executed child must keep each complete identity contiguous and timestamps
-/// ascending. The adapter checks adjacent runs only; it cannot prove global absence of A/B/A.
-/// Encoded read keys are opaque identities comparable only within this converter stream; they
-/// are not canonical cross-region or wire encodings. Float fields share their source buffers;
-/// native-histogram struct fields use the existing histogram bridge.
-pub fn record_batches_to_raw_series(
- source: DfSendableRecordBatchStream,
- mapping: InputMapping,
-) -> DfResult {
- let arrow_schema = source.schema();
- let (mapping, schema, converter) = validate_mapping(&arrow_schema, mapping)?;
- Ok(Box::pin(InputStream {
- source: Some(source),
- arrow_schema,
- mapping,
- schema,
- converter,
- batch: None,
- row: 0,
- pending: None,
- label_source: RawLabelSource::Explicit,
- }))
-}
-
-fn validate_mapping(
- schema: &SchemaRef,
- mut mapping: InputMapping,
-) -> DfResult<(InputMapping, Arc, RowConverter)> {
- let mut used = HashSet::new();
- let mut use_index = |index: usize| -> DfResult<()> {
- if index >= schema.fields().len() {
- return Err(invalid("mapped column index is out of bounds"));
- }
- if !used.insert(index) {
- return Err(invalid("source column is mapped to multiple roles"));
- }
- Ok(())
- };
- use_index(mapping.time_index)?;
- if schema.field(mapping.time_index).data_type()
- != &DataType::Timestamp(TimeUnit::Millisecond, None)
- {
- return Err(invalid("time column must be Timestamp(Millisecond, None)"));
- }
- for (_, index) in &mapping.tags {
- use_index(*index)?;
- let ty = schema.field(*index).data_type();
- if !string_type(ty) && !integer_key_type(ty) {
- return Err(invalid(
- "tag column must be string or integer-key string dictionary",
- ));
- }
- if matches!(ty, DataType::Dictionary(_, value) if !string_type(value)) {
- return Err(invalid("tag dictionary values must be strings"));
- }
- }
- if mapping.fields.is_empty() {
- return Err(invalid("at least one field is required"));
- }
- let mut field_names = HashSet::new();
- for (spec, index) in &mapping.fields {
- use_index(*index)?;
- if !field_names.insert(spec.name.clone()) {
- return Err(invalid("duplicate logical field name"));
- }
- let ty = schema.field(*index).data_type();
- if ty != &DataType::Float64 && ty != &native_histogram_arrow_type() {
- return Err(invalid("field must be Float64 or native histogram"));
- }
- }
- // SeriesLabels validates logical label names and duplicate tags through its canonical path.
- SeriesLabels::try_new(
- mapping.metric_name.clone(),
- mapping
- .tags
- .iter()
- .map(|(name, _)| Label {
- name: name.clone(),
- value: Arc::from(""),
- })
- .collect(),
- )
- .map_err(codec_error)?;
- mapping.tags.sort_by(|a, b| a.0.cmp(&b.0));
- let raw_schema = Arc::new(SeriesSchema {
- fields: mapping
- .fields
- .iter()
- .map(|(spec, _)| spec.clone())
- .collect::>()
- .into(),
- eval_grid: None,
- });
- let mut sorts = Vec::with_capacity(1 + mapping.tags.len() * 2);
- sorts.push(SortField::new(DataType::Utf8));
- for (_, index) in &mapping.tags {
- sorts.push(SortField::new(DataType::Utf8));
- sorts.push(SortField::new(schema.field(*index).data_type().clone()));
- }
- let converter = RowConverter::new(sorts)?;
- Ok((mapping, raw_schema, converter))
-}
-
-fn string_type(ty: &DataType) -> bool {
- matches!(
- ty,
- DataType::Utf8 | DataType::LargeUtf8 | DataType::Utf8View
- )
-}
-fn integer_key_type(ty: &DataType) -> bool {
- matches!(ty, DataType::Dictionary(key, value) if matches!(key.as_ref(), DataType::Int8 | DataType::Int16 | DataType::Int32 | DataType::Int64 | DataType::UInt8 | DataType::UInt16 | DataType::UInt32 | DataType::UInt64) && string_type(value))
-}
-
-fn stream_error(error: DataFusionError) -> BoxedError {
- BoxedError::new(Error::GeneralDataFusion {
- error,
- location: Location::default(),
- })
-}
-
-struct InputStream {
- source: Option,
- arrow_schema: SchemaRef,
- mapping: InputMapping,
- schema: Arc,
- converter: RowConverter,
- batch: Option<(DfRecordBatch, Rows)>,
- row: usize,
- pending: Option,
- label_source: RawLabelSource,
-}
-impl RawSeriesStream for InputStream {
- fn schema(&self) -> &Arc {
- &self.schema
- }
- fn label_source(&self) -> &RawLabelSource {
- &self.label_source
- }
- fn ordering(&self) -> SeriesOrdering {
- SeriesOrdering::Contiguous
- }
-}
-impl InputStream {
- fn finish(&mut self) {
- self.source = None;
- self.batch = None;
- self.row = 0;
- self.pending = None;
- }
- fn batch_key_columns(&self, batch: &DfRecordBatch) -> Vec {
- let n = batch.num_rows();
- let metric: ArrayRef = match &self.mapping.metric_name {
- Some(name) => Arc::new(StringArray::from_iter_values(std::iter::repeat_n(
- name.as_ref(),
- n,
- ))),
- None => Arc::new(StringArray::new_null(n)),
- };
- let mut cols = vec![metric];
- for (tag, index) in &self.mapping.tags {
- cols.push(Arc::new(StringArray::from_iter_values(
- std::iter::repeat_n(tag.as_ref(), n),
- )));
- cols.push(batch.column(*index).clone());
- }
- cols
- }
- fn read_chunk(
- &self,
- batch: &DfRecordBatch,
- start: usize,
- end: usize,
- key: Vec,
- ) -> DfResult {
- let len = end - start;
- let time = batch
- .column(self.mapping.time_index)
- .as_any()
- .downcast_ref::()
- .ok_or_else(|| invalid("invalid timestamp array"))?;
- if time.slice(start, len).null_count() != 0 {
- return Err(invalid("time values must not be null"));
- }
- let axis = RawTimestamps::try_new(time.values().slice(start, len)).map_err(codec_error)?;
- let mut fields = Vec::with_capacity(self.mapping.fields.len());
- for (_, index) in &self.mapping.fields {
- let array = batch.column(*index);
- if array.data_type() == &DataType::Float64 {
- let values = array
- .as_any()
- .downcast_ref::()
- .ok_or_else(|| invalid("invalid Float64 array"))?;
- fields.push(Samples::from_float(values.slice(start, len)));
- } else {
- let hist = array
- .as_any()
- .downcast_ref::()
- .ok_or_else(|| invalid("invalid histogram array"))?;
- fields.push(histogram_samples_from_arrow(&hist.slice(start, len))?);
- }
- }
- let fields = SeriesFields::try_from_vec(fields).map_err(codec_error)?;
- let metric_name = self.mapping.metric_name.clone();
- let mut labels = Vec::new();
- for (name, index) in &self.mapping.tags {
- if let Some(value) = string_array_value_at_index(batch.column(*index), start) {
- labels.push(Label {
- name: name.clone(),
- value: Arc::from(value),
- });
- }
- }
- let metric = SeriesLabels::try_new(metric_name, labels).map_err(codec_error)?;
- RawSeriesChunk::try_new(
- &self.schema,
- Buffer::from(key),
- Some(metric),
- axis,
- fields,
- true,
- )
- .map_err(codec_error)
- }
-}
-impl Stream for InputStream {
- type Item = Result;
- fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll