Add helper functions for dshash tables with string keys.
authorNathan Bossart <[email protected]>
Mon, 26 Feb 2024 21:47:13 +0000 (15:47 -0600)
committerNathan Bossart <[email protected]>
Mon, 26 Feb 2024 21:47:13 +0000 (15:47 -0600)
Presently, string keys are not well-supported for dshash tables.
The dshash code always copies key_size bytes into new entries'
keys, and dshash.h only provides compare and hash functions that
forward to memcmp() and tag_hash(), both of which do not stop at
the first NUL.  This means that callers must pad string keys so
that the data beyond the first NUL does not adversely affect the
results of copying, comparing, and hashing the keys.

To better support string keys in dshash tables, this commit does
a couple things:

* A new copy_function field is added to the dshash_parameters
  struct.  This function pointer specifies how the key should be
  copied into new table entries.  For example, we only want to copy
  up to the first NUL byte for string keys.  A dshash_memcpy()
  helper function is provided and used for all existing in-tree
  dshash tables without string keys.

* A set of helper functions for string keys are provided.  These
  helper functions forward to strcmp(), strcpy(), and
  string_hash(), all of which ignore data beyond the first NUL.

This commit also adjusts the DSM registry's dshash table to use the
new helper functions for string keys.

Reviewed-by: Andy Fan
Discussion: https://postgr.es/m/20240119215941.GA1322079%40nathanxps13

src/backend/lib/dshash.c
src/backend/replication/logical/launcher.c
src/backend/storage/ipc/dsm_registry.c
src/backend/utils/activity/pgstat_shmem.c
src/backend/utils/cache/typcache.c
src/include/lib/dshash.h

index b0bc0abda0094d10acba8d85e8b4db515b5f4119..cc49b4ca51300d265e15c35f6a52f342fc439c9b 100644 (file)
@@ -188,6 +188,8 @@ static bool delete_item_from_bucket(dshash_table *hash_table,
 static inline dshash_hash hash_key(dshash_table *hash_table, const void *key);
 static inline bool equal_keys(dshash_table *hash_table,
                              const void *a, const void *b);
+static inline void copy_key(dshash_table *hash_table, void *dest,
+                           const void *src);
 
 #define PARTITION_LOCK(hash_table, i)          \
    (&(hash_table)->control->partitions[(i)].lock)
@@ -583,6 +585,49 @@ dshash_memhash(const void *v, size_t size, void *arg)
    return tag_hash(v, size);
 }
 
+/*
+ * A copy function that forwards to memcpy.
+ */
+void
+dshash_memcpy(void *dest, const void *src, size_t size, void *arg)
+{
+   (void) memcpy(dest, src, size);
+}
+
+/*
+ * A compare function that forwards to strcmp.
+ */
+int
+dshash_strcmp(const void *a, const void *b, size_t size, void *arg)
+{
+   Assert(strlen((const char *) a) < size);
+   Assert(strlen((const char *) b) < size);
+
+   return strcmp((const char *) a, (const char *) b);
+}
+
+/*
+ * A hash function that forwards to string_hash.
+ */
+dshash_hash
+dshash_strhash(const void *v, size_t size, void *arg)
+{
+   Assert(strlen((const char *) v) < size);
+
+   return string_hash((const char *) v, size);
+}
+
+/*
+ * A copy function that forwards to strcpy.
+ */
+void
+dshash_strcpy(void *dest, const void *src, size_t size, void *arg)
+{
+   Assert(strlen((const char *) src) < size);
+
+   (void) strcpy((char *) dest, (const char *) src);
+}
+
 /*
  * Sequentially scan through dshash table and return all the elements one by
  * one, return NULL when all elements have been returned.
@@ -949,7 +994,7 @@ insert_into_bucket(dshash_table *hash_table,
                                hash_table->params.entry_size +
                                MAXALIGN(sizeof(dshash_table_item)));
    item = dsa_get_address(hash_table->area, item_pointer);
-   memcpy(ENTRY_FROM_ITEM(item), key, hash_table->params.key_size);
+   copy_key(hash_table, ENTRY_FROM_ITEM(item), key);
    insert_item_into_bucket(hash_table, item_pointer, item, bucket);
    return item;
 }
@@ -1032,3 +1077,14 @@ equal_keys(dshash_table *hash_table, const void *a, const void *b)
                                               hash_table->params.key_size,
                                               hash_table->arg) == 0;
 }
+
+/*
+ * Copy a key.
+ */
+static inline void
+copy_key(dshash_table *hash_table, void *dest, const void *src)
+{
+   hash_table->params.copy_function(dest, src,
+                                    hash_table->params.key_size,
+                                    hash_table->arg);
+}
index ee66c292bd0493a96b32ec53a4b3b90b2925aa4c..487f141a596e7852ce78906144299bcfa425266a 100644 (file)
@@ -88,6 +88,7 @@ static const dshash_parameters dsh_params = {
    sizeof(LauncherLastStartTimesEntry),
    dshash_memcmp,
    dshash_memhash,
+   dshash_memcpy,
    LWTRANCHE_LAUNCHER_HASH
 };
 
