@@ -315,6 +315,341 @@ CREATE OPERATOR CLASS ore_64_8_v1_btree_ops DEFAULT FOR TYPE ore_64_8_v1 USING b
315315 OPERATOR 4 >= ,
316316 OPERATOR 5 > ,
317317 FUNCTION 1 compare_ore_64_8_v1(a ore_64_8_v1, b ore_64_8_v1);
318+
319+ -- -
320+ -- - ORE CLLW types, functions, and operators
321+ -- -
322+
323+ -- Represents a ciphertext encrypted with the CLLW ORE scheme
324+ -- Each output block is 8-bits
325+ CREATE TYPE ore_cllw_8_v1 AS (
326+ bytes bytea
327+ );
328+
329+
330+ CREATE OR REPLACE FUNCTION __compare_inner_ore_cllw_8_v1 (a ore_cllw_8_v1, b ore_cllw_8_v1)
331+ RETURNS int AS $$
332+ DECLARE
333+ len_a INT ;
334+ x BYTEA ;
335+ y BYTEA ;
336+ i INT ;
337+ differing RECORD;
338+ BEGIN
339+ len_a := LENGTH(a .bytes );
340+
341+ -- Iterate over each byte and compare them
342+ FOR i IN 1 ..len_a LOOP
343+ x := SUBSTRING (a .bytes FROM i FOR 1 );
344+ y := SUBSTRING (b .bytes FROM i FOR 1 );
345+
346+ -- Check if there's a difference
347+ IF x != y THEN
348+ differing := (x, y);
349+ EXIT;
350+ END IF;
351+ END LOOP;
352+
353+ -- If a difference is found, compare the bytes as in Rust logic
354+ IF differing IS NOT NULL THEN
355+ IF (get_byte(y, 0 ) + 1 ) % 256 = get_byte(x, 0 ) THEN
356+ RETURN 1 ;
357+ ELSE
358+ RETURN - 1 ;
359+ END IF;
360+ ELSE
361+ RETURN 0 ;
362+ END IF;
363+ END;
364+ $$ LANGUAGE plpgsql;
365+
366+
367+ CREATE OR REPLACE FUNCTION compare_ore_cllw_8_v1 (a ore_cllw_8_v1, b ore_cllw_8_v1)
368+ RETURNS int AS $$
369+ DECLARE
370+ len_a INT ;
371+ len_b INT ;
372+ x BYTEA ;
373+ y BYTEA ;
374+ i INT ;
375+ differing RECORD;
376+ BEGIN
377+ -- Check if the lengths of the two bytea arguments are the same
378+ len_a := LENGTH(a .bytes );
379+ len_b := LENGTH(b .bytes );
380+
381+ IF len_a != len_b THEN
382+ RAISE EXCEPTION ' Bytea arguments must have the same length' ;
383+ END IF;
384+
385+ RETURN __compare_inner_ore_cllw_8_v1(a, b);
386+ END;
387+ $$ LANGUAGE plpgsql;
388+
389+ CREATE OR REPLACE FUNCTION compare_lex_ore_cllw_8_v1 (a ore_cllw_8_v1, b ore_cllw_8_v1)
390+ RETURNS int AS $$
391+ DECLARE
392+ len_a INT ;
393+ len_b INT ;
394+ cmp_result int ;
395+ BEGIN
396+ -- Get the lengths of both bytea inputs
397+ len_a := LENGTH(a .bytes );
398+ len_b := LENGTH(b .bytes );
399+
400+ -- Handle empty cases
401+ IF len_a = 0 AND len_b = 0 THEN
402+ RETURN 0 ;
403+ ELSIF len_a = 0 THEN
404+ RETURN - 1 ;
405+ ELSIF len_b = 0 THEN
406+ RETURN 1 ;
407+ END IF;
408+
409+ -- Use the compare_bytea function to compare byte by byte
410+ cmp_result := __compare_inner_ore_cllw_8_v1(a, b);
411+
412+ -- If the comparison returns 'less' or 'greater', return that result
413+ IF cmp_result = - 1 THEN
414+ RETURN - 1 ;
415+ ELSIF cmp_result = 1 THEN
416+ RETURN 1 ;
417+ END IF;
418+
419+ -- If the bytea comparison is 'equal', compare lengths
420+ IF len_a < len_b THEN
421+ RETURN 1 ;
422+ ELSIF len_a > len_b THEN
423+ RETURN - 1 ;
424+ ELSE
425+ RETURN 0 ;
426+ END IF;
427+ END;
428+ $$ LANGUAGE plpgsql;
429+
430+ CREATE OR REPLACE FUNCTION ore_cllw_8_v1_eq (a ore_cllw_8_v1, b ore_cllw_8_v1) RETURNS boolean AS $$
431+ SELECT compare_ore_cllw_8_v1(a, b) = 0
432+ $$ LANGUAGE SQL;
433+
434+ CREATE OR REPLACE FUNCTION ore_cllw_8_v1_neq (a ore_cllw_8_v1, b ore_cllw_8_v1) RETURNS boolean AS $$
435+ SELECT compare_ore_cllw_8_v1(a, b) <> 0
436+ $$ LANGUAGE SQL;
437+
438+ CREATE OR REPLACE FUNCTION ore_cllw_8_v1_lt (a ore_cllw_8_v1, b ore_cllw_8_v1) RETURNS boolean AS $$
439+ SELECT compare_ore_cllw_8_v1(a, b) = - 1
440+ $$ LANGUAGE SQL;
441+
442+ CREATE OR REPLACE FUNCTION ore_cllw_8_v1_lte (a ore_cllw_8_v1, b ore_cllw_8_v1) RETURNS boolean AS $$
443+ SELECT compare_ore_cllw_8_v1(a, b) != 1
444+ $$ LANGUAGE SQL;
445+
446+ CREATE OR REPLACE FUNCTION ore_cllw_8_v1_gt (a ore_cllw_8_v1, b ore_cllw_8_v1) RETURNS boolean AS $$
447+ SELECT compare_ore_cllw_8_v1(a, b) = 1
448+ $$ LANGUAGE SQL;
449+
450+ CREATE OR REPLACE FUNCTION ore_cllw_8_v1_gte (a ore_cllw_8_v1, b ore_cllw_8_v1) RETURNS boolean AS $$
451+ SELECT compare_ore_cllw_8_v1(a, b) != - 1
452+ $$ LANGUAGE SQL;
453+
454+ CREATE OPERATOR = (
455+ PROCEDURE= " ore_cllw_8_v1_eq" ,
456+ LEFTARG= ore_cllw_8_v1,
457+ RIGHTARG= ore_cllw_8_v1,
458+ NEGATOR = <> ,
459+ RESTRICT = eqsel,
460+ JOIN = eqjoinsel,
461+ HASHES,
462+ MERGES
463+ );
464+
465+ CREATE OPERATOR <> (
466+ PROCEDURE= " ore_cllw_8_v1_neq" ,
467+ LEFTARG= ore_cllw_8_v1,
468+ RIGHTARG= ore_cllw_8_v1,
469+ NEGATOR = = ,
470+ RESTRICT = eqsel,
471+ JOIN = eqjoinsel,
472+ HASHES,
473+ MERGES
474+ );
475+
476+ CREATE OPERATOR > (
477+ PROCEDURE= " ore_cllw_8_v1_gt" ,
478+ LEFTARG= ore_cllw_8_v1,
479+ RIGHTARG= ore_cllw_8_v1,
480+ NEGATOR = <= ,
481+ RESTRICT = scalarltsel,
482+ JOIN = scalarltjoinsel,
483+ HASHES,
484+ MERGES
485+ );
486+
487+ CREATE OPERATOR < (
488+ PROCEDURE= " ore_cllw_8_v1_lt" ,
489+ LEFTARG= ore_cllw_8_v1,
490+ RIGHTARG= ore_cllw_8_v1,
491+ NEGATOR = >= ,
492+ RESTRICT = scalargtsel,
493+ JOIN = scalargtjoinsel,
494+ HASHES,
495+ MERGES
496+ );
497+
498+ CREATE OPERATOR >= (
499+ PROCEDURE= " ore_cllw_8_v1_gte" ,
500+ LEFTARG= ore_cllw_8_v1,
501+ RIGHTARG= ore_cllw_8_v1,
502+ NEGATOR = < ,
503+ RESTRICT = scalarltsel,
504+ JOIN = scalarltjoinsel,
505+ HASHES,
506+ MERGES
507+ );
508+
509+ CREATE OPERATOR <= (
510+ PROCEDURE= " ore_cllw_8_v1_lte" ,
511+ LEFTARG= ore_cllw_8_v1,
512+ RIGHTARG= ore_cllw_8_v1,
513+ NEGATOR = > ,
514+ RESTRICT = scalargtsel,
515+ JOIN = scalargtjoinsel,
516+ HASHES,
517+ MERGES
518+ );
519+
520+ CREATE OPERATOR FAMILY ore_cllw_8_v1_btree_ops USING btree;
521+ CREATE OPERATOR CLASS ore_cllw_8_v1_btree_ops DEFAULT FOR TYPE ore_cllw_8_v1 USING btree FAMILY ore_cllw_8_v1_btree_ops AS
522+ OPERATOR 1 < ,
523+ OPERATOR 2 <= ,
524+ OPERATOR 3 = ,
525+ OPERATOR 4 >= ,
526+ OPERATOR 5 > ,
527+ FUNCTION 1 compare_ore_cllw_8_v1(a ore_cllw_8_v1, b ore_cllw_8_v1);
528+
529+ -- -
530+ -- - SteVec types, functions, and operators
531+ -- -
532+
533+ CREATE TYPE ste_vec_v1_entry AS (
534+ tokenized_selector text ,
535+ term ore_cllw_8_v1,
536+ ciphertext text
537+ );
538+
539+ CREATE TYPE cs_ste_vec_index_v1 AS (
540+ entries ste_vec_v1_entry[]
541+ );
542+
543+ -- Determine if a == b (ignoring ciphertext values)
544+ CREATE OR REPLACE FUNCTION ste_vec_v1_entry_eq (a ste_vec_v1_entry, b ste_vec_v1_entry)
545+ RETURNS boolean AS $$
546+ DECLARE
547+ sel_cmp int ;
548+ term_cmp int ;
549+ BEGIN
550+ -- Constant time comparison
551+ IF a .tokenized_selector = b .tokenized_selector THEN
552+ sel_cmp := 1 ;
553+ ELSE
554+ sel_cmp := 0 ;
555+ END IF;
556+ IF a .term = b .term THEN
557+ term_cmp := 1 ;
558+ ELSE
559+ term_cmp := 0 ;
560+ END IF;
561+ RETURN (sel_cmp # term_cmp) = 0;
562+ END;
563+ $$ LANGUAGE plpgsql;
564+
565+ -- Determine if a contains b (ignoring ciphertext values)
566+ CREATE OR REPLACE FUNCTION ste_vec_v1_logical_contains (a cs_ste_vec_index_v1, b cs_ste_vec_index_v1)
567+ RETURNS boolean AS $$
568+ DECLARE
569+ result boolean ;
570+ intermediate_result boolean ;
571+ BEGIN
572+ result := true;
573+ IF array_length(b .entries , 1 ) IS NULL THEN
574+ RETURN result;
575+ END IF;
576+ FOR i IN 1 ..array_length(b .entries , 1 ) LOOP
577+ intermediate_result := ste_vec_v1_entry_array_contains_entry(a .entries , b .entries [i]);
578+ result := result AND intermediate_result;
579+ END LOOP;
580+ RETURN result;
581+ END;
582+ $$ LANGUAGE plpgsql;
583+
584+ -- Determine if a contains b (ignoring ciphertext values)
585+ CREATE OR REPLACE FUNCTION ste_vec_v1_entry_array_contains_entry (a ste_vec_v1_entry[], b ste_vec_v1_entry)
586+ RETURNS boolean AS $$
587+ DECLARE
588+ result boolean ;
589+ intermediate_result boolean ;
590+ BEGIN
591+ IF array_length(a, 1 ) IS NULL THEN
592+ RETURN false;
593+ END IF;
594+
595+ result := false;
596+ FOR i IN 1 ..array_length(a, 1 ) LOOP
597+ intermediate_result := a[i].tokenized_selector = b .tokenized_selector AND a[i].term = b .term ;
598+ result := result OR intermediate_result;
599+ END LOOP;
600+ RETURN result;
601+ END;
602+ $$ LANGUAGE plpgsql;
603+
604+ -- Determine if a is contained by b (ignoring ciphertext values)
605+ CREATE OR REPLACE FUNCTION ste_vec_v1_logical_is_contained (a cs_ste_vec_index_v1, b cs_ste_vec_index_v1)
606+ RETURNS boolean AS $$
607+ BEGIN
608+ RETURN ste_vec_v1_logical_contains(b, a);
609+ END;
610+ $$ LANGUAGE plpgsql;
611+
612+
613+ CREATE OR REPLACE FUNCTION jsonb_to_cs_ste_vec_index_v1 (input jsonb)
614+ RETURNS cs_ste_vec_index_v1 AS $$
615+ DECLARE
616+ vec_entry ste_vec_v1_entry;
617+ entry_array ste_vec_v1_entry[];
618+ entry_json jsonb;
619+ entry_json_array jsonb[];
620+ entry_array_length int ;
621+ i int ;
622+ BEGIN
623+ FOR entry_json IN SELECT * FROM jsonb_array_elements(input)
624+ LOOP
625+ vec_entry := ROW(
626+ entry_json- >> 0 ,
627+ ROW((entry_json- >> 1 )::bytea )::ore_cllw_8_v1,
628+ entry_json- >> 2
629+ )::ste_vec_v1_entry;
630+ entry_array := array_append(entry_array, vec_entry);
631+ END LOOP;
632+
633+ RETURN ROW(entry_array)::cs_ste_vec_index_v1;
634+ END;
635+ $$ LANGUAGE plpgsql;
636+
637+ CREATE CAST (jsonb AS cs_ste_vec_index_v1)
638+ WITH FUNCTION jsonb_to_cs_ste_vec_index_v1(jsonb) AS IMPLICIT;
639+
640+ CREATE OPERATOR @> (
641+ PROCEDURE= " ste_vec_v1_logical_contains" ,
642+ LEFTARG= cs_ste_vec_index_v1,
643+ RIGHTARG= cs_ste_vec_index_v1,
644+ COMMUTATOR = < @
645+ );
646+
647+ CREATE OPERATOR < @ (
648+ PROCEDURE= " ste_vec_v1_logical_is_contained" ,
649+ LEFTARG= cs_ste_vec_index_v1,
650+ RIGHTARG= cs_ste_vec_index_v1,
651+ COMMUTATOR = @>
652+ );
318653DROP CAST IF EXISTS (text AS ore_64_8_v1_term);
319654
320655DROP FUNCTION IF EXISTS cs_match_v1;
@@ -338,7 +673,6 @@ DROP DOMAIN IF EXISTS cs_unique_index_v1;
338673
339674CREATE DOMAIN cs_match_index_v1 AS smallint [];
340675CREATE DOMAIN cs_unique_index_v1 AS text ;
341- CREATE DOMAIN cs_ste_vec_index_v1 AS text [];
342676
343677-- cs_encrypted_v1 is a column type and cannot be dropped if in use
344678DO $$
@@ -354,7 +688,10 @@ CREATE FUNCTION _cs_encrypted_check_kind(val jsonb)
354688 RETURNS BOOLEAN
355689LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
356690BEGIN ATOMIC
357- RETURN (val- >> ' k' = ' ct' AND val ? ' c' ) AND NOT val ? ' p' ;
691+ RETURN (
692+ (val- >> ' k' = ' ct' AND val ? ' c' ) OR
693+ (val- >> ' k' = ' sv' AND val ? ' sv' )
694+ ) AND NOT val ? ' p' ;
358695END;
359696
360697CREATE FUNCTION cs_check_encrypted_v1 (val jsonb)
@@ -451,7 +788,7 @@ CREATE OR REPLACE FUNCTION cs_ste_vec_v1_v0_0(col jsonb)
451788 RETURNS cs_ste_vec_index_v1
452789 LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
453790BEGIN ATOMIC
454- SELECT ARRAY( SELECT jsonb_array_elements( col- > ' sv' ) )::cs_ste_vec_index_v1;
791+ SELECT ( col- > ' sv' )::cs_ste_vec_index_v1;
455792END;
456793
457794CREATE OR REPLACE FUNCTION cs_ste_vec_v1_v0 (col jsonb)
0 commit comments