|
| 1 | +:sectnums: |
| 2 | +:sectnumlevels: 5 |
| 3 | + |
| 4 | += hstore |
| 5 | + |
| 6 | +== Overview |
| 7 | + |
| 8 | +`hstore` is an extension included with IvorySQL that stores sets of text key/value pairs in a single column. It is useful for sparse attributes, application settings, labels, and other semi-structured data that does not require nested JSON documents. |
| 9 | + |
| 10 | +This guide was verified with IvorySQL 5.4 (PostgreSQL 18.4) and hstore 1.8 on Ubuntu 22.04 x86_64. |
| 11 | + |
| 12 | +== Compatibility |
| 13 | + |
| 14 | +[cols="1,1,3"] |
| 15 | +|=== |
| 16 | +|IvorySQL mode |Status |Verified operations |
| 17 | + |
| 18 | +|PostgreSQL |
| 19 | +|Supported |
| 20 | +|Extension installation, key lookup, containment, update, JSON conversion, and GIN indexing |
| 21 | + |
| 22 | +|Oracle compatible |
| 23 | +|Supported |
| 24 | +|The same hstore data type, functions, operators, and indexes are available after switching `ivorysql.compatible_mode` |
| 25 | +|=== |
| 26 | + |
| 27 | +[NOTE] |
| 28 | +`hstore` is an IvorySQL/PostgreSQL extension data type, not an Oracle Database native data type. Applications that must also run on Oracle Database should isolate hstore-specific SQL. |
| 29 | + |
| 30 | +== Installation |
| 31 | + |
| 32 | +The official IvorySQL 5.4 binary package already contains hstore. Connect as a user allowed to create extensions and run: |
| 33 | + |
| 34 | +[source,sql] |
| 35 | +---- |
| 36 | +CREATE EXTENSION hstore; |
| 37 | +
|
| 38 | +SELECT extversion |
| 39 | +FROM pg_extension |
| 40 | +WHERE extname = 'hstore'; |
| 41 | +---- |
| 42 | + |
| 43 | +The expected extension version in IvorySQL 5.4 is `1.8`. |
| 44 | + |
| 45 | +For an IvorySQL installation built from source, install hstore from the same source tree before creating the extension: |
| 46 | + |
| 47 | +[source,shell] |
| 48 | +---- |
| 49 | +cd /path/to/IvorySQL |
| 50 | +make -C contrib/hstore |
| 51 | +make -C contrib/hstore install |
| 52 | +---- |
| 53 | + |
| 54 | +== Usage |
| 55 | + |
| 56 | +=== Store and query attributes |
| 57 | + |
| 58 | +[source,sql] |
| 59 | +---- |
| 60 | +CREATE TABLE application_settings ( |
| 61 | + id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, |
| 62 | + attributes hstore NOT NULL |
| 63 | +); |
| 64 | +
|
| 65 | +INSERT INTO application_settings(attributes) |
| 66 | +VALUES ('theme=>dark, region=>cn, notifications=>enabled'); |
| 67 | +
|
| 68 | +-- Fetch one value. |
| 69 | +SELECT attributes -> 'theme' AS theme |
| 70 | +FROM application_settings; |
| 71 | +
|
| 72 | +-- Test whether a key exists. |
| 73 | +SELECT id |
| 74 | +FROM application_settings |
| 75 | +WHERE attributes ? 'region'; |
| 76 | +
|
| 77 | +-- Test whether all supplied pairs are present. |
| 78 | +SELECT id |
| 79 | +FROM application_settings |
| 80 | +WHERE attributes @> 'theme=>dark'::hstore; |
| 81 | +---- |
| 82 | + |
| 83 | +=== Update and remove keys |
| 84 | + |
| 85 | +The concatenation operator replaces an existing value when the right-hand hstore contains the same key. |
| 86 | + |
| 87 | +[source,sql] |
| 88 | +---- |
| 89 | +UPDATE application_settings |
| 90 | +SET attributes = attributes || 'theme=>light, locale=>zh_CN'::hstore |
| 91 | +WHERE id = 1; |
| 92 | +
|
| 93 | +UPDATE application_settings |
| 94 | +SET attributes = delete(attributes, 'notifications') |
| 95 | +WHERE id = 1; |
| 96 | +---- |
| 97 | + |
| 98 | +=== Add a GIN index |
| 99 | + |
| 100 | +GIN indexes accelerate key-existence and containment predicates such as `?`, `?&`, `?|`, and `@>`. |
| 101 | + |
| 102 | +[source,sql] |
| 103 | +---- |
| 104 | +CREATE INDEX application_settings_attributes_gin |
| 105 | +ON application_settings |
| 106 | +USING gin (attributes); |
| 107 | +
|
| 108 | +ANALYZE application_settings; |
| 109 | +---- |
| 110 | + |
| 111 | +=== Convert to JSON |
| 112 | + |
| 113 | +[source,sql] |
| 114 | +---- |
| 115 | +SELECT hstore_to_json(attributes) |
| 116 | +FROM application_settings; |
| 117 | +---- |
| 118 | + |
| 119 | +`hstore_to_json()` preserves SQL NULL values as JSON `null`, while all non-NULL hstore values are represented as JSON strings. |
| 120 | + |
| 121 | +== Oracle-compatible mode |
| 122 | + |
| 123 | +No separate extension installation is required. The extension is database-wide and remains available when the session changes mode: |
| 124 | + |
| 125 | +[source,sql] |
| 126 | +---- |
| 127 | +SET ivorysql.compatible_mode = oracle; |
| 128 | +
|
| 129 | +SELECT 'a=>1, b=>2'::hstore -> 'b' FROM dual; |
| 130 | +SELECT exist('a=>1, b=>2'::hstore, 'a') FROM dual; |
| 131 | +---- |
| 132 | + |
| 133 | +Both statements return successfully (`2` and `true`, respectively) on IvorySQL 5.4. |
| 134 | + |
| 135 | +== Verification |
| 136 | + |
| 137 | +IvorySQL's bundled regression suites can be run from the source tree against an installed server: |
| 138 | + |
| 139 | +[source,shell] |
| 140 | +---- |
| 141 | +cd /path/to/IvorySQL/contrib/hstore |
| 142 | +make installcheck |
| 143 | +make oracle-installcheck |
| 144 | +---- |
| 145 | + |
| 146 | +The IvorySQL 5.4 verification completed both PostgreSQL tests (`hstore`, `hstore_utf8`) and both Oracle-compatible tests (`ivy_hstore`, `hstore_utf8`) successfully. |
| 147 | + |
| 148 | +== Limitations and guidance |
| 149 | + |
| 150 | +* Keys and non-NULL values are text; hstore does not provide nested objects or arrays. |
| 151 | +* Each key is unique within an hstore value. If input contains duplicate keys, only one value is retained and applications must not rely on which duplicate is kept. |
| 152 | +* Use `jsonb` instead when the data needs nesting, JSON numeric/Boolean types, or JSONPath queries. |
| 153 | +* Extension installation requires appropriate database privileges. Application roles only need privileges on the tables and functions they use. |
| 154 | + |
| 155 | +For the complete operator and function reference, see the https://www.postgresql.org/docs/18/hstore.html[PostgreSQL hstore documentation]. |
0 commit comments