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