@@ -8,6 +8,8 @@ use uuid::Uuid;
88
99use pgrx:: prelude:: * ;
1010
11+ use std:: hash:: { Hash , Hasher } ;
12+
1113pgrx:: pg_module_magic!( ) ;
1214
1315#[ pg_extern]
@@ -66,6 +68,21 @@ fn typeid_ne(a: TypeID, b: TypeID) -> bool {
6668 typeid_cmp ( a, b) != 0
6769}
6870
71+ #[ pg_extern]
72+ fn typeid_hash ( typeid : TypeID ) -> i32 {
73+ let mut hasher = gxhash:: GxHasher :: default ( ) ;
74+ typeid. hash ( & mut hasher) ;
75+ hasher. finish ( ) as i32
76+ }
77+
78+ #[ pg_extern]
79+ fn typeid_hash_extended ( typeid : TypeID , seed : i64 ) -> i64 {
80+ let mut hasher = gxhash:: GxHasher :: with_seed ( seed) ;
81+
82+ typeid. hash ( & mut hasher) ;
83+ hasher. finish ( ) as i64
84+ }
85+
6986extension_sql ! {
7087r#"
7188 CREATE OPERATOR < (
115132 OPERATOR 4 >= (typeid, typeid),
116133 OPERATOR 5 > (typeid, typeid),
117134 FUNCTION 1 typeid_cmp(typeid, typeid);
135+
136+ CREATE OPERATOR FAMILY typeid_hash_ops USING hash;
137+
138+ CREATE OPERATOR CLASS typeid_hash_ops DEFAULT FOR TYPE typeid USING hash AS
139+ OPERATOR 1 = (typeid, typeid),
140+ FUNCTION 1 typeid_hash(typeid),
141+ FUNCTION 2 typeid_hash_extended(typeid, bigint);
118142 "# ,
119143 name = "create_typeid_operator_class" ,
120144 finalize,
@@ -129,6 +153,7 @@ fn uuid_generate_v7() -> pgrx::Uuid {
129153#[ cfg( any( test, feature = "pg_test" ) ) ]
130154#[ pg_schema]
131155mod tests {
156+ use crate :: TypeID ;
132157 use pgrx:: prelude:: * ;
133158 use uuid:: Uuid ;
134159
@@ -147,6 +172,95 @@ mod tests {
147172
148173 assert_eq ! ( converted. get_version_num( ) , 7 ) ;
149174 }
175+
176+ #[ pg_test]
177+ fn test_hashing ( ) {
178+ use crate :: typeid_hash;
179+ use crate :: TypeID ;
180+
181+ let id = TypeID :: from_string ( "qual_01j1acv2aeehk8hcapaw7qyjvq" ) . unwrap ( ) ;
182+ let id2 = TypeID :: from_string ( "qual_01j1acv2aeehk8hcapaw7qyjvq" ) . unwrap ( ) ;
183+
184+ let hash = typeid_hash ( id) ;
185+ let hash2 = typeid_hash ( id2) ;
186+ println ! ( "UUID: {:?}" , hash) ;
187+
188+ assert_eq ! (
189+ hash, hash2,
190+ "Hashes should be consistent for the same input"
191+ ) ;
192+ }
193+
194+ #[ pg_test]
195+ fn test_custom_type_in_query ( ) {
196+ use crate :: typeid_generate;
197+ // Create tables
198+ Spi :: run ( "CREATE TABLE question (id typeid);" ) . unwrap ( ) ;
199+ Spi :: run ( "CREATE TABLE answer (id typeid, question typeid);" ) . unwrap ( ) ;
200+
201+ println ! ( "Creating tables" ) ;
202+ // Generate and insert test data
203+ let typeid1 = typeid_generate ( "qual" ) ;
204+ let typeid2 = typeid_generate ( "answer" ) ;
205+ let typeid3 = typeid_generate ( "answer" ) ;
206+
207+ insert_into_table ( "question" , & typeid1) ;
208+
209+ insert_answer ( & typeid2, & typeid1) ;
210+ insert_answer ( & typeid3, & typeid1) ;
211+
212+ // Execute the query and check results
213+ let result = Spi :: get_one :: < i64 > (
214+ "SELECT COUNT(*) FROM answer WHERE question IN (SELECT id FROM question)" ,
215+ )
216+ . unwrap ( ) ;
217+ assert_eq ! ( result, Some ( 2 ) ) ;
218+ }
219+
220+ fn oid_for_type ( type_name : & str ) -> Result < Option < PgOid > , pgrx:: spi:: Error > {
221+ use crate :: pg_sys:: Oid ;
222+
223+ let oid = Spi :: get_one_with_args :: < u32 > (
224+ "SELECT oid FROM pg_type WHERE typname = $1" ,
225+ vec ! [ ( PgBuiltInOids :: TEXTOID . oid( ) , type_name. into_datum( ) ) ] ,
226+ ) ?;
227+ Ok ( oid. map ( |oid| PgOid :: from ( Oid :: from ( oid) ) ) )
228+ }
229+
230+ fn insert_answer ( typeid : & TypeID , reference : & TypeID ) {
231+ let query = format ! (
232+ "INSERT INTO {} (id, question) VALUES ($1::typeid, $2::typeid)" ,
233+ "answer"
234+ ) ;
235+ let oid = oid_for_type ( "typeid" )
236+ . unwrap ( )
237+ . expect ( "expected to find oid" ) ;
238+
239+ println ! ( "Inserting into table: {:?}" , oid) ;
240+ Spi :: run_with_args (
241+ & query,
242+ Some ( vec ! [
243+ ( oid, typeid. clone( ) . into_datum( ) ) ,
244+ ( oid, reference. clone( ) . into_datum( ) ) ,
245+ ] ) ,
246+ )
247+ . unwrap ( ) ;
248+ }
249+
250+ fn insert_into_table ( table_name : & str , typeid : & TypeID ) {
251+ let query = format ! ( "INSERT INTO {} (id) VALUES ($1::typeid)" , table_name) ;
252+ let oid = oid_for_type ( "typeid" ) . unwrap ( ) ;
253+
254+ println ! ( "Inserting into table: {:?}" , oid. unwrap( ) ) ;
255+ Spi :: run_with_args (
256+ & query,
257+ Some ( vec ! [ (
258+ oid. expect( "expected to find oid" ) ,
259+ typeid. clone( ) . into_datum( ) ,
260+ ) ] ) ,
261+ )
262+ . unwrap ( ) ;
263+ }
150264}
151265
152266/// This module is required by `cargo pgrx test` invocations.
0 commit comments