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