MCPcopy Create free account
hub / github.com/apache/datafusion / build_like_match

Function build_like_match

datafusion/pruning/src/pruning_predicate.rs:1822–1879  ·  view source on GitHub ↗

Convert `column LIKE literal` where P is a constant prefix of the literal to a range check on the column: `P <= column && column < P'`, where P' is the lowest string after all P* strings.

(
    expr_builder: &mut PruningExpressionBuilder,
)

Source from the content-addressed store, hash-verified

1820/// to a range check on the column: `P <= column && column < P'`, where P' is the
1821/// lowest string after all P* strings.
1822fn build_like_match(
1823 expr_builder: &mut PruningExpressionBuilder,
1824) -> Option<Arc<dyn PhysicalExpr>> {
1825 // column LIKE literal => (min, max) LIKE literal split at unescaped % => min <= split literal && split literal <= max
1826 // column LIKE 'foo%' => min <= 'foo' && 'foo' <= max
1827 // column LIKE 'foo\_%' => min <= 'foo_' && 'foo_' <= max (the _ is escaped)
1828 // column LIKE 'foo\%%' => min <= 'foo%' && 'foo%' <= max (the % is escaped)
1829 // column LIKE '%foo' => min <= '' && '' <= max => true
1830 // column LIKE '%foo%' => min <= '' && '' <= max => true
1831 // column LIKE 'foo' => min <= 'foo' && 'foo' <= max
1832
1833 // TODO Handle ILIKE perhaps by making the min lowercase and max uppercase
1834 // this may involve building the physical expressions that call lower() and upper()
1835 let min_column_expr = expr_builder.min_column_expr().ok()?;
1836 let max_column_expr = expr_builder.max_column_expr().ok()?;
1837 let scalar_expr = expr_builder.scalar_expr();
1838 // check that the scalar is a string literal
1839 let s = extract_string_literal(scalar_expr)?;
1840 // ANSI SQL specifies two wildcards: % and _. % matches zero or more characters, _ matches exactly one character.
1841 let (decoded_prefix, rest) = split_constant_prefix(s);
1842 let has_wildcard = !rest.is_empty();
1843 if has_wildcard && decoded_prefix.is_empty() {
1844 // there's no filtering we could possibly do, return None and have this be handled by the unhandled hook
1845 return None;
1846 }
1847 let (lower_bound, upper_bound) = if has_wildcard {
1848 let incremented_prefix = increment_utf8(&decoded_prefix)?;
1849 let lower_bound_lit = Arc::new(phys_expr::Literal::new(ScalarValue::Utf8(Some(
1850 decoded_prefix,
1851 ))));
1852 let upper_bound_lit = Arc::new(phys_expr::Literal::new(ScalarValue::Utf8(Some(
1853 incremented_prefix,
1854 ))));
1855 (lower_bound_lit, upper_bound_lit)
1856 } else {
1857 // the like expression is a literal and can be converted into a comparison
1858 let bound = Arc::new(phys_expr::Literal::new(ScalarValue::Utf8(Some(
1859 decoded_prefix,
1860 ))));
1861 (Arc::clone(&bound), bound)
1862 };
1863 let lower_bound_expr = Arc::new(phys_expr::BinaryExpr::new(
1864 lower_bound,
1865 Operator::LtEq,
1866 Arc::clone(&max_column_expr),
1867 ));
1868 let upper_bound_expr = Arc::new(phys_expr::BinaryExpr::new(
1869 Arc::clone(&min_column_expr),
1870 Operator::LtEq,
1871 upper_bound,
1872 ));
1873 let combined = Arc::new(phys_expr::BinaryExpr::new(
1874 upper_bound_expr,
1875 Operator::And,
1876 lower_bound_expr,
1877 ));
1878 Some(combined)
1879}

Callers 1

build_statistics_exprFunction · 0.85

Calls 8

extract_string_literalFunction · 0.85
split_constant_prefixFunction · 0.85
increment_utf8Function · 0.85
newFunction · 0.85
min_column_exprMethod · 0.80
max_column_exprMethod · 0.80
scalar_exprMethod · 0.80
is_emptyMethod · 0.45

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…