index c17817365325bec99727df9a3b6def1b938cc83f..9f58ea611b913cc7c0b163e99d12499aabadf2ca 100644 (file)
@@ -50,8 +50,9 @@ typedef struct DSMRegistryEntry
 static const dshash_parameters dsh_params = {
    offsetof(DSMRegistryEntry, handle),
    sizeof(DSMRegistryEntry),
-   dshash_memcmp,
-   dshash_memhash,
+   dshash_strcmp,
+   dshash_strhash,
+   dshash_strcpy,
    LWTRANCHE_DSM_REGISTRY_HASH
 };
 
@@ -132,7 +133,6 @@ GetNamedDSMSegment(const char *name, size_t size,
 {
    DSMRegistryEntry *entry;
    MemoryContext oldcontext;
-   char        name_padded[offsetof(DSMRegistryEntry, handle)] = {0};
    void       *ret;
 
    Assert(found);
@@ -155,8 +155,7 @@ GetNamedDSMSegment(const char *name, size_t size,
    /* Connect to the registry. */
    init_dsm_registry();
 
-   strcpy(name_padded, name);
-   entry = dshash_find_or_insert(dsm_registry_table, name_padded, found);
+   entry = dshash_find_or_insert(dsm_registry_table, name, found);
    if (!(*found))
    {
        /* Initialize the segment. */
index 71410ddd3fb94ffec590b227bfc69ea97f9ede2a..91591da3958f48a8c4484dce41e521242634879d 100644 (file)
@@ -64,6 +64,7 @@ static const dshash_parameters dsh_params = {
    sizeof(PgStatShared_HashEntry),
    pgstat_cmp_hash_key,
    pgstat_hash_hash_key,
+   dshash_memcpy,
    LWTRANCHE_PGSTATS_HASH
 };
 
index 2842bde907174e6440357f19ed06459f426580c5..f411e33b8e757c0f917c3bd41a26355fdfc60fbc 100644 (file)
@@ -259,6 +259,7 @@ static const dshash_parameters srtr_record_table_params = {
    sizeof(SharedRecordTableEntry),
    shared_record_table_compare,
    shared_record_table_hash,
+   dshash_memcpy,
    LWTRANCHE_PER_SESSION_RECORD_TYPE
 };
 
@@ -268,6 +269,7 @@ static const dshash_parameters srtr_typmod_table_params = {
    sizeof(SharedTypmodTableEntry),
    dshash_memcmp,
    dshash_memhash,
+   dshash_memcpy,
    LWTRANCHE_PER_SESSION_RECORD_TYPMOD
 };
 
index 91f70ac2b5fe6adc95667ea92f55a521288de407..2ff1ba6c24dec1ee669f61ec8f292500e420b166 100644 (file)
@@ -37,6 +37,10 @@ typedef int (*dshash_compare_function) (const void *a, const void *b,
 typedef dshash_hash (*dshash_hash_function) (const void *v, size_t size,
                                             void *arg);
 
+/* A function type for copying keys. */
+typedef void (*dshash_copy_function) (void *dest, const void *src, size_t size,
+                                     void *arg);
+
 /*
  * The set of parameters needed to create or attach to a hash table.  The
  * members tranche_id and tranche_name do not need to be initialized when
@@ -55,6 +59,7 @@ typedef struct dshash_parameters
    size_t      entry_size;     /* Total size of entry */
    dshash_compare_function compare_function;   /* Compare function */
    dshash_hash_function hash_function; /* Hash function */
+   dshash_copy_function copy_function; /* Copy function */
    int         tranche_id;     /* The tranche ID to use for locks */
 } dshash_parameters;
 
@@ -105,9 +110,21 @@ extern void *dshash_seq_next(dshash_seq_status *status);
 extern void dshash_seq_term(dshash_seq_status *status);
 extern void dshash_delete_current(dshash_seq_status *status);
 
-/* Convenience hash and compare functions wrapping memcmp and tag_hash. */
+/*
+ * Convenience hash, compare, and copy functions wrapping memcmp, tag_hash, and
+ * memcpy.
+ */
 extern int dshash_memcmp(const void *a, const void *b, size_t size, void *arg);
 extern dshash_hash dshash_memhash(const void *v, size_t size, void *arg);
+extern void dshash_memcpy(void *dest, const void *src, size_t size, void *arg);
+
+/*
+ * Convenience hash, compare, and copy functions wrapping strcmp, string_hash,
+ * and strcpy.
+ */
+extern int dshash_strcmp(const void *a, const void *b, size_t size, void *arg);
+extern dshash_hash dshash_strhash(const void *v, size_t size, void *arg);
+extern void dshash_strcpy(void *dest, const void *src, size_t size, void *arg);
 
 /* Debugging support. */
 extern void dshash_dump(dshash_table *hash_table);