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