1use std::{fmt, str};
17
18use mz_repr::{Datum, InputDatumType, OutputDatumType, ReprColumnType, RowArena, SqlColumnType};
19
20use crate::Eval;
21use crate::EvalError;
22use crate::scalar::func::RedactSql;
23use crate::scalar::func::impls::*;
24
25pub trait LazyUnaryFunc {
28 fn eval<'a>(
29 &'a self,
30 datums: &[Datum<'a>],
31 temp_storage: &'a RowArena,
32 a: &'a impl Eval,
33 ) -> Result<Datum<'a>, EvalError>;
34
35 fn output_sql_type(&self, input_type: SqlColumnType) -> SqlColumnType;
37
38 fn output_type(&self, input_type: ReprColumnType) -> ReprColumnType {
39 ReprColumnType::from(&self.output_sql_type(SqlColumnType::from_repr(&input_type)))
40 }
41
42 fn propagates_nulls(&self) -> bool;
44
45 fn introduces_nulls(&self) -> bool;
47
48 fn could_error(&self) -> bool {
50 true
52 }
53
54 fn preserves_uniqueness(&self) -> bool;
67
68 fn inverse(&self) -> Option<crate::UnaryFunc>;
96
97 fn is_monotone(&self) -> bool;
104
105 fn is_eliminable_cast(&self) -> bool;
107}
108
109pub trait EagerUnaryFunc {
111 type Input<'a>: InputDatumType<'a, EvalError>;
112 type Output<'a>: OutputDatumType<'a, EvalError>;
113
114 fn call<'a>(&self, input: Self::Input<'a>) -> Self::Output<'a>;
115
116 fn output_sql_type(&self, input_type: SqlColumnType) -> SqlColumnType;
118
119 fn output_type(&self, input_type: ReprColumnType) -> ReprColumnType {
121 ReprColumnType::from(&self.output_sql_type(SqlColumnType::from_repr(&input_type)))
122 }
123
124 fn propagates_nulls(&self) -> bool {
126 !Self::Input::<'_>::nullable()
128 }
129
130 fn introduces_nulls(&self) -> bool {
132 Self::Output::<'_>::nullable()
134 }
135
136 fn could_error(&self) -> bool {
138 Self::Output::<'_>::fallible()
139 }
140
141 fn preserves_uniqueness(&self) -> bool {
143 false
144 }
145
146 fn inverse(&self) -> Option<crate::UnaryFunc> {
147 None
148 }
149
150 fn is_monotone(&self) -> bool {
151 false
152 }
153
154 fn is_eliminable_cast(&self) -> bool {
155 false
156 }
157}
158
159impl<T: EagerUnaryFunc> LazyUnaryFunc for T {
160 fn eval<'a>(
161 &'a self,
162 datums: &[Datum<'a>],
163 temp_storage: &'a RowArena,
164 a: &'a impl Eval,
165 ) -> Result<Datum<'a>, EvalError> {
166 match T::Input::<'_>::try_from_result(a.eval(datums, temp_storage)) {
167 Ok(input) => self.call(input).into_result(temp_storage),
169 Err(Ok(datum)) if !datum.is_null() => {
171 Err(EvalError::Internal("invalid input type".into()))
172 }
173 Err(res) => res,
175 }
176 }
177
178 fn output_sql_type(&self, input_type: SqlColumnType) -> SqlColumnType {
179 self.output_sql_type(input_type)
180 }
181
182 fn propagates_nulls(&self) -> bool {
183 self.propagates_nulls()
184 }
185
186 fn introduces_nulls(&self) -> bool {
187 self.introduces_nulls()
188 }
189
190 fn could_error(&self) -> bool {
191 self.could_error()
192 }
193
194 fn preserves_uniqueness(&self) -> bool {
195 self.preserves_uniqueness()
196 }
197
198 fn inverse(&self) -> Option<crate::UnaryFunc> {
199 self.inverse()
200 }
201
202 fn is_monotone(&self) -> bool {
203 self.is_monotone()
204 }
205
206 fn is_eliminable_cast(&self) -> bool {
207 self.is_eliminable_cast()
208 }
209}
210
211derive_unary!(
212 Not,
213 IsNull,
214 IsTrue,
215 IsFalse,
216 BitNotInt16,
217 BitNotInt32,
218 BitNotInt64,
219 BitNotUint16,
220 BitNotUint32,
221 BitNotUint64,
222 NegInt16,
223 NegInt32,
224 NegInt64,
225 NegFloat32,
226 NegFloat64,
227 NegNumeric,
228 NegInterval,
229 SqrtFloat64,
230 SqrtNumeric,
231 CbrtFloat64,
232 AbsInt16,
233 AbsInt32,
234 AbsInt64,
235 AbsFloat32,
236 AbsFloat64,
237 AbsNumeric,
238 CastBoolToString,
239 CastBoolToStringNonstandard,
240 CastBoolToInt32,
241 CastBoolToInt64,
242 CastInt16ToFloat32,
243 CastInt16ToFloat64,
244 CastInt16ToInt32,
245 CastInt16ToInt64,
246 CastInt16ToUint16,
247 CastInt16ToUint32,
248 CastInt16ToUint64,
249 CastInt16ToString,
250 CastInt2VectorToArray,
251 CastInt32ToBool,
252 CastInt32ToFloat32,
253 CastInt32ToFloat64,
254 CastInt32ToOid,
255 CastInt32ToPgLegacyChar,
256 CastInt32ToInt16,
257 CastInt32ToInt64,
258 CastInt32ToUint16,
259 CastInt32ToUint32,
260 CastInt32ToUint64,
261 CastInt32ToString,
262 CastOidToInt32,
263 CastOidToInt64,
264 CastOidToString,
265 CastOidToRegClass,
266 CastRegClassToOid,
267 CastOidToRegProc,
268 CastRegProcToOid,
269 CastOidToRegType,
270 CastRegTypeToOid,
271 CastInt64ToInt16,
272 CastInt64ToInt32,
273 CastInt64ToUint16,
274 CastInt64ToUint32,
275 CastInt64ToUint64,
276 CastInt16ToNumeric,
277 CastInt32ToNumeric,
278 CastInt64ToBool,
279 CastInt64ToNumeric,
280 CastInt64ToFloat32,
281 CastInt64ToFloat64,
282 CastInt64ToOid,
283 CastInt64ToString,
284 CastUint16ToUint32,
285 CastUint16ToUint64,
286 CastUint16ToInt16,
287 CastUint16ToInt32,
288 CastUint16ToInt64,
289 CastUint16ToNumeric,
290 CastUint16ToFloat32,
291 CastUint16ToFloat64,
292 CastUint16ToString,
293 CastUint32ToUint16,
294 CastUint32ToUint64,
295 CastUint32ToInt16,
296 CastUint32ToInt32,
297 CastUint32ToInt64,
298 CastUint32ToNumeric,
299 CastUint32ToFloat32,
300 CastUint32ToFloat64,
301 CastUint32ToString,
302 CastUint64ToUint16,
303 CastUint64ToUint32,
304 CastUint64ToInt16,
305 CastUint64ToInt32,
306 CastUint64ToInt64,
307 CastUint64ToNumeric,
308 CastUint64ToFloat32,
309 CastUint64ToFloat64,
310 CastUint64ToString,
311 CastFloat32ToInt16,
312 CastFloat32ToInt32,
313 CastFloat32ToInt64,
314 CastFloat32ToUint16,
315 CastFloat32ToUint32,
316 CastFloat32ToUint64,
317 CastFloat32ToFloat64,
318 CastFloat32ToString,
319 CastFloat32ToNumeric,
320 CastFloat64ToNumeric,
321 CastFloat64ToInt16,
322 CastFloat64ToInt32,
323 CastFloat64ToInt64,
324 CastFloat64ToUint16,
325 CastFloat64ToUint32,
326 CastFloat64ToUint64,
327 CastFloat64ToFloat32,
328 CastFloat64ToString,
329 CastNumericToFloat32,
330 CastNumericToFloat64,
331 CastNumericToInt16,
332 CastNumericToInt32,
333 CastNumericToInt64,
334 CastNumericToUint16,
335 CastNumericToUint32,
336 CastNumericToUint64,
337 CastNumericToString,
338 CastMzTimestampToString,
339 CastMzTimestampToTimestamp,
340 CastMzTimestampToTimestampTz,
341 CastStringToMzTimestamp,
342 CastUint64ToMzTimestamp,
343 CastUint32ToMzTimestamp,
344 CastInt64ToMzTimestamp,
345 CastInt32ToMzTimestamp,
346 CastNumericToMzTimestamp,
347 CastTimestampToMzTimestamp,
348 CastTimestampTzToMzTimestamp,
349 CastDateToMzTimestamp,
350 CastStringToBool,
351 CastStringToPgLegacyChar,
352 CastStringToPgLegacyName,
353 CastStringToBytes,
354 CastStringToInt16,
355 CastStringToInt32,
356 CastStringToInt64,
357 CastStringToUint16,
358 CastStringToUint32,
359 CastStringToUint64,
360 CastStringToInt2Vector,
361 CastStringToOid,
362 CastStringToFloat32,
363 CastStringToFloat64,
364 CastStringToDate,
365 CastStringToArray,
366 CastStringToList,
367 CastStringToMap,
368 CastStringToRange,
369 CastStringToTime,
370 CastStringToTimestamp,
371 CastStringToTimestampTz,
372 CastStringToInterval,
373 CastStringToNumeric,
374 CastStringToUuid,
375 CastStringToChar,
376 PadChar,
377 CastStringToVarChar,
378 CastCharToString,
379 CastVarCharToString,
380 CastDateToTimestamp,
381 CastDateToTimestampTz,
382 CastDateToString,
383 CastTimeToInterval,
384 CastTimeToString,
385 CastIntervalToString,
386 CastIntervalToTime,
387 CastTimestampToDate,
388 AdjustTimestampPrecision,
389 CastTimestampToTimestampTz,
390 CastTimestampToString,
391 CastTimestampToTime,
392 CastTimestampTzToDate,
393 CastTimestampTzToTimestamp,
394 AdjustTimestampTzPrecision,
395 CastTimestampTzToString,
396 CastTimestampTzToTime,
397 CastPgLegacyCharToString,
398 CastPgLegacyCharToChar,
399 CastPgLegacyCharToVarChar,
400 CastPgLegacyCharToInt32,
401 CastBytesToString,
402 CastStringToJsonb,
403 CastJsonbToString,
404 CastJsonbableToJsonb,
405 CastJsonbToInt16,
406 CastJsonbToInt32,
407 CastJsonbToInt64,
408 CastJsonbToFloat32,
409 CastJsonbToFloat64,
410 CastJsonbToNumeric,
411 CastJsonbToBool,
412 CastUuidToString,
413 CastRecordToString,
414 CastRecord1ToRecord2,
415 CastArrayToArray,
416 CastArrayToJsonb,
417 CastArrayToString,
418 CastListToString,
419 CastListToJsonb,
420 CastList1ToList2,
421 CastArrayToListOneDim,
422 CastMapToString,
423 CastInt2VectorToString,
424 CastRangeToString,
425 CeilFloat32,
426 CeilFloat64,
427 CeilNumeric,
428 FloorFloat32,
429 FloorFloat64,
430 FloorNumeric,
431 Ascii,
432 BitCountBytes,
433 BitLengthBytes,
434 BitLengthString,
435 ByteLengthBytes,
436 ByteLengthString,
437 CharLength,
438 Chr,
439 IsLikeMatch,
440 IsRegexpMatch,
441 RegexpMatch,
442 ExtractInterval,
443 ExtractTime,
444 ExtractTimestamp,
445 ExtractTimestampTz,
446 ExtractDate,
447 DatePartInterval,
448 DatePartTime,
449 DatePartTimestamp,
450 DatePartTimestampTz,
451 DateTruncTimestamp,
452 DateTruncTimestampTz,
453 TimezoneTimestamp,
454 TimezoneTimestampTz,
455 TimezoneTime,
456 ToTimestamp,
457 ToCharTimestamp,
458 ToCharTimestampTz,
459 JustifyDays,
460 JustifyHours,
461 JustifyInterval,
462 JsonbArrayLength,
463 JsonbTypeof,
464 JsonbStripNulls,
465 JsonbPretty,
466 ParseCatalogAclMode,
467 ParseCatalogAuditLogDetails,
468 ParseCatalogCreateSql,
469 ParseCatalogId,
470 ParseCatalogPrivileges,
471 ParseKafkaSourceDetails,
472 ParsePostgresSourceDetails,
473 RedactSql,
474 RoundFloat32,
475 RoundFloat64,
476 RoundNumeric,
477 TruncFloat32,
478 TruncFloat64,
479 TruncNumeric,
480 TrimWhitespace,
481 TrimLeadingWhitespace,
482 TrimTrailingWhitespace,
483 Initcap,
484 RecordGet,
485 ListLength,
486 MapLength,
487 MapBuildFromRecordList,
488 Upper,
489 Lower,
490 Cos,
491 Acos,
492 Cosh,
493 Acosh,
494 Sin,
495 Asin,
496 Sinh,
497 Asinh,
498 Tan,
499 Atan,
500 Tanh,
501 Atanh,
502 Cot,
503 Degrees,
504 Radians,
505 Log10,
506 Log10Numeric,
507 Ln,
508 LnNumeric,
509 Exp,
510 ExpNumeric,
511 Sleep,
512 Panic,
513 AdjustNumericScale,
514 PgColumnSize,
515 MzRowSize,
516 MzTypeName,
517 StepMzTimestamp,
518 RangeLower,
519 RangeUpper,
520 RangeEmpty,
521 RangeLowerInc,
522 RangeUpperInc,
523 RangeLowerInf,
524 RangeUpperInf,
525 MzAclItemGrantor,
526 MzAclItemGrantee,
527 MzAclItemPrivileges,
528 MzFormatPrivileges,
529 MzValidatePrivileges,
530 MzValidateRolePrivilege,
531 AclItemGrantor,
532 AclItemGrantee,
533 AclItemPrivileges,
534 QuoteIdent,
535 TryParseMonotonicIso8601Timestamp,
536 RegexpSplitToArray,
537 PgSizePretty,
538 Crc32Bytes,
539 Crc32String,
540 KafkaMurmur2Bytes,
541 KafkaMurmur2String,
542 SeahashBytes,
543 SeahashString,
544 Reverse
545);
546
547impl UnaryFunc {
548 pub fn is(&self) -> Option<&'static str> {
552 match self {
553 UnaryFunc::IsNull(_) => Some("NULL"),
554 UnaryFunc::IsTrue(_) => Some("TRUE"),
555 UnaryFunc::IsFalse(_) => Some("FALSE"),
556 _ => None,
557 }
558 }
559}
560
561#[cfg(test)]
562mod test {
563 use itertools::Itertools;
564 use mz_repr::{PropDatum, SqlScalarType};
565
566 use crate::{MirScalarExpr, like_pattern};
567
568 use super::*;
569
570 #[mz_ore::test]
571 fn test_could_error() {
572 for func in [
573 UnaryFunc::IsNull(IsNull),
574 UnaryFunc::CastVarCharToString(CastVarCharToString),
575 UnaryFunc::Not(Not),
576 UnaryFunc::IsLikeMatch(IsLikeMatch(like_pattern::compile("%hi%", false).unwrap())),
577 ] {
578 assert!(!func.could_error())
579 }
580 }
581
582 #[mz_ore::test]
583 #[cfg_attr(miri, ignore)] fn test_is_monotone() {
585 use proptest::prelude::*;
586
587 fn assert_monotone<'a, const N: usize>(
590 expr: &MirScalarExpr,
591 arena: &'a RowArena,
592 datums: &[[Datum<'a>; N]],
593 ) {
594 let Ok(results) = datums
596 .iter()
597 .map(|args| expr.eval(args.as_slice(), arena))
598 .collect::<Result<Vec<_>, _>>()
599 else {
600 return;
601 };
602
603 let forward = results.iter().tuple_windows().all(|(a, b)| a <= b);
604 let reverse = results.iter().tuple_windows().all(|(a, b)| a >= b);
605 assert!(
606 forward || reverse,
607 "expected {expr} to be monotone, but passing {datums:?} returned {results:?}"
608 );
609 }
610
611 fn proptest_unary<'a>(
612 func: UnaryFunc,
613 arena: &'a RowArena,
614 arg: impl Strategy<Value = PropDatum>,
615 ) {
616 let is_monotone = func.is_monotone();
617 let expr = MirScalarExpr::CallUnary {
618 func,
619 expr: Box::new(MirScalarExpr::column(0)),
620 };
621 if is_monotone {
622 proptest!(|(
623 mut arg in proptest::array::uniform3(arg),
624 )| {
625 arg.sort();
626 let args: Vec<_> = arg.iter().map(|a| [Datum::from(a)]).collect();
627 assert_monotone(&expr, arena, &args);
628 });
629 }
630 }
631
632 let interesting_i32s: Vec<Datum<'static>> =
633 SqlScalarType::Int32.interesting_datums().collect();
634 let i32_datums = proptest::strategy::Union::new([
635 any::<i32>().prop_map(PropDatum::Int32).boxed(),
636 (0..interesting_i32s.len())
637 .prop_map(move |i| {
638 let Datum::Int32(val) = interesting_i32s[i] else {
639 unreachable!("interesting int32 has non-i32s")
640 };
641 PropDatum::Int32(val)
642 })
643 .boxed(),
644 (-10i32..10).prop_map(PropDatum::Int32).boxed(),
645 ]);
646
647 let arena = RowArena::new();
648
649 proptest_unary(
653 UnaryFunc::CastInt32ToNumeric(CastInt32ToNumeric(None)),
654 &arena,
655 &i32_datums,
656 );
657 proptest_unary(
658 UnaryFunc::CastInt32ToUint16(CastInt32ToUint16),
659 &arena,
660 &i32_datums,
661 );
662 proptest_unary(
663 UnaryFunc::CastInt32ToString(CastInt32ToString),
664 &arena,
665 &i32_datums,
666 );
667 }
668}