{"id":16926694,"url":"https://github.com/pksunkara/pgx_ulid","last_synced_at":"2025-04-09T06:10:51.364Z","repository":{"id":132561511,"uuid":"611858623","full_name":"pksunkara/pgx_ulid","owner":"pksunkara","description":"Postgres extension for ulid","archived":false,"fork":false,"pushed_at":"2024-09-27T16:12:14.000Z","size":86,"stargazers_count":327,"open_issues_count":10,"forks_count":21,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-14T20:31:18.476Z","etag":null,"topics":["postgres","postgres-extension","ulid"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pksunkara.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-03-09T17:30:11.000Z","updated_at":"2024-10-07T22:30:32.000Z","dependencies_parsed_at":"2023-12-14T06:22:47.099Z","dependency_job_id":"b31d6d8c-a5c9-42ae-8609-66c51bf2e8bb","html_url":"https://github.com/pksunkara/pgx_ulid","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pksunkara%2Fpgx_ulid","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pksunkara%2Fpgx_ulid/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pksunkara%2Fpgx_ulid/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pksunkara%2Fpgx_ulid/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pksunkara","download_url":"https://codeload.github.com/pksunkara/pgx_ulid/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247987285,"owners_count":21028895,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["postgres","postgres-extension","ulid"],"created_at":"2024-10-13T20:31:02.846Z","updated_at":"2025-04-09T06:10:51.341Z","avatar_url":"https://github.com/pksunkara.png","language":"Rust","funding_links":[],"categories":["Rust"],"sub_categories":[],"readme":"\u003c!-- omit from toc --\u003e\n# pgx_ulid\n\nA postgres extension to support [ulid][].\n\n1. [Why should I use this?](#why-should-i-use-this)\n2. [Why should I use ulid over uuid?](#why-should-i-use-ulid-over-uuid)\n3. [Monotonicity](#monotonicity)\n4. [Usage](#usage)\n5. [Installation](#installation)\n6. [Troubleshooting](#troubleshooting)\n\n## Why should I use this?\n\nThere are several different postgres extensions for [ulid][], but all of them have feature gaps. A good extension should have:\n\n- **Generator**: A generator function to generate [ulid][] identifiers.\n- **Binary**: Data be stored as binary and not text.\n- **Type**: A postgres type `ulid` which is displayed as [ulid][] text.\n- **Uuid**: Support for casting between UUID and [ulid][]\n- **Timestamp**: Support to cast an [ulid][] to a timestamp\n- **Monotonic**: Support [monotonicity][]\n\n|                             Name                              | Language | Generator | Binary | Type |  UUID  | Timestamp | Monotonic |\n| :-----------------------------------------------------------: | :------: | :-------: | :----: | :--: | :----: | :-------: | :-------: |\n|      [`pgx_ulid`](https://github.com/pksunkara/pgx_ulid)      |   Rust   |    ✔️      |   ✔️    |  ✔️   |   ✔️    |    ✔️      |    ✔️      |\n|       [`pgulid`](https://github.com/geckoboard/pgulid)        | PL/pgSQL |    ✔️      |   ❌   |  ❌  |   ❌   |    ❌     |    ❌     |\n|      [`pg_idkit`](https://github.com/VADOSWARE/pg_idkit)      |   Rust   |    ✔️      |   ❌   |  ❌  |   ❌   |    ❌     |    ❌     |\n|   [`uids-postgres`](https://github.com/spa5k/uids-postgres)   |   Rust   |    ✔️      | ⁉️[^1]  |  ❌  | ⁉️[^2]  |    ❌     |    ❌     |\n|    [`pgsql_ulid`](https://github.com/scoville/pgsql-ulid)     | PL/pgSQL |    ❌     | ⁉️[^1]  |  ❌  |   ✔️    |    ❌     |    ❌     |\n|        [`pg-ulid`](https://github.com/edoceo/pg-ulid)         |    C     |    ✔️      |   ❌   |  ❌  |   ❌   |    ❌     |    ❌     |\n| [`ulid-postgres`](https://github.com/schinckel/ulid-postgres) | PL/pgSQL |    ✔️      |   ❌   |  ✔️   |   ❌   |    ✔️      |    ❌     |\n|       [`pg_ulid`](https://github.com/iCyberon/pg_ulid)        |    Go    |    ✔️      |   ❌   |  ❌  |   ❌   |    ✔️      |    ❌     |\n|        [`pg_ulid`](https://github.com/RPG-18/pg_ulid)         |   C++    |    ✔️      | ⁉️[^1]  |  ❌  |   ✔️    |    ❌     |    ❌     |\n\n[^1]: You can convert the [ulid][] into `uuid` or `bytea` and store it like that.\n[^2]: Supports casting indirectly through `bytea`.\n\n## Why should I use ulid over uuid?\n\nThe main advantages are:\n\n* Indexes created over ULIDs are less fragmented compared to UUIDs due to the timestamp and [monotonicity][] that was encoded in the ULID when it was created.\n* ULIDs don't use special characters, so they can be used in URLs or even HTML.\n* ULIDs are shorter than UUIDs as they are comprised of 26 characters compared to UUIDs' 36 characters.\n\nThis extension is approximately **30% faster** than both `pgcrypto`'s UUID and `pg_uuidv7`'s UUIDv7 when generating a million identifiers.\n\n\u003cdetails\u003e\n\n```\nulid=# EXPLAIN ANALYSE SELECT gen_random_uuid() FROM generate_series(1, 1000000);\n                                                            QUERY PLAN\n-----------------------------------------------------------------------------------------------------------------------------------\n Function Scan on generate_series  (cost=0.00..12500.00 rows=1000000 width=16) (actual time=46.630..1401.638 rows=1000000 loops=1)\n Planning Time: 0.020 ms\n Execution Time: 1430.364 ms\n(3 rows)\n\nulid=# EXPLAIN ANALYSE SELECT uuid_generate_v7() FROM generate_series(1, 1000000);\n                                                            QUERY PLAN\n-----------------------------------------------------------------------------------------------------------------------------------\n Function Scan on generate_series  (cost=0.00..12500.00 rows=1000000 width=16) (actual time=46.977..1427.477 rows=1000000 loops=1)\n Planning Time: 0.031 ms\n Execution Time: 1456.333 ms\n(3 rows)\n\nulid=# EXPLAIN ANALYSE SELECT gen_ulid() FROM generate_series(1, 1000000);\n                                                            QUERY PLAN\n-----------------------------------------------------------------------------------------------------------------------------------\n Function Scan on generate_series  (cost=0.00..12500.00 rows=1000000 width=32) (actual time=46.820..1070.447 rows=1000000 loops=1)\n Planning Time: 0.020 ms\n Execution Time: 1098.086 ms\n(3 rows)\n```\n\n\u003c/details\u003e\n\nThis extension is approximately **20% faster** than both `pgcrypto`'s UUID and `pg_uuidv7`'s UUIDv7 when generating and inserting a million identifiers.\n\n\u003cdetails\u003e\n\n```\nulid=# EXPLAIN ANALYSE INSERT INTO uuid_keys(id) SELECT gen_random_uuid() FROM generate_series(1, 1000000);\n                                                               QUERY PLAN\n-----------------------------------------------------------------------------------------------------------------------------------------\n Insert on uuid_keys  (cost=0.00..22500.00 rows=0 width=0) (actual time=2006.633..2006.634 rows=0 loops=1)\n   -\u003e  Function Scan on generate_series  (cost=0.00..12500.00 rows=1000000 width=16) (actual time=46.846..1459.869 rows=1000000 loops=1)\n Planning Time: 0.029 ms\n Execution Time: 2008.195 ms\n(4 rows)\n\nulid=# EXPLAIN ANALYSE INSERT INTO uuid7_keys(id) SELECT uuid_generate_v7() FROM generate_series(1, 1000000);\n                                                               QUERY PLAN\n-----------------------------------------------------------------------------------------------------------------------------------------\n Insert on uuid7_keys  (cost=0.00..22500.00 rows=0 width=0) (actual time=2030.731..2030.731 rows=0 loops=1)\n   -\u003e  Function Scan on generate_series  (cost=0.00..12500.00 rows=1000000 width=16) (actual time=46.894..1479.223 rows=1000000 loops=1)\n Planning Time: 0.030 ms\n Execution Time: 2032.296 ms\n(4 rows)\n\nulid=# EXPLAIN ANALYSE INSERT INTO ulid_keys(id) SELECT gen_ulid() FROM generate_series(1, 1000000);\n                                                               QUERY PLAN\n-----------------------------------------------------------------------------------------------------------------------------------------\n Insert on ulid_keys  (cost=0.00..22500.00 rows=0 width=0) (actual time=1665.380..1665.380 rows=0 loops=1)\n   -\u003e  Function Scan on generate_series  (cost=0.00..12500.00 rows=1000000 width=32) (actual time=46.719..1140.979 rows=1000000 loops=1)\n Planning Time: 0.029 ms\n Execution Time: 1666.867 ms\n(4 rows)\n```\n\n\u003c/details\u003e\n\n## Monotonicity\n\nThis extension supports [monotonicity][] through `gen_monotonic_ulid()` function. To achive this, it uses PostgreSQL's shared memory and LWLock to store last generated ULID.\n\nTo be able to use [monotonic][monotonicity] ULID's, it is necessary to add this extension to `postgresql.conf`'s `shared_preload_libraries` configuration setting.\n\n```conf\nshared_preload_libraries = 'pgx_ulid'\t# (change requires restart)\n```\n\n\u003cdetails\u003e\n\n```\nulid=# EXPLAIN ANALYSE SELECT gen_ulid() FROM generate_series(1, 1000000);\n                                                            QUERY PLAN\n-----------------------------------------------------------------------------------------------------------------------------------\n Function Scan on generate_series  (cost=0.00..12500.00 rows=1000000 width=32) (actual time=47.207..2908.978 rows=1000000 loops=1)\n Planning Time: 0.035 ms\n Execution Time: 4053.482 ms\n(3 rows)\n\nulid=# EXPLAIN ANALYSE SELECT gen_monotonic_ulid() FROM generate_series(1, 1000000);\n                                                            QUERY PLAN\n-----------------------------------------------------------------------------------------------------------------------------------\n Function Scan on generate_series  (cost=0.00..12500.00 rows=1000000 width=32) (actual time=46.479..2586.654 rows=1000000 loops=1)\n Planning Time: 0.037 ms\n Execution Time: 3693.901 ms\n(3 rows)\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\n```\nulid=# EXPLAIN ANALYZE INSERT INTO users (name) SELECT 'Client 1' FROM generate_series(1, 1000000);\n                                                               QUERY PLAN\n-----------------------------------------------------------------------------------------------------------------------------------------\n Insert on users  (cost=0.00..12500.00 rows=0 width=0) (actual time=8418.257..8418.261 rows=0 loops=1)\n   -\u003e  Function Scan on generate_series  (cost=0.00..12500.00 rows=1000000 width=64) (actual time=99.804..3013.333 rows=1000000 loops=1)\n Planning Time: 0.066 ms\n Execution Time: 8419.571 ms\n(4 rows)\n\nulid=# EXPLAIN ANALYZE INSERT INTO users (name) SELECT 'Client 2' FROM generate_series(1, 1000000);\n                                                               QUERY PLAN\n-----------------------------------------------------------------------------------------------------------------------------------------\n Insert on users  (cost=0.00..12500.00 rows=0 width=0) (actual time=8359.558..8359.561 rows=0 loops=1)\n   -\u003e  Function Scan on generate_series  (cost=0.00..12500.00 rows=1000000 width=64) (actual time=64.449..2976.754 rows=1000000 loops=1)\n Planning Time: 0.090 ms\n Execution Time: 8360.840 ms\n(4 rows)\n```\n\n\u003c/details\u003e\n\n\u003c!-- omit from toc --\u003e\n### Pros\n\n1. Monotonic ULIDs are better for indexing, as they are sorted by default.\n2. Monotonic ULIDs slightly faster than `gen_ulid()` when generating lots of ULIDs within one millisecond. Because, in this case, there is no need to generate random component of ULID. Instead it is just incremented.\n\n\u003c!-- omit from toc --\u003e\n### Cons\n\n1. Previously generated ULID is saved in shmem and accessed via LWLock. i.e. it is exclusive for function invocation within database. Theoretically this can lead to slowdowns.\n\n    *...But, in practice (at least in our testing) `gen_monotonic_ulid()` is slightly faster than `gen_ulid()`.*\n\n2. Extensions that use shared memory must be loaded via `postgresql.conf`'s `shared_preload_libraries` configuration setting.\n\n    *...But, it only affects `gen_monotonic_ulid()` function. Other functions of this extension will work normally even without this config.*\n\n3. Monotonic ULIDs may overflow and throw an error.\n\n    *...But, chances are negligible.*\n\n## Usage\n\nUse the extension in the database:\n\n```sql\nCREATE EXTENSION ulid;\n-- or try \"CREATE EXTENSION pgx_ulid;\" if installed manually\n```\n\nCreate a table with [ulid][] as a primary key:\n\n```sql\nCREATE TABLE users (\n  id ulid NOT NULL DEFAULT gen_ulid() PRIMARY KEY,\n  name text NOT NULL\n);\n```\n\nOr, create a table with [monotonic][monotonicity] [ulid][] as a primary key:\n\n```sql\nCREATE TABLE users (\n  id ulid NOT NULL DEFAULT gen_monotonic_ulid() PRIMARY KEY,\n  name text NOT NULL\n);\n```\n\nOperate it normally with text in queries:\n\n```sql\nSELECT * FROM users WHERE id = '01ARZ3NDEKTSV4RRFFQ69G5FAV';\n```\n\nCast [ulid][] to timestamp:\n\n```sql\nALTER TABLE users\nADD COLUMN created_at timestamp GENERATED ALWAYS AS (id::timestamp) STORED;\n```\n\nCast timestamp to [ulid][], this generates a zeroed ULID with the timestamp prefixed (TTTTTTTTTT0000000000000000):\n\n```sql\n-- gets all users where the ID was created on 2023-09-15, without using another column and taking advantage of the index\nSELECT * FROM users WHERE id BETWEEN '2023-09-15'::timestamp::ulid AND '2023-09-16'::timestamp::ulid;\n```\n\n## Installation\n\nUse [pgrx][]. You can clone this repo and install this extension locally by following [this guide](https://github.com/tcdi/pgrx/blob/master/cargo-pgrx/README.md#installing-your-extension-locally).\n\nYou can also download relevant installation packages from [releases](https://github.com/pksunkara/pgx_ulid/releases) page.\n\n## Troubleshooting\n\nIf you encounter the exclusive lock error while using `pgx_ulid`, follow these steps to resolve the issue:\n\n1. Alter the system to set `shared_preload_libraries` to `pgx_ulid` by running the following SQL command:\n\n   ```sql\n   ALTER SYSTEM SET shared_preload_libraries = 'pgx_ulid';\n   ```\n\n2. Restart the PostgreSQL service to apply the changes. The command to restart PostgreSQL depends on your system.\n3. Verify that `ulid` is successfully loaded into shared libraries by executing:\n\n   ```sql\n   SHOW shared_preload_libraries;\n   ```\n\n\u003c!-- omit from toc --\u003e\n## Contributors\n\nHere is a list of [Contributors](http://github.com/pksunkara/pgx_ulid/contributors)\n\n\u003c!-- omit from toc --\u003e\n### TODO\n\n\u003c!-- omit from toc --\u003e\n## License\n\nMIT/X11\n\n\u003c!-- omit from toc --\u003e\n## Bug Reports\n\nReport [here](http://github.com/pksunkara/pgx_ulid/issues).\n\n\u003c!-- omit from toc --\u003e\n## Creator\n\nPavan Kumar Sunkara (pavan.sss1991@gmail.com)\n\nFollow me on [github](https://github.com/users/follow?target=pksunkara), [twitter](http://twitter.com/pksunkara)\n\n[ulid]: https://github.com/ulid/spec\n[pgrx]: https://github.com/tcdi/pgrx\n[monotonicity]: https://github.com/ulid/spec#monotonicity\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpksunkara%2Fpgx_ulid","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpksunkara%2Fpgx_ulid","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpksunkara%2Fpgx_ulid/lists"}