|
| 1 | +:sectnums: |
| 2 | +:sectnumlevels: 5 |
| 3 | + |
| 4 | +:imagesdir: ./_images |
| 5 | + |
| 6 | += Global Unique Index |
| 7 | + |
| 8 | +== Overview |
| 9 | + |
| 10 | +The Global Unique Index is a cross-partition uniqueness constraint feature provided by IvorySQL for partitioned tables. |
| 11 | + |
| 12 | +Standard PostgreSQL unique indexes only enforce uniqueness within a single partition and cannot guarantee uniqueness across partitions. IvorySQL introduces the `GLOBAL` keyword: when specified during unique index creation, the database scans all partitions on every INSERT or UPDATE to ensure no duplicate values exist across the entire partitioned table. |
| 13 | + |
| 14 | +This feature is supported in both PG-compatible mode and Oracle-compatible mode. |
| 15 | + |
| 16 | +== Syntax |
| 17 | + |
| 18 | +[source,sql] |
| 19 | +---- |
| 20 | +CREATE UNIQUE INDEX [ index_name ] ON partitioned_table ( column [, ...] ) GLOBAL; |
| 21 | +---- |
| 22 | + |
| 23 | +Parameter description: |
| 24 | + |
| 25 | +- `UNIQUE`: must be used together with `GLOBAL` to specify the uniqueness constraint; |
| 26 | +- `GLOBAL`: enables cross-partition uniqueness checking; only valid on partitioned tables; |
| 27 | +- `index_name`: optional; if omitted, the database generates an index name automatically. |
| 28 | + |
| 29 | +== Test Cases |
| 30 | + |
| 31 | +=== Create a Partitioned Table and Global Unique Index |
| 32 | + |
| 33 | +[source,sql] |
| 34 | +---- |
| 35 | +-- Create a partitioned table |
| 36 | +CREATE TABLE gidxpart (a int, b int, c text) PARTITION BY RANGE (a); |
| 37 | +CREATE TABLE gidxpart1 PARTITION OF gidxpart FOR VALUES FROM (1) TO (10); |
| 38 | +CREATE TABLE gidxpart2 PARTITION OF gidxpart FOR VALUES FROM (10) TO (100); |
| 39 | +CREATE TABLE gidxpart3 PARTITION OF gidxpart FOR VALUES FROM (100) TO (200); |
| 40 | +
|
| 41 | +-- Create a global unique index with an explicit name |
| 42 | +CREATE UNIQUE INDEX gidx_u ON gidxpart USING btree(b) GLOBAL; |
| 43 | +
|
| 44 | +-- Create a global unique index without specifying a name |
| 45 | +CREATE UNIQUE INDEX ON gidxpart (b) GLOBAL; |
| 46 | +---- |
| 47 | + |
| 48 | +=== INSERT: Cross-Partition Uniqueness Validation |
| 49 | + |
| 50 | +[source,sql] |
| 51 | +---- |
| 52 | +-- These inserts succeed (no duplicate values in column b across partitions) |
| 53 | +INSERT INTO gidxpart VALUES (1, 1, 'first'); |
| 54 | +INSERT INTO gidxpart VALUES (11, 11, 'eleventh'); |
| 55 | +INSERT INTO gidxpart VALUES (2, 120, 'second'); |
| 56 | +INSERT INTO gidxpart VALUES (12, 2, 'twelfth'); |
| 57 | +INSERT INTO gidxpart VALUES (150, 13, 'no duplicate b'); |
| 58 | +
|
| 59 | +-- These inserts fail: b=11 already exists in another partition |
| 60 | +INSERT INTO gidxpart VALUES (2, 11, 'duplicated (b)=(11) on other partition'); |
| 61 | +-- ERROR: duplicate key value violates unique constraint |
| 62 | +
|
| 63 | +INSERT INTO gidxpart VALUES (12, 1, 'duplicated (b)=(1) on other partition'); |
| 64 | +-- ERROR: duplicate key value violates unique constraint |
| 65 | +
|
| 66 | +INSERT INTO gidxpart VALUES (150, 11, 'duplicated (b)=(11) on other partition'); |
| 67 | +-- ERROR: duplicate key value violates unique constraint |
| 68 | +---- |
| 69 | + |
| 70 | +=== UPDATE: Cross-Partition Uniqueness Validation |
| 71 | + |
| 72 | +[source,sql] |
| 73 | +---- |
| 74 | +-- UPDATE operations are also subject to the global unique index |
| 75 | +UPDATE gidxpart SET b = 2 WHERE a = 2; |
| 76 | +-- ERROR: duplicate key value violates unique constraint (b=2 already exists) |
| 77 | +
|
| 78 | +UPDATE gidxpart SET b = 12 WHERE a = 12; |
| 79 | +-- Succeeds (b=12 is unique across all partitions) |
| 80 | +---- |
| 81 | + |
| 82 | +=== Partition ATTACH and DETACH |
| 83 | + |
| 84 | +[source,sql] |
| 85 | +---- |
| 86 | +-- Create a standalone table for ATTACH testing |
| 87 | +CREATE TABLE gidxpart_new (a int, b int, c text); |
| 88 | +INSERT INTO gidxpart_new VALUES (100001, 11, 'conflict with gidxpart1'); |
| 89 | +
|
| 90 | +-- ATTACH fails if the new partition contains values that duplicate existing ones |
| 91 | +ALTER TABLE gidxpart ATTACH PARTITION gidxpart_new |
| 92 | + FOR VALUES FROM (100000) TO (199999); |
| 93 | +-- ERROR: duplicate key value violates unique constraint |
| 94 | +
|
| 95 | +-- DETACH is allowed; the partition's global index reverts to a regular local index |
| 96 | +ALTER TABLE gidxpart DETACH PARTITION gidxpart2; |
| 97 | +---- |
| 98 | + |
| 99 | +== Limitations |
| 100 | + |
| 101 | +. The `GLOBAL` keyword must be used together with `UNIQUE`; global non-unique indexes are not supported; |
| 102 | +. Global unique indexes are only applicable to **partitioned tables**; the keyword is not valid on regular tables; |
| 103 | +. Every INSERT or UPDATE requires scanning all partitions to verify uniqueness, which introduces **performance overhead** when the number of partitions or data volume is large; |
| 104 | +. When attaching a partition via `ATTACH PARTITION`, the operation will fail if the new partition contains data that duplicates values in existing partitions; |
| 105 | +. After a partition is detached with `DETACH PARTITION`, the corresponding global index automatically reverts to a regular local (partition-level) index; |
| 106 | +. Creating a global unique index independently on a sub-partition (second-level partition) is not supported; cross-partition uniqueness is managed exclusively at the top-level partitioned table. |
0 commit comments