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 ParseSourceExportDetails,
474 RedactSql,
475 RoundFloat32,
476 RoundFloat64,
477 RoundNumeric,
478 TruncFloat32,
479 TruncFloat64,
480 TruncNumeric,
481 TrimWhitespace,
482 TrimLeadingWhitespace,
483 TrimTrailingWhitespace,
484 Initcap,
485 RecordGet,
486 ListLength,
487 MapLength,
488 MapBuildFromRecordList,
489 Upper,
490 Lower,
491 Cos,
492 Acos,
493 Cosh,
494 Acosh,
495 Sin,
496 Asin,
497 Sinh,
498 Asinh,
499 Tan,
500 Atan,
501 Tanh,
502 Atanh,
503 Cot,
504 Degrees,
505 Radians,
506 Log10,
507 Log10Numeric,
508 Ln,
509 LnNumeric,
510 Exp,
511 ExpNumeric,
512 Sleep,
513 Panic,
514 AdjustNumericScale,
515 PgColumnSize,
516 MzRowSize,
517 MzTypeName,
518 StepMzTimestamp,
519 RangeLower,
520 RangeUpper,
521 RangeEmpty,
522 RangeLowerInc,
523 RangeUpperInc,
524 RangeLowerInf,
525 RangeUpperInf,
526 MzAclItemGrantor,
527 MzAclItemGrantee,
528 MzAclItemPrivileges,
529 MzFormatPrivileges,
530 MzValidatePrivileges,
531 MzValidateRolePrivilege,
532 AclItemGrantor,
533 AclItemGrantee,
534 AclItemPrivileges,
535 QuoteIdent,
536 TryParseMonotonicIso8601Timestamp,
537 RegexpSplitToArray,
538 PgSizePretty,
539 Crc32Bytes,
540 Crc32String,
541 KafkaMurmur2Bytes,
542 KafkaMurmur2String,
543 SeahashBytes,
544 SeahashString,
545 Reverse
546);
547
548impl UnaryFunc {
549 pub fn is(&self) -> Option<&'static str> {
553 match self {
554 UnaryFunc::IsNull(_) => Some("NULL"),
555 UnaryFunc::IsTrue(_) => Some("TRUE"),
556 UnaryFunc::IsFalse(_) => Some("FALSE"),
557 _ => None,
558 }
559 }
560}
561
562#[cfg(test)]
563mod test {
564 use itertools::Itertools;
565 use mz_repr::{PropDatum, SqlScalarType};
566
567 use crate::{MirScalarExpr, like_pattern};
568
569 use super::*;
570
571 #[mz_ore::test]
572 fn test_could_error() {
573 for func in [
574 UnaryFunc::IsNull(IsNull),
575 UnaryFunc::CastVarCharToString(CastVarCharToString),
576 UnaryFunc::Not(Not),
577 UnaryFunc::IsLikeMatch(IsLikeMatch(like_pattern::compile("%hi%", false).unwrap())),
578 ] {
579 assert!(!func.could_error())
580 }
581 }
582
583 #[mz_ore::test]
584 #[cfg_attr(miri, ignore)] fn test_is_monotone() {
586 use proptest::prelude::*;
587
588 fn assert_monotone<'a, const N: usize>(
591 expr: &MirScalarExpr,
592 arena: &'a RowArena,
593 datums: &[[Datum<'a>; N]],
594 ) {
595 let Ok(results) = datums
597 .iter()
598 .map(|args| expr.eval(args.as_slice(), arena))
599 .collect::<Result<Vec<_>, _>>()
600 else {
601 return;
602 };
603
604 let forward = results.iter().tuple_windows().all(|(a, b)| a <= b);
605 let reverse = results.iter().tuple_windows().all(|(a, b)| a >= b);
606 assert!(
607 forward || reverse,
608 "expected {expr} to be monotone, but passing {datums:?} returned {results:?}"
609 );
610 }
611
612 fn proptest_unary<'a>(
613 func: UnaryFunc,
614 arena: &'a RowArena,
615 arg: impl Strategy<Value = PropDatum>,
616 ) {
617 let is_monotone = func.is_monotone();
618 let expr = MirScalarExpr::CallUnary {
619 func,
620 expr: Box::new(MirScalarExpr::column(0)),
621 };
622 if is_monotone {
623 proptest!(|(
624 mut arg in proptest::array::uniform3(arg),
625 )| {
626 arg.sort();
627 let args: Vec<_> = arg.iter().map(|a| [Datum::from(a)]).collect();
628 assert_monotone(&expr, arena, &args);
629 });
630 }
631 }
632
633 let interesting_i32s: Vec<Datum<'static>> =
634 SqlScalarType::Int32.interesting_datums().collect();
635 let i32_datums = proptest::strategy::Union::new([
636 any::<i32>().prop_map(PropDatum::Int32).boxed(),
637 (0..interesting_i32s.len())
638 .prop_map(move |i| {
639 let Datum::Int32(val) = interesting_i32s[i] else {
640 unreachable!("interesting int32 has non-i32s")
641 };
642 PropDatum::Int32(val)
643 })
644 .boxed(),
645 (-10i32..10).prop_map(PropDatum::Int32).boxed(),
646 ]);
647
648 let arena = RowArena::new();
649
650 proptest_unary(
654 UnaryFunc::CastInt32ToNumeric(CastInt32ToNumeric(None)),
655 &arena,
656 &i32_datums,
657 );
658 proptest_unary(
659 UnaryFunc::CastInt32ToUint16(CastInt32ToUint16),
660 &arena,
661 &i32_datums,
662 );
663 proptest_unary(
664 UnaryFunc::CastInt32ToString(CastInt32ToString),
665 &arena,
666 &i32_datums,
667 );
668 }
669}