1use std::collections::HashSet;
25
26use rudb_common::{LogicalType, Result, Value};
27use rudb_vector::{Data, Form, Validity, Vector};
28
29use crate::shape::{first, identity, nulls_of, single};
30
31#[derive(Debug)]
37pub struct Members {
38 held: Held,
39 has_null: bool,
45 negated: bool,
47}
48
49#[derive(Debug)]
51enum Held {
52 Whole(HashSet<i128>),
56 Text(HashSet<String>),
58}
59
60impl Members {
61 #[must_use]
67 pub fn of(values: &[Value], negated: bool) -> Option<Self> {
68 if values.len() < 2 {
69 return None;
70 }
71 let mut whole: HashSet<i128> = HashSet::new();
72 let mut text: HashSet<String> = HashSet::new();
73 let mut has_null = false;
74 let mut kind: Option<std::mem::Discriminant<Value>> = None;
75 for value in values {
76 if matches!(value, Value::Null) {
77 has_null = true;
78 continue;
79 }
80 let held = std::mem::discriminant(value);
84 if *kind.get_or_insert(held) != held {
85 return None;
86 }
87 match value {
88 Value::Varchar(held) => {
89 text.insert(held.clone());
90 }
91 other => {
92 whole.insert(number(other)?);
93 }
94 }
95 }
96 let held = if text.is_empty() {
97 if whole.is_empty() {
98 return None;
101 }
102 Held::Whole(whole)
103 } else {
104 Held::Text(text)
105 };
106 Some(Self { held, has_null, negated })
107 }
108
109 #[must_use]
111 pub fn len(&self) -> usize {
112 match &self.held {
113 Held::Whole(set) => set.len(),
114 Held::Text(set) => set.len(),
115 }
116 }
117
118 #[must_use]
120 pub fn is_empty(&self) -> bool {
121 self.len() == 0
122 }
123}
124
125fn number(value: &Value) -> Option<i128> {
127 match *value {
128 Value::TinyInt(held) => Some(i128::from(held)),
129 Value::SmallInt(held) => Some(i128::from(held)),
130 Value::Integer(held) | Value::Date(held) => Some(i128::from(held)),
131 Value::BigInt(held)
132 | Value::Time(held)
133 | Value::TimeTz(held)
134 | Value::Timestamp(held)
135 | Value::TimestampTz(held) => Some(i128::from(held)),
136 Value::HugeInt(held) => Some(held),
137 Value::UTinyInt(held) => Some(i128::from(held)),
138 Value::USmallInt(held) => Some(i128::from(held)),
139 Value::UInteger(held) => Some(i128::from(held)),
140 Value::UBigInt(held) => Some(i128::from(held)),
141 _ => None,
142 }
143}
144
145pub fn in_set(input: &Vector, members: &Members, returns: &LogicalType) -> Result<Vector> {
151 let rows = input.len();
152 let base = nulls_of(input);
153 match input.form() {
154 Form::Flat => match input.data() {
155 Some(data) => look(data, identity, members, &base, rows, returns),
156 None => row_at_a_time(input, members, &base, rows, returns),
157 },
158 Form::Dictionary | Form::Rle => {
159 let Some((codes, values)) = input.positions() else {
160 return row_at_a_time(input, members, &base, rows, returns);
161 };
162 let Some(data) = values.data().filter(|_| codes.len() >= rows) else {
163 return row_at_a_time(input, members, &base, rows, returns);
164 };
165 let at = move |index: usize| codes[index] as usize;
166 look(data, at, members, &base, rows, returns)
167 }
168 Form::Constant => {
169 let Some(value) = input.constant_value() else {
170 return row_at_a_time(input, members, &base, rows, returns);
171 };
172 let Some(held) = single(input.logical_type(), value) else {
173 return row_at_a_time(input, members, &base, rows, returns);
174 };
175 match held.data() {
176 Some(data) => look(data, first, members, &base, rows, returns),
177 None => row_at_a_time(input, members, &base, rows, returns),
178 }
179 }
180 _ => row_at_a_time(input, members, &base, rows, returns),
181 }
182}
183
184fn look<A: Fn(usize) -> usize>(
190 data: &Data,
191 at: A,
192 members: &Members,
193 base: &Validity,
194 rows: usize,
195 returns: &LogicalType,
196) -> Result<Vector> {
197 match (&members.held, data) {
198 (Held::Text(set), Data::Varlen(column)) => answer(rows, base, members, returns, |index| {
199 column.get(at(index)).is_some_and(|text| set.contains(text))
200 }),
201 (Held::Whole(set), Data::Int8(held)) => {
202 answer(rows, base, members, returns, |index| holds(set, held.as_slice(), at(index)))
203 }
204 (Held::Whole(set), Data::Int16(held)) => {
205 answer(rows, base, members, returns, |index| holds(set, held.as_slice(), at(index)))
206 }
207 (Held::Whole(set), Data::Int32(held)) => {
208 answer(rows, base, members, returns, |index| holds(set, held.as_slice(), at(index)))
209 }
210 (Held::Whole(set), Data::Int64(held)) => {
211 answer(rows, base, members, returns, |index| holds(set, held.as_slice(), at(index)))
212 }
213 (Held::Whole(set), Data::Int128(held)) => {
214 answer(rows, base, members, returns, |index| holds(set, held.as_slice(), at(index)))
215 }
216 (Held::Whole(set), Data::UInt8(held)) => {
217 answer(rows, base, members, returns, |index| holds(set, held.as_slice(), at(index)))
218 }
219 (Held::Whole(set), Data::UInt16(held)) => {
220 answer(rows, base, members, returns, |index| holds(set, held.as_slice(), at(index)))
221 }
222 (Held::Whole(set), Data::UInt32(held)) => {
223 answer(rows, base, members, returns, |index| holds(set, held.as_slice(), at(index)))
224 }
225 (Held::Whole(set), Data::UInt64(held)) => {
226 answer(rows, base, members, returns, |index| holds(set, held.as_slice(), at(index)))
227 }
228 _ => Err(rudb_common::Error::internal(format!(
232 "an IN list over a column this kernel does not read, which is {returns}"
233 ))),
234 }
235}
236
237fn holds<T: Copy>(set: &HashSet<i128>, values: &[T], index: usize) -> bool
239where
240 i128: From<T>,
241{
242 values.get(index).is_some_and(|&held| set.contains(&i128::from(held)))
243}
244
245fn answer(
247 rows: usize,
248 base: &Validity,
249 members: &Members,
250 returns: &LogicalType,
251 found: impl Fn(usize) -> bool,
252) -> Result<Vector> {
253 let mut out = vec![false; rows];
254 let mut live = vec![false; rows];
255 for index in 0..rows {
256 if !base.is_valid(index) {
257 continue;
258 }
259 let hit = found(index);
260 live[index] = hit || !members.has_null;
263 out[index] = hit != members.negated;
264 }
265 let validity = Validity::from_run(&live).normalize(rows);
266 Ok(Vector::flat(returns.clone(), Data::Bool(out.into()))?.with_validity(validity))
267}
268
269fn row_at_a_time(
275 input: &Vector,
276 members: &Members,
277 base: &Validity,
278 rows: usize,
279 returns: &LogicalType,
280) -> Result<Vector> {
281 let held: Vec<Value> =
282 (0..rows).map(|index| input.try_value_at(index)).collect::<Result<_>>()?;
283 answer(rows, base, members, returns, |index| match (&members.held, &held[index]) {
284 (Held::Text(set), Value::Varchar(text)) => set.contains(text.as_str()),
285 (Held::Whole(set), value) => number(value).is_some_and(|held| set.contains(&held)),
286 _ => false,
287 })
288}
289
290#[cfg(test)]
291mod tests {
292 use rudb_common::{LogicalType, Value};
293 use rudb_vector::Vector;
294
295 use super::{Members, in_set};
296
297 fn over(input: &Vector, list: &[Value], negated: bool) -> Vec<Value> {
299 let members = Members::of(list, negated).expect("this list folds");
300 let answer = in_set(input, &members, &LogicalType::Boolean).expect("the lookup runs");
301 (0..input.len()).map(|row| answer.value_at(row)).collect()
302 }
303
304 fn numbers() -> Vector {
305 Vector::from_values(
306 LogicalType::Integer,
307 &[Value::Integer(1), Value::Integer(7), Value::Null, Value::Integer(3)],
308 )
309 .expect("four integers")
310 }
311
312 #[test]
313 fn a_row_in_the_list_is_true_and_a_row_outside_it_is_false() {
314 assert_eq!(
315 over(&numbers(), &[Value::Integer(1), Value::Integer(3)], false),
316 [Value::Boolean(true), Value::Boolean(false), Value::Null, Value::Boolean(true)]
317 );
318 }
319
320 #[test]
321 fn a_not_in_is_the_same_lookup_read_the_other_way() {
322 assert_eq!(
323 over(&numbers(), &[Value::Integer(1), Value::Integer(3)], true),
324 [Value::Boolean(false), Value::Boolean(true), Value::Null, Value::Boolean(false)]
325 );
326 }
327
328 #[test]
332 fn a_miss_against_a_list_with_a_null_in_it_is_null() {
333 let list = [Value::Integer(1), Value::Null, Value::Integer(3)];
334 assert_eq!(
335 over(&numbers(), &list, false),
336 [Value::Boolean(true), Value::Null, Value::Null, Value::Boolean(true)]
337 );
338 assert_eq!(
339 over(&numbers(), &list, true),
340 [Value::Boolean(false), Value::Null, Value::Null, Value::Boolean(false)]
341 );
342 }
343
344 #[test]
345 fn a_dictionary_column_is_read_through_its_codes() {
346 let values = Vector::from_values(
347 LogicalType::Varchar,
348 &[Value::Varchar("a".into()), Value::Varchar("b".into()), Value::Null],
349 )
350 .expect("builds");
351 let text = Vector::dictionary(vec![0, 2, 1, 0], values).expect("codes are in range");
352 let list = [Value::Varchar("a".into()), Value::Varchar("c".into())];
353 assert_eq!(
354 over(&text, &list, false),
355 [Value::Boolean(true), Value::Null, Value::Boolean(false), Value::Boolean(true)]
356 );
357 }
358
359 #[test]
360 fn a_constant_column_answers_every_row_the_same() {
361 let held = Vector::constant(LogicalType::Integer, Value::Integer(3), 3);
362 let list = [Value::Integer(1), Value::Integer(3)];
363 assert_eq!(over(&held, &list, false), vec![Value::Boolean(true); 3]);
364 }
365
366 #[test]
367 fn a_run_length_column_reads_the_same_as_the_flat_one_it_stands_for() {
368 let flat = Vector::from_values(
369 LogicalType::Integer,
370 &[Value::Integer(1), Value::Integer(1), Value::Integer(7), Value::Integer(7)],
371 )
372 .expect("four integers");
373 let runs = flat.clone().run_encoded().expect("two runs");
374 let list = [Value::Integer(1), Value::Integer(3)];
375 assert_eq!(over(&runs, &list, false), over(&flat, &list, false));
376 }
377
378 #[test]
379 fn a_list_of_one_is_left_alone_because_a_comparison_is_already_that() {
380 assert!(Members::of(&[Value::Integer(1)], false).is_none());
381 }
382
383 #[test]
384 fn a_list_of_floats_does_not_fold() {
385 assert!(Members::of(&[Value::Double(1.0), Value::Double(2.0)], false).is_none());
388 }
389
390 #[test]
391 fn a_list_of_two_kinds_does_not_fold() {
392 let mixed = [Value::Integer(1), Value::Varchar("a".into())];
393 assert!(Members::of(&mixed, false).is_none());
394 }
395
396 #[test]
397 fn a_list_of_nothing_but_nulls_does_not_fold() {
398 assert!(Members::of(&[Value::Null, Value::Null], false).is_none());
399 }
400
401 #[test]
402 fn a_list_says_how_many_distinct_values_it_holds() {
403 let list = [Value::Integer(1), Value::Integer(1), Value::Integer(2), Value::Null];
404 let members = Members::of(&list, false).expect("this list folds");
405 assert_eq!(members.len(), 2);
406 assert!(!members.is_empty());
407 }
408}