Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions doc/_quarto.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ website:
href: syntax/clause/visualise.qmd
- text: "`DRAW`"
href: syntax/clause/draw.qmd
- text: "`PLACE`"
href: syntax/clause/place.qmd
- text: "`SCALE`"
href: syntax/clause/scale.qmd
- text: "`FACET`"
Expand Down Expand Up @@ -69,6 +71,8 @@ website:
href: syntax/clause/visualise.qmd
- text: "`DRAW`"
href: syntax/clause/draw.qmd
- text: "`PLACE`"
href: syntax/clause/place.qmd
- text: "`SCALE`"
href: syntax/clause/scale.qmd
- text: "`FACET`"
Expand Down
37 changes: 37 additions & 0 deletions doc/syntax/clause/place.qmd
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
---
title: "Create annotation layers with `PLACE`"
---

The `PLACE` clause is the little sibling of the [`DRAW` clause](../clause/draw.qmd) that also creates layers.
A layer created with `PLACE` has no mappings to data, all aesthetics are set using literal values instead.

## Clause syntax
The `PLACE` clause takes just a type and a setting clause, all of them required.

```ggsql
PLACE <layer-type>
SETTING <parameter/aesthetic> => <value>, ...
```

Like `DRAW`, the layer type is required and specifies the type of layer to draw, like `point` or `text`.
It defines how the remaining settings are interpreted.
The [main syntax page](../index.qmd#layers) has a list of all available layer types

Unlike the `DRAW` clause, the `PLACE` clause does not support `FILTER`, `PARTITION BY`, and `ORDER BY` clauses since
everything is declared inline.

### `SETTING`
```ggsql
SETTING <parameter/aesthetic> => <value>, ...
```

The `SETTING` clause can be used for to different things:

* *Setting aesthetics*: All aesthetics in `PLACE` layers are specified using literal value, e.g. 'red' (as in the color red).
Aesthetics that are set will not go through a scale but will use the provided value as-is.
You cannot set an aesthetic to a column, only to a literal values.
Contrary to `DRAW` layers, `PLACE` layers can take multiple literal values in an array.
* *Setting parameters*: Some layers take additional arguments that control how they behave.
Often, but not always, these modify the statistical transformation in some way.
An example would be the binwidth parameter in histogram which controls the width of each bin during histogram calculation.
This is not a statistical property since it is not related to each record, but to the calculation as a whole.
1 change: 1 addition & 0 deletions doc/syntax/index.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ ggsql augments the standard SQL syntax with a number of new clauses to describe

- [`VISUALISE`](clause/visualise.qmd) initiates the visualisation part of the query
- [`DRAW`](clause/draw.qmd) adds a new layer to the visualisation
- [`PLACE`](clause/place.qmd) adds an annotation layer
- [`SCALE`](clause/scale.qmd) specify how an aesthetic should be scaled
- [`FACET`](clause/facet.qmd) describes how data should be split into small multiples
- [`PROJECT`](clause/project.qmd) is used for selecting the coordinate system to use
Expand Down
53 changes: 3 additions & 50 deletions src/execute/casting.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
//! This module handles determining which columns need type casting based on
//! scale requirements and updating type info accordingly.

use crate::naming;
use crate::plot::scale::coerce_dtypes;
use crate::plot::{CastTargetType, Layer, ParameterValue, Plot, SqlTypeNames};
use crate::{naming, DataSource};
use crate::plot::{CastTargetType, Plot, SqlTypeNames};
use polars::prelude::{DataType, TimeUnit};
use std::collections::{HashMap, HashSet};
use std::collections::HashMap;

use super::schema::TypeInfo;

Expand All @@ -22,24 +22,6 @@ pub struct TypeRequirement {
pub sql_type_name: String,
}

/// Format a literal value as SQL
pub fn literal_to_sql(lit: &ParameterValue) -> String {
match lit {
ParameterValue::String(s) => format!("'{}'", s.replace('\'', "''")),
ParameterValue::Number(n) => n.to_string(),
ParameterValue::Boolean(b) => {
if *b {
"TRUE".to_string()
} else {
"FALSE".to_string()
}
}
ParameterValue::Array(_) | ParameterValue::Null => {
unreachable!("Grammar prevents arrays and null in literal aesthetic mappings")
}
}
}

/// Determine which columns need casting based on scale requirements.
///
/// For each layer, collects columns that need casting to match the scale's
Expand Down Expand Up @@ -201,32 +183,3 @@ pub fn update_type_info_for_casting(type_info: &mut [TypeInfo], requirements: &[
}
}
}

/// Determine the data source table name for a layer.
///
/// Returns the table/CTE name to query from:
/// - Layer with explicit source (CTE, table, file) → that source name
/// - Layer using global data → global table name
pub fn determine_layer_source(
layer: &Layer,
materialized_ctes: &HashSet<String>,
has_global: bool,
) -> String {
match &layer.source {
Some(DataSource::Identifier(name)) => {
if materialized_ctes.contains(name) {
naming::cte_table(name)
} else {
name.clone()
}
}
Some(DataSource::FilePath(path)) => {
format!("'{}'", path)
}
None => {
// Layer uses global data - caller must ensure has_global is true
debug_assert!(has_global, "Layer has no source and no global data");
naming::global_table()
}
}
}
Loading
Loading