Skip to content

Commit 571e1ef

Browse files
committed
Add repeat_row_non_negative as a new SQL function
1 parent 55d2af2 commit 571e1ef

5 files changed

Lines changed: 124 additions & 2 deletions

File tree

src/expr/src/relation/func.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3289,6 +3289,22 @@ pub fn repeat_row(a: Datum) -> Option<(Row, Diff)> {
32893289
}
32903290
}
32913291

3292+
pub fn repeat_row_non_negative<'a>(
3293+
a: Datum,
3294+
) -> Result<Box<dyn Iterator<Item = (Row, Diff)> + 'a>, EvalError> {
3295+
let n = a.unwrap_int64();
3296+
if n < 0 {
3297+
Err(EvalError::InvalidParameterValue(
3298+
format!("repeat_row_non_negative got {}", n).into(),
3299+
))
3300+
} else if n == 0 {
3301+
Ok(Box::new(iter::empty()))
3302+
} else {
3303+
// iterator with 1 element; n goes into the diff
3304+
Ok(Box::new(iter::once((Row::default(), n.into()))))
3305+
}
3306+
}
3307+
32923308
fn wrap<'a>(datums: &'a [Datum<'a>], width: usize) -> impl Iterator<Item = (Row, Diff)> + 'a {
32933309
datums
32943310
.chunks(width)
@@ -3396,7 +3412,16 @@ pub enum TableFunc {
33963412
GuardSubquerySize {
33973413
column_type: SqlScalarType,
33983414
},
3415+
/// Repeats the input row the given number of times. Can even repeat a negative number of times,
3416+
/// which has some important consequences:
3417+
/// - can lead to negative accumulations downstream;
3418+
/// - can't be used in `WITH ORDINALITY` and other constructs that are implemented by
3419+
/// `TableFunc::WithOrdinality`, e.g., `ROWS FROM`;
3420+
/// - output is non-monotonic.
33993421
RepeatRow,
3422+
/// Same as `RepeatRow`, but errors on a negative count, and thereby avoids the above
3423+
/// peculiarities.
3424+
RepeatRowNonNegative,
34003425
UnnestArray {
34013426
el_typ: SqlScalarType,
34023427
},
@@ -3471,7 +3496,8 @@ impl TableFunc {
34713496
| TableFunc::GenerateSeriesTimestamp
34723497
| TableFunc::GenerateSeriesTimestampTz
34733498
| TableFunc::GuardSubquerySize { .. }
3474-
| TableFunc::RepeatRow
3499+
| TableFunc::RepeatRow // TODO: will move this to the not allowed category in the next commit
3500+
| TableFunc::RepeatRowNonNegative
34753501
| TableFunc::UnnestArray { .. }
34763502
| TableFunc::UnnestList { .. }
34773503
| TableFunc::UnnestMap { .. }
@@ -3567,6 +3593,7 @@ impl TableFunc {
35673593
}
35683594
}
35693595
TableFunc::RepeatRow => Ok(Box::new(repeat_row(datums[0]).into_iter())),
3596+
TableFunc::RepeatRowNonNegative => repeat_row_non_negative(datums[0]),
35703597
TableFunc::UnnestArray { .. } => Ok(Box::new(unnest_array(datums[0]))),
35713598
TableFunc::UnnestList { .. } => Ok(Box::new(unnest_list(datums[0]))),
35723599
TableFunc::UnnestMap { .. } => Ok(Box::new(unnest_map(datums[0]))),
@@ -3682,7 +3709,7 @@ impl TableFunc {
36823709
let keys = vec![];
36833710
(column_types, keys)
36843711
}
3685-
TableFunc::RepeatRow => {
3712+
TableFunc::RepeatRow | TableFunc::RepeatRowNonNegative => {
36863713
let column_types = vec![];
36873714
let keys = vec![];
36883715
(column_types, keys)
@@ -3764,6 +3791,7 @@ impl TableFunc {
37643791
TableFunc::GenerateSubscriptsArray => 1,
37653792
TableFunc::GuardSubquerySize { .. } => 1,
37663793
TableFunc::RepeatRow => 0,
3794+
TableFunc::RepeatRowNonNegative => 0,
37673795
TableFunc::UnnestArray { .. } => 1,
37683796
TableFunc::UnnestList { .. } => 1,
37693797
TableFunc::UnnestMap { .. } => 2,
@@ -3791,6 +3819,7 @@ impl TableFunc {
37913819
| TableFunc::RegexpExtract(_)
37923820
| TableFunc::CsvExtract(_)
37933821
| TableFunc::RepeatRow
3822+
| TableFunc::RepeatRowNonNegative
37943823
| TableFunc::UnnestArray { .. }
37953824
| TableFunc::UnnestList { .. }
37963825
| TableFunc::UnnestMap { .. }
@@ -3822,6 +3851,7 @@ impl TableFunc {
38223851
TableFunc::GenerateSeriesTimestampTz => true,
38233852
TableFunc::GenerateSubscriptsArray => true,
38243853
TableFunc::RepeatRow => false,
3854+
TableFunc::RepeatRowNonNegative => true,
38253855
TableFunc::UnnestArray { .. } => true,
38263856
TableFunc::UnnestList { .. } => true,
38273857
TableFunc::UnnestMap { .. } => true,
@@ -3853,6 +3883,7 @@ impl fmt::Display for TableFunc {
38533883
TableFunc::GenerateSubscriptsArray => f.write_str("generate_subscripts"),
38543884
TableFunc::GuardSubquerySize { .. } => f.write_str("guard_subquery_size"),
38553885
TableFunc::RepeatRow => f.write_str("repeat_row"),
3886+
TableFunc::RepeatRowNonNegative => f.write_str("repeat_row_non_negative"),
38563887
TableFunc::UnnestArray { .. } => f.write_str("unnest_array"),
38573888
TableFunc::UnnestList { .. } => f.write_str("unnest_list"),
38583889
TableFunc::UnnestMap { .. } => f.write_str("unnest_map"),

src/pgrepr-consts/src/oid.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -792,3 +792,4 @@ pub const VIEW_MZ_MCP_DATA_PRODUCT_DETAILS_OID: u32 = 17071;
792792
pub const VIEW_MZ_BUILTIN_MATERIALIZED_VIEWS_OID: u32 = 17072;
793793
pub const FUNC_PARSE_CATALOG_CREATE_SQL_OID: u32 = 17073;
794794
pub const FUNC_REDACT_SQL_OID: u32 = 17074;
795+
pub const FUNC_REPEAT_ROW_NON_NEGATIVE_OID: u32 = 17075;

src/sql/src/func.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4745,6 +4745,18 @@ pub static MZ_CATALOG_BUILTINS: LazyLock<BTreeMap<&'static str, Func>> = LazyLoc
47454745
})
47464746
}) => ReturnType::none(true), oid::FUNC_REPEAT_ROW_OID;
47474747
},
4748+
"repeat_row_non_negative" => Table {
4749+
params!(Int64) => Operation::unary(move |ecx, n| {
4750+
ecx.require_feature_flag(&vars::ENABLE_REPEAT_ROW_NON_NEGATIVE)?;
4751+
Ok(TableFuncPlan {
4752+
imp: TableFuncImpl::CallTable {
4753+
func: TableFunc::RepeatRowNonNegative,
4754+
exprs: vec![n],
4755+
},
4756+
column_names: vec![]
4757+
})
4758+
}) => ReturnType::none(true), oid::FUNC_REPEAT_ROW_NON_NEGATIVE_OID;
4759+
},
47484760
"seahash" => Scalar {
47494761
params!(String) => UnaryFunc::SeahashString(func::SeahashString)
47504762
=> UInt32, oid::FUNC_SEAHASH_STRING_OID;

src/sql/src/session/vars/definitions.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1880,6 +1880,12 @@ feature_flags!(
18801880
default: false,
18811881
enable_for_item_parsing: true,
18821882
},
1883+
{
1884+
name: enable_repeat_row_non_negative,
1885+
desc: "the repeat_row_non_negative function",
1886+
default: false,
1887+
enable_for_item_parsing: true,
1888+
},
18831889
{
18841890
name: enable_replica_targeted_materialized_views,
18851891
desc: "replica-targeted materialized views",

test/sqllogictest/table_func.slt

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,11 @@ ALTER SYSTEM SET enable_repeat_row = true
2626
----
2727
COMPLETE 0
2828

29+
simple conn=mz_system,user=mz_system
30+
ALTER SYSTEM SET enable_repeat_row_non_negative = true
31+
----
32+
COMPLETE 0
33+
2934
statement ok
3035
CREATE TABLE y (a JSONB);
3136

@@ -462,6 +467,16 @@ SELECT * FROM generate_series(0,3), repeat_row(generate_series);
462467
3
463468
3
464469

470+
query I
471+
SELECT * FROM generate_series(0,3), repeat_row_non_negative(generate_series);
472+
----
473+
1
474+
2
475+
2
476+
3
477+
3
478+
3
479+
465480
query II
466481
SELECT * FROM generate_series(0,3), repeat_row(generate_series) WITH ORDINALITY;
467482
----
@@ -472,6 +487,16 @@ SELECT * FROM generate_series(0,3), repeat_row(generate_series) WITH ORDINALITY;
472487
3 2
473488
3 3
474489

490+
query II
491+
SELECT * FROM generate_series(0,3), repeat_row_non_negative(generate_series) WITH ORDINALITY;
492+
----
493+
1 1
494+
2 1
495+
2 2
496+
3 1
497+
3 2
498+
3 3
499+
475500
query II
476501
SELECT * FROM generate_series(0,3), repeat_row(abs(generate_series - 1)) WITH ORDINALITY;
477502
----
@@ -480,12 +505,34 @@ SELECT * FROM generate_series(0,3), repeat_row(abs(generate_series - 1)) WITH OR
480505
3 1
481506
3 2
482507

508+
query II
509+
SELECT * FROM generate_series(0,3), repeat_row_non_negative(abs(generate_series - 1)) WITH ORDINALITY;
510+
----
511+
0 1
512+
2 1
513+
3 1
514+
3 2
515+
483516
query I
484517
SELECT abs(generate_series) FROM generate_series(-1, 2), repeat_row(generate_series);
485518
----
486519
2
487520
2
488521

522+
query error repeat_row_non_negative got \-1
523+
SELECT abs(generate_series) FROM generate_series(-1, 2), repeat_row_non_negative(generate_series);
524+
525+
query error repeat_row_non_negative got \-7
526+
SELECT 4 FROM repeat_row_non_negative(-7);
527+
528+
query I
529+
SELECT 5 FROM repeat_row(null);
530+
----
531+
532+
query I
533+
SELECT 5 FROM repeat_row_non_negative(null);
534+
----
535+
489536
statement error Negative multiplicity in constant result: -1
490537
SELECT * FROM (values ('a')), repeat_row(-1)
491538

@@ -802,6 +849,31 @@ repeat_row
802849
()
803850
(1)
804851

852+
query TI
853+
SELECT repeat_row(5), generate_series(1,2);
854+
----
855+
() NULL
856+
() NULL
857+
() NULL
858+
() 1
859+
() 2
860+
861+
query TI
862+
SELECT repeat_row_non_negative(5), generate_series(1,2);
863+
----
864+
() NULL
865+
() NULL
866+
() NULL
867+
() 1
868+
() 2
869+
870+
query T colnames,rowsort
871+
SELECT repeat_row_non_negative FROM ROWS FROM (repeat_row_non_negative(2), generate_series(1, 1));
872+
----
873+
repeat_row_non_negative
874+
()
875+
(1)
876+
805877
query T colnames
806878
SELECT g.a FROM generate_series(1, 1) AS g(a)
807879
----

0 commit comments

Comments
 (0)