@@ -315,6 +315,341 @@ CREATE OPERATOR CLASS ore_64_8_v1_btree_ops DEFAULT FOR TYPE ore_64_8_v1 USING b
315
315
OPERATOR 4 >= ,
316
316
OPERATOR 5 > ,
317
317
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
+ );
318
653
DROP CAST IF EXISTS (text AS ore_64_8_v1_term);
319
654
320
655
DROP FUNCTION IF EXISTS cs_match_v1;
@@ -338,7 +673,6 @@ DROP DOMAIN IF EXISTS cs_unique_index_v1;
338
673
339
674
CREATE DOMAIN cs_match_index_v1 AS smallint [];
340
675
CREATE DOMAIN cs_unique_index_v1 AS text ;
341
- CREATE DOMAIN cs_ste_vec_index_v1 AS text [];
342
676
343
677
-- cs_encrypted_v1 is a column type and cannot be dropped if in use
344
678
DO $$
@@ -354,7 +688,10 @@ CREATE FUNCTION _cs_encrypted_check_kind(val jsonb)
354
688
RETURNS BOOLEAN
355
689
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
356
690
BEGIN 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' ;
358
695
END;
359
696
360
697
CREATE FUNCTION cs_check_encrypted_v1 (val jsonb)
@@ -451,7 +788,7 @@ CREATE OR REPLACE FUNCTION cs_ste_vec_v1_v0_0(col jsonb)
451
788
RETURNS cs_ste_vec_index_v1
452
789
LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE
453
790
BEGIN ATOMIC
454
- SELECT ARRAY( SELECT jsonb_array_elements( col- > ' sv' ) )::cs_ste_vec_index_v1;
791
+ SELECT ( col- > ' sv' )::cs_ste_vec_index_v1;
455
792
END;
456
793
457
794
CREATE OR REPLACE FUNCTION cs_ste_vec_v1_v0 (col jsonb)
0 commit comments