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) | Value::Time(held) | Value::Timestamp(held) => Some(i128::from(held)),
132 Value::HugeInt(held) => Some(held),
133 Value::UTinyInt(held) => Some(i128::from(held)),
134 Value::USmallInt(held) => Some(i128::from(held)),
135 Value::UInteger(held) => Some(i128::from(held)),
136 Value::UBigInt(held) => Some(i128::from(held)),
137 _ => None,
138 }
139}
140
141pub fn in_set(input: &Vector, members: &Members, returns: &LogicalType) -> Result<Vector> {
147 let rows = input.len();
148 let base = nulls_of(input);
149 match input.form() {
150 Form::Flat => match input.data() {
151 Some(data) => look(data, identity, members, &base, rows, returns),
152 None => row_at_a_time(input, members, &base, rows, returns),
153 },
154 Form::Dictionary | Form::Rle => {
155 let Some((codes, values)) = input.positions() else {
156 return row_at_a_time(input, members, &base, rows, returns);
157 };
158 let Some(data) = values.data().filter(|_| codes.len() >= rows) else {
159 return row_at_a_time(input, members, &base, rows, returns);
160 };
161 let at = move |index: usize| codes[index] as usize;
162 look(data, at, members, &base, rows, returns)
163 }
164 Form::Constant => {
165 let Some(value) = input.constant_value() else {
166 return row_at_a_time(input, members, &base, rows, returns);
167 };
168 let Some(held) = single(input.logical_type(), value) else {
169 return row_at_a_time(input, members, &base, rows, returns);
170 };
171 match held.data() {
172 Some(data) => look(data, first, members, &base, rows, returns),
173 None => row_at_a_time(input, members, &base, rows, returns),
174 }
175 }
176 _ => row_at_a_time(input, members, &base, rows, returns),
177 }
178}
179
180fn look<A: Fn(usize) -> usize>(
186 data: &Data,
187 at: A,
188 members: &Members,
189 base: &Validity,
190 rows: usize,
191 returns: &LogicalType,
192) -> Result<Vector> {
193 match (&members.held, data) {
194 (Held::Text(set), Data::Varlen(column)) => answer(rows, base, members, returns, |index| {
195 column.get(at(index)).is_some_and(|text| set.contains(text))
196 }),
197 (Held::Whole(set), Data::Int8(held)) => {
198 answer(rows, base, members, returns, |index| holds(set, held.as_slice(), at(index)))
199 }
200 (Held::Whole(set), Data::Int16(held)) => {
201 answer(rows, base, members, returns, |index| holds(set, held.as_slice(), at(index)))
202 }
203 (Held::Whole(set), Data::Int32(held)) => {
204 answer(rows, base, members, returns, |index| holds(set, held.as_slice(), at(index)))
205 }
206 (Held::Whole(set), Data::Int64(held)) => {
207 answer(rows, base, members, returns, |index| holds(set, held.as_slice(), at(index)))
208 }
209 (Held::Whole(set), Data::Int128(held)) => {
210 answer(rows, base, members, returns, |index| holds(set, held.as_slice(), at(index)))
211 }
212 (Held::Whole(set), Data::UInt8(held)) => {
213 answer(rows, base, members, returns, |index| holds(set, held.as_slice(), at(index)))
214 }
215 (Held::Whole(set), Data::UInt16(held)) => {
216 answer(rows, base, members, returns, |index| holds(set, held.as_slice(), at(index)))
217 }
218 (Held::Whole(set), Data::UInt32(held)) => {
219 answer(rows, base, members, returns, |index| holds(set, held.as_slice(), at(index)))
220 }
221 (Held::Whole(set), Data::UInt64(held)) => {
222 answer(rows, base, members, returns, |index| holds(set, held.as_slice(), at(index)))
223 }
224 _ => Err(rudb_common::Error::internal(format!(
228 "an IN list over a column this kernel does not read, which is {returns}"
229 ))),
230 }
231}
232
233fn holds<T: Copy>(set: &HashSet<i128>, values: &[T], index: usize) -> bool
235where
236 i128: From<T>,
237{
238 values.get(index).is_some_and(|&held| set.contains(&i128::from(held)))
239}
240
241fn answer(
243 rows: usize,
244 base: &Validity,
245 members: &Members,
246 returns: &LogicalType,
247 found: impl Fn(usize) -> bool,
248) -> Result<Vector> {
249 let mut out = vec![false; rows];
250 let mut live = vec![false; rows];
251 for index in 0..rows {
252 if !base.is_valid(index) {
253 continue;
254 }
255 let hit = found(index);
256 live[index] = hit || !members.has_null;
259 out[index] = hit != members.negated;
260 }
261 let validity = Validity::from_run(&live).normalize(rows);
262 Ok(Vector::flat(returns.clone(), Data::Bool(out.into()))?.with_validity(validity))
263}
264
265fn row_at_a_time(
271 input: &Vector,
272 members: &Members,
273 base: &Validity,
274 rows: usize,
275 returns: &LogicalType,
276) -> Result<Vector> {
277 let held: Vec<Value> = (0..rows).map(|index| input.value_at(index)).collect();
278 answer(rows, base, members, returns, |index| match (&members.held, &held[index]) {
279 (Held::Text(set), Value::Varchar(text)) => set.contains(text.as_str()),
280 (Held::Whole(set), value) => number(value).is_some_and(|held| set.contains(&held)),
281 _ => false,
282 })
283}
284
285#[cfg(test)]
286mod tests {
287 use rudb_common::{LogicalType, Value};
288 use rudb_vector::Vector;
289
290 use super::{Members, in_set};
291
292 fn over(input: &Vector, list: &[Value], negated: bool) -> Vec<Value> {
294 let members = Members::of(list, negated).expect("this list folds");
295 let answer = in_set(input, &members, &LogicalType::Boolean).expect("the lookup runs");
296 (0..input.len()).map(|row| answer.value_at(row)).collect()
297 }
298
299 fn numbers() -> Vector {
300 Vector::from_values(
301 LogicalType::Integer,
302 &[Value::Integer(1), Value::Integer(7), Value::Null, Value::Integer(3)],
303 )
304 .expect("four integers")
305 }
306
307 #[test]
308 fn a_row_in_the_list_is_true_and_a_row_outside_it_is_false() {
309 assert_eq!(
310 over(&numbers(), &[Value::Integer(1), Value::Integer(3)], false),
311 [Value::Boolean(true), Value::Boolean(false), Value::Null, Value::Boolean(true)]
312 );
313 }
314
315 #[test]
316 fn a_not_in_is_the_same_lookup_read_the_other_way() {
317 assert_eq!(
318 over(&numbers(), &[Value::Integer(1), Value::Integer(3)], true),
319 [Value::Boolean(false), Value::Boolean(true), Value::Null, Value::Boolean(false)]
320 );
321 }
322
323 #[test]
327 fn a_miss_against_a_list_with_a_null_in_it_is_null() {
328 let list = [Value::Integer(1), Value::Null, Value::Integer(3)];
329 assert_eq!(
330 over(&numbers(), &list, false),
331 [Value::Boolean(true), Value::Null, Value::Null, Value::Boolean(true)]
332 );
333 assert_eq!(
334 over(&numbers(), &list, true),
335 [Value::Boolean(false), Value::Null, Value::Null, Value::Boolean(false)]
336 );
337 }
338
339 #[test]
340 fn a_dictionary_column_is_read_through_its_codes() {
341 let values = Vector::from_values(
342 LogicalType::Varchar,
343 &[Value::Varchar("a".into()), Value::Varchar("b".into()), Value::Null],
344 )
345 .expect("builds");
346 let text = Vector::dictionary(vec![0, 2, 1, 0], values).expect("codes are in range");
347 let list = [Value::Varchar("a".into()), Value::Varchar("c".into())];
348 assert_eq!(
349 over(&text, &list, false),
350 [Value::Boolean(true), Value::Null, Value::Boolean(false), Value::Boolean(true)]
351 );
352 }
353
354 #[test]
355 fn a_constant_column_answers_every_row_the_same() {
356 let held = Vector::constant(LogicalType::Integer, Value::Integer(3), 3);
357 let list = [Value::Integer(1), Value::Integer(3)];
358 assert_eq!(over(&held, &list, false), vec![Value::Boolean(true); 3]);
359 }
360
361 #[test]
362 fn a_run_length_column_reads_the_same_as_the_flat_one_it_stands_for() {
363 let flat = Vector::from_values(
364 LogicalType::Integer,
365 &[Value::Integer(1), Value::Integer(1), Value::Integer(7), Value::Integer(7)],
366 )
367 .expect("four integers");
368 let runs = flat.clone().run_encoded().expect("two runs");
369 let list = [Value::Integer(1), Value::Integer(3)];
370 assert_eq!(over(&runs, &list, false), over(&flat, &list, false));
371 }
372
373 #[test]
374 fn a_list_of_one_is_left_alone_because_a_comparison_is_already_that() {
375 assert!(Members::of(&[Value::Integer(1)], false).is_none());
376 }
377
378 #[test]
379 fn a_list_of_floats_does_not_fold() {
380 assert!(Members::of(&[Value::Double(1.0), Value::Double(2.0)], false).is_none());
383 }
384
385 #[test]
386 fn a_list_of_two_kinds_does_not_fold() {
387 let mixed = [Value::Integer(1), Value::Varchar("a".into())];
388 assert!(Members::of(&mixed, false).is_none());
389 }
390
391 #[test]
392 fn a_list_of_nothing_but_nulls_does_not_fold() {
393 assert!(Members::of(&[Value::Null, Value::Null], false).is_none());
394 }
395
396 #[test]
397 fn a_list_says_how_many_distinct_values_it_holds() {
398 let list = [Value::Integer(1), Value::Integer(1), Value::Integer(2), Value::Null];
399 let members = Members::of(&list, false).expect("this list folds");
400 assert_eq!(members.len(), 2);
401 assert!(!members.is_empty());
402 }
403}