Created
December 30, 2013 19:17
-
-
Save surjikal/8186631 to your computer and use it in GitHub Desktop.
Nested Hstore Patch
http://www.postgresql.org/message-id/[email protected]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/contrib/hstore/.gitignore b/contrib/hstore/.gitignore | |
index 5dcb3ff..b84aac6 100644 | |
--- a/contrib/hstore/.gitignore | |
+++ b/contrib/hstore/.gitignore | |
@@ -2,3 +2,6 @@ | |
/log/ | |
/results/ | |
/tmp_check/ | |
+/hstore_gram.c | |
+/hstore_gram.h | |
+/hstore_scan.c | |
diff --git a/contrib/hstore/Makefile b/contrib/hstore/Makefile | |
index 43b7e5f..125a29b 100644 | |
--- a/contrib/hstore/Makefile | |
+++ b/contrib/hstore/Makefile | |
@@ -2,13 +2,16 @@ | |
MODULE_big = hstore | |
OBJS = hstore_io.o hstore_op.o hstore_gist.o hstore_gin.o hstore_compat.o \ | |
- crc32.o | |
+ hstore_gram.o hstore_support.o | |
EXTENSION = hstore | |
-DATA = hstore--1.2.sql hstore--1.1--1.2.sql hstore--1.0--1.1.sql \ | |
- hstore--unpackaged--1.0.sql | |
+DATA = hstore--1.3.sql hstore--1.0--1.1.sql hstore--1.1--1.2.sql \ | |
+ hstore--1.2--1.3.sql hstore--unpackaged--1.0.sql | |
-REGRESS = hstore | |
+REGRESS = hstore nested types | |
+ | |
+EXTRA_CLEAN = y.tab.c y.tab.h \ | |
+ hstore_gram.c hstore_scan.c hstore_gram.h | |
ifdef USE_PGXS | |
PG_CONFIG = pg_config | |
@@ -20,3 +23,13 @@ top_builddir = ../.. | |
include $(top_builddir)/src/Makefile.global | |
include $(top_srcdir)/contrib/contrib-global.mk | |
endif | |
+ | |
+hstore_gram.o: hstore_scan.c | |
+ | |
+hstore_gram.c: BISONFLAGS += -d | |
+ | |
+distprep: hstore_gram.c hstore_scan.c | |
+ | |
+maintainer-clean: | |
+ rm -f hstore_gram.c hstore_scan.c hstore_gram.h | |
+ | |
diff --git a/contrib/hstore/crc32.c b/contrib/hstore/crc32.c | |
deleted file mode 100644 | |
index c82fc66..0000000 | |
--- a/contrib/hstore/crc32.c | |
+++ /dev/null | |
@@ -1,106 +0,0 @@ | |
-/* | |
- * contrib/hstore/crc32.c | |
- * | |
- * Both POSIX and CRC32 checksums */ | |
- | |
-#include <sys/types.h> | |
-#include <stdio.h> | |
-#include <sys/types.h> | |
- | |
-#include "crc32.h" | |
- | |
-/* | |
- * This code implements the AUTODIN II polynomial | |
- * The variable corresponding to the macro argument "crc" should | |
- * be an unsigned long. | |
- * Original code by Spencer Garrett <[email protected]> | |
- */ | |
- | |
-#define _CRC32_(crc, ch) (crc = (crc >> 8) ^ crc32tab[(crc ^ (ch)) & 0xff]) | |
- | |
-/* generated using the AUTODIN II polynomial | |
- * x^32 + x^26 + x^23 + x^22 + x^16 + | |
- * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1 | |
- */ | |
- | |
-static const unsigned int crc32tab[256] = { | |
- 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, | |
- 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, | |
- 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, | |
- 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, | |
- 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, | |
- 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, | |
- 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, | |
- 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, | |
- 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, | |
- 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, | |
- 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, | |
- 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, | |
- 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, | |
- 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, | |
- 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, | |
- 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, | |
- 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, | |
- 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, | |
- 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, | |
- 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, | |
- 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, | |
- 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, | |
- 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, | |
- 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, | |
- 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, | |
- 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, | |
- 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, | |
- 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, | |
- 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, | |
- 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, | |
- 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, | |
- 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, | |
- 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, | |
- 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, | |
- 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, | |
- 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, | |
- 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, | |
- 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, | |
- 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, | |
- 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, | |
- 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, | |
- 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, | |
- 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, | |
- 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, | |
- 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, | |
- 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, | |
- 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, | |
- 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, | |
- 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, | |
- 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, | |
- 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, | |
- 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, | |
- 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, | |
- 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, | |
- 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, | |
- 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, | |
- 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, | |
- 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, | |
- 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, | |
- 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, | |
- 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, | |
- 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, | |
- 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, | |
- 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d, | |
-}; | |
- | |
-unsigned int | |
-crc32_sz(char *buf, int size) | |
-{ | |
- unsigned int crc = ~((unsigned int) 0); | |
- char *p; | |
- int len, | |
- nr; | |
- | |
- len = 0; | |
- nr = size; | |
- for (len += nr, p = buf; nr--; ++p) | |
- _CRC32_(crc, *p); | |
- return ~crc; | |
-} | |
diff --git a/contrib/hstore/crc32.h b/contrib/hstore/crc32.h | |
deleted file mode 100644 | |
index f5bfd82..0000000 | |
--- a/contrib/hstore/crc32.h | |
+++ /dev/null | |
@@ -1,13 +0,0 @@ | |
-/* | |
- * contrib/hstore/crc32.h | |
- */ | |
-#ifndef _CRC32_H | |
-#define _CRC32_H | |
- | |
-/* Returns crc32 of data block */ | |
-extern unsigned int crc32_sz(char *buf, int size); | |
- | |
-/* Returns crc32 of null-terminated string */ | |
-#define crc32(buf) crc32_sz((buf),strlen(buf)) | |
- | |
-#endif | |
diff --git a/contrib/hstore/expected/hstore.out b/contrib/hstore/expected/hstore.out | |
index 2114143..bf22abc 100644 | |
--- a/contrib/hstore/expected/hstore.out | |
+++ b/contrib/hstore/expected/hstore.out | |
@@ -199,6 +199,24 @@ select 'aa=>"NuLl"'::hstore; | |
"aa"=>"NuLl" | |
(1 row) | |
+select 'aa=>nul'::hstore; | |
+ hstore | |
+------------- | |
+ "aa"=>"nul" | |
+(1 row) | |
+ | |
+select 'aa=>NuL'::hstore; | |
+ hstore | |
+------------- | |
+ "aa"=>"NuL" | |
+(1 row) | |
+ | |
+select 'aa=>"NuL"'::hstore; | |
+ hstore | |
+------------- | |
+ "aa"=>"NuL" | |
+(1 row) | |
+ | |
select e'\\=a=>q=w'::hstore; | |
hstore | |
------------- | |
@@ -432,63 +450,63 @@ select hstore 'a=>NULL, b=>qq' ?& '{}'::text[]; | |
-- delete | |
select delete('a=>1 , b=>2, c=>3'::hstore, 'a'); | |
- delete | |
--------------------- | |
- "b"=>"2", "c"=>"3" | |
+ delete | |
+---------------- | |
+ "b"=>2, "c"=>3 | |
(1 row) | |
select delete('a=>null , b=>2, c=>3'::hstore, 'a'); | |
- delete | |
--------------------- | |
- "b"=>"2", "c"=>"3" | |
+ delete | |
+---------------- | |
+ "b"=>2, "c"=>3 | |
(1 row) | |
select delete('a=>1 , b=>2, c=>3'::hstore, 'b'); | |
- delete | |
--------------------- | |
- "a"=>"1", "c"=>"3" | |
+ delete | |
+---------------- | |
+ "a"=>1, "c"=>3 | |
(1 row) | |
select delete('a=>1 , b=>2, c=>3'::hstore, 'c'); | |
- delete | |
--------------------- | |
- "a"=>"1", "b"=>"2" | |
+ delete | |
+---------------- | |
+ "a"=>1, "b"=>2 | |
(1 row) | |
select delete('a=>1 , b=>2, c=>3'::hstore, 'd'); | |
- delete | |
------------------------------- | |
- "a"=>"1", "b"=>"2", "c"=>"3" | |
+ delete | |
+------------------------ | |
+ "a"=>1, "b"=>2, "c"=>3 | |
(1 row) | |
select 'a=>1 , b=>2, c=>3'::hstore - 'a'::text; | |
- ?column? | |
--------------------- | |
- "b"=>"2", "c"=>"3" | |
+ ?column? | |
+---------------- | |
+ "b"=>2, "c"=>3 | |
(1 row) | |
select 'a=>null , b=>2, c=>3'::hstore - 'a'::text; | |
- ?column? | |
--------------------- | |
- "b"=>"2", "c"=>"3" | |
+ ?column? | |
+---------------- | |
+ "b"=>2, "c"=>3 | |
(1 row) | |
select 'a=>1 , b=>2, c=>3'::hstore - 'b'::text; | |
- ?column? | |
--------------------- | |
- "a"=>"1", "c"=>"3" | |
+ ?column? | |
+---------------- | |
+ "a"=>1, "c"=>3 | |
(1 row) | |
select 'a=>1 , b=>2, c=>3'::hstore - 'c'::text; | |
- ?column? | |
--------------------- | |
- "a"=>"1", "b"=>"2" | |
+ ?column? | |
+---------------- | |
+ "a"=>1, "b"=>2 | |
(1 row) | |
select 'a=>1 , b=>2, c=>3'::hstore - 'd'::text; | |
- ?column? | |
------------------------------- | |
- "a"=>"1", "b"=>"2", "c"=>"3" | |
+ ?column? | |
+------------------------ | |
+ "a"=>1, "b"=>2, "c"=>3 | |
(1 row) | |
select pg_column_size('a=>1 , b=>2, c=>3'::hstore - 'b'::text) | |
@@ -500,21 +518,21 @@ select pg_column_size('a=>1 , b=>2, c=>3'::hstore - 'b'::text) | |
-- delete (array) | |
select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['d','e']); | |
- delete | |
------------------------------- | |
- "a"=>"1", "b"=>"2", "c"=>"3" | |
+ delete | |
+------------------------ | |
+ "a"=>1, "b"=>2, "c"=>3 | |
(1 row) | |
select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['d','b']); | |
- delete | |
--------------------- | |
- "a"=>"1", "c"=>"3" | |
+ delete | |
+---------------- | |
+ "a"=>1, "c"=>3 | |
(1 row) | |
select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY['a','c']); | |
- delete | |
----------- | |
- "b"=>"2" | |
+ delete | |
+-------- | |
+ "b"=>2 | |
(1 row) | |
select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY[['b'],['c'],['a']]); | |
@@ -524,27 +542,27 @@ select delete('a=>1 , b=>2, c=>3'::hstore, ARRAY[['b'],['c'],['a']]); | |
(1 row) | |
select delete('a=>1 , b=>2, c=>3'::hstore, '{}'::text[]); | |
- delete | |
------------------------------- | |
- "a"=>"1", "b"=>"2", "c"=>"3" | |
+ delete | |
+------------------------ | |
+ "a"=>1, "b"=>2, "c"=>3 | |
(1 row) | |
select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['d','e']; | |
- ?column? | |
------------------------------- | |
- "a"=>"1", "b"=>"2", "c"=>"3" | |
+ ?column? | |
+------------------------ | |
+ "a"=>1, "b"=>2, "c"=>3 | |
(1 row) | |
select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['d','b']; | |
- ?column? | |
--------------------- | |
- "a"=>"1", "c"=>"3" | |
+ ?column? | |
+---------------- | |
+ "a"=>1, "c"=>3 | |
(1 row) | |
select 'a=>1 , b=>2, c=>3'::hstore - ARRAY['a','c']; | |
?column? | |
---------- | |
- "b"=>"2" | |
+ "b"=>2 | |
(1 row) | |
select 'a=>1 , b=>2, c=>3'::hstore - ARRAY[['b'],['c'],['a']]; | |
@@ -554,9 +572,9 @@ select 'a=>1 , b=>2, c=>3'::hstore - ARRAY[['b'],['c'],['a']]; | |
(1 row) | |
select 'a=>1 , b=>2, c=>3'::hstore - '{}'::text[]; | |
- ?column? | |
------------------------------- | |
- "a"=>"1", "b"=>"2", "c"=>"3" | |
+ ?column? | |
+------------------------ | |
+ "a"=>1, "b"=>2, "c"=>3 | |
(1 row) | |
select pg_column_size('a=>1 , b=>2, c=>3'::hstore - ARRAY['a','c']) | |
@@ -575,15 +593,15 @@ select pg_column_size('a=>1 , b=>2, c=>3'::hstore - '{}'::text[]) | |
-- delete (hstore) | |
select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>4, b=>2'::hstore); | |
- delete | |
---------------------- | |
- "c"=>"3", "aa"=>"1" | |
+ delete | |
+----------------- | |
+ "c"=>3, "aa"=>1 | |
(1 row) | |
select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>NULL, c=>3'::hstore); | |
- delete | |
---------------------- | |
- "b"=>"2", "aa"=>"1" | |
+ delete | |
+----------------- | |
+ "b"=>2, "aa"=>1 | |
(1 row) | |
select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>1, b=>2, c=>3'::hstore); | |
@@ -593,27 +611,27 @@ select delete('aa=>1 , b=>2, c=>3'::hstore, 'aa=>1, b=>2, c=>3'::hstore); | |
(1 row) | |
select delete('aa=>1 , b=>2, c=>3'::hstore, 'b=>2'::hstore); | |
- delete | |
---------------------- | |
- "c"=>"3", "aa"=>"1" | |
+ delete | |
+----------------- | |
+ "c"=>3, "aa"=>1 | |
(1 row) | |
select delete('aa=>1 , b=>2, c=>3'::hstore, ''::hstore); | |
- delete | |
-------------------------------- | |
- "b"=>"2", "c"=>"3", "aa"=>"1" | |
+ delete | |
+------------------------- | |
+ "b"=>2, "c"=>3, "aa"=>1 | |
(1 row) | |
select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>4, b=>2'::hstore; | |
- ?column? | |
---------------------- | |
- "c"=>"3", "aa"=>"1" | |
+ ?column? | |
+----------------- | |
+ "c"=>3, "aa"=>1 | |
(1 row) | |
select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>NULL, c=>3'::hstore; | |
- ?column? | |
---------------------- | |
- "b"=>"2", "aa"=>"1" | |
+ ?column? | |
+----------------- | |
+ "b"=>2, "aa"=>1 | |
(1 row) | |
select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>1, b=>2, c=>3'::hstore; | |
@@ -623,15 +641,15 @@ select 'aa=>1 , b=>2, c=>3'::hstore - 'aa=>1, b=>2, c=>3'::hstore; | |
(1 row) | |
select 'aa=>1 , b=>2, c=>3'::hstore - 'b=>2'::hstore; | |
- ?column? | |
---------------------- | |
- "c"=>"3", "aa"=>"1" | |
+ ?column? | |
+----------------- | |
+ "c"=>3, "aa"=>1 | |
(1 row) | |
select 'aa=>1 , b=>2, c=>3'::hstore - ''::hstore; | |
- ?column? | |
-------------------------------- | |
- "b"=>"2", "c"=>"3", "aa"=>"1" | |
+ ?column? | |
+------------------------- | |
+ "b"=>2, "c"=>3, "aa"=>1 | |
(1 row) | |
select pg_column_size('a=>1 , b=>2, c=>3'::hstore - 'b=>2'::hstore) | |
@@ -650,33 +668,33 @@ select pg_column_size('a=>1 , b=>2, c=>3'::hstore - ''::hstore) | |
-- || | |
select 'aa=>1 , b=>2, cq=>3'::hstore || 'cq=>l, b=>g, fg=>f'; | |
- ?column? | |
-------------------------------------------- | |
- "b"=>"g", "aa"=>"1", "cq"=>"l", "fg"=>"f" | |
+ ?column? | |
+--------------------------------------- | |
+ "b"=>"g", "aa"=>1, "cq"=>"l", "fg"=>f | |
(1 row) | |
select 'aa=>1 , b=>2, cq=>3'::hstore || 'aq=>l'; | |
- ?column? | |
-------------------------------------------- | |
- "b"=>"2", "aa"=>"1", "aq"=>"l", "cq"=>"3" | |
+ ?column? | |
+------------------------------------- | |
+ "b"=>2, "aa"=>1, "aq"=>"l", "cq"=>3 | |
(1 row) | |
select 'aa=>1 , b=>2, cq=>3'::hstore || 'aa=>l'; | |
- ?column? | |
--------------------------------- | |
- "b"=>"2", "aa"=>"l", "cq"=>"3" | |
+ ?column? | |
+---------------------------- | |
+ "b"=>2, "aa"=>"l", "cq"=>3 | |
(1 row) | |
select 'aa=>1 , b=>2, cq=>3'::hstore || ''; | |
- ?column? | |
--------------------------------- | |
- "b"=>"2", "aa"=>"1", "cq"=>"3" | |
+ ?column? | |
+-------------------------- | |
+ "b"=>2, "aa"=>1, "cq"=>3 | |
(1 row) | |
select ''::hstore || 'cq=>l, b=>g, fg=>f'; | |
- ?column? | |
--------------------------------- | |
- "b"=>"g", "cq"=>"l", "fg"=>"f" | |
+ ?column? | |
+------------------------------ | |
+ "b"=>"g", "cq"=>"l", "fg"=>f | |
(1 row) | |
select pg_column_size(''::hstore || ''::hstore) = pg_column_size(''::hstore); | |
@@ -759,21 +777,21 @@ select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['g','h','i']); | |
(1 row) | |
select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b']); | |
- slice | |
--------------------- | |
- "b"=>"2", "c"=>"3" | |
+ slice | |
+---------------- | |
+ "b"=>2, "c"=>3 | |
(1 row) | |
select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['aa','b']); | |
- slice | |
---------------------- | |
- "b"=>"2", "aa"=>"1" | |
+ slice | |
+----------------- | |
+ "b"=>2, "aa"=>1 | |
(1 row) | |
select slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b','aa']); | |
- slice | |
-------------------------------- | |
- "b"=>"2", "c"=>"3", "aa"=>"1" | |
+ slice | |
+------------------------- | |
+ "b"=>2, "c"=>3, "aa"=>1 | |
(1 row) | |
select pg_column_size(slice(hstore 'aa=>1, b=>2, c=>3', ARRAY['c','b'])) | |
@@ -907,9 +925,9 @@ select pg_column_size(hstore(ARRAY['a','b','asd'], ARRAY['g','h','i'])) | |
-- records | |
select hstore(v) from (values (1, 'foo', 1.2, 3::float8)) v(a,b,c,d); | |
- hstore | |
--------------------------------------------- | |
- "a"=>"1", "b"=>"foo", "c"=>"1.2", "d"=>"3" | |
+ hstore | |
+------------------------------------------ | |
+ "a"=>"1", "b"=>"foo", "c"=>1.2, "d"=>"3" | |
(1 row) | |
create domain hstestdom1 as integer not null default 0; | |
@@ -918,9 +936,9 @@ create table testhstore1 (a integer, b text, c numeric, d float8, e hstestdom1); | |
insert into testhstore0 values (1, 'foo', 1.2, 3::float8); | |
insert into testhstore1 values (1, 'foo', 1.2, 3::float8); | |
select hstore(v) from testhstore1 v; | |
- hstore | |
------------------------------------------------------- | |
- "a"=>"1", "b"=>"foo", "c"=>"1.2", "d"=>"3", "e"=>"0" | |
+ hstore | |
+---------------------------------------------------- | |
+ "a"=>"1", "b"=>"foo", "c"=>1.2, "d"=>"3", "e"=>"0" | |
(1 row) | |
select hstore(null::testhstore0); | |
@@ -936,7 +954,7 @@ select hstore(null::testhstore1); | |
(1 row) | |
select pg_column_size(hstore(v)) | |
- = pg_column_size('a=>1, b=>"foo", c=>"1.2", d=>"3", e=>"0"'::hstore) | |
+ = pg_column_size('a=>"1", b=>"foo", c=>1.2, d=>"3", e=>"0"'::hstore) | |
from testhstore1 v; | |
?column? | |
---------- | |
@@ -1336,6 +1354,7 @@ select count(*) from testhstore where h ?& ARRAY['public','disabled']; | |
42 | |
(1 row) | |
+RESET enable_seqscan; | |
drop index hidx; | |
create index hidx on testhstore using gin (h); | |
set enable_seqscan=off; | |
@@ -1375,6 +1394,7 @@ select count(*) from testhstore where h ?& ARRAY['public','disabled']; | |
42 | |
(1 row) | |
+RESET enable_seqscan; | |
select count(*) from (select (each(h)).key from testhstore) as wow ; | |
count | |
------- | |
@@ -1437,6 +1457,8 @@ select distinct * from (values (hstore '' || ''),('')) v(h); | |
(1 row) | |
set enable_sort = true; | |
+RESET enable_hashagg; | |
+RESET enable_sort; | |
-- btree | |
drop index hidx; | |
create index hidx on testhstore using btree (h); | |
@@ -1444,7 +1466,7 @@ set enable_seqscan=off; | |
select count(*) from testhstore where h #># 'p=>1'; | |
count | |
------- | |
- 125 | |
+ 884 | |
(1 row) | |
select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexed=>t'; | |
@@ -1453,39 +1475,63 @@ select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexe | |
1 | |
(1 row) | |
+--gin hash | |
+drop index hidx; | |
+create index hidx on testhstore using gin (h gin_hstore_hash_ops); | |
+set enable_seqscan=off; | |
+select count(*) from testhstore where h @> 'wait=>NULL'; | |
+ count | |
+------- | |
+ 1 | |
+(1 row) | |
+ | |
+select count(*) from testhstore where h @> 'wait=>CC'; | |
+ count | |
+------- | |
+ 15 | |
+(1 row) | |
+ | |
+select count(*) from testhstore where h @> 'wait=>CC, public=>t'; | |
+ count | |
+------- | |
+ 2 | |
+(1 row) | |
+ | |
+RESET enable_seqscan; | |
+drop index hidx; | |
-- json | |
select hstore_to_json('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4'); | |
- hstore_to_json | |
-------------------------------------------------------------------------------------------------- | |
- {"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"} | |
+ hstore_to_json | |
+--------------------------------------------------------------------------------------- | |
+ {"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 23450, "a key": 1} | |
(1 row) | |
select cast( hstore '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as json); | |
- json | |
-------------------------------------------------------------------------------------------------- | |
- {"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"} | |
+ json | |
+--------------------------------------------------------------------------------------- | |
+ {"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 23450, "a key": 1} | |
(1 row) | |
select hstore_to_json_loose('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4'); | |
- hstore_to_json_loose | |
------------------------------------------------------------------------------------------- | |
- {"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 2.345e+4, "a key": 1} | |
+ hstore_to_json_loose | |
+--------------------------------------------------------------------------------------- | |
+ {"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 23450, "a key": 1} | |
(1 row) | |
create table test_json_agg (f1 text, f2 hstore); | |
insert into test_json_agg values ('rec1','"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4'), | |
('rec2','"a key" =>2, b => f, c => "null", d=> -12345, e => 012345.6, f=> -1.234, g=> 0.345e-4'); | |
select json_agg(q) from test_json_agg q; | |
- json_agg | |
----------------------------------------------------------------------------------------------------------------------------- | |
- [{"f1":"rec1","f2":{"b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4", "a key": "1"}}, + | |
- {"f1":"rec2","f2":{"b": "f", "c": "null", "d": "-12345", "e": "012345.6", "f": "-1.234", "g": "0.345e-4", "a key": "2"}}] | |
+ json_agg | |
+----------------------------------------------------------------------------------------------------------------------- | |
+ [{"f1":"rec1","f2":{"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 23450, "a key": 1}}, + | |
+ {"f1":"rec2","f2":{"b": false, "c": "null", "d": -12345, "e": "012345.6", "f": -1.234, "g": 0.0000345, "a key": 2}}] | |
(1 row) | |
select json_agg(q) from (select f1, hstore_to_json_loose(f2) as f2 from test_json_agg) q; | |
- json_agg | |
----------------------------------------------------------------------------------------------------------------------- | |
- [{"f1":"rec1","f2":{"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 2.345e+4, "a key": 1}}, + | |
- {"f1":"rec2","f2":{"b": false, "c": "null", "d": -12345, "e": "012345.6", "f": -1.234, "g": 0.345e-4, "a key": 2}}] | |
+ json_agg | |
+----------------------------------------------------------------------------------------------------------------------- | |
+ [{"f1":"rec1","f2":{"b": true, "c": null, "d": 12345, "e": "012345", "f": 1.234, "g": 23450, "a key": 1}}, + | |
+ {"f1":"rec2","f2":{"b": false, "c": "null", "d": -12345, "e": "012345.6", "f": -1.234, "g": 0.0000345, "a key": 2}}] | |
(1 row) | |
diff --git a/contrib/hstore/expected/nested.out b/contrib/hstore/expected/nested.out | |
new file mode 100644 | |
index 0000000..a7786ce8 | |
--- /dev/null | |
+++ b/contrib/hstore/expected/nested.out | |
@@ -0,0 +1,2249 @@ | |
+SELECT 'ff => {a=>12, b=>16}'::hstore; | |
+ hstore | |
+-------------------------- | |
+ "ff"=>{"a"=>12, "b"=>16} | |
+(1 row) | |
+ | |
+SELECT 'ff => {a=>12, b=>16}, qq=> 123'::hstore; | |
+ hstore | |
+------------------------------------- | |
+ "ff"=>{"a"=>12, "b"=>16}, "qq"=>123 | |
+(1 row) | |
+ | |
+SELECT 'aa => {a,aaa}, qq=>{ a=>12, b=>16 , c=> { c1, c2}, d=>{d1=>d1, d2=>d2, d1=>d3} }'::hstore; | |
+ hstore | |
+------------------------------------------------------------------------------------------------ | |
+ "aa"=>["a", "aaa"], "qq"=>{"a"=>12, "b"=>16, "c"=>["c1", "c2"], "d"=>{"d1"=>"d3", "d2"=>"d2"}} | |
+(1 row) | |
+ | |
+SELECT '"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore; | |
+ hstore | |
+---------------------------------------------------------------------------------------------------- | |
+ "aa"=>["a", "aaa"], "qq"=>{"a"=>"12", "b"=>"16", "c"=>["c1", "c2"], "d"=>{"d1"=>"d1", "d2"=>"d2"}} | |
+(1 row) | |
+ | |
+SELECT '"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2,{c3},{c4=>4}}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore; | |
+ hstore | |
+----------------------------------------------------------------------------------------------------------------------- | |
+ "aa"=>["a", "aaa"], "qq"=>{"a"=>"12", "b"=>"16", "c"=>["c1", "c2", ["c3"], {"c4"=>4}], "d"=>{"d1"=>"d1", "d2"=>"d2"}} | |
+(1 row) | |
+ | |
+SELECT 'ff => {a,aaa}'::hstore; | |
+ hstore | |
+-------------------- | |
+ "ff"=>["a", "aaa"] | |
+(1 row) | |
+ | |
+select 'null'::hstore; | |
+ hstore | |
+-------- | |
+ NULL | |
+(1 row) | |
+ | |
+select '{null}'::hstore; | |
+ hstore | |
+-------- | |
+ [NULL] | |
+(1 row) | |
+ | |
+select ''::hstore; | |
+ hstore | |
+-------- | |
+ | |
+(1 row) | |
+ | |
+select '{}'::hstore; | |
+ hstore | |
+-------- | |
+ | |
+(1 row) | |
+ | |
+--test optional outer braces | |
+SELECT 'a=>1'::hstore; | |
+ hstore | |
+-------- | |
+ "a"=>1 | |
+(1 row) | |
+ | |
+SELECT '{a=>1}'::hstore; | |
+ hstore | |
+-------- | |
+ "a"=>1 | |
+(1 row) | |
+ | |
+SELECT '{a,b}'::hstore; | |
+ hstore | |
+------------ | |
+ ["a", "b"] | |
+(1 row) | |
+ | |
+SELECT '{a,{b}}'::hstore; | |
+ hstore | |
+-------------- | |
+ ["a", ["b"]] | |
+(1 row) | |
+ | |
+SELECT '{{a},b}'::hstore; | |
+ hstore | |
+-------------- | |
+ [["a"], "b"] | |
+(1 row) | |
+ | |
+SELECT '{a,{b},{c}}'::hstore; | |
+ hstore | |
+--------------------- | |
+ ["a", ["b"], ["c"]] | |
+(1 row) | |
+ | |
+SELECT '{{a},{b},c}'::hstore; | |
+ hstore | |
+--------------------- | |
+ [["a"], ["b"], "c"] | |
+(1 row) | |
+ | |
+SELECT '{{a},b,{c}}'::hstore; | |
+ hstore | |
+--------------------- | |
+ [["a"], "b", ["c"]] | |
+(1 row) | |
+ | |
+SELECT '{a,{b=>1}}'::hstore; | |
+ hstore | |
+----------------- | |
+ ["a", {"b"=>1}] | |
+(1 row) | |
+ | |
+SELECT '{{a},{b=>1}}'::hstore; | |
+ hstore | |
+------------------- | |
+ [["a"], {"b"=>1}] | |
+(1 row) | |
+ | |
+SELECT 'a'::hstore; | |
+ hstore | |
+-------- | |
+ "a" | |
+(1 row) | |
+ | |
+SELECT '{a}'::hstore; | |
+ hstore | |
+-------- | |
+ ["a"] | |
+(1 row) | |
+ | |
+SELECT ''::hstore; | |
+ hstore | |
+-------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '{}'::hstore; | |
+ hstore | |
+-------- | |
+ | |
+(1 row) | |
+ | |
+--nested json | |
+SELECT hstore_to_json('a=>1'); | |
+ hstore_to_json | |
+---------------- | |
+ {"a": 1} | |
+(1 row) | |
+ | |
+SELECT hstore_to_json('{a=>1}'); | |
+ hstore_to_json | |
+---------------- | |
+ {"a": 1} | |
+(1 row) | |
+ | |
+SELECT hstore_to_json('{a,b}'); | |
+ hstore_to_json | |
+---------------- | |
+ ["a", "b"] | |
+(1 row) | |
+ | |
+SELECT hstore_to_json('{a,{b}}'); | |
+ hstore_to_json | |
+---------------- | |
+ ["a", ["b"]] | |
+(1 row) | |
+ | |
+SELECT hstore_to_json('{{a},b}'); | |
+ hstore_to_json | |
+---------------- | |
+ [["a"], "b"] | |
+(1 row) | |
+ | |
+SELECT hstore_to_json('{a,{b},{c}}'); | |
+ hstore_to_json | |
+--------------------- | |
+ ["a", ["b"], ["c"]] | |
+(1 row) | |
+ | |
+SELECT hstore_to_json('{{a},{b},c}'); | |
+ hstore_to_json | |
+--------------------- | |
+ [["a"], ["b"], "c"] | |
+(1 row) | |
+ | |
+SELECT hstore_to_json('{{a},b,{c}}'); | |
+ hstore_to_json | |
+--------------------- | |
+ [["a"], "b", ["c"]] | |
+(1 row) | |
+ | |
+SELECT hstore_to_json('{a,{b=>1}}'); | |
+ hstore_to_json | |
+----------------- | |
+ ["a", {"b": 1}] | |
+(1 row) | |
+ | |
+SELECT hstore_to_json('{{a},{b=>1}}'); | |
+ hstore_to_json | |
+------------------- | |
+ [["a"], {"b": 1}] | |
+(1 row) | |
+ | |
+SELECT hstore_to_json('{{a},{b=>1},{c}}'); | |
+ hstore_to_json | |
+-------------------------- | |
+ [["a"], {"b": 1}, ["c"]] | |
+(1 row) | |
+ | |
+SELECT hstore_to_json('a'); | |
+ hstore_to_json | |
+---------------- | |
+ "a" | |
+(1 row) | |
+ | |
+SELECT hstore_to_json('{a}'); | |
+ hstore_to_json | |
+---------------- | |
+ ["a"] | |
+(1 row) | |
+ | |
+SELECT hstore_to_json(''); | |
+ hstore_to_json | |
+---------------- | |
+ {} | |
+(1 row) | |
+ | |
+SELECT hstore_to_json('{}'); | |
+ hstore_to_json | |
+---------------- | |
+ {} | |
+(1 row) | |
+ | |
+SELECT hstore_to_json('"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2,{c3},{c4=>4}}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore); | |
+ hstore_to_json | |
+------------------------------------------------------------------------------------------------------------------------- | |
+ {"aa": ["a", "aaa"], "qq": {"a": "12", "b": "16", "c": ["c1", "c2", ["c3"], {"c4": 4}], "d": {"d1": "d1", "d2": "d2"}}} | |
+(1 row) | |
+ | |
+-- | |
+SELECT 'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'ff', | |
+ 'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'qq', | |
+ ('ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'Y') IS NULL AS t, | |
+ 'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'x'; | |
+ ?column? | ?column? | t | ?column? | |
+------------------+----------+---+---------- | |
+ "a"=>12, "b"=>16 | 123 | t | [1, 2] | |
+(1 row) | |
+ | |
+SELECT '[ a, b, c, d]'::hstore -> 'a'; | |
+ ?column? | |
+---------- | |
+ a | |
+(1 row) | |
+ | |
+-- | |
+CREATE TABLE testtype (i int, h hstore, a int[], j json); | |
+INSERT INTO testtype VALUES (1, 'a=>1', '{1,2,3}', '{"x": 2}'); | |
+SELECT populate_record(v, 'i=>2'::hstore) FROM testtype v; | |
+ populate_record | |
+--------------------------------------- | |
+ (2,"""a""=>1","{1,2,3}","{""x"": 2}") | |
+(1 row) | |
+ | |
+SELECT populate_record(v, 'i=>2, a=>{7,8,9}'::hstore) FROM testtype v; | |
+ populate_record | |
+--------------------------------------- | |
+ (2,"""a""=>1","{7,8,9}","{""x"": 2}") | |
+(1 row) | |
+ | |
+SELECT populate_record(v, 'i=>2, h=>{b=>3}, a=>{7,8,9}'::hstore) FROM testtype v; | |
+ populate_record | |
+--------------------------------------- | |
+ (2,"""b""=>3","{7,8,9}","{""x"": 2}") | |
+(1 row) | |
+ | |
+SELECT populate_record(v, 'i=>2, h=>{b=>3}, a=>{7,8,9}, j=>{a=>{1,2,3}}'::hstore) FROM testtype v; | |
+ populate_record | |
+----------------------------------------------- | |
+ (2,"""b""=>3","{7,8,9}","{""a"": [1, 2, 3]}") | |
+(1 row) | |
+ | |
+--complex delete | |
+SELECT 'b=>{a,c}'::hstore - 'a'::text; | |
+ ?column? | |
+----------------- | |
+ "b"=>["a", "c"] | |
+(1 row) | |
+ | |
+SELECT 'b=>{a,c}, a=>1'::hstore - 'a'::text; | |
+ ?column? | |
+----------------- | |
+ "b"=>["a", "c"] | |
+(1 row) | |
+ | |
+SELECT 'b=>{a,c}, a=>[2,3]'::hstore - 'a'::text; | |
+ ?column? | |
+----------------- | |
+ "b"=>["a", "c"] | |
+(1 row) | |
+ | |
+SELECT 'b=>{a,c}, a=>[2,3]'::hstore - 'a'::text; | |
+ ?column? | |
+----------------- | |
+ "b"=>["a", "c"] | |
+(1 row) | |
+ | |
+SELECT '[2,3,a]'::hstore - 'a'::text; | |
+ ?column? | |
+---------- | |
+ [2, 3] | |
+(1 row) | |
+ | |
+SELECT '[a,2,3,a]'::hstore - 'a'::text; | |
+ ?column? | |
+---------- | |
+ [2, 3] | |
+(1 row) | |
+ | |
+SELECT '[a,a]'::hstore - 'a'::text; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '[a]'::hstore - 'a'::text; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT 'a=>1'::hstore - 'a'::text; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT ''::hstore - 'a'::text; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '{a, 1 , b,2, c,3}'::hstore - ARRAY['d','b']; | |
+ ?column? | |
+--------------------- | |
+ ["a", 1, 2, "c", 3] | |
+(1 row) | |
+ | |
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v'::hstore; | |
+ ?column? | |
+----------------------- | |
+ "a"=>[1, 2], "b"=>"c" | |
+(1 row) | |
+ | |
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v=>23'::hstore; | |
+ ?column? | |
+----------------------- | |
+ "a"=>[1, 2], "b"=>"c" | |
+(1 row) | |
+ | |
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v=>{1,2}'::hstore; | |
+ ?column? | |
+-------------------------------- | |
+ "a"=>[1, 2], "b"=>"c", "v"=>23 | |
+(1 row) | |
+ | |
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'a=>{1,2}'::hstore; | |
+ ?column? | |
+------------------- | |
+ "b"=>"c", "v"=>23 | |
+(1 row) | |
+ | |
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - 'v'::hstore; | |
+ ?column? | |
+----------------------------- | |
+ ["a", [1, 2], 23, "b", "c"] | |
+(1 row) | |
+ | |
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - 'v=>23'::hstore; | |
+ ?column? | |
+------------------------- | |
+ ["a", [1, 2], "b", "c"] | |
+(1 row) | |
+ | |
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - '[v,23]'::hstore; | |
+ ?column? | |
+------------------------- | |
+ ["a", [1, 2], "b", "c"] | |
+(1 row) | |
+ | |
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - '[v,{1,2}]'::hstore; | |
+ ?column? | |
+--------------------- | |
+ ["a", 23, "b", "c"] | |
+(1 row) | |
+ | |
+--joining | |
+SELECT 'aa=>1 , b=>2, cq=>3'::hstore || '{cq,l, b,g, fg,f, 1,2}'::hstore; | |
+ ?column? | |
+----------------------------------------------- | |
+ "1"=>2, "b"=>"g", "aa"=>1, "cq"=>"l", "fg"=>f | |
+(1 row) | |
+ | |
+SELECT '{aa,1 , b,2, cq,3}'::hstore || '{cq,l, b,g, fg,f, 1,2}'::hstore; | |
+ ?column? | |
+---------------------------------------------------------------- | |
+ ["aa", 1, "b", 2, "cq", 3, "cq", "l", "b", "g", "fg", f, 1, 2] | |
+(1 row) | |
+ | |
+--slice | |
+SELECT slice_array(hstore 'aa=>1, b=>2, c=>3', ARRAY['g','h','i']); | |
+ slice_array | |
+------------------ | |
+ {NULL,NULL,NULL} | |
+(1 row) | |
+ | |
+SELECT slice_array(hstore '{aa,1, b,2, c,3}', ARRAY['g','h','i']); | |
+ slice_array | |
+------------------ | |
+ {NULL,NULL,NULL} | |
+(1 row) | |
+ | |
+SELECT slice_array(hstore 'aa=>1, b=>2, c=>3', ARRAY['b','c']); | |
+ slice_array | |
+------------- | |
+ {2,3} | |
+(1 row) | |
+ | |
+SELECT slice_array(hstore '{aa,1, b,2, c,3}', ARRAY['b','c']); | |
+ slice_array | |
+------------- | |
+ {b,c} | |
+(1 row) | |
+ | |
+SELECT slice_array(hstore 'aa=>1, b=>{2=>1}, c=>{1,2}', ARRAY['b','c']); | |
+ slice_array | |
+----------------------- | |
+ {"\"2\"=>1","[1, 2]"} | |
+(1 row) | |
+ | |
+SELECT slice(hstore '{aa=>1, b=>2, c=>3}', ARRAY['g','h','i']); | |
+ slice | |
+------- | |
+ | |
+(1 row) | |
+ | |
+SELECT slice(hstore '{aa,1, b,2, c,3}', ARRAY['g','h','i']); | |
+ slice | |
+------- | |
+ | |
+(1 row) | |
+ | |
+SELECT slice(hstore '{aa=>1, b=>2, c=>3}', ARRAY['b','c']); | |
+ slice | |
+---------------- | |
+ "b"=>2, "c"=>3 | |
+(1 row) | |
+ | |
+SELECT slice(hstore '{aa,1, b,2, c,3}', ARRAY['b','c']); | |
+ slice | |
+------------ | |
+ ["b", "c"] | |
+(1 row) | |
+ | |
+SELECT slice(hstore '{aa=>1, b=>{2=>1}, c=>{1,2}}', ARRAY['b','c']); | |
+ slice | |
+---------------------------- | |
+ "b"=>{"2"=>1}, "c"=>[1, 2] | |
+(1 row) | |
+ | |
+--to array | |
+SELECT %% 'aa=>1, cq=>l, b=>{a,n}, fg=>NULL'; | |
+ ?column? | |
+---------------------------------------- | |
+ {b,"[\"a\", \"n\"]",aa,1,cq,l,fg,NULL} | |
+(1 row) | |
+ | |
+SELECT %% '{aa,1, cq,l, b,g, fg,NULL}'; | |
+ ?column? | |
+------------------------- | |
+ {aa,1,cq,l,b,g,fg,NULL} | |
+(1 row) | |
+ | |
+SELECT hstore_to_matrix( 'aa=>1, cq=>l, b=>{a,n}, fg=>NULL'); | |
+ hstore_to_matrix | |
+------------------------------------------------ | |
+ {{b,"[\"a\", \"n\"]"},{aa,1},{cq,l},{fg,NULL}} | |
+(1 row) | |
+ | |
+SELECT hstore_to_matrix( '{aa,1, cq,l, b,g, fg,NULL}'); | |
+ hstore_to_matrix | |
+--------------------------------- | |
+ {{aa,1},{cq,l},{b,g},{fg,NULL}} | |
+(1 row) | |
+ | |
+--contains | |
+SELECT 'a=>b'::hstore @> 'a=>b, c=>b'; | |
+ ?column? | |
+---------- | |
+ f | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>b'::hstore @> 'a=>b'; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{1,2}'; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'a=>{2,1}, c=>b'::hstore @> 'a=>{1,2}'; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'a=>{1=>2}, c=>b'::hstore @> 'a=>{1,2}'; | |
+ ?column? | |
+---------- | |
+ f | |
+(1 row) | |
+ | |
+SELECT 'a=>{2=>1}, c=>b'::hstore @> 'a=>{1,2}'; | |
+ ?column? | |
+---------- | |
+ f | |
+(1 row) | |
+ | |
+SELECT 'a=>{1=>2}, c=>b'::hstore @> 'a=>{1=>2}'; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'a=>{2=>1}, c=>b'::hstore @> 'a=>{1=>2}'; | |
+ ?column? | |
+---------- | |
+ f | |
+(1 row) | |
+ | |
+SELECT '{a,b}'::hstore @> '{a,b, c,b}'; | |
+ ?column? | |
+---------- | |
+ f | |
+(1 row) | |
+ | |
+SELECT '{a,b, c,b}'::hstore @> '{a,b}'; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT '{a,b, c,{1,2}}'::hstore @> '{a,{1,2}}'; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT '{a,b, c,{1,2}}'::hstore @> '{b,{1,2}}'; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{1}'; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{2}'; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{3}'; | |
+ ?column? | |
+---------- | |
+ f | |
+(1 row) | |
+ | |
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{c=>3}}'; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4}}'; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4},3}'; | |
+ ?column? | |
+---------- | |
+ f | |
+(1 row) | |
+ | |
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4},1}'; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+-- %> | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'n'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'a'; | |
+ ?column? | |
+---------- | |
+ 1 | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'b'; | |
+ ?column? | |
+---------- | |
+ [1, 2] | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'c'; | |
+ ?column? | |
+---------- | |
+ "1"=>2 | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'd'; | |
+ ?column? | |
+------------- | |
+ "1"=>[2, 3] | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'd' %> '1'; | |
+ ?column? | |
+---------- | |
+ [2, 3] | |
+(1 row) | |
+ | |
+SELECT '[1,2,3,{a,b}]'::hstore %> '1'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '["1",2,3,{a,b}]'::hstore %> '1'; | |
+ ?column? | |
+---------- | |
+ "1" | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 5; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 4; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 3; | |
+ ?column? | |
+------------- | |
+ "1"=>[2, 3] | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 2; | |
+ ?column? | |
+---------- | |
+ "1"=>2 | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 1; | |
+ ?column? | |
+---------- | |
+ [1, 2] | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 0; | |
+ ?column? | |
+---------- | |
+ 1 | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 5; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 4; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 3; | |
+ ?column? | |
+---------- | |
+ [1, 2] | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 2; | |
+ ?column? | |
+---------- | |
+ "c" | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 1; | |
+ ?column? | |
+---------- | |
+ "b" | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 0; | |
+ ?column? | |
+---------- | |
+ "a" | |
+(1 row) | |
+ | |
+-- -> | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 5; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 4; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 3; | |
+ ?column? | |
+------------- | |
+ "1"=>[2, 3] | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 2; | |
+ ?column? | |
+---------- | |
+ "1"=>2 | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 1; | |
+ ?column? | |
+---------- | |
+ [1, 2] | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 0; | |
+ ?column? | |
+---------- | |
+ 1 | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 5; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 4; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 3; | |
+ ?column? | |
+---------- | |
+ [1, 2] | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 2; | |
+ ?column? | |
+---------- | |
+ c | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 1; | |
+ ?column? | |
+---------- | |
+ b | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 0; | |
+ ?column? | |
+---------- | |
+ a | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -6; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -5; | |
+ ?column? | |
+---------- | |
+ 1 | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -4; | |
+ ?column? | |
+---------- | |
+ [1, 2] | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -3; | |
+ ?column? | |
+---------- | |
+ "1"=>2 | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -2; | |
+ ?column? | |
+------------- | |
+ "1"=>[2, 3] | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -1; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -6; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -5; | |
+ ?column? | |
+---------- | |
+ a | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -4; | |
+ ?column? | |
+---------- | |
+ b | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -3; | |
+ ?column? | |
+---------- | |
+ c | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -2; | |
+ ?column? | |
+---------- | |
+ [1, 2] | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -1; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+-- #> | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{0}'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{a}'; | |
+ ?column? | |
+---------- | |
+ b | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c}'; | |
+ ?column? | |
+----------- | |
+ [1, 2, 3] | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 0}'; | |
+ ?column? | |
+---------- | |
+ 1 | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 1}'; | |
+ ?column? | |
+---------- | |
+ 2 | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 2}'; | |
+ ?column? | |
+---------- | |
+ 3 | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 3}'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -1}'; | |
+ ?column? | |
+---------- | |
+ 3 | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -2}'; | |
+ ?column? | |
+---------- | |
+ 2 | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -3}'; | |
+ ?column? | |
+---------- | |
+ 1 | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -4}'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{0}'; | |
+ ?column? | |
+---------- | |
+ 0 | |
+(1 row) | |
+ | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{3}'; | |
+ ?column? | |
+---------- | |
+ [3, 4] | |
+(1 row) | |
+ | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{4}'; | |
+ ?column? | |
+------------- | |
+ "5"=>"five" | |
+(1 row) | |
+ | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{4,5}'; | |
+ ?column? | |
+---------- | |
+ five | |
+(1 row) | |
+ | |
+-- #%> | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{0}'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{a}'; | |
+ ?column? | |
+---------- | |
+ "b" | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c}'; | |
+ ?column? | |
+----------- | |
+ [1, 2, 3] | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 0}'; | |
+ ?column? | |
+---------- | |
+ 1 | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 1}'; | |
+ ?column? | |
+---------- | |
+ 2 | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 2}'; | |
+ ?column? | |
+---------- | |
+ 3 | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 3}'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -1}'; | |
+ ?column? | |
+---------- | |
+ 3 | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -2}'; | |
+ ?column? | |
+---------- | |
+ 2 | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -3}'; | |
+ ?column? | |
+---------- | |
+ 1 | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -4}'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{0}'; | |
+ ?column? | |
+---------- | |
+ 0 | |
+(1 row) | |
+ | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{3}'; | |
+ ?column? | |
+---------- | |
+ [3, 4] | |
+(1 row) | |
+ | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{4}'; | |
+ ?column? | |
+------------- | |
+ "5"=>"five" | |
+(1 row) | |
+ | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{4,5}'; | |
+ ?column? | |
+---------- | |
+ "five" | |
+(1 row) | |
+ | |
+-- ? | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 5; | |
+ ?column? | |
+---------- | |
+ f | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 4; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 3; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 2; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 1; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 0; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 5; | |
+ ?column? | |
+---------- | |
+ f | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 4; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 3; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 2; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 1; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 0; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -6; | |
+ ?column? | |
+---------- | |
+ f | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -5; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -4; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -3; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -2; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -1; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -6; | |
+ ?column? | |
+---------- | |
+ f | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -5; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -4; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -3; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -2; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -1; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{0}'::text[]; | |
+ ?column? | |
+---------- | |
+ f | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{a}'::text[]; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c}'::text[]; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{b}'::text[]; | |
+ ?column? | |
+---------- | |
+ f | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 0}'::text[]; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 1}'::text[]; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 2}'::text[]; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 3}'::text[]; | |
+ ?column? | |
+---------- | |
+ f | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -1}'::text[]; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -2}'::text[]; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -3}'::text[]; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -4}'::text[]; | |
+ ?column? | |
+---------- | |
+ f | |
+(1 row) | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -5}'::text[]; | |
+ ?column? | |
+---------- | |
+ f | |
+(1 row) | |
+ | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{0}'::text[]; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{3}'::text[]; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{4}'::text[]; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{4,5}'::text[]; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+--deep delete | |
+SELECT 'a=>1'::hstore #- '{x}'; | |
+ ?column? | |
+---------- | |
+ "a"=>1 | |
+(1 row) | |
+ | |
+SELECT 'a=>1'::hstore #- '{a}'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT 'a=>1'::hstore #- '{NULL}'; | |
+ ?column? | |
+---------- | |
+ "a"=>1 | |
+(1 row) | |
+ | |
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{x}'; | |
+ ?column? | |
+------------------------ | |
+ "a"=>1, "b"=>2, "c"=>3 | |
+(1 row) | |
+ | |
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{a}'; | |
+ ?column? | |
+---------------- | |
+ "b"=>2, "c"=>3 | |
+(1 row) | |
+ | |
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{b}'; | |
+ ?column? | |
+---------------- | |
+ "a"=>1, "c"=>3 | |
+(1 row) | |
+ | |
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{c}'; | |
+ ?column? | |
+---------------- | |
+ "a"=>1, "b"=>2 | |
+(1 row) | |
+ | |
+SELECT 'a=>1'::hstore #- '{x,1}'; | |
+ ?column? | |
+---------- | |
+ "a"=>1 | |
+(1 row) | |
+ | |
+SELECT 'a=>1'::hstore #- '{a,1}'; | |
+ ?column? | |
+---------- | |
+ "a"=>1 | |
+(1 row) | |
+ | |
+SELECT 'a=>1'::hstore #- '{NULL,1}'; | |
+ ?column? | |
+---------- | |
+ "a"=>1 | |
+(1 row) | |
+ | |
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{x,1}'; | |
+ ?column? | |
+------------------------ | |
+ "a"=>1, "b"=>2, "c"=>3 | |
+(1 row) | |
+ | |
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{a,1}'; | |
+ ?column? | |
+------------------------ | |
+ "a"=>1, "b"=>2, "c"=>3 | |
+(1 row) | |
+ | |
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{b,1}'; | |
+ ?column? | |
+------------------------ | |
+ "a"=>1, "b"=>2, "c"=>3 | |
+(1 row) | |
+ | |
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{c,1}'; | |
+ ?column? | |
+------------------------ | |
+ "a"=>1, "b"=>2, "c"=>3 | |
+(1 row) | |
+ | |
+SELECT '[a]'::hstore #- '{2}'; | |
+ ?column? | |
+---------- | |
+ ["a"] | |
+(1 row) | |
+ | |
+SELECT '[a]'::hstore #- '{1}'; | |
+ ?column? | |
+---------- | |
+ ["a"] | |
+(1 row) | |
+ | |
+SELECT '[a]'::hstore #- '{0}'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '[a]'::hstore #- '{-1}'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '[a]'::hstore #- '{-2}'; | |
+ ?column? | |
+---------- | |
+ ["a"] | |
+(1 row) | |
+ | |
+SELECT '[a,b,c]'::hstore #- '{3}'; | |
+ ?column? | |
+----------------- | |
+ ["a", "b", "c"] | |
+(1 row) | |
+ | |
+SELECT '[a,b,c]'::hstore #- '{2}'; | |
+ ?column? | |
+------------ | |
+ ["a", "b"] | |
+(1 row) | |
+ | |
+SELECT '[a,b,c]'::hstore #- '{1}'; | |
+ ?column? | |
+------------ | |
+ ["a", "c"] | |
+(1 row) | |
+ | |
+SELECT '[a,b,c]'::hstore #- '{0}'; | |
+ ?column? | |
+------------ | |
+ ["b", "c"] | |
+(1 row) | |
+ | |
+SELECT '[a,b,c]'::hstore #- '{-1}'; | |
+ ?column? | |
+------------ | |
+ ["a", "b"] | |
+(1 row) | |
+ | |
+SELECT '[a,b,c]'::hstore #- '{-2}'; | |
+ ?column? | |
+------------ | |
+ ["a", "c"] | |
+(1 row) | |
+ | |
+SELECT '[a,b,c]'::hstore #- '{-3}'; | |
+ ?column? | |
+------------ | |
+ ["b", "c"] | |
+(1 row) | |
+ | |
+SELECT '[a,b,c]'::hstore #- '{-4}'; | |
+ ?column? | |
+----------------- | |
+ ["a", "b", "c"] | |
+(1 row) | |
+ | |
+SELECT '[a,b,c]'::hstore #- '{0,0}'; | |
+ ?column? | |
+----------------- | |
+ ["a", "b", "c"] | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{x}'; | |
+ ?column? | |
+------------------------------------------------------------------- | |
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{a}'; | |
+ ?column? | |
+----------------------------------------------------------- | |
+ "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b}'; | |
+ ?column? | |
+------------------------------------------------------ | |
+ "a"=>1, "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c}'; | |
+ ?column? | |
+---------------------------------------------------- | |
+ "a"=>1, "b"=>[1, 2], "d"=>{"1"=>[2, 3]}, "n"=>NULL | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d}'; | |
+ ?column? | |
+----------------------------------------------- | |
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "n"=>NULL | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, 0}'; | |
+ ?column? | |
+---------------------------------------------------------------- | |
+ "a"=>1, "b"=>[2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, -1}'; | |
+ ?column? | |
+---------------------------------------------------------------- | |
+ "a"=>1, "b"=>[1], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, -1}' #- '{b, -1}'; | |
+ ?column? | |
+----------------------------------------------------------------- | |
+ "a"=>1, "b"=>NULL, "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c, 1}'; | |
+ ?column? | |
+--------------------------------------------------------------- | |
+ "a"=>1, "b"=>[1, 2], "c"=>NULL, "d"=>{"1"=>[2, 3]}, "n"=>NULL | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c, 2}'; | |
+ ?column? | |
+------------------------------------------------------------------- | |
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, -2}'; | |
+ ?column? | |
+---------------------------------------------------------------- | |
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[3]}, "n"=>NULL | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 1}'; | |
+ ?column? | |
+---------------------------------------------------------------- | |
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2]}, "n"=>NULL | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}'; | |
+ ?column? | |
+---------------------------------------------------------------- | |
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[3]}, "n"=>NULL | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}' #- '{d, 1, 0}'; | |
+ ?column? | |
+----------------------------------------------------------------- | |
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>NULL}, "n"=>NULL | |
+(1 row) | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}' #- '{d, 1, 0}' #- '{d, 1, 0}'; | |
+ ?column? | |
+----------------------------------------------------------------- | |
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>NULL}, "n"=>NULL | |
+(1 row) | |
+ | |
+-- delete(int) | |
+SELECT '[a,b,c]'::hstore - 3; | |
+ ?column? | |
+----------------- | |
+ ["a", "b", "c"] | |
+(1 row) | |
+ | |
+SELECT '[a,b,c]'::hstore - 2; | |
+ ?column? | |
+------------ | |
+ ["a", "b"] | |
+(1 row) | |
+ | |
+SELECT '[a,b,c]'::hstore - 1; | |
+ ?column? | |
+------------ | |
+ ["a", "c"] | |
+(1 row) | |
+ | |
+SELECT '[a,b,c]'::hstore - 0; | |
+ ?column? | |
+------------ | |
+ ["b", "c"] | |
+(1 row) | |
+ | |
+SELECT '[a,b,c]'::hstore - -1; | |
+ ?column? | |
+------------ | |
+ ["a", "b"] | |
+(1 row) | |
+ | |
+SELECT '[a,b,c]'::hstore - -2; | |
+ ?column? | |
+------------ | |
+ ["a", "c"] | |
+(1 row) | |
+ | |
+SELECT '[a,b,c]'::hstore - -3; | |
+ ?column? | |
+------------ | |
+ ["b", "c"] | |
+(1 row) | |
+ | |
+SELECT '[a,b,c]'::hstore - -4; | |
+ ?column? | |
+----------------- | |
+ ["a", "b", "c"] | |
+(1 row) | |
+ | |
+SELECT 'a=>1, b=>2, c=>3'::hstore - 3; | |
+ ?column? | |
+------------------------ | |
+ "a"=>1, "b"=>2, "c"=>3 | |
+(1 row) | |
+ | |
+SELECT 'a=>1, b=>2, c=>3'::hstore - 2; | |
+ ?column? | |
+---------------- | |
+ "a"=>1, "b"=>2 | |
+(1 row) | |
+ | |
+SELECT 'a=>1, b=>2, c=>3'::hstore - 1; | |
+ ?column? | |
+---------------- | |
+ "a"=>1, "c"=>3 | |
+(1 row) | |
+ | |
+SELECT 'a=>1, b=>2, c=>3'::hstore - 0; | |
+ ?column? | |
+---------------- | |
+ "b"=>2, "c"=>3 | |
+(1 row) | |
+ | |
+SELECT 'a=>1, b=>2, c=>3'::hstore - -1; | |
+ ?column? | |
+---------------- | |
+ "a"=>1, "b"=>2 | |
+(1 row) | |
+ | |
+SELECT 'a=>1, b=>2, c=>3'::hstore - -2; | |
+ ?column? | |
+---------------- | |
+ "a"=>1, "c"=>3 | |
+(1 row) | |
+ | |
+SELECT 'a=>1, b=>2, c=>3'::hstore - -3; | |
+ ?column? | |
+---------------- | |
+ "b"=>2, "c"=>3 | |
+(1 row) | |
+ | |
+SELECT 'a=>1, b=>2, c=>3'::hstore - -4; | |
+ ?column? | |
+------------------------ | |
+ "a"=>1, "b"=>2, "c"=>3 | |
+(1 row) | |
+ | |
+--replace | |
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', '{1,2,3}'); | |
+ replace | |
+------------------------------------------------------------------------ | |
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>[1, 2, 3] | |
+(1 row) | |
+ | |
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{b,-1}', '{1,2,3}'); | |
+ replace | |
+--------------------------------------------------------------------------- | |
+ "a"=>1, "b"=>[1, [1, 2, 3]], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL | |
+(1 row) | |
+ | |
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1,0}', '{1,2,3}'); | |
+ replace | |
+--------------------------------------------------------------------------- | |
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[[1, 2, 3], 3]}, "n"=>NULL | |
+(1 row) | |
+ | |
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,NULL,0}', '{1,2,3}'); | |
+ replace | |
+------------------------------------------------------------------- | |
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL | |
+(1 row) | |
+ | |
+--deep concat | |
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{}', 'n=>not_null'); | |
+ concat_path | |
+------------------------------------------------------------------------- | |
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>"not_null" | |
+(1 row) | |
+ | |
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', 'n=>not_null'); | |
+ concat_path | |
+-------------------------------------------------------------------------------- | |
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>{"n"=>"not_null"} | |
+(1 row) | |
+ | |
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', 'not_null'); | |
+ concat_path | |
+------------------------------------------------------------------------- | |
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>"not_null" | |
+(1 row) | |
+ | |
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', '{not_null}'); | |
+ concat_path | |
+--------------------------------------------------------------------------- | |
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>["not_null"] | |
+(1 row) | |
+ | |
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{}', 'b=>{3,4}'); | |
+ concat_path | |
+------------------------------------------------------------------- | |
+ "a"=>1, "b"=>[3, 4], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL | |
+(1 row) | |
+ | |
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{b}', '{3,4}'); | |
+ concat_path | |
+------------------------------------------------------------------------- | |
+ "a"=>1, "b"=>[1, 2, 3, 4], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL | |
+(1 row) | |
+ | |
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1}', '{4,5}'); | |
+ concat_path | |
+------------------------------------------------------------------------- | |
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3, 4, 5]}, "n"=>NULL | |
+(1 row) | |
+ | |
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1}', '4=>5'); | |
+ concat_path | |
+--------------------------------------------------------------------------- | |
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3, "4", 5]}, "n"=>NULL | |
+(1 row) | |
+ | |
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d}', '2=>{4,5}'); | |
+ concat_path | |
+-------------------------------------------------------------------------------- | |
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3], "2"=>[4, 5]}, "n"=>NULL | |
+(1 row) | |
+ | |
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{NULL,1}', '4=>5'); | |
+ concat_path | |
+------------------------------------------------------------------- | |
+ "a"=>1, "b"=>[1, 2], "c"=>{"1"=>2}, "d"=>{"1"=>[2, 3]}, "n"=>NULL | |
+(1 row) | |
+ | |
+--cast | |
+SELECT ('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::text)::hstore AS err; | |
+ERROR: bad hstore representation | |
+DETAIL: syntax error, unexpected STRING_P, expecting '}' or ',' at end of input | |
+SELECT ('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json)::hstore AS ok; | |
+ ok | |
+---------------------------------------------------- | |
+ "f2"=>{"f3"=>1}, "f4"=>{"f5"=>99, "f6"=>"stringy"} | |
+(1 row) | |
+ | |
+--hvals | |
+SELECT q->'tags' FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore) AS q; | |
+ ?column? | |
+---------- | |
+ 1 | |
+ 3 | |
+(2 rows) | |
+ | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{}') AS q; | |
+ q | |
+---------------------------------------------- | |
+ [{"sh"=>2, "tags"=>1}, {"sh"=>4, "tags"=>3}] | |
+(1 row) | |
+ | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3}') AS q; | |
+ q | |
+--- | |
+(0 rows) | |
+ | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2}') AS q; | |
+ q | |
+-------------------- | |
+ "sh"=>2, "tags"=>1 | |
+(1 row) | |
+ | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1}') AS q; | |
+ q | |
+-------------------- | |
+ "sh"=>4, "tags"=>3 | |
+(1 row) | |
+ | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0}') AS q; | |
+ q | |
+-------------------- | |
+ "sh"=>2, "tags"=>1 | |
+(1 row) | |
+ | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1}') AS q; | |
+ q | |
+-------------------- | |
+ "sh"=>4, "tags"=>3 | |
+(1 row) | |
+ | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2}') AS q; | |
+ q | |
+--- | |
+(0 rows) | |
+ | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL}') AS q; | |
+ q | |
+-------------------- | |
+ "sh"=>2, "tags"=>1 | |
+ "sh"=>4, "tags"=>3 | |
+(2 rows) | |
+ | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3,tags}') AS q; | |
+ q | |
+--- | |
+(0 rows) | |
+ | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2,tags}') AS q; | |
+ q | |
+--- | |
+ 1 | |
+(1 row) | |
+ | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1,tags}') AS q; | |
+ q | |
+--- | |
+ 3 | |
+(1 row) | |
+ | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0,tags}') AS q; | |
+ q | |
+--- | |
+ 1 | |
+(1 row) | |
+ | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1,tags}') AS q; | |
+ q | |
+--- | |
+ 3 | |
+(1 row) | |
+ | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2,tags}') AS q; | |
+ q | |
+--- | |
+(0 rows) | |
+ | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL,tags}') AS q; | |
+ q | |
+--- | |
+ 1 | |
+ 3 | |
+(2 rows) | |
+ | |
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{}') AS q; | |
+ q | |
+----------------------------------------------------------------- | |
+ "1"=>"first", "a"=>{"b"=>"c", "c"=>"b"}, "b"=>[1, 2], "c"=>"cc" | |
+(1 row) | |
+ | |
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{1}') AS q; | |
+ q | |
+--------- | |
+ "first" | |
+(1 row) | |
+ | |
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a}') AS q; | |
+ q | |
+-------------------- | |
+ "b"=>"c", "c"=>"b" | |
+(1 row) | |
+ | |
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b}') AS q; | |
+ q | |
+-------- | |
+ [1, 2] | |
+(1 row) | |
+ | |
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{c}') AS q; | |
+ q | |
+------ | |
+ "cc" | |
+(1 row) | |
+ | |
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL}') AS q; | |
+ q | |
+-------------------- | |
+ "first" | |
+ "b"=>"c", "c"=>"b" | |
+ [1, 2] | |
+ "cc" | |
+(4 rows) | |
+ | |
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a,c}') AS q; | |
+ q | |
+----- | |
+ "b" | |
+(1 row) | |
+ | |
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,c}') AS q; | |
+ q | |
+----- | |
+ "b" | |
+(1 row) | |
+ | |
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b,NULL}') AS q; | |
+ q | |
+--- | |
+ 1 | |
+ 2 | |
+(2 rows) | |
+ | |
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q; | |
+ q | |
+--- | |
+ 2 | |
+(1 row) | |
+ | |
+SELECT q FROM hvals('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q; | |
+ q | |
+--------- | |
+ "first" | |
+ 2 | |
+(2 rows) | |
+ | |
+--svals path | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{}') AS q; | |
+ q | |
+---------------------------------------------- | |
+ [{"sh"=>2, "tags"=>1}, {"sh"=>4, "tags"=>3}] | |
+(1 row) | |
+ | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3}') AS q; | |
+ q | |
+--- | |
+(0 rows) | |
+ | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2}') AS q; | |
+ q | |
+-------------------- | |
+ "sh"=>2, "tags"=>1 | |
+(1 row) | |
+ | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1}') AS q; | |
+ q | |
+-------------------- | |
+ "sh"=>4, "tags"=>3 | |
+(1 row) | |
+ | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0}') AS q; | |
+ q | |
+-------------------- | |
+ "sh"=>2, "tags"=>1 | |
+(1 row) | |
+ | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1}') AS q; | |
+ q | |
+-------------------- | |
+ "sh"=>4, "tags"=>3 | |
+(1 row) | |
+ | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2}') AS q; | |
+ q | |
+--- | |
+(0 rows) | |
+ | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL}') AS q; | |
+ q | |
+-------------------- | |
+ "sh"=>2, "tags"=>1 | |
+ "sh"=>4, "tags"=>3 | |
+(2 rows) | |
+ | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3,tags}') AS q; | |
+ q | |
+--- | |
+(0 rows) | |
+ | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2,tags}') AS q; | |
+ q | |
+--- | |
+ 1 | |
+(1 row) | |
+ | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1,tags}') AS q; | |
+ q | |
+--- | |
+ 3 | |
+(1 row) | |
+ | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0,tags}') AS q; | |
+ q | |
+--- | |
+ 1 | |
+(1 row) | |
+ | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1,tags}') AS q; | |
+ q | |
+--- | |
+ 3 | |
+(1 row) | |
+ | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2,tags}') AS q; | |
+ q | |
+--- | |
+(0 rows) | |
+ | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL,tags}') AS q; | |
+ q | |
+--- | |
+ 1 | |
+ 3 | |
+(2 rows) | |
+ | |
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{}') AS q; | |
+ q | |
+----------------------------------------------------------------- | |
+ "1"=>"first", "a"=>{"b"=>"c", "c"=>"b"}, "b"=>[1, 2], "c"=>"cc" | |
+(1 row) | |
+ | |
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{1}') AS q; | |
+ q | |
+------- | |
+ first | |
+(1 row) | |
+ | |
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a}') AS q; | |
+ q | |
+-------------------- | |
+ "b"=>"c", "c"=>"b" | |
+(1 row) | |
+ | |
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b}') AS q; | |
+ q | |
+-------- | |
+ [1, 2] | |
+(1 row) | |
+ | |
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{c}') AS q; | |
+ q | |
+---- | |
+ cc | |
+(1 row) | |
+ | |
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL}') AS q; | |
+ q | |
+-------------------- | |
+ first | |
+ "b"=>"c", "c"=>"b" | |
+ [1, 2] | |
+ cc | |
+(4 rows) | |
+ | |
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a,c}') AS q; | |
+ q | |
+--- | |
+ b | |
+(1 row) | |
+ | |
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,c}') AS q; | |
+ q | |
+--- | |
+ b | |
+(1 row) | |
+ | |
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b,NULL}') AS q; | |
+ q | |
+--- | |
+ 1 | |
+ 2 | |
+(2 rows) | |
+ | |
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q; | |
+ q | |
+--- | |
+ 2 | |
+(1 row) | |
+ | |
+SELECT q FROM svals('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q; | |
+ q | |
+------- | |
+ first | |
+ 2 | |
+(2 rows) | |
+ | |
+--each | |
+SELECT * FROM each('a=>b, c=>cc'::hstore) AS q; | |
+ key | value | |
+-----+------- | |
+ a | b | |
+ c | cc | |
+(2 rows) | |
+ | |
+SELECT * FROM each('[a, b, c, cc]'::hstore) AS q; | |
+ key | value | |
+-----+------- | |
+ | a | |
+ | b | |
+ | c | |
+ | cc | |
+(4 rows) | |
+ | |
+SELECT * FROM each('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first, n=>null'::hstore) AS q; | |
+ key | value | |
+-----+---------------------------------- | |
+ 1 | first | |
+ a | "1"=>"first", "b"=>"c", "c"=>"b" | |
+ b | [1, 2] | |
+ c | cc | |
+ n | | |
+(5 rows) | |
+ | |
+SELECT * FROM each_hstore('a=>b, c=>cc'::hstore) AS q; | |
+ key | value | |
+-----+------- | |
+ a | "b" | |
+ c | "cc" | |
+(2 rows) | |
+ | |
+SELECT * FROM each_hstore('[a, b, c, cc]'::hstore) AS q; | |
+ key | value | |
+-----+------- | |
+ | "a" | |
+ | "b" | |
+ | "c" | |
+ | "cc" | |
+(4 rows) | |
+ | |
+SELECT * FROM each_hstore('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first, n=>null'::hstore) AS q; | |
+ key | value | |
+-----+---------------------------------- | |
+ 1 | "first" | |
+ a | "1"=>"first", "b"=>"c", "c"=>"b" | |
+ b | [1, 2] | |
+ c | "cc" | |
+ n | | |
+(5 rows) | |
+ | |
+--decoration | |
+SELECT 'a=>1, b=>{c=>3}, d=>[4,[5]]'::hstore AS h, '[a, {b=>c}, [c, d, e]]'::hstore AS a; | |
+ h | a | |
+--------------------------------------+------------------------------------ | |
+ "a"=>1, "b"=>{"c"=>3}, "d"=>[4, [5]] | ["a", {"b"=>"c"}, ["c", "d", "e"]] | |
+(1 row) | |
+ | |
+SET hstore.pretty_print = true; | |
+SELECT 'a=>1, b=>{c=>3}, d=>[4,[5]], e=>[1,2,3,4], f=>g, g=>j'::hstore AS h, | |
+ '[a, {b=>c, c=>d}, [c, d, e, [1,2], h, {f=>g, g=>f}]]'::hstore AS a; | |
+ h | a | |
+------------+------------------------ | |
+ "a"=>1, +| [ + | |
+ "b"=> +| "a", + | |
+ { +| { + | |
+ "c"=>3+| "b"=>"c", + | |
+ }, +| "c"=>"d" + | |
+ "d"=> +| }, + | |
+ [ +| [ + | |
+ 4, +| "c", + | |
+ [ +| "d", + | |
+ 5 +| "e", + | |
+ ] +| [ + | |
+ ], +| 1, + | |
+ "e"=> +| 2 + | |
+ [ +| ], + | |
+ 1, +| "h", + | |
+ 2, +| { + | |
+ 3, +| "f"=>"g", + | |
+ 4 +| "g"=>f + | |
+ ], +| } + | |
+ "f"=>"g", +| ] + | |
+ "g"=>"j" | ] | |
+(1 row) | |
+ | |
+RESET hstore.pretty_print; | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore); | |
+ hstore_print | |
+----------------------------------------------------------------------- | |
+ "a"=>t, "f"=>t, "t"=>"f", "123"=>"string", "arr"=>[1, 2, 3, "3", "x"] | |
+(1 row) | |
+ | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true ); | |
+ hstore_print | |
+------------------------------------------------------------------- | |
+ "a"=>t, "f"=>t, "t"=>f, "123"=>"string", "arr"=>[1, 2, 3, 3, "x"] | |
+(1 row) | |
+ | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true ); | |
+ hstore_print | |
+------------------------------------------------------------------------- | |
+ {"a"=>t, "f"=>t, "t"=>"f", "123"=>"string", "arr"=>[1, 2, 3, "3", "x"]} | |
+(1 row) | |
+ | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, array_curly_braces := true ); | |
+ hstore_print | |
+----------------------------------------------------------------------- | |
+ "a"=>t, "f"=>t, "t"=>"f", "123"=>"string", "arr"=>{1, 2, 3, "3", "x"} | |
+(1 row) | |
+ | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true ); | |
+ hstore_print | |
+----------------------------------------------------------------------------- | |
+ "a": true, "f": true, "t": "f", "123": "string", "arr": [1, 2, 3, "3", "x"] | |
+(1 row) | |
+ | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, loose := true ); | |
+ hstore_print | |
+----------------------------------------------------------------------------- | |
+ "a": true, "f": true, "t": false, "123": "string", "arr": [1, 2, 3, 3, "x"] | |
+(1 row) | |
+ | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, root_hash_decorated := true ); | |
+ hstore_print | |
+------------------------------------------------------------------------------- | |
+ {"a": true, "f": true, "t": "f", "123": "string", "arr": [1, 2, 3, "3", "x"]} | |
+(1 row) | |
+ | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, array_curly_braces := true ); | |
+ hstore_print | |
+----------------------------------------------------------------------------- | |
+ "a": true, "f": true, "t": "f", "123": "string", "arr": {1, 2, 3, "3", "x"} | |
+(1 row) | |
+ | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true, root_hash_decorated := true ); | |
+ hstore_print | |
+--------------------------------------------------------------------- | |
+ {"a"=>t, "f"=>t, "t"=>f, "123"=>"string", "arr"=>[1, 2, 3, 3, "x"]} | |
+(1 row) | |
+ | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true, array_curly_braces := true ); | |
+ hstore_print | |
+------------------------------------------------------------------- | |
+ "a"=>t, "f"=>t, "t"=>f, "123"=>"string", "arr"=>{1, 2, 3, 3, "x"} | |
+(1 row) | |
+ | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true, array_curly_braces := true ); | |
+ hstore_print | |
+------------------------------------------------------------------------- | |
+ {"a"=>t, "f"=>t, "t"=>"f", "123"=>"string", "arr"=>{1, 2, 3, "3", "x"}} | |
+(1 row) | |
+ | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true, array_curly_braces := true, loose := true); | |
+ hstore_print | |
+--------------------------------------------------------------------- | |
+ {"a"=>t, "f"=>t, "t"=>f, "123"=>"string", "arr"=>{1, 2, 3, 3, "x"}} | |
+(1 row) | |
+ | |
diff --git a/contrib/hstore/expected/types.out b/contrib/hstore/expected/types.out | |
new file mode 100644 | |
index 0000000..8233d52 | |
--- /dev/null | |
+++ b/contrib/hstore/expected/types.out | |
@@ -0,0 +1,732 @@ | |
+SELECT '"foo"=>true'::hstore; | |
+ hstore | |
+---------- | |
+ "foo"=>t | |
+(1 row) | |
+ | |
+SELECT 'foo=>true'::hstore; | |
+ hstore | |
+---------- | |
+ "foo"=>t | |
+(1 row) | |
+ | |
+SELECT '"true"=>true'::hstore; | |
+ hstore | |
+----------- | |
+ "true"=>t | |
+(1 row) | |
+ | |
+SELECT 'true=>true'::hstore; | |
+ hstore | |
+----------- | |
+ "true"=>t | |
+(1 row) | |
+ | |
+SELECT '"t"=>true'::hstore; | |
+ hstore | |
+-------- | |
+ "t"=>t | |
+(1 row) | |
+ | |
+SELECT 't=>true'::hstore; | |
+ hstore | |
+-------- | |
+ "t"=>t | |
+(1 row) | |
+ | |
+SELECT '"false"=>true'::hstore; | |
+ hstore | |
+------------ | |
+ "false"=>t | |
+(1 row) | |
+ | |
+SELECT 'false=>true'::hstore; | |
+ hstore | |
+------------ | |
+ "false"=>t | |
+(1 row) | |
+ | |
+SELECT '"f"=>true'::hstore; | |
+ hstore | |
+-------- | |
+ "f"=>t | |
+(1 row) | |
+ | |
+SELECT 'f=>true'::hstore; | |
+ hstore | |
+-------- | |
+ "f"=>t | |
+(1 row) | |
+ | |
+SELECT '"foo"=>false'::hstore; | |
+ hstore | |
+---------- | |
+ "foo"=>f | |
+(1 row) | |
+ | |
+SELECT 'foo=>false'::hstore; | |
+ hstore | |
+---------- | |
+ "foo"=>f | |
+(1 row) | |
+ | |
+SELECT '"false"=>false'::hstore; | |
+ hstore | |
+------------ | |
+ "false"=>f | |
+(1 row) | |
+ | |
+SELECT 'false=>false'::hstore; | |
+ hstore | |
+------------ | |
+ "false"=>f | |
+(1 row) | |
+ | |
+SELECT '"t"=>false'::hstore; | |
+ hstore | |
+-------- | |
+ "t"=>f | |
+(1 row) | |
+ | |
+SELECT 't=>false'::hstore; | |
+ hstore | |
+-------- | |
+ "t"=>f | |
+(1 row) | |
+ | |
+SELECT '"false"=>false'::hstore; | |
+ hstore | |
+------------ | |
+ "false"=>f | |
+(1 row) | |
+ | |
+SELECT 'false=>false'::hstore; | |
+ hstore | |
+------------ | |
+ "false"=>f | |
+(1 row) | |
+ | |
+SELECT '"f"=>false'::hstore; | |
+ hstore | |
+-------- | |
+ "f"=>f | |
+(1 row) | |
+ | |
+SELECT 'f=>false'::hstore; | |
+ hstore | |
+-------- | |
+ "f"=>f | |
+(1 row) | |
+ | |
+SELECT '"1"=>x'::hstore; | |
+ hstore | |
+---------- | |
+ "1"=>"x" | |
+(1 row) | |
+ | |
+SELECT '1=>x'::hstore; | |
+ hstore | |
+---------- | |
+ "1"=>"x" | |
+(1 row) | |
+ | |
+SELECT 'foo=>1'::hstore; | |
+ hstore | |
+---------- | |
+ "foo"=>1 | |
+(1 row) | |
+ | |
+SELECT 'foo=>1.'::hstore; | |
+ hstore | |
+---------- | |
+ "foo"=>1 | |
+(1 row) | |
+ | |
+SELECT 'foo=>1.0'::hstore; | |
+ hstore | |
+------------ | |
+ "foo"=>1.0 | |
+(1 row) | |
+ | |
+SELECT 'foo=>1.01'::hstore; | |
+ hstore | |
+------------- | |
+ "foo"=>1.01 | |
+(1 row) | |
+ | |
+SELECT 'foo=>1.01e'::hstore; | |
+ hstore | |
+---------------- | |
+ "foo"=>"1.01e" | |
+(1 row) | |
+ | |
+SELECT 'foo=>1.01e1'::hstore; | |
+ hstore | |
+------------- | |
+ "foo"=>10.1 | |
+(1 row) | |
+ | |
+SELECT 'foo=>1.01e+1'::hstore; | |
+ hstore | |
+------------- | |
+ "foo"=>10.1 | |
+(1 row) | |
+ | |
+SELECT 'foo=>1.01e-1'::hstore; | |
+ hstore | |
+-------------- | |
+ "foo"=>0.101 | |
+(1 row) | |
+ | |
+SELECT 'foo=>.1'::hstore; | |
+ hstore | |
+------------ | |
+ "foo"=>0.1 | |
+(1 row) | |
+ | |
+SELECT 'foo=>.1e'::hstore; | |
+ hstore | |
+-------------- | |
+ "foo"=>".1e" | |
+(1 row) | |
+ | |
+SELECT 'foo=>.1e1'::hstore; | |
+ hstore | |
+---------- | |
+ "foo"=>1 | |
+(1 row) | |
+ | |
+SELECT 'foo=>.1e+1'::hstore; | |
+ hstore | |
+---------- | |
+ "foo"=>1 | |
+(1 row) | |
+ | |
+SELECT 'foo=>.1e-1'::hstore; | |
+ hstore | |
+------------- | |
+ "foo"=>0.01 | |
+(1 row) | |
+ | |
+SELECT 'foo=>+1'::hstore; | |
+ hstore | |
+---------- | |
+ "foo"=>1 | |
+(1 row) | |
+ | |
+SELECT 'foo=>+1.'::hstore; | |
+ hstore | |
+---------- | |
+ "foo"=>1 | |
+(1 row) | |
+ | |
+SELECT 'foo=>+1.0'::hstore; | |
+ hstore | |
+------------ | |
+ "foo"=>1.0 | |
+(1 row) | |
+ | |
+SELECT 'foo=>+1.01'::hstore; | |
+ hstore | |
+------------- | |
+ "foo"=>1.01 | |
+(1 row) | |
+ | |
+SELECT 'foo=>+1.01e'::hstore; | |
+ hstore | |
+----------------- | |
+ "foo"=>"+1.01e" | |
+(1 row) | |
+ | |
+SELECT 'foo=>+1.01e1'::hstore; | |
+ hstore | |
+------------- | |
+ "foo"=>10.1 | |
+(1 row) | |
+ | |
+SELECT 'foo=>+1.01e+1'::hstore; | |
+ hstore | |
+------------- | |
+ "foo"=>10.1 | |
+(1 row) | |
+ | |
+SELECT 'foo=>+1.01e-1'::hstore; | |
+ hstore | |
+-------------- | |
+ "foo"=>0.101 | |
+(1 row) | |
+ | |
+SELECT 'foo=>+.1'::hstore; | |
+ hstore | |
+------------ | |
+ "foo"=>0.1 | |
+(1 row) | |
+ | |
+SELECT 'foo=>+.1e'::hstore; | |
+ hstore | |
+--------------- | |
+ "foo"=>"+.1e" | |
+(1 row) | |
+ | |
+SELECT 'foo=>+.1e1'::hstore; | |
+ hstore | |
+---------- | |
+ "foo"=>1 | |
+(1 row) | |
+ | |
+SELECT 'foo=>+.1e+1'::hstore; | |
+ hstore | |
+---------- | |
+ "foo"=>1 | |
+(1 row) | |
+ | |
+SELECT 'foo=>+.1e-1'::hstore; | |
+ hstore | |
+------------- | |
+ "foo"=>0.01 | |
+(1 row) | |
+ | |
+SELECT 'foo=>-1'::hstore; | |
+ hstore | |
+----------- | |
+ "foo"=>-1 | |
+(1 row) | |
+ | |
+SELECT 'foo=>-1.'::hstore; | |
+ hstore | |
+----------- | |
+ "foo"=>-1 | |
+(1 row) | |
+ | |
+SELECT 'foo=>-1.0'::hstore; | |
+ hstore | |
+------------- | |
+ "foo"=>-1.0 | |
+(1 row) | |
+ | |
+SELECT 'foo=>-1.01'::hstore; | |
+ hstore | |
+-------------- | |
+ "foo"=>-1.01 | |
+(1 row) | |
+ | |
+SELECT 'foo=>-1.01e'::hstore; | |
+ hstore | |
+----------------- | |
+ "foo"=>"-1.01e" | |
+(1 row) | |
+ | |
+SELECT 'foo=>-1.01e1'::hstore; | |
+ hstore | |
+-------------- | |
+ "foo"=>-10.1 | |
+(1 row) | |
+ | |
+SELECT 'foo=>-1.01e+1'::hstore; | |
+ hstore | |
+-------------- | |
+ "foo"=>-10.1 | |
+(1 row) | |
+ | |
+SELECT 'foo=>-1.01e-1'::hstore; | |
+ hstore | |
+--------------- | |
+ "foo"=>-0.101 | |
+(1 row) | |
+ | |
+SELECT 'foo=>-.1'::hstore; | |
+ hstore | |
+------------- | |
+ "foo"=>-0.1 | |
+(1 row) | |
+ | |
+SELECT 'foo=>-.1e'::hstore; | |
+ hstore | |
+--------------- | |
+ "foo"=>"-.1e" | |
+(1 row) | |
+ | |
+SELECT 'foo=>-.1e1'::hstore; | |
+ hstore | |
+----------- | |
+ "foo"=>-1 | |
+(1 row) | |
+ | |
+SELECT 'foo=>-.1e+1'::hstore; | |
+ hstore | |
+----------- | |
+ "foo"=>-1 | |
+(1 row) | |
+ | |
+SELECT 'foo=>-.1e-1'::hstore; | |
+ hstore | |
+-------------- | |
+ "foo"=>-0.01 | |
+(1 row) | |
+ | |
+SELECT 'foo=>1e2000'::hstore; | |
+ hstore | |
+----------------- | |
+ "foo"=>"1e2000" | |
+(1 row) | |
+ | |
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 'foo'; | |
+ ?column? | |
+--------------- | |
+ 1000000000000 | |
+(1 row) | |
+ | |
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 'bar'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 0; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 1; | |
+ ?column? | |
+--------------- | |
+ 1000000000000 | |
+(1 row) | |
+ | |
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 'foo'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 'bar'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 0; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 1; | |
+ ?column? | |
+--------------- | |
+ 1000000000000 | |
+(1 row) | |
+ | |
+SELECT 'foo=>{x, 1e-12}'::hstore #^> '{foo, 0}'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT 'foo=>{x, 1e-12}'::hstore #^> '{foo, 1}'; | |
+ ?column? | |
+---------------- | |
+ 0.000000000001 | |
+(1 row) | |
+ | |
+SELECT 'foo=>t, bar=>x'::hstore ?> 'foo'; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'foo=>t, bar=>x'::hstore ?> 'bar'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT 'foo=>t, bar=>x'::hstore ?> 0; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT 'foo=>t, bar=>x'::hstore ?> 1; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT '[foo, t, bar, x]'::hstore ?> 'foo'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '[foo, t, bar, x]'::hstore ?> 'bar'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '[foo, t, bar, x]'::hstore ?> 0; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '[foo, t, bar, x]'::hstore ?> 1; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'foo=>{x, t}'::hstore #?> '{foo, 0}'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT 'foo=>{x, t}'::hstore #?> '{foo, 1}'; | |
+ ?column? | |
+---------- | |
+ t | |
+(1 row) | |
+ | |
+SELECT 'foo=>f, bar=>x'::hstore ?> 'foo'; | |
+ ?column? | |
+---------- | |
+ f | |
+(1 row) | |
+ | |
+SELECT 'foo=>f, bar=>x'::hstore ?> 'bar'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT 'foo=>f, bar=>x'::hstore ?> 0; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT 'foo=>f, bar=>x'::hstore ?> 1; | |
+ ?column? | |
+---------- | |
+ f | |
+(1 row) | |
+ | |
+SELECT '[foo, f, bar, x]'::hstore ?> 'foo'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '[foo, f, bar, x]'::hstore ?> 'bar'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '[foo, f, bar, x]'::hstore ?> 0; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT '[foo, f, bar, x]'::hstore ?> 1; | |
+ ?column? | |
+---------- | |
+ f | |
+(1 row) | |
+ | |
+SELECT 'foo=>{x, f}'::hstore #?> '{foo, 0}'; | |
+ ?column? | |
+---------- | |
+ | |
+(1 row) | |
+ | |
+SELECT 'foo=>{x, f}'::hstore #?> '{foo, 1}'; | |
+ ?column? | |
+---------- | |
+ f | |
+(1 row) | |
+ | |
+SELECT hstore_typeof('a=>b') AS hash; | |
+ hash | |
+------ | |
+ hash | |
+(1 row) | |
+ | |
+SELECT hstore_typeof('{a=>b}') AS hash; | |
+ hash | |
+------ | |
+ hash | |
+(1 row) | |
+ | |
+SELECT hstore_typeof('{a, b}') AS array; | |
+ array | |
+------- | |
+ array | |
+(1 row) | |
+ | |
+SELECT hstore_typeof('{{a=>b}}') AS array; | |
+ array | |
+------- | |
+ array | |
+(1 row) | |
+ | |
+SELECT hstore_typeof('[a, b]') AS array; | |
+ array | |
+------- | |
+ array | |
+(1 row) | |
+ | |
+SELECT hstore_typeof('') AS "NULL"; | |
+ NULL | |
+------ | |
+ | |
+(1 row) | |
+ | |
+SELECT hstore_typeof('NULL') AS "null"; | |
+ null | |
+------ | |
+ null | |
+(1 row) | |
+ | |
+SELECT hstore_typeof('1.0') AS numeric; | |
+ numeric | |
+--------- | |
+ numeric | |
+(1 row) | |
+ | |
+SELECT hstore_typeof('t') AS bool; | |
+ bool | |
+------ | |
+ bool | |
+(1 row) | |
+ | |
+SELECT hstore_typeof('f') AS bool; | |
+ bool | |
+------ | |
+ bool | |
+(1 row) | |
+ | |
+SELECT hstore('xxx', 't'::bool); | |
+ hstore | |
+---------- | |
+ "xxx"=>t | |
+(1 row) | |
+ | |
+SELECT hstore('xxx', 'f'::bool); | |
+ hstore | |
+---------- | |
+ "xxx"=>f | |
+(1 row) | |
+ | |
+SELECT hstore('xxx', 3.14); | |
+ hstore | |
+------------- | |
+ "xxx"=>3.14 | |
+(1 row) | |
+ | |
+SELECT hstore('xxx', 3.14::numeric); | |
+ hstore | |
+------------- | |
+ "xxx"=>3.14 | |
+(1 row) | |
+ | |
+SELECT hstore('xxx', '3.14'::numeric); | |
+ hstore | |
+------------- | |
+ "xxx"=>3.14 | |
+(1 row) | |
+ | |
+SELECT hstore(NULL); | |
+ hstore | |
+-------- | |
+ | |
+(1 row) | |
+ | |
+SELECT hstore('NULL'); | |
+ hstore | |
+-------- | |
+ NULL | |
+(1 row) | |
+ | |
+SELECT hstore('t'::bool) AS "true", hstore('f'::bool) AS "false"; | |
+ true | false | |
+------+------- | |
+ t | f | |
+(1 row) | |
+ | |
+SELECT hstore(3.14), hstore(3.14::numeric), hstore('3.14'::numeric); | |
+ hstore | hstore | hstore | |
+--------+--------+-------- | |
+ 3.14 | 3.14 | 3.14 | |
+(1 row) | |
+ | |
+SELECT hstore('xxx', 'foo=>t, bar=>3.14, zzz=>xxx'::hstore); | |
+ hstore | |
+---------------------------------------------- | |
+ "xxx"=>{"bar"=>3.14, "foo"=>t, "zzz"=>"xxx"} | |
+(1 row) | |
+ | |
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int2[]); | |
+ array_to_hstore | |
+------------------------- | |
+ [[1, 1, 4], [23, 3, 5]] | |
+(1 row) | |
+ | |
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int4[]); | |
+ array_to_hstore | |
+------------------------- | |
+ [[1, 1, 4], [23, 3, 5]] | |
+(1 row) | |
+ | |
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int8[]); | |
+ array_to_hstore | |
+------------------------- | |
+ [[1, 1, 4], [23, 3, 5]] | |
+(1 row) | |
+ | |
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::float4[]); | |
+ array_to_hstore | |
+------------------------- | |
+ [[1, 1, 4], [23, 3, 5]] | |
+(1 row) | |
+ | |
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::float8[]); | |
+ array_to_hstore | |
+------------------------- | |
+ [[1, 1, 4], [23, 3, 5]] | |
+(1 row) | |
+ | |
+SELECT array_to_hstore('{{1,1,f},{f,t,NULL}}'::bool[]); | |
+ array_to_hstore | |
+--------------------------- | |
+ [[t, t, f], [f, t, NULL]] | |
+(1 row) | |
+ | |
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::text[]); | |
+ array_to_hstore | |
+------------------------------------- | |
+ [["1", "1", "4"], ["23", "3", "5"]] | |
+(1 row) | |
+ | |
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::varchar[]); | |
+ array_to_hstore | |
+------------------------------------- | |
+ [["1", "1", "4"], ["23", "3", "5"]] | |
+(1 row) | |
+ | |
+SELECT array_to_hstore('{{{1,11},{1,1},{4,41}},{{23,231},{3,31},{5,51}}}'::int4[]); | |
+ array_to_hstore | |
+------------------------------------------------------------- | |
+ [[[1, 11], [1, 1], [4, 41], [23, 231]], [[3, 31], [5, 51]]] | |
+(1 row) | |
+ | |
+SELECT hstore('array', array_to_hstore('{{{1,11},{1,1},{4,41}},{{23,231},{3,31},{5,51}}}'::int4[])); | |
+ hstore | |
+---------------------------------------------------------------------- | |
+ "array"=>[[[1, 11], [1, 1], [4, 41], [23, 231]], [[3, 31], [5, 51]]] | |
+(1 row) | |
+ | |
diff --git a/contrib/hstore/hstore--1.2--1.3.sql b/contrib/hstore/hstore--1.2--1.3.sql | |
new file mode 100644 | |
index 0000000..13e310b | |
--- /dev/null | |
+++ b/contrib/hstore/hstore--1.2--1.3.sql | |
@@ -0,0 +1,322 @@ | |
+/* contrib/hstore/hstore--1.2--1.3.sql */ | |
+ | |
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION | |
+\echo Use "ALTER EXTENSION hstore UPDATE TO '1.2'" to load this file. \quit | |
+ | |
+CREATE FUNCTION fetchval_numeric(hstore,text) | |
+RETURNS numeric | |
+AS 'MODULE_PATHNAME','hstore_fetchval_numeric' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR ^> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text, | |
+ PROCEDURE = fetchval_numeric | |
+); | |
+ | |
+CREATE FUNCTION fetchval_boolean(hstore,text) | |
+RETURNS boolean | |
+AS 'MODULE_PATHNAME','hstore_fetchval_boolean' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR ?> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text, | |
+ PROCEDURE = fetchval_boolean | |
+); | |
+ | |
+CREATE FUNCTION fetchval(hstore,int) | |
+RETURNS text | |
+AS 'MODULE_PATHNAME','hstore_fetchval_n' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR -> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = int, | |
+ PROCEDURE = fetchval | |
+); | |
+ | |
+CREATE FUNCTION fetchval_numeric(hstore,int) | |
+RETURNS numeric | |
+AS 'MODULE_PATHNAME','hstore_fetchval_n_numeric' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR ^> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = int, | |
+ PROCEDURE = fetchval_numeric | |
+); | |
+ | |
+CREATE FUNCTION fetchval_boolean(hstore,int) | |
+RETURNS boolean | |
+AS 'MODULE_PATHNAME','hstore_fetchval_n_boolean' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR ?> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = int, | |
+ PROCEDURE = fetchval_boolean | |
+); | |
+ | |
+CREATE FUNCTION fetchval(hstore,text[]) | |
+RETURNS text | |
+AS 'MODULE_PATHNAME','hstore_fetchval_path' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR #> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text[], | |
+ PROCEDURE = fetchval | |
+); | |
+ | |
+CREATE FUNCTION fetchval_numeric(hstore,text[]) | |
+RETURNS numeric | |
+AS 'MODULE_PATHNAME','hstore_fetchval_path_numeric' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR #^> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text[], | |
+ PROCEDURE = fetchval_numeric | |
+); | |
+ | |
+CREATE FUNCTION fetchval_boolean(hstore,text[]) | |
+RETURNS boolean | |
+AS 'MODULE_PATHNAME','hstore_fetchval_path_boolean' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR #?> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text[], | |
+ PROCEDURE = fetchval_boolean | |
+); | |
+ | |
+CREATE FUNCTION fetchval_hstore(hstore,text) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_fetchval_hstore' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR %> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text, | |
+ PROCEDURE = fetchval_hstore | |
+); | |
+ | |
+CREATE FUNCTION fetchval_hstore(hstore,int) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_fetchval_n_hstore' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR %> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = int, | |
+ PROCEDURE = fetchval_hstore | |
+); | |
+ | |
+CREATE FUNCTION fetchval_hstore(hstore,text[]) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_fetchval_path_hstore' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR #%> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text[], | |
+ PROCEDURE = fetchval_hstore | |
+); | |
+ | |
+CREATE FUNCTION json_to_hstore(json) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','json_to_hstore' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE CAST (json AS hstore) | |
+WITH FUNCTION json_to_hstore(json); | |
+ | |
+CREATE FUNCTION isexists(hstore,int) | |
+RETURNS bool | |
+AS 'MODULE_PATHNAME','hstore_exists_idx' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION exist(hstore,int) | |
+RETURNS bool | |
+AS 'MODULE_PATHNAME','hstore_exists_idx' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR ? ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = int, | |
+ PROCEDURE = exist, | |
+ RESTRICT = contsel, | |
+ JOIN = contjoinsel | |
+); | |
+ | |
+CREATE FUNCTION isexists(hstore,text[]) | |
+RETURNS bool | |
+AS 'MODULE_PATHNAME','hstore_exists_path' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION exist(hstore,text[]) | |
+RETURNS bool | |
+AS 'MODULE_PATHNAME','hstore_exists_path' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR #? ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text[], | |
+ PROCEDURE = exist, | |
+ RESTRICT = contsel, | |
+ JOIN = contjoinsel | |
+); | |
+ | |
+CREATE FUNCTION delete_path(hstore,text[]) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_delete_path' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR #- ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text[], | |
+ PROCEDURE = delete_path | |
+); | |
+ | |
+CREATE FUNCTION delete(hstore,int) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_delete_idx' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR - ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = int, | |
+ PROCEDURE = delete | |
+); | |
+ | |
+CREATE FUNCTION replace(hstore,text[],hstore) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_replace' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION svals(hstore, text[]) | |
+RETURNS setof text | |
+AS 'MODULE_PATHNAME','hstore_svals_path' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION hvals(hstore) | |
+RETURNS setof hstore | |
+AS 'MODULE_PATHNAME','hstore_hvals' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION hvals(hstore, text[]) | |
+RETURNS setof hstore | |
+AS 'MODULE_PATHNAME','hstore_hvals_path' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION concat_path(hstore,text[],hstore) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_deep_concat' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION each_hstore(IN hs hstore, | |
+ OUT key text, | |
+ OUT value hstore) | |
+RETURNS SETOF record | |
+AS 'MODULE_PATHNAME','hstore_each_hstore' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION hstore_typeof(hstore) | |
+RETURNS text | |
+AS 'MODULE_PATHNAME','hstore_typeof' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION hstore(text,bool) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_from_bool' | |
+LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL) | |
+ | |
+CREATE FUNCTION hstore(text,numeric) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_from_numeric' | |
+LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL) | |
+ | |
+CREATE FUNCTION hstore(text) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_scalar_from_text' | |
+LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL) | |
+ | |
+CREATE FUNCTION hstore(bool) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_scalar_from_bool' | |
+LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL) | |
+ | |
+CREATE FUNCTION hstore(numeric) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_scalar_from_numeric' | |
+LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL) | |
+ | |
+ | |
+-- GIN support: hash based opclass | |
+ | |
+FUNCTION gin_extract_hstore_hash(internal, internal) | |
+RETURNS internal | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE FUNCTION gin_extract_hstore_hash_query(internal, internal, int2, internal, internal) | |
+RETURNS internal | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE FUNCTION gin_consistent_hstore_hash(internal, int2, internal, int4, internal, internal) | |
+RETURNS bool | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE OPERATOR CLASS gin_hstore_hash_ops | |
+FOR TYPE hstore USING gin | |
+AS | |
+ OPERATOR 7 @>, | |
+ FUNCTION 1 btint4cmp(int4,int4), | |
+ FUNCTION 2 gin_extract_hstore_hash(internal, internal), | |
+ FUNCTION 3 gin_extract_hstore_hash_query(internal, internal, int2, internal, internal), | |
+ FUNCTION 4 gin_consistent_hstore_hash(internal, int2, internal, int4, internal, internal), | |
+STORAGE int4; | |
+ | |
+CREATE FUNCTION array_to_hstore(anyarray) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','array_to_hstore' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION hstore_pretty_print() | |
+RETURNS int4 | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE FUNCTION hstore_array_curly_braces() | |
+RETURNS int4 | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE FUNCTION hstore_json() | |
+RETURNS int4 | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE FUNCTION hstore_root_hash_decorated() | |
+RETURNS int4 | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE FUNCTION hstore_loose() | |
+RETURNS int4 | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE FUNCTION hstore_print(hstore, | |
+ pretty_print bool DEFAULT false, | |
+ array_curly_braces bool DEFAULT false, | |
+ root_hash_decorated bool DEFAULT false, | |
+ json bool DEFAULT false, | |
+ loose bool DEFAULT false) | |
+RETURNS text | |
+AS 'MODULE_PATHNAME', 'hstore_print' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
diff --git a/contrib/hstore/hstore--1.2.sql b/contrib/hstore/hstore--1.2.sql | |
deleted file mode 100644 | |
index f415a72..0000000 | |
--- a/contrib/hstore/hstore--1.2.sql | |
+++ /dev/null | |
@@ -1,537 +0,0 @@ | |
-/* contrib/hstore/hstore--1.1.sql */ | |
- | |
--- complain if script is sourced in psql, rather than via CREATE EXTENSION | |
-\echo Use "CREATE EXTENSION hstore" to load this file. \quit | |
- | |
-CREATE TYPE hstore; | |
- | |
-CREATE FUNCTION hstore_in(cstring) | |
-RETURNS hstore | |
-AS 'MODULE_PATHNAME' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION hstore_out(hstore) | |
-RETURNS cstring | |
-AS 'MODULE_PATHNAME' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION hstore_recv(internal) | |
-RETURNS hstore | |
-AS 'MODULE_PATHNAME' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION hstore_send(hstore) | |
-RETURNS bytea | |
-AS 'MODULE_PATHNAME' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE TYPE hstore ( | |
- INTERNALLENGTH = -1, | |
- INPUT = hstore_in, | |
- OUTPUT = hstore_out, | |
- RECEIVE = hstore_recv, | |
- SEND = hstore_send, | |
- STORAGE = extended | |
-); | |
- | |
-CREATE FUNCTION hstore_version_diag(hstore) | |
-RETURNS integer | |
-AS 'MODULE_PATHNAME','hstore_version_diag' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION fetchval(hstore,text) | |
-RETURNS text | |
-AS 'MODULE_PATHNAME','hstore_fetchval' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE OPERATOR -> ( | |
- LEFTARG = hstore, | |
- RIGHTARG = text, | |
- PROCEDURE = fetchval | |
-); | |
- | |
-CREATE FUNCTION slice_array(hstore,text[]) | |
-RETURNS text[] | |
-AS 'MODULE_PATHNAME','hstore_slice_to_array' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE OPERATOR -> ( | |
- LEFTARG = hstore, | |
- RIGHTARG = text[], | |
- PROCEDURE = slice_array | |
-); | |
- | |
-CREATE FUNCTION slice(hstore,text[]) | |
-RETURNS hstore | |
-AS 'MODULE_PATHNAME','hstore_slice_to_hstore' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION isexists(hstore,text) | |
-RETURNS bool | |
-AS 'MODULE_PATHNAME','hstore_exists' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION exist(hstore,text) | |
-RETURNS bool | |
-AS 'MODULE_PATHNAME','hstore_exists' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE OPERATOR ? ( | |
- LEFTARG = hstore, | |
- RIGHTARG = text, | |
- PROCEDURE = exist, | |
- RESTRICT = contsel, | |
- JOIN = contjoinsel | |
-); | |
- | |
-CREATE FUNCTION exists_any(hstore,text[]) | |
-RETURNS bool | |
-AS 'MODULE_PATHNAME','hstore_exists_any' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE OPERATOR ?| ( | |
- LEFTARG = hstore, | |
- RIGHTARG = text[], | |
- PROCEDURE = exists_any, | |
- RESTRICT = contsel, | |
- JOIN = contjoinsel | |
-); | |
- | |
-CREATE FUNCTION exists_all(hstore,text[]) | |
-RETURNS bool | |
-AS 'MODULE_PATHNAME','hstore_exists_all' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE OPERATOR ?& ( | |
- LEFTARG = hstore, | |
- RIGHTARG = text[], | |
- PROCEDURE = exists_all, | |
- RESTRICT = contsel, | |
- JOIN = contjoinsel | |
-); | |
- | |
-CREATE FUNCTION isdefined(hstore,text) | |
-RETURNS bool | |
-AS 'MODULE_PATHNAME','hstore_defined' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION defined(hstore,text) | |
-RETURNS bool | |
-AS 'MODULE_PATHNAME','hstore_defined' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION delete(hstore,text) | |
-RETURNS hstore | |
-AS 'MODULE_PATHNAME','hstore_delete' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION delete(hstore,text[]) | |
-RETURNS hstore | |
-AS 'MODULE_PATHNAME','hstore_delete_array' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION delete(hstore,hstore) | |
-RETURNS hstore | |
-AS 'MODULE_PATHNAME','hstore_delete_hstore' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE OPERATOR - ( | |
- LEFTARG = hstore, | |
- RIGHTARG = text, | |
- PROCEDURE = delete | |
-); | |
- | |
-CREATE OPERATOR - ( | |
- LEFTARG = hstore, | |
- RIGHTARG = text[], | |
- PROCEDURE = delete | |
-); | |
- | |
-CREATE OPERATOR - ( | |
- LEFTARG = hstore, | |
- RIGHTARG = hstore, | |
- PROCEDURE = delete | |
-); | |
- | |
-CREATE FUNCTION hs_concat(hstore,hstore) | |
-RETURNS hstore | |
-AS 'MODULE_PATHNAME','hstore_concat' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE OPERATOR || ( | |
- LEFTARG = hstore, | |
- RIGHTARG = hstore, | |
- PROCEDURE = hs_concat | |
-); | |
- | |
-CREATE FUNCTION hs_contains(hstore,hstore) | |
-RETURNS bool | |
-AS 'MODULE_PATHNAME','hstore_contains' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION hs_contained(hstore,hstore) | |
-RETURNS bool | |
-AS 'MODULE_PATHNAME','hstore_contained' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE OPERATOR @> ( | |
- LEFTARG = hstore, | |
- RIGHTARG = hstore, | |
- PROCEDURE = hs_contains, | |
- COMMUTATOR = '<@', | |
- RESTRICT = contsel, | |
- JOIN = contjoinsel | |
-); | |
- | |
-CREATE OPERATOR <@ ( | |
- LEFTARG = hstore, | |
- RIGHTARG = hstore, | |
- PROCEDURE = hs_contained, | |
- COMMUTATOR = '@>', | |
- RESTRICT = contsel, | |
- JOIN = contjoinsel | |
-); | |
- | |
--- obsolete: | |
-CREATE OPERATOR @ ( | |
- LEFTARG = hstore, | |
- RIGHTARG = hstore, | |
- PROCEDURE = hs_contains, | |
- COMMUTATOR = '~', | |
- RESTRICT = contsel, | |
- JOIN = contjoinsel | |
-); | |
- | |
-CREATE OPERATOR ~ ( | |
- LEFTARG = hstore, | |
- RIGHTARG = hstore, | |
- PROCEDURE = hs_contained, | |
- COMMUTATOR = '@', | |
- RESTRICT = contsel, | |
- JOIN = contjoinsel | |
-); | |
- | |
-CREATE FUNCTION tconvert(text,text) | |
-RETURNS hstore | |
-AS 'MODULE_PATHNAME','hstore_from_text' | |
-LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL) | |
- | |
-CREATE FUNCTION hstore(text,text) | |
-RETURNS hstore | |
-AS 'MODULE_PATHNAME','hstore_from_text' | |
-LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL) | |
- | |
-CREATE FUNCTION hstore(text[],text[]) | |
-RETURNS hstore | |
-AS 'MODULE_PATHNAME', 'hstore_from_arrays' | |
-LANGUAGE C IMMUTABLE; -- not STRICT; allows (keys,null) | |
- | |
-CREATE FUNCTION hstore(text[]) | |
-RETURNS hstore | |
-AS 'MODULE_PATHNAME', 'hstore_from_array' | |
-LANGUAGE C IMMUTABLE STRICT; | |
- | |
-CREATE CAST (text[] AS hstore) | |
- WITH FUNCTION hstore(text[]); | |
- | |
-CREATE FUNCTION hstore_to_json(hstore) | |
-RETURNS json | |
-AS 'MODULE_PATHNAME', 'hstore_to_json' | |
-LANGUAGE C IMMUTABLE STRICT; | |
- | |
-CREATE CAST (hstore AS json) | |
- WITH FUNCTION hstore_to_json(hstore); | |
- | |
-CREATE FUNCTION hstore_to_json_loose(hstore) | |
-RETURNS json | |
-AS 'MODULE_PATHNAME', 'hstore_to_json_loose' | |
-LANGUAGE C IMMUTABLE STRICT; | |
- | |
-CREATE FUNCTION hstore(record) | |
-RETURNS hstore | |
-AS 'MODULE_PATHNAME', 'hstore_from_record' | |
-LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::recordtype) | |
- | |
-CREATE FUNCTION hstore_to_array(hstore) | |
-RETURNS text[] | |
-AS 'MODULE_PATHNAME','hstore_to_array' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE OPERATOR %% ( | |
- RIGHTARG = hstore, | |
- PROCEDURE = hstore_to_array | |
-); | |
- | |
-CREATE FUNCTION hstore_to_matrix(hstore) | |
-RETURNS text[] | |
-AS 'MODULE_PATHNAME','hstore_to_matrix' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE OPERATOR %# ( | |
- RIGHTARG = hstore, | |
- PROCEDURE = hstore_to_matrix | |
-); | |
- | |
-CREATE FUNCTION akeys(hstore) | |
-RETURNS text[] | |
-AS 'MODULE_PATHNAME','hstore_akeys' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION avals(hstore) | |
-RETURNS text[] | |
-AS 'MODULE_PATHNAME','hstore_avals' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION skeys(hstore) | |
-RETURNS setof text | |
-AS 'MODULE_PATHNAME','hstore_skeys' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION svals(hstore) | |
-RETURNS setof text | |
-AS 'MODULE_PATHNAME','hstore_svals' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION each(IN hs hstore, | |
- OUT key text, | |
- OUT value text) | |
-RETURNS SETOF record | |
-AS 'MODULE_PATHNAME','hstore_each' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION populate_record(anyelement,hstore) | |
-RETURNS anyelement | |
-AS 'MODULE_PATHNAME', 'hstore_populate_record' | |
-LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::rectype,hstore) | |
- | |
-CREATE OPERATOR #= ( | |
- LEFTARG = anyelement, | |
- RIGHTARG = hstore, | |
- PROCEDURE = populate_record | |
-); | |
- | |
--- btree support | |
- | |
-CREATE FUNCTION hstore_eq(hstore,hstore) | |
-RETURNS boolean | |
-AS 'MODULE_PATHNAME','hstore_eq' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION hstore_ne(hstore,hstore) | |
-RETURNS boolean | |
-AS 'MODULE_PATHNAME','hstore_ne' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION hstore_gt(hstore,hstore) | |
-RETURNS boolean | |
-AS 'MODULE_PATHNAME','hstore_gt' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION hstore_ge(hstore,hstore) | |
-RETURNS boolean | |
-AS 'MODULE_PATHNAME','hstore_ge' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION hstore_lt(hstore,hstore) | |
-RETURNS boolean | |
-AS 'MODULE_PATHNAME','hstore_lt' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION hstore_le(hstore,hstore) | |
-RETURNS boolean | |
-AS 'MODULE_PATHNAME','hstore_le' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION hstore_cmp(hstore,hstore) | |
-RETURNS integer | |
-AS 'MODULE_PATHNAME','hstore_cmp' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE OPERATOR = ( | |
- LEFTARG = hstore, | |
- RIGHTARG = hstore, | |
- PROCEDURE = hstore_eq, | |
- COMMUTATOR = =, | |
- NEGATOR = <>, | |
- RESTRICT = eqsel, | |
- JOIN = eqjoinsel, | |
- MERGES, | |
- HASHES | |
-); | |
-CREATE OPERATOR <> ( | |
- LEFTARG = hstore, | |
- RIGHTARG = hstore, | |
- PROCEDURE = hstore_ne, | |
- COMMUTATOR = <>, | |
- NEGATOR = =, | |
- RESTRICT = neqsel, | |
- JOIN = neqjoinsel | |
-); | |
- | |
--- the comparison operators have funky names (and are undocumented) | |
--- in an attempt to discourage anyone from actually using them. they | |
--- only exist to support the btree opclass | |
- | |
-CREATE OPERATOR #<# ( | |
- LEFTARG = hstore, | |
- RIGHTARG = hstore, | |
- PROCEDURE = hstore_lt, | |
- COMMUTATOR = #>#, | |
- NEGATOR = #>=#, | |
- RESTRICT = scalarltsel, | |
- JOIN = scalarltjoinsel | |
-); | |
-CREATE OPERATOR #<=# ( | |
- LEFTARG = hstore, | |
- RIGHTARG = hstore, | |
- PROCEDURE = hstore_le, | |
- COMMUTATOR = #>=#, | |
- NEGATOR = #>#, | |
- RESTRICT = scalarltsel, | |
- JOIN = scalarltjoinsel | |
-); | |
-CREATE OPERATOR #># ( | |
- LEFTARG = hstore, | |
- RIGHTARG = hstore, | |
- PROCEDURE = hstore_gt, | |
- COMMUTATOR = #<#, | |
- NEGATOR = #<=#, | |
- RESTRICT = scalargtsel, | |
- JOIN = scalargtjoinsel | |
-); | |
-CREATE OPERATOR #>=# ( | |
- LEFTARG = hstore, | |
- RIGHTARG = hstore, | |
- PROCEDURE = hstore_ge, | |
- COMMUTATOR = #<=#, | |
- NEGATOR = #<#, | |
- RESTRICT = scalargtsel, | |
- JOIN = scalargtjoinsel | |
-); | |
- | |
-CREATE OPERATOR CLASS btree_hstore_ops | |
-DEFAULT FOR TYPE hstore USING btree | |
-AS | |
- OPERATOR 1 #<# , | |
- OPERATOR 2 #<=# , | |
- OPERATOR 3 = , | |
- OPERATOR 4 #>=# , | |
- OPERATOR 5 #># , | |
- FUNCTION 1 hstore_cmp(hstore,hstore); | |
- | |
--- hash support | |
- | |
-CREATE FUNCTION hstore_hash(hstore) | |
-RETURNS integer | |
-AS 'MODULE_PATHNAME','hstore_hash' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE OPERATOR CLASS hash_hstore_ops | |
-DEFAULT FOR TYPE hstore USING hash | |
-AS | |
- OPERATOR 1 = , | |
- FUNCTION 1 hstore_hash(hstore); | |
- | |
--- GiST support | |
- | |
-CREATE TYPE ghstore; | |
- | |
-CREATE FUNCTION ghstore_in(cstring) | |
-RETURNS ghstore | |
-AS 'MODULE_PATHNAME' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE FUNCTION ghstore_out(ghstore) | |
-RETURNS cstring | |
-AS 'MODULE_PATHNAME' | |
-LANGUAGE C STRICT IMMUTABLE; | |
- | |
-CREATE TYPE ghstore ( | |
- INTERNALLENGTH = -1, | |
- INPUT = ghstore_in, | |
- OUTPUT = ghstore_out | |
-); | |
- | |
-CREATE FUNCTION ghstore_compress(internal) | |
-RETURNS internal | |
-AS 'MODULE_PATHNAME' | |
-LANGUAGE C IMMUTABLE STRICT; | |
- | |
-CREATE FUNCTION ghstore_decompress(internal) | |
-RETURNS internal | |
-AS 'MODULE_PATHNAME' | |
-LANGUAGE C IMMUTABLE STRICT; | |
- | |
-CREATE FUNCTION ghstore_penalty(internal,internal,internal) | |
-RETURNS internal | |
-AS 'MODULE_PATHNAME' | |
-LANGUAGE C IMMUTABLE STRICT; | |
- | |
-CREATE FUNCTION ghstore_picksplit(internal, internal) | |
-RETURNS internal | |
-AS 'MODULE_PATHNAME' | |
-LANGUAGE C IMMUTABLE STRICT; | |
- | |
-CREATE FUNCTION ghstore_union(internal, internal) | |
-RETURNS internal | |
-AS 'MODULE_PATHNAME' | |
-LANGUAGE C IMMUTABLE STRICT; | |
- | |
-CREATE FUNCTION ghstore_same(internal, internal, internal) | |
-RETURNS internal | |
-AS 'MODULE_PATHNAME' | |
-LANGUAGE C IMMUTABLE STRICT; | |
- | |
-CREATE FUNCTION ghstore_consistent(internal,internal,int,oid,internal) | |
-RETURNS bool | |
-AS 'MODULE_PATHNAME' | |
-LANGUAGE C IMMUTABLE STRICT; | |
- | |
-CREATE OPERATOR CLASS gist_hstore_ops | |
-DEFAULT FOR TYPE hstore USING gist | |
-AS | |
- OPERATOR 7 @> , | |
- OPERATOR 9 ?(hstore,text) , | |
- OPERATOR 10 ?|(hstore,text[]) , | |
- OPERATOR 11 ?&(hstore,text[]) , | |
- --OPERATOR 8 <@ , | |
- OPERATOR 13 @ , | |
- --OPERATOR 14 ~ , | |
- FUNCTION 1 ghstore_consistent (internal, internal, int, oid, internal), | |
- FUNCTION 2 ghstore_union (internal, internal), | |
- FUNCTION 3 ghstore_compress (internal), | |
- FUNCTION 4 ghstore_decompress (internal), | |
- FUNCTION 5 ghstore_penalty (internal, internal, internal), | |
- FUNCTION 6 ghstore_picksplit (internal, internal), | |
- FUNCTION 7 ghstore_same (internal, internal, internal), | |
- STORAGE ghstore; | |
- | |
--- GIN support | |
- | |
-CREATE FUNCTION gin_extract_hstore(internal, internal) | |
-RETURNS internal | |
-AS 'MODULE_PATHNAME' | |
-LANGUAGE C IMMUTABLE STRICT; | |
- | |
-CREATE FUNCTION gin_extract_hstore_query(internal, internal, int2, internal, internal) | |
-RETURNS internal | |
-AS 'MODULE_PATHNAME' | |
-LANGUAGE C IMMUTABLE STRICT; | |
- | |
-CREATE FUNCTION gin_consistent_hstore(internal, int2, internal, int4, internal, internal) | |
-RETURNS bool | |
-AS 'MODULE_PATHNAME' | |
-LANGUAGE C IMMUTABLE STRICT; | |
- | |
-CREATE OPERATOR CLASS gin_hstore_ops | |
-DEFAULT FOR TYPE hstore USING gin | |
-AS | |
- OPERATOR 7 @>, | |
- OPERATOR 9 ?(hstore,text), | |
- OPERATOR 10 ?|(hstore,text[]), | |
- OPERATOR 11 ?&(hstore,text[]), | |
- FUNCTION 1 bttextcmp(text,text), | |
- FUNCTION 2 gin_extract_hstore(internal, internal), | |
- FUNCTION 3 gin_extract_hstore_query(internal, internal, int2, internal, internal), | |
- FUNCTION 4 gin_consistent_hstore(internal, int2, internal, int4, internal, internal), | |
- STORAGE text; | |
diff --git a/contrib/hstore/hstore--1.3.sql b/contrib/hstore/hstore--1.3.sql | |
new file mode 100644 | |
index 0000000..24e0b4b | |
--- /dev/null | |
+++ b/contrib/hstore/hstore--1.3.sql | |
@@ -0,0 +1,838 @@ | |
+/* contrib/hstore/hstore--1.3.sql */ | |
+ | |
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION | |
+\echo Use "CREATE EXTENSION hstore" to load this file. \quit | |
+ | |
+CREATE TYPE hstore; | |
+ | |
+CREATE FUNCTION hstore_in(cstring) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION hstore_out(hstore) | |
+RETURNS cstring | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION hstore_recv(internal) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION hstore_send(hstore) | |
+RETURNS bytea | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE TYPE hstore ( | |
+ INTERNALLENGTH = -1, | |
+ INPUT = hstore_in, | |
+ OUTPUT = hstore_out, | |
+ RECEIVE = hstore_recv, | |
+ SEND = hstore_send, | |
+ STORAGE = extended | |
+); | |
+ | |
+CREATE FUNCTION hstore_version_diag(hstore) | |
+RETURNS integer | |
+AS 'MODULE_PATHNAME','hstore_version_diag' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION fetchval(hstore,text) | |
+RETURNS text | |
+AS 'MODULE_PATHNAME','hstore_fetchval' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR -> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text, | |
+ PROCEDURE = fetchval | |
+); | |
+ | |
+CREATE FUNCTION fetchval_numeric(hstore,text) | |
+RETURNS numeric | |
+AS 'MODULE_PATHNAME','hstore_fetchval_numeric' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR ^> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text, | |
+ PROCEDURE = fetchval_numeric | |
+); | |
+ | |
+CREATE FUNCTION fetchval_boolean(hstore,text) | |
+RETURNS boolean | |
+AS 'MODULE_PATHNAME','hstore_fetchval_boolean' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR ?> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text, | |
+ PROCEDURE = fetchval_boolean | |
+); | |
+ | |
+CREATE FUNCTION fetchval(hstore,int) | |
+RETURNS text | |
+AS 'MODULE_PATHNAME','hstore_fetchval_n' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR -> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = int, | |
+ PROCEDURE = fetchval | |
+); | |
+ | |
+CREATE FUNCTION fetchval_numeric(hstore,int) | |
+RETURNS numeric | |
+AS 'MODULE_PATHNAME','hstore_fetchval_n_numeric' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR ^> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = int, | |
+ PROCEDURE = fetchval_numeric | |
+); | |
+ | |
+CREATE FUNCTION fetchval_boolean(hstore,int) | |
+RETURNS boolean | |
+AS 'MODULE_PATHNAME','hstore_fetchval_n_boolean' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR ?> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = int, | |
+ PROCEDURE = fetchval_boolean | |
+); | |
+ | |
+CREATE FUNCTION fetchval(hstore,text[]) | |
+RETURNS text | |
+AS 'MODULE_PATHNAME','hstore_fetchval_path' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR #> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text[], | |
+ PROCEDURE = fetchval | |
+); | |
+ | |
+CREATE FUNCTION fetchval_numeric(hstore,text[]) | |
+RETURNS numeric | |
+AS 'MODULE_PATHNAME','hstore_fetchval_path_numeric' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR #^> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text[], | |
+ PROCEDURE = fetchval_numeric | |
+); | |
+ | |
+CREATE FUNCTION fetchval_boolean(hstore,text[]) | |
+RETURNS boolean | |
+AS 'MODULE_PATHNAME','hstore_fetchval_path_boolean' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR #?> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text[], | |
+ PROCEDURE = fetchval_boolean | |
+); | |
+ | |
+CREATE FUNCTION fetchval_hstore(hstore,text) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_fetchval_hstore' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR %> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text, | |
+ PROCEDURE = fetchval_hstore | |
+); | |
+ | |
+CREATE FUNCTION fetchval_hstore(hstore,int) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_fetchval_n_hstore' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR %> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = int, | |
+ PROCEDURE = fetchval_hstore | |
+); | |
+ | |
+CREATE FUNCTION fetchval_hstore(hstore,text[]) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_fetchval_path_hstore' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR #%> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text[], | |
+ PROCEDURE = fetchval_hstore | |
+); | |
+ | |
+CREATE FUNCTION slice_array(hstore,text[]) | |
+RETURNS text[] | |
+AS 'MODULE_PATHNAME','hstore_slice_to_array' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR -> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text[], | |
+ PROCEDURE = slice_array | |
+); | |
+ | |
+CREATE FUNCTION slice(hstore,text[]) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_slice_to_hstore' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION isexists(hstore,text) | |
+RETURNS bool | |
+AS 'MODULE_PATHNAME','hstore_exists' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION exist(hstore,text) | |
+RETURNS bool | |
+AS 'MODULE_PATHNAME','hstore_exists' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR ? ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text, | |
+ PROCEDURE = exist, | |
+ RESTRICT = contsel, | |
+ JOIN = contjoinsel | |
+); | |
+ | |
+CREATE FUNCTION isexists(hstore,int) | |
+RETURNS bool | |
+AS 'MODULE_PATHNAME','hstore_exists_idx' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION exist(hstore,int) | |
+RETURNS bool | |
+AS 'MODULE_PATHNAME','hstore_exists_idx' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR ? ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = int, | |
+ PROCEDURE = exist, | |
+ RESTRICT = contsel, | |
+ JOIN = contjoinsel | |
+); | |
+ | |
+CREATE FUNCTION isexists(hstore,text[]) | |
+RETURNS bool | |
+AS 'MODULE_PATHNAME','hstore_exists_path' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION exist(hstore,text[]) | |
+RETURNS bool | |
+AS 'MODULE_PATHNAME','hstore_exists_path' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR #? ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text[], | |
+ PROCEDURE = exist, | |
+ RESTRICT = contsel, | |
+ JOIN = contjoinsel | |
+); | |
+ | |
+CREATE FUNCTION exists_any(hstore,text[]) | |
+RETURNS bool | |
+AS 'MODULE_PATHNAME','hstore_exists_any' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR ?| ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text[], | |
+ PROCEDURE = exists_any, | |
+ RESTRICT = contsel, | |
+ JOIN = contjoinsel | |
+); | |
+ | |
+CREATE FUNCTION exists_all(hstore,text[]) | |
+RETURNS bool | |
+AS 'MODULE_PATHNAME','hstore_exists_all' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR ?& ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text[], | |
+ PROCEDURE = exists_all, | |
+ RESTRICT = contsel, | |
+ JOIN = contjoinsel | |
+); | |
+ | |
+CREATE FUNCTION isdefined(hstore,text) | |
+RETURNS bool | |
+AS 'MODULE_PATHNAME','hstore_defined' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION defined(hstore,text) | |
+RETURNS bool | |
+AS 'MODULE_PATHNAME','hstore_defined' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION delete(hstore,text) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_delete' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION delete(hstore,int) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_delete_idx' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION delete(hstore,text[]) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_delete_array' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION delete(hstore,hstore) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_delete_hstore' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION delete_path(hstore,text[]) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_delete_path' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR - ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text, | |
+ PROCEDURE = delete | |
+); | |
+ | |
+CREATE OPERATOR - ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = int, | |
+ PROCEDURE = delete | |
+); | |
+ | |
+CREATE OPERATOR - ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text[], | |
+ PROCEDURE = delete | |
+); | |
+ | |
+CREATE OPERATOR - ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = hstore, | |
+ PROCEDURE = delete | |
+); | |
+ | |
+CREATE OPERATOR #- ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = text[], | |
+ PROCEDURE = delete_path | |
+); | |
+ | |
+CREATE FUNCTION replace(hstore,text[],hstore) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_replace' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION hs_concat(hstore,hstore) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_concat' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR || ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = hstore, | |
+ PROCEDURE = hs_concat | |
+); | |
+ | |
+CREATE FUNCTION concat_path(hstore,text[],hstore) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_deep_concat' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION hs_contains(hstore,hstore) | |
+RETURNS bool | |
+AS 'MODULE_PATHNAME','hstore_contains' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION hs_contained(hstore,hstore) | |
+RETURNS bool | |
+AS 'MODULE_PATHNAME','hstore_contained' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR @> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = hstore, | |
+ PROCEDURE = hs_contains, | |
+ COMMUTATOR = '<@', | |
+ RESTRICT = contsel, | |
+ JOIN = contjoinsel | |
+); | |
+ | |
+CREATE OPERATOR <@ ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = hstore, | |
+ PROCEDURE = hs_contained, | |
+ COMMUTATOR = '@>', | |
+ RESTRICT = contsel, | |
+ JOIN = contjoinsel | |
+); | |
+ | |
+-- obsolete: | |
+CREATE OPERATOR @ ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = hstore, | |
+ PROCEDURE = hs_contains, | |
+ COMMUTATOR = '~', | |
+ RESTRICT = contsel, | |
+ JOIN = contjoinsel | |
+); | |
+ | |
+CREATE OPERATOR ~ ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = hstore, | |
+ PROCEDURE = hs_contained, | |
+ COMMUTATOR = '@', | |
+ RESTRICT = contsel, | |
+ JOIN = contjoinsel | |
+); | |
+ | |
+CREATE FUNCTION tconvert(text,text) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_from_text' | |
+LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL) | |
+ | |
+CREATE FUNCTION hstore(text,text) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_from_text' | |
+LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL) | |
+ | |
+CREATE FUNCTION hstore(text,bool) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_from_bool' | |
+LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL) | |
+ | |
+CREATE FUNCTION hstore(text,numeric) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_from_numeric' | |
+LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL) | |
+ | |
+CREATE FUNCTION hstore(text,hstore) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_from_th' | |
+LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL) | |
+ | |
+CREATE FUNCTION hstore(text) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_scalar_from_text' | |
+LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL) | |
+ | |
+CREATE FUNCTION hstore(bool) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_scalar_from_bool' | |
+LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL) | |
+ | |
+CREATE FUNCTION hstore(numeric) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','hstore_scalar_from_numeric' | |
+LANGUAGE C IMMUTABLE; -- not STRICT; needs to allow (key,NULL) | |
+ | |
+CREATE FUNCTION hstore(text[],text[]) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME', 'hstore_from_arrays' | |
+LANGUAGE C IMMUTABLE; -- not STRICT; allows (keys,null) | |
+ | |
+CREATE FUNCTION hstore(text[]) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME', 'hstore_from_array' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE CAST (text[] AS hstore) | |
+ WITH FUNCTION hstore(text[]); | |
+ | |
+CREATE FUNCTION hstore_to_json(hstore) | |
+RETURNS json | |
+AS 'MODULE_PATHNAME', 'hstore_to_json' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE CAST (hstore AS json) | |
+ WITH FUNCTION hstore_to_json(hstore); | |
+ | |
+CREATE FUNCTION hstore_to_json_loose(hstore) | |
+RETURNS json | |
+AS 'MODULE_PATHNAME', 'hstore_to_json_loose' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE FUNCTION hstore(record) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME', 'hstore_from_record' | |
+LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::recordtype) | |
+ | |
+CREATE FUNCTION hstore_to_array(hstore) | |
+RETURNS text[] | |
+AS 'MODULE_PATHNAME','hstore_to_array' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR %% ( | |
+ RIGHTARG = hstore, | |
+ PROCEDURE = hstore_to_array | |
+); | |
+ | |
+CREATE FUNCTION hstore_to_matrix(hstore) | |
+RETURNS text[] | |
+AS 'MODULE_PATHNAME','hstore_to_matrix' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR %# ( | |
+ RIGHTARG = hstore, | |
+ PROCEDURE = hstore_to_matrix | |
+); | |
+ | |
+CREATE FUNCTION akeys(hstore) | |
+RETURNS text[] | |
+AS 'MODULE_PATHNAME','hstore_akeys' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION avals(hstore) | |
+RETURNS text[] | |
+AS 'MODULE_PATHNAME','hstore_avals' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION skeys(hstore) | |
+RETURNS setof text | |
+AS 'MODULE_PATHNAME','hstore_skeys' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION svals(hstore) | |
+RETURNS setof text | |
+AS 'MODULE_PATHNAME','hstore_svals' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION svals(hstore, text[]) | |
+RETURNS setof text | |
+AS 'MODULE_PATHNAME','hstore_svals_path' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION hvals(hstore) | |
+RETURNS setof hstore | |
+AS 'MODULE_PATHNAME','hstore_hvals' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION hvals(hstore, text[]) | |
+RETURNS setof hstore | |
+AS 'MODULE_PATHNAME','hstore_hvals_path' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION each(IN hs hstore, | |
+ OUT key text, | |
+ OUT value text) | |
+RETURNS SETOF record | |
+AS 'MODULE_PATHNAME','hstore_each' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION each_hstore(IN hs hstore, | |
+ OUT key text, | |
+ OUT value hstore) | |
+RETURNS SETOF record | |
+AS 'MODULE_PATHNAME','hstore_each_hstore' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION hstore_typeof(hstore) | |
+RETURNS text | |
+AS 'MODULE_PATHNAME','hstore_typeof' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION populate_record(anyelement,hstore) | |
+RETURNS anyelement | |
+AS 'MODULE_PATHNAME', 'hstore_populate_record' | |
+LANGUAGE C IMMUTABLE; -- not STRICT; allows (null::rectype,hstore) | |
+ | |
+CREATE OPERATOR #= ( | |
+ LEFTARG = anyelement, | |
+ RIGHTARG = hstore, | |
+ PROCEDURE = populate_record | |
+); | |
+ | |
+CREATE FUNCTION json_to_hstore(json) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','json_to_hstore' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE CAST (json AS hstore) | |
+WITH FUNCTION json_to_hstore(json); | |
+ | |
+CREATE FUNCTION array_to_hstore(anyarray) | |
+RETURNS hstore | |
+AS 'MODULE_PATHNAME','array_to_hstore' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+-- btree support | |
+ | |
+CREATE FUNCTION hstore_eq(hstore,hstore) | |
+RETURNS boolean | |
+AS 'MODULE_PATHNAME','hstore_eq' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION hstore_ne(hstore,hstore) | |
+RETURNS boolean | |
+AS 'MODULE_PATHNAME','hstore_ne' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION hstore_gt(hstore,hstore) | |
+RETURNS boolean | |
+AS 'MODULE_PATHNAME','hstore_gt' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION hstore_ge(hstore,hstore) | |
+RETURNS boolean | |
+AS 'MODULE_PATHNAME','hstore_ge' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION hstore_lt(hstore,hstore) | |
+RETURNS boolean | |
+AS 'MODULE_PATHNAME','hstore_lt' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION hstore_le(hstore,hstore) | |
+RETURNS boolean | |
+AS 'MODULE_PATHNAME','hstore_le' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION hstore_cmp(hstore,hstore) | |
+RETURNS integer | |
+AS 'MODULE_PATHNAME','hstore_cmp' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR = ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = hstore, | |
+ PROCEDURE = hstore_eq, | |
+ COMMUTATOR = =, | |
+ NEGATOR = <>, | |
+ RESTRICT = eqsel, | |
+ JOIN = eqjoinsel, | |
+ MERGES, | |
+ HASHES | |
+); | |
+CREATE OPERATOR <> ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = hstore, | |
+ PROCEDURE = hstore_ne, | |
+ COMMUTATOR = <>, | |
+ NEGATOR = =, | |
+ RESTRICT = neqsel, | |
+ JOIN = neqjoinsel | |
+); | |
+ | |
+-- the comparison operators have funky names (and are undocumented) | |
+-- in an attempt to discourage anyone from actually using them. they | |
+-- only exist to support the btree opclass | |
+ | |
+CREATE OPERATOR #<# ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = hstore, | |
+ PROCEDURE = hstore_lt, | |
+ COMMUTATOR = #>#, | |
+ NEGATOR = #>=#, | |
+ RESTRICT = scalarltsel, | |
+ JOIN = scalarltjoinsel | |
+); | |
+CREATE OPERATOR #<=# ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = hstore, | |
+ PROCEDURE = hstore_le, | |
+ COMMUTATOR = #>=#, | |
+ NEGATOR = #>#, | |
+ RESTRICT = scalarltsel, | |
+ JOIN = scalarltjoinsel | |
+); | |
+CREATE OPERATOR #># ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = hstore, | |
+ PROCEDURE = hstore_gt, | |
+ COMMUTATOR = #<#, | |
+ NEGATOR = #<=#, | |
+ RESTRICT = scalargtsel, | |
+ JOIN = scalargtjoinsel | |
+); | |
+CREATE OPERATOR #>=# ( | |
+ LEFTARG = hstore, | |
+ RIGHTARG = hstore, | |
+ PROCEDURE = hstore_ge, | |
+ COMMUTATOR = #<=#, | |
+ NEGATOR = #<#, | |
+ RESTRICT = scalargtsel, | |
+ JOIN = scalargtjoinsel | |
+); | |
+ | |
+CREATE OPERATOR CLASS btree_hstore_ops | |
+DEFAULT FOR TYPE hstore USING btree | |
+AS | |
+ OPERATOR 1 #<# , | |
+ OPERATOR 2 #<=# , | |
+ OPERATOR 3 = , | |
+ OPERATOR 4 #>=# , | |
+ OPERATOR 5 #># , | |
+ FUNCTION 1 hstore_cmp(hstore,hstore); | |
+ | |
+-- hash support | |
+ | |
+CREATE FUNCTION hstore_hash(hstore) | |
+RETURNS integer | |
+AS 'MODULE_PATHNAME','hstore_hash' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE OPERATOR CLASS hash_hstore_ops | |
+DEFAULT FOR TYPE hstore USING hash | |
+AS | |
+ OPERATOR 1 = , | |
+ FUNCTION 1 hstore_hash(hstore); | |
+ | |
+-- GiST support | |
+ | |
+CREATE TYPE ghstore; | |
+ | |
+CREATE FUNCTION ghstore_in(cstring) | |
+RETURNS ghstore | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE FUNCTION ghstore_out(ghstore) | |
+RETURNS cstring | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C STRICT IMMUTABLE; | |
+ | |
+CREATE TYPE ghstore ( | |
+ INTERNALLENGTH = -1, | |
+ INPUT = ghstore_in, | |
+ OUTPUT = ghstore_out | |
+); | |
+ | |
+CREATE FUNCTION ghstore_compress(internal) | |
+RETURNS internal | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE FUNCTION ghstore_decompress(internal) | |
+RETURNS internal | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE FUNCTION ghstore_penalty(internal,internal,internal) | |
+RETURNS internal | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE FUNCTION ghstore_picksplit(internal, internal) | |
+RETURNS internal | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE FUNCTION ghstore_union(internal, internal) | |
+RETURNS internal | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE FUNCTION ghstore_same(internal, internal, internal) | |
+RETURNS internal | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE FUNCTION ghstore_consistent(internal,internal,int,oid,internal) | |
+RETURNS bool | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE OPERATOR CLASS gist_hstore_ops | |
+DEFAULT FOR TYPE hstore USING gist | |
+AS | |
+ OPERATOR 7 @> , | |
+ OPERATOR 9 ?(hstore,text) , | |
+ OPERATOR 10 ?|(hstore,text[]) , | |
+ OPERATOR 11 ?&(hstore,text[]) , | |
+ --OPERATOR 8 <@ , | |
+ OPERATOR 13 @ , | |
+ --OPERATOR 14 ~ , | |
+ FUNCTION 1 ghstore_consistent (internal, internal, int, oid, internal), | |
+ FUNCTION 2 ghstore_union (internal, internal), | |
+ FUNCTION 3 ghstore_compress (internal), | |
+ FUNCTION 4 ghstore_decompress (internal), | |
+ FUNCTION 5 ghstore_penalty (internal, internal, internal), | |
+ FUNCTION 6 ghstore_picksplit (internal, internal), | |
+ FUNCTION 7 ghstore_same (internal, internal, internal), | |
+ STORAGE ghstore; | |
+ | |
+-- GIN support: default opclass | |
+ | |
+CREATE FUNCTION gin_extract_hstore(internal, internal) | |
+RETURNS internal | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE FUNCTION gin_extract_hstore_query(internal, internal, int2, internal, internal) | |
+RETURNS internal | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE FUNCTION gin_consistent_hstore(internal, int2, internal, int4, internal, internal) | |
+RETURNS bool | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE OPERATOR CLASS gin_hstore_ops | |
+DEFAULT FOR TYPE hstore USING gin | |
+AS | |
+ OPERATOR 7 @>, | |
+ OPERATOR 9 ?(hstore,text), | |
+ OPERATOR 10 ?|(hstore,text[]), | |
+ OPERATOR 11 ?&(hstore,text[]), | |
+ FUNCTION 1 bttextcmp(text,text), | |
+ FUNCTION 2 gin_extract_hstore(internal, internal), | |
+ FUNCTION 3 gin_extract_hstore_query(internal, internal, int2, internal, internal), | |
+ FUNCTION 4 gin_consistent_hstore(internal, int2, internal, int4, internal, internal), | |
+ STORAGE text; | |
+ | |
+-- GIN support: hash based opclass | |
+ | |
+CREATE FUNCTION gin_extract_hstore_hash(internal, internal) | |
+RETURNS internal | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE FUNCTION gin_extract_hstore_hash_query(internal, internal, int2, internal, internal) | |
+RETURNS internal | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE FUNCTION gin_consistent_hstore_hash(internal, int2, internal, int4, internal, internal) | |
+RETURNS bool | |
+AS 'MODULE_PATHNAME' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+CREATE OPERATOR CLASS gin_hstore_hash_ops | |
+FOR TYPE hstore USING gin | |
+AS | |
+ OPERATOR 7 @>, | |
+ FUNCTION 1 btint4cmp(int4,int4), | |
+ FUNCTION 2 gin_extract_hstore_hash(internal, internal), | |
+ FUNCTION 3 gin_extract_hstore_hash_query(internal, internal, int2, internal, internal), | |
+ FUNCTION 4 gin_consistent_hstore_hash(internal, int2, internal, int4, internal, internal), | |
+ STORAGE int4; | |
+ | |
+-- output | |
+ | |
+CREATE FUNCTION hstore_print(hstore, | |
+ pretty_print bool DEFAULT false, | |
+ array_curly_braces bool DEFAULT false, | |
+ root_hash_decorated bool DEFAULT false, | |
+ json bool DEFAULT false, | |
+ loose bool DEFAULT false) | |
+RETURNS text | |
+AS 'MODULE_PATHNAME', 'hstore_print' | |
+LANGUAGE C IMMUTABLE STRICT; | |
+ | |
+ | |
+ | |
diff --git a/contrib/hstore/hstore.control b/contrib/hstore/hstore.control | |
index 9daf5e2..dcc3b68 100644 | |
--- a/contrib/hstore/hstore.control | |
+++ b/contrib/hstore/hstore.control | |
@@ -1,5 +1,5 @@ | |
# hstore extension | |
comment = 'data type for storing sets of (key, value) pairs' | |
-default_version = '1.2' | |
+default_version = '1.3' | |
module_pathname = '$libdir/hstore' | |
relocatable = true | |
diff --git a/contrib/hstore/hstore.h b/contrib/hstore/hstore.h | |
index 23c8a6f..e18818f 100644 | |
--- a/contrib/hstore/hstore.h | |
+++ b/contrib/hstore/hstore.h | |
@@ -5,8 +5,9 @@ | |
#define __HSTORE_H__ | |
#include "fmgr.h" | |
+#include "lib/stringinfo.h" | |
#include "utils/array.h" | |
- | |
+#include "utils/numeric.h" | |
/* | |
* HEntry: there is one of these for each key _and_ value in an hstore | |
@@ -20,13 +21,33 @@ typedef struct | |
uint32 entry; | |
} HEntry; | |
-#define HENTRY_ISFIRST 0x80000000 | |
-#define HENTRY_ISNULL 0x40000000 | |
-#define HENTRY_POSMASK 0x3FFFFFFF | |
+#define HENTRY_ISFIRST 0x80000000 | |
+#define HENTRY_ISSTRING (0x00000000) /* keep binary compatibility */ | |
+#define HENTRY_ISNUMERIC (0x10000000) | |
+#define HENTRY_ISNEST (0x20000000) | |
+#define HENTRY_ISNULL (0x40000000) /* keep binary compatibility */ | |
+#define HENTRY_ISBOOL (0x10000000 | 0x20000000) | |
+#define HENTRY_ISFALSE HENTRY_ISBOOL | |
+#define HENTRY_ISTRUE (0x10000000 | 0x20000000 | 0x40000000) | |
+ | |
+/* HENTRY_ISHASH, HENTRY_ISARRAY and HENTRY_ISCALAR is only used in send/recv */ | |
+#define HENTRY_ISHASH (0x20000000) | |
+#define HENTRY_ISARRAY (0x20000000 | 0x40000000) | |
+#define HENTRY_ISCALAR (0x10000000 | 0x40000000) | |
+ | |
+#define HENTRY_POSMASK 0x0FFFFFFF | |
+#define HENTRY_TYPEMASK (~(HENTRY_POSMASK | HENTRY_ISFIRST)) | |
/* note possible multiple evaluations, also access to prior array element */ | |
-#define HSE_ISFIRST(he_) (((he_).entry & HENTRY_ISFIRST) != 0) | |
-#define HSE_ISNULL(he_) (((he_).entry & HENTRY_ISNULL) != 0) | |
+#define HSE_ISFIRST(he_) (((he_).entry & HENTRY_ISFIRST) != 0) | |
+#define HSE_ISSTRING(he_) (((he_).entry & HENTRY_TYPEMASK) == HENTRY_ISSTRING) | |
+#define HSE_ISNUMERIC(he_) (((he_).entry & HENTRY_TYPEMASK) == HENTRY_ISNUMERIC) | |
+#define HSE_ISNEST(he_) (((he_).entry & HENTRY_TYPEMASK) == HENTRY_ISNEST) | |
+#define HSE_ISNULL(he_) (((he_).entry & HENTRY_TYPEMASK) == HENTRY_ISNULL) | |
+#define HSE_ISBOOL(he_) (((he_).entry & HENTRY_TYPEMASK & HENTRY_ISBOOL) == HENTRY_ISBOOL) | |
+#define HSE_ISBOOL_TRUE(he_) (((he_).entry & HENTRY_TYPEMASK) == HENTRY_ISTRUE) | |
+#define HSE_ISBOOL_FALSE(he_) (HSE_ISBOOL(he_) && !HSE_ISBOOL_TRUE(he_)) | |
+ | |
#define HSE_ENDPOS(he_) ((he_).entry & HENTRY_POSMASK) | |
#define HSE_OFF(he_) (HSE_ISFIRST(he_) ? 0 : HSE_ENDPOS((&(he_))[-1])) | |
#define HSE_LEN(he_) (HSE_ISFIRST(he_) \ | |
@@ -34,17 +55,15 @@ typedef struct | |
: HSE_ENDPOS(he_) - HSE_ENDPOS((&(he_))[-1])) | |
/* | |
- * determined by the size of "endpos" (ie HENTRY_POSMASK), though this is a | |
- * bit academic since currently varlenas (and hence both the input and the | |
- * whole hstore) have the same limit | |
+ * determined by the size of "endpos" (ie HENTRY_POSMASK) | |
*/ | |
-#define HSTORE_MAX_KEY_LEN 0x3FFFFFFF | |
-#define HSTORE_MAX_VALUE_LEN 0x3FFFFFFF | |
+#define HSTORE_MAX_KEY_LEN HENTRY_POSMASK | |
+#define HSTORE_MAX_VALUE_LEN HENTRY_POSMASK | |
typedef struct | |
{ | |
int32 vl_len_; /* varlena header (do not touch directly!) */ | |
- uint32 size_; /* flags and number of items in hstore */ | |
+ /* header of hash or array hstore type */ | |
/* array of HEntry follows */ | |
} HStore; | |
@@ -53,120 +72,195 @@ typedef struct | |
* so we reserve the top few bits of the size field. See hstore_compat.c | |
* for one reason why. Some bits are left for future use here. | |
*/ | |
-#define HS_FLAG_NEWVERSION 0x80000000 | |
+#define HS_FLAG_NEWVERSION 0x80000000 | |
+#define HS_FLAG_ARRAY 0x40000000 | |
+#define HS_FLAG_HSTORE 0x20000000 | |
+#define HS_FLAG_SCALAR 0x10000000 | |
-#define HS_COUNT(hsp_) ((hsp_)->size_ & 0x0FFFFFFF) | |
-#define HS_SETCOUNT(hsp_,c_) ((hsp_)->size_ = (c_) | HS_FLAG_NEWVERSION) | |
+#define HS_COUNT_MASK 0x0FFFFFFF | |
+#define HS_ISEMPTY(hsp_) (VARSIZE(hsp_) <= VARHDRSZ) | |
+#define HS_ROOT_COUNT(hsp_) (HS_ISEMPTY(hsp_) ? 0 : ( *(uint32*)VARDATA(hsp_) & HS_COUNT_MASK)) | |
+#define HS_ROOT_IS_HASH(hsp_) (HS_ISEMPTY(hsp_) ? 0 : ( *(uint32*)VARDATA(hsp_) & HS_FLAG_HSTORE)) | |
+#define HS_ROOT_IS_ARRAY(hsp_) (HS_ISEMPTY(hsp_) ? 0 : ( *(uint32*)VARDATA(hsp_) & HS_FLAG_ARRAY)) | |
+#define HS_ROOT_IS_SCALAR(hsp_) (HS_ISEMPTY(hsp_) ? 0 : ( *(uint32*)VARDATA(hsp_) & HS_FLAG_SCALAR)) | |
-#define HSHRDSIZE (sizeof(HStore)) | |
-#define CALCDATASIZE(x, lenstr) ( (x) * 2 * sizeof(HEntry) + HSHRDSIZE + (lenstr) ) | |
+/* DatumGetHStoreP includes support for reading old-format hstore values */ | |
+extern HStore *hstoreUpgrade(Datum orig); | |
-/* note multiple evaluations of x */ | |
-#define ARRPTR(x) ( (HEntry*) ( (HStore*)(x) + 1 ) ) | |
-#define STRPTR(x) ( (char*)(ARRPTR(x) + HS_COUNT((HStore*)(x)) * 2) ) | |
+#define DatumGetHStoreP(d) hstoreUpgrade(d) | |
-/* note multiple/non evaluations */ | |
-#define HS_KEY(arr_,str_,i_) ((str_) + HSE_OFF((arr_)[2*(i_)])) | |
-#define HS_VAL(arr_,str_,i_) ((str_) + HSE_OFF((arr_)[2*(i_)+1])) | |
-#define HS_KEYLEN(arr_,i_) (HSE_LEN((arr_)[2*(i_)])) | |
-#define HS_VALLEN(arr_,i_) (HSE_LEN((arr_)[2*(i_)+1])) | |
-#define HS_VALISNULL(arr_,i_) (HSE_ISNULL((arr_)[2*(i_)+1])) | |
+#define PG_GETARG_HS(x) DatumGetHStoreP(PG_GETARG_DATUM(x)) | |
-/* | |
- * currently, these following macros are the _only_ places that rely | |
- * on internal knowledge of HEntry. Everything else should be using | |
- * the above macros. Exception: the in-place upgrade in hstore_compat.c | |
- * messes with entries directly. | |
- */ | |
+typedef struct HStorePair HStorePair; | |
+typedef struct HStoreValue HStoreValue; | |
-/* | |
- * copy one key/value pair (which must be contiguous starting at | |
- * sptr_) into an under-construction hstore; dent_ is an HEntry*, | |
- * dbuf_ is the destination's string buffer, dptr_ is the current | |
- * position in the destination. lots of modification and multiple | |
- * evaluation here. | |
- */ | |
-#define HS_COPYITEM(dent_,dbuf_,dptr_,sptr_,klen_,vlen_,vnull_) \ | |
- do { \ | |
- memcpy((dptr_), (sptr_), (klen_)+(vlen_)); \ | |
- (dptr_) += (klen_)+(vlen_); \ | |
- (dent_)++->entry = ((dptr_) - (dbuf_) - (vlen_)) & HENTRY_POSMASK; \ | |
- (dent_)++->entry = ((((dptr_) - (dbuf_)) & HENTRY_POSMASK) \ | |
- | ((vnull_) ? HENTRY_ISNULL : 0)); \ | |
- } while(0) | |
+struct HStoreValue { | |
+ enum { | |
+ hsvNull, | |
+ hsvString, | |
+ hsvNumeric, | |
+ hsvBool, | |
+ hsvArray, | |
+ hsvHash, | |
+ hsvBinary /* binary form of hsvArray/hsvHash */ | |
+ } type; | |
-/* | |
- * add one key/item pair, from a Pairs structure, into an | |
- * under-construction hstore | |
- */ | |
-#define HS_ADDITEM(dent_,dbuf_,dptr_,pair_) \ | |
- do { \ | |
- memcpy((dptr_), (pair_).key, (pair_).keylen); \ | |
- (dptr_) += (pair_).keylen; \ | |
- (dent_)++->entry = ((dptr_) - (dbuf_)) & HENTRY_POSMASK; \ | |
- if ((pair_).isnull) \ | |
- (dent_)++->entry = ((((dptr_) - (dbuf_)) & HENTRY_POSMASK) \ | |
- | HENTRY_ISNULL); \ | |
- else \ | |
- { \ | |
- memcpy((dptr_), (pair_).val, (pair_).vallen); \ | |
- (dptr_) += (pair_).vallen; \ | |
- (dent_)++->entry = ((dptr_) - (dbuf_)) & HENTRY_POSMASK; \ | |
- } \ | |
- } while (0) | |
- | |
-/* finalize a newly-constructed hstore */ | |
-#define HS_FINALIZE(hsp_,count_,buf_,ptr_) \ | |
- do { \ | |
- int buflen = (ptr_) - (buf_); \ | |
- if ((count_)) \ | |
- ARRPTR(hsp_)[0].entry |= HENTRY_ISFIRST; \ | |
- if ((count_) != HS_COUNT((hsp_))) \ | |
- { \ | |
- HS_SETCOUNT((hsp_),(count_)); \ | |
- memmove(STRPTR(hsp_), (buf_), buflen); \ | |
- } \ | |
- SET_VARSIZE((hsp_), CALCDATASIZE((count_), buflen)); \ | |
- } while (0) | |
- | |
-/* ensure the varlena size of an existing hstore is correct */ | |
-#define HS_FIXSIZE(hsp_,count_) \ | |
- do { \ | |
- int bl = (count_) ? HSE_ENDPOS(ARRPTR(hsp_)[2*(count_)-1]) : 0; \ | |
- SET_VARSIZE((hsp_), CALCDATASIZE((count_),bl)); \ | |
- } while (0) | |
+ uint32 size; /* estimation size of node (including subnodes) */ | |
-/* DatumGetHStoreP includes support for reading old-format hstore values */ | |
-extern HStore *hstoreUpgrade(Datum orig); | |
+ union { | |
+ Numeric numeric; | |
+ bool boolean; | |
+ struct { | |
+ uint32 len; | |
+ char *val; /* could be not null-terminated */ | |
+ } string; | |
-#define DatumGetHStoreP(d) hstoreUpgrade(d) | |
+ struct { | |
+ int nelems; | |
+ HStoreValue *elems; | |
+ bool scalar; /* scalar actually shares representation with array */ | |
+ } array; | |
-#define PG_GETARG_HS(x) DatumGetHStoreP(PG_GETARG_DATUM(x)) | |
+ struct { | |
+ int npairs; | |
+ HStorePair *pairs; | |
+ } hash; | |
+ struct { | |
+ uint32 len; | |
+ char *data; | |
+ } binary; | |
+ }; | |
+ | |
+}; | |
+ | |
+struct HStorePair { | |
+ HStoreValue key; | |
+ HStoreValue value; | |
+ uint32 order; /* to keep order of pairs with equal key */ | |
+}; | |
+ | |
+ | |
+extern HStoreValue* parseHStore(const char *str, int len, bool json); | |
/* | |
- * Pairs is a "decompressed" representation of one key/value pair. | |
- * The two strings are not necessarily null-terminated. | |
+ * hstore support functios | |
*/ | |
-typedef struct | |
+ | |
+#define WHS_KEY (0x001) | |
+#define WHS_VALUE (0x002) | |
+#define WHS_ELEM (0x004) | |
+#define WHS_BEGIN_ARRAY (0x008) | |
+#define WHS_END_ARRAY (0x010) | |
+#define WHS_BEGIN_HASH (0x020) | |
+#define WHS_END_HASH (0x040) | |
+ | |
+typedef void (*walk_hstore_cb)(void* /*arg*/, HStoreValue* /* value */, | |
+ uint32 /* flags */, uint32 /* level */); | |
+extern void walkUncompressedHStore(HStoreValue *v, walk_hstore_cb cb, void *cb_arg); | |
+ | |
+extern int compareHStoreStringValue(const void *a, const void *b, void *arg); | |
+extern int compareHStorePair(const void *a, const void *b, void *arg); | |
+ | |
+extern int compareHStoreBinaryValue(char *a, char *b); | |
+extern int compareHStoreValue(HStoreValue *a, HStoreValue *b); | |
+ | |
+extern HStoreValue* findUncompressedHStoreValueByValue(char *buffer, uint32 flags, | |
+ uint32 *lowbound, HStoreValue* key); | |
+extern HStoreValue* findUncompressedHStoreValue(char *buffer, uint32 flags, | |
+ uint32 *lowbound, char *key, uint32 keylen); | |
+ | |
+extern HStoreValue* getHStoreValue(char *buffer, uint32 flags, int32 i); | |
+ | |
+extern bool stringIsNumber(char *string, int len); | |
+ | |
+typedef enum HStoreOutputKind { | |
+ JsonOutput = 0x01, | |
+ LooseOutput = 0x02, | |
+ ArrayCurlyBraces = 0x04, | |
+ RootHashDecorated = 0x08, | |
+ PrettyPrint = 0x10 | |
+} HStoreOutputKind; | |
+ | |
+extern char* hstoreToCString(StringInfo out, char *in, | |
+ int len /* just estimation */, | |
+ HStoreOutputKind kind); | |
+text* HStoreValueToText(HStoreValue *v); | |
+ | |
+typedef struct ToHStoreState | |
+{ | |
+ HStoreValue v; | |
+ uint32 size; | |
+ struct ToHStoreState *next; | |
+} ToHStoreState; | |
+ | |
+extern HStoreValue* pushHStoreValue(ToHStoreState **state, int r /* WHS_* */, HStoreValue *v); | |
+ | |
+/* be aware: size effects for n argument */ | |
+#define ORDER_PAIRS(a, n, delaction) \ | |
+ do { \ | |
+ bool hasNonUniq = false; \ | |
+ \ | |
+ if ((n) > 1) \ | |
+ qsort_arg((a), (n), sizeof(HStorePair), compareHStorePair, &hasNonUniq);\ | |
+ \ | |
+ if (hasNonUniq) \ | |
+ { \ | |
+ HStorePair *ptr = (a) + 1, \ | |
+ *res = (a); \ | |
+ \ | |
+ while (ptr - (a) < (n)) \ | |
+ { \ | |
+ if (ptr->key.string.len == res->key.string.len && \ | |
+ memcmp(ptr->key.string.val, res->key.string.val, \ | |
+ ptr->key.string.len) == 0) \ | |
+ { \ | |
+ delaction; \ | |
+ } \ | |
+ else \ | |
+ { \ | |
+ res++; \ | |
+ if (ptr != res) \ | |
+ memcpy(res, ptr, sizeof(*res)); \ | |
+ } \ | |
+ ptr++; \ | |
+ } \ | |
+ \ | |
+ (n) = res + 1 - (a); \ | |
+ } \ | |
+ } while(0) | |
+ | |
+uint32 compressHStore(HStoreValue *v, char *buffer); | |
+ | |
+ | |
+typedef struct HStoreIterator | |
{ | |
- char *key; | |
- char *val; | |
- size_t keylen; | |
- size_t vallen; | |
- bool isnull; /* value is null? */ | |
- bool needfree; /* need to pfree the value? */ | |
-} Pairs; | |
+ uint32 type; | |
+ uint32 nelems; | |
+ HEntry *array; | |
+ bool isScalar; | |
+ char *data; | |
+ char *buffer; /* unparsed buffer */ | |
+ | |
+ int i; | |
-extern int hstoreUniquePairs(Pairs *a, int32 l, int32 *buflen); | |
-extern HStore *hstorePairs(Pairs *pairs, int32 pcount, int32 buflen); | |
+ /* | |
+ * enum members should be freely OR'ed with HS_FLAG_ARRAY/HS_FLAG_HSTORE | |
+ * with possiblity of decoding. See optimization in HStoreIteratorGet() | |
+ */ | |
+ enum { | |
+ hsi_start = 0x00, | |
+ hsi_key = 0x01, | |
+ hsi_value = 0x02, | |
+ hsi_elem = 0x04 | |
+ } state; | |
-extern size_t hstoreCheckKeyLen(size_t len); | |
-extern size_t hstoreCheckValLen(size_t len); | |
+ struct HStoreIterator *next; | |
+} HStoreIterator; | |
-extern int hstoreFindKey(HStore *hs, int *lowbound, char *key, int keylen); | |
-extern Pairs *hstoreArrayToPairs(ArrayType *a, int *npairs); | |
+extern HStoreIterator* HStoreIteratorInit(char *buffer); | |
+extern int /* WHS_* */ HStoreIteratorGet(HStoreIterator **it, HStoreValue *v, bool skipNested); | |
#define HStoreContainsStrategyNumber 7 | |
#define HStoreExistsStrategyNumber 9 | |
@@ -194,4 +288,18 @@ extern Pairs *hstoreArrayToPairs(ArrayType *a, int *npairs); | |
extern int no_such_variable | |
#endif | |
+/* | |
+ * When using a GIN/GiST index for hstore, we choose to index both keys and values. | |
+ * The storage format is "text" values, with K, V, or N prepended to the string | |
+ * to indicate key, value, or null values. (As of 9.1 it might be better to | |
+ * store null values as nulls, but we'll keep it this way for on-disk | |
+ * compatibility.) | |
+ */ | |
+#define ELEMFLAG 'E' | |
+#define KEYFLAG 'K' | |
+#define VALFLAG 'V' | |
+#define NULLFLAG 'N' | |
+ | |
+ | |
+ | |
#endif /* __HSTORE_H__ */ | |
diff --git a/contrib/hstore/hstore_compat.c b/contrib/hstore/hstore_compat.c | |
index 6327a8e..4236fae 100644 | |
--- a/contrib/hstore/hstore_compat.c | |
+++ b/contrib/hstore/hstore_compat.c | |
@@ -105,9 +105,41 @@ typedef struct | |
pos:31; | |
} HOldEntry; | |
-static int hstoreValidNewFormat(HStore *hs); | |
-static int hstoreValidOldFormat(HStore *hs); | |
+/* | |
+ * New Old version (new not-nested version of hstore, v2 version) | |
+ * V2 and v3 (nested) are upward binary compatible. But | |
+ * framework was fully changed. Keep here old definitions (v2) | |
+ */ | |
+ | |
+ | |
+typedef struct | |
+{ | |
+ int32 vl_len_; /* varlena header (do not touch directly!) */ | |
+ uint32 size_; /* flags and number of items in hstore */ | |
+ /* array of HEntry follows */ | |
+} HStoreV2; | |
+ | |
+static int hstoreValidNewFormat(HStoreV2 *hs); | |
+static int hstoreValidOldFormat(HStoreV2 *hs); | |
+ | |
+#define HS_COUNT(hsp_) (HS_ISEMPTY(hsp_) ? 0 : ((hsp_)->size_ & HS_COUNT_MASK)) | |
+#define HS_SETCOUNT(hsp_,c_) ((hsp_)->size_ = (c_) | HS_FLAG_NEWVERSION | ((hsp_)->size_ & ~HS_COUNT_MASK)) | |
+#define HSHRDSIZE (sizeof(HStoreV2)) | |
+#define CALCDATASIZE(x, lenstr) ( (x) * 2 * sizeof(HEntry) + HSHRDSIZE + (lenstr) ) | |
+/* note multiple evaluations of x */ | |
+#define ARRPTR(x) ( (HEntry*) ( (HStoreV2*)(x) + 1 ) ) | |
+#define STRPTR(x) ( (char*)(ARRPTR(x) + HS_ROOT_COUNT((HStoreV2*)(x)) * 2) ) | |
+ | |
+/* note multiple/non evaluations */ | |
+#define HS_KEYLEN(arr_,i_) (HSE_LEN((arr_)[2*(i_)])) | |
+ | |
+/* ensure the varlena size of an existing hstore is correct */ | |
+#define HS_FIXSIZE(hsp_,count_) \ | |
+ do { \ | |
+ int bl = (count_) ? HSE_ENDPOS(ARRPTR(hsp_)[2*(count_)-1]) : 0; \ | |
+ SET_VARSIZE((hsp_), CALCDATASIZE((count_),bl)); \ | |
+ } while (0) | |
/* | |
* Validity test for a new-format hstore. | |
@@ -116,7 +148,7 @@ static int hstoreValidOldFormat(HStore *hs); | |
* 2 = exactly valid | |
*/ | |
static int | |
-hstoreValidNewFormat(HStore *hs) | |
+hstoreValidNewFormat(HStoreV2 *hs) | |
{ | |
int count = HS_COUNT(hs); | |
HEntry *entries = ARRPTR(hs); | |
@@ -168,7 +200,7 @@ hstoreValidNewFormat(HStore *hs) | |
* 2 = exactly valid | |
*/ | |
static int | |
-hstoreValidOldFormat(HStore *hs) | |
+hstoreValidOldFormat(HStoreV2 *hs) | |
{ | |
int count = hs->size_; | |
HOldEntry *entries = (HOldEntry *) ARRPTR(hs); | |
@@ -235,16 +267,26 @@ hstoreValidOldFormat(HStore *hs) | |
HStore * | |
hstoreUpgrade(Datum orig) | |
{ | |
- HStore *hs = (HStore *) PG_DETOAST_DATUM(orig); | |
+ HStoreV2 *hs = (HStoreV2 *) PG_DETOAST_DATUM(orig); | |
int valid_new; | |
int valid_old; | |
bool writable; | |
/* Return immediately if no conversion needed */ | |
- if ((hs->size_ & HS_FLAG_NEWVERSION) || | |
+ if (VARSIZE_ANY(hs) <= VARHDRSZ || | |
+ (hs->size_ & HS_FLAG_NEWVERSION) || | |
hs->size_ == 0 || | |
(VARSIZE(hs) < 32768 && HSE_ISFIRST((ARRPTR(hs)[0])))) | |
- return hs; | |
+ { | |
+ if (VARSIZE_ANY_EXHDR(hs) == sizeof(hs->size_)) | |
+ { | |
+ /* 'new' format but not nested. And empty */ | |
+ hs = palloc(sizeof(VARHDRSZ)); | |
+ SET_VARSIZE(hs, VARHDRSZ); | |
+ } | |
+ | |
+ return (HStore*)hs; | |
+ } | |
valid_new = hstoreValidNewFormat(hs); | |
valid_old = hstoreValidOldFormat(hs); | |
@@ -266,7 +308,7 @@ hstoreUpgrade(Datum orig) | |
HS_SETCOUNT(hs, HS_COUNT(hs)); | |
HS_FIXSIZE(hs, HS_COUNT(hs)); | |
} | |
- return hs; | |
+ return (HStore*)hs; | |
} | |
else | |
{ | |
@@ -323,7 +365,7 @@ hstoreUpgrade(Datum orig) | |
*/ | |
if (!writable) | |
- hs = (HStore *) PG_DETOAST_DATUM_COPY(orig); | |
+ hs = (HStoreV2 *) PG_DETOAST_DATUM_COPY(orig); | |
{ | |
int count = hs->size_; | |
@@ -352,7 +394,7 @@ hstoreUpgrade(Datum orig) | |
HS_FIXSIZE(hs, count); | |
} | |
- return hs; | |
+ return (HStore*)hs; | |
} | |
@@ -361,7 +403,7 @@ Datum hstore_version_diag(PG_FUNCTION_ARGS); | |
Datum | |
hstore_version_diag(PG_FUNCTION_ARGS) | |
{ | |
- HStore *hs = (HStore *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); | |
+ HStoreV2 *hs = (HStoreV2 *) PG_DETOAST_DATUM(PG_GETARG_DATUM(0)); | |
int valid_new = hstoreValidNewFormat(hs); | |
int valid_old = hstoreValidOldFormat(hs); | |
diff --git a/contrib/hstore/hstore_gin.c b/contrib/hstore/hstore_gin.c | |
index 2007801..77cfdd8 100644 | |
--- a/contrib/hstore/hstore_gin.c | |
+++ b/contrib/hstore/hstore_gin.c | |
@@ -6,21 +6,11 @@ | |
#include "access/gin.h" | |
#include "access/skey.h" | |
#include "catalog/pg_type.h" | |
+#include "utils/builtins.h" | |
#include "hstore.h" | |
-/* | |
- * When using a GIN index for hstore, we choose to index both keys and values. | |
- * The storage format is "text" values, with K, V, or N prepended to the string | |
- * to indicate key, value, or null values. (As of 9.1 it might be better to | |
- * store null values as nulls, but we'll keep it this way for on-disk | |
- * compatibility.) | |
- */ | |
-#define KEYFLAG 'K' | |
-#define VALFLAG 'V' | |
-#define NULLFLAG 'N' | |
- | |
PG_FUNCTION_INFO_V1(gin_extract_hstore); | |
Datum gin_extract_hstore(PG_FUNCTION_ARGS); | |
@@ -41,37 +31,82 @@ makeitem(char *str, int len, char flag) | |
return item; | |
} | |
+static text * | |
+makeitemFromValue(HStoreValue *v, char flag) | |
+{ | |
+ text *item; | |
+ char *cstr; | |
+ | |
+ switch(v->type) | |
+ { | |
+ case hsvNull: | |
+ item = makeitem(NULL, 0, NULLFLAG); | |
+ break; | |
+ case hsvBool: | |
+ item = makeitem((v->boolean) ? " t" : " f", 2, flag); | |
+ break; | |
+ case hsvNumeric: | |
+ cstr = DatumGetCString(DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric))); | |
+ item = makeitem(cstr, strlen(cstr), flag); | |
+ break; | |
+ case hsvString: | |
+ item = makeitem(v->string.val, v->string.len, flag); | |
+ break; | |
+ default: | |
+ elog(ERROR, "Wrong hstore type"); | |
+ } | |
+ | |
+ return item; | |
+} | |
+ | |
+ | |
Datum | |
gin_extract_hstore(PG_FUNCTION_ARGS) | |
{ | |
- HStore *hs = PG_GETARG_HS(0); | |
- int32 *nentries = (int32 *) PG_GETARG_POINTER(1); | |
- Datum *entries = NULL; | |
- HEntry *hsent = ARRPTR(hs); | |
- char *ptr = STRPTR(hs); | |
- int count = HS_COUNT(hs); | |
- int i; | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ int32 *nentries = (int32 *) PG_GETARG_POINTER(1); | |
+ Datum *entries = NULL; | |
+ int total = 2 * HS_ROOT_COUNT(hs); | |
+ int i = 0, r; | |
+ HStoreIterator *it; | |
+ HStoreValue v; | |
- *nentries = 2 * count; | |
- if (count) | |
- entries = (Datum *) palloc(sizeof(Datum) * 2 * count); | |
- | |
- for (i = 0; i < count; ++i) | |
+ if (total == 0) | |
{ | |
- text *item; | |
+ *nentries = 0; | |
+ PG_RETURN_POINTER(NULL); | |
+ } | |
- item = makeitem(HS_KEY(hsent, ptr, i), HS_KEYLEN(hsent, i), | |
- KEYFLAG); | |
- entries[2 * i] = PointerGetDatum(item); | |
+ entries = (Datum *) palloc(sizeof(Datum) * total); | |
- if (HS_VALISNULL(hsent, i)) | |
- item = makeitem(NULL, 0, NULLFLAG); | |
- else | |
- item = makeitem(HS_VAL(hsent, ptr, i), HS_VALLEN(hsent, i), | |
- VALFLAG); | |
- entries[2 * i + 1] = PointerGetDatum(item); | |
+ it = HStoreIteratorInit(VARDATA(hs)); | |
+ | |
+ while((r = HStoreIteratorGet(&it, &v, false)) != 0) | |
+ { | |
+ if (i >= total) | |
+ { | |
+ total *= 2; | |
+ entries = (Datum *) repalloc(entries, sizeof(Datum) * total); | |
+ } | |
+ | |
+ switch(r) | |
+ { | |
+ case WHS_KEY: | |
+ entries[i++] = PointerGetDatum(makeitemFromValue(&v, KEYFLAG)); | |
+ break; | |
+ case WHS_VALUE: | |
+ entries[i++] = PointerGetDatum(makeitemFromValue(&v, VALFLAG)); | |
+ break; | |
+ case WHS_ELEM: | |
+ entries[i++] = PointerGetDatum(makeitemFromValue(&v, ELEMFLAG)); | |
+ break; | |
+ default: | |
+ break; | |
+ } | |
} | |
+ *nentries = i; | |
+ | |
PG_RETURN_POINTER(entries); | |
} | |
@@ -211,3 +246,195 @@ gin_consistent_hstore(PG_FUNCTION_ARGS) | |
PG_RETURN_BOOL(res); | |
} | |
+ | |
+PG_FUNCTION_INFO_V1(gin_consistent_hstore_hash); | |
+Datum gin_consistent_hstore_hash(PG_FUNCTION_ARGS); | |
+ | |
+Datum | |
+gin_consistent_hstore_hash(PG_FUNCTION_ARGS) | |
+{ | |
+ bool *check = (bool *) PG_GETARG_POINTER(0); | |
+ StrategyNumber strategy = PG_GETARG_UINT16(1); | |
+ | |
+ /* HStore *query = PG_GETARG_HS(2); */ | |
+ int32 nkeys = PG_GETARG_INT32(3); | |
+ | |
+ /* Pointer *extra_data = (Pointer *) PG_GETARG_POINTER(4); */ | |
+ bool *recheck = (bool *) PG_GETARG_POINTER(5); | |
+ bool res = true; | |
+ int32 i; | |
+ | |
+ if (strategy == HStoreContainsStrategyNumber) | |
+ { | |
+ /* | |
+ * Index doesn't have information about correspondence of keys and | |
+ * values, so we need recheck. However, if not all the keys are | |
+ * present, we can fail at once. | |
+ */ | |
+ *recheck = true; | |
+ for (i = 0; i < nkeys; i++) | |
+ { | |
+ if (!check[i]) | |
+ { | |
+ res = false; | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ else | |
+ elog(ERROR, "unrecognized strategy number: %d", strategy); | |
+ | |
+ PG_RETURN_BOOL(res); | |
+} | |
+ | |
+PG_FUNCTION_INFO_V1(gin_extract_hstore_hash); | |
+Datum gin_extract_hstore_hash(PG_FUNCTION_ARGS); | |
+ | |
+typedef struct PathHashStack | |
+{ | |
+ pg_crc32 hash_state; | |
+ struct PathHashStack *next; | |
+} PathHashStack; | |
+ | |
+#define PATH_SEPARATOR ("\0") | |
+ | |
+static void | |
+hash_value(HStoreValue *v, PathHashStack *stack) | |
+{ | |
+ switch(v->type) | |
+ { | |
+ case hsvNull: | |
+ COMP_CRC32(stack->hash_state, "NULL", 5 /* include trailing \0 */); | |
+ break; | |
+ case hsvBool: | |
+ COMP_CRC32(stack->hash_state, (v->boolean) ? " t" : " f", 2 /* include trailing \0 */); | |
+ break; | |
+ case hsvNumeric: | |
+ COMP_CRC32(stack->hash_state, VARDATA_ANY(v->numeric), VARSIZE_ANY_EXHDR(v->numeric)); | |
+ break; | |
+ case hsvString: | |
+ COMP_CRC32(stack->hash_state, v->string.val, v->string.len); | |
+ break; | |
+ default: | |
+ elog(ERROR, "Shouldn't take hash of array"); | |
+ break; | |
+ } | |
+} | |
+ | |
+Datum | |
+gin_extract_hstore_hash(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ int32 *nentries = (int32 *) PG_GETARG_POINTER(1); | |
+ Datum *entries = NULL; | |
+ int total = 2 * HS_ROOT_COUNT(hs); | |
+ int i = 0, r; | |
+ HStoreIterator *it; | |
+ HStoreValue v; | |
+ PathHashStack tail; | |
+ PathHashStack *stack, *tmp; | |
+ pg_crc32 path_crc32; | |
+ | |
+ if (total == 0) | |
+ { | |
+ *nentries = 0; | |
+ PG_RETURN_POINTER(NULL); | |
+ } | |
+ | |
+ entries = (Datum *) palloc(sizeof(Datum) * total); | |
+ | |
+ it = HStoreIteratorInit(VARDATA(hs)); | |
+ | |
+ tail.next = NULL; | |
+ INIT_CRC32(tail.hash_state); | |
+ stack = &tail; | |
+ | |
+ /* | |
+ * Calculate hashes of all key_1.key_2. ... .key_n.value paths as entries. | |
+ * Order of array elements doesn't matter so array keys are empty in path. | |
+ * For faster calculation of hashes use stack for precalculated hashes | |
+ * of prefixes. | |
+ */ | |
+ while((r = HStoreIteratorGet(&it, &v, false)) != 0) | |
+ { | |
+ if (i >= total) | |
+ { | |
+ total *= 2; | |
+ entries = (Datum *) repalloc(entries, sizeof(Datum) * total); | |
+ } | |
+ | |
+ switch(r) | |
+ { | |
+ case WHS_BEGIN_ARRAY: | |
+ tmp = stack; | |
+ stack = (PathHashStack *)palloc(sizeof(PathHashStack)); | |
+ stack->next = tmp; | |
+ stack->hash_state = tmp->hash_state; | |
+ COMP_CRC32(stack->hash_state, PATH_SEPARATOR, 1); | |
+ break; | |
+ case WHS_BEGIN_HASH: | |
+ /* Preserve stack item for key */ | |
+ tmp = stack; | |
+ stack = (PathHashStack *)palloc(sizeof(PathHashStack)); | |
+ stack->next = tmp; | |
+ break; | |
+ case WHS_KEY: | |
+ /* Calc hash of key and separated into preserved stack item */ | |
+ stack->hash_state = stack->next->hash_state; | |
+ hash_value(&v, stack); | |
+ COMP_CRC32(stack->hash_state, PATH_SEPARATOR, 1); | |
+ break; | |
+ case WHS_VALUE: | |
+ case WHS_ELEM: | |
+ path_crc32 = stack->hash_state; | |
+ hash_value(&v, stack); | |
+ FIN_CRC32(path_crc32); | |
+ entries[i++] = path_crc32; | |
+ break; | |
+ case WHS_END_ARRAY: | |
+ case WHS_END_HASH: | |
+ /* Pop stack item */ | |
+ tmp = stack->next; | |
+ pfree(stack); | |
+ stack = tmp; | |
+ break; | |
+ default: | |
+ break; | |
+ } | |
+ } | |
+ | |
+ *nentries = i; | |
+ | |
+ PG_RETURN_POINTER(entries); | |
+} | |
+ | |
+PG_FUNCTION_INFO_V1(gin_extract_hstore_hash_query); | |
+Datum gin_extract_hstore_hash_query(PG_FUNCTION_ARGS); | |
+ | |
+Datum | |
+gin_extract_hstore_hash_query(PG_FUNCTION_ARGS) | |
+{ | |
+ int32 *nentries = (int32 *) PG_GETARG_POINTER(1); | |
+ StrategyNumber strategy = PG_GETARG_UINT16(2); | |
+ int32 *searchMode = (int32 *) PG_GETARG_POINTER(6); | |
+ Datum *entries; | |
+ | |
+ if (strategy == HStoreContainsStrategyNumber) | |
+ { | |
+ /* Query is an hstore, so just apply gin_extract_hstore... */ | |
+ entries = (Datum *) | |
+ DatumGetPointer(DirectFunctionCall2(gin_extract_hstore_hash, | |
+ PG_GETARG_DATUM(0), | |
+ PointerGetDatum(nentries))); | |
+ /* ... except that "contains {}" requires a full index scan */ | |
+ if (entries == NULL) | |
+ *searchMode = GIN_SEARCH_MODE_ALL; | |
+ } | |
+ else | |
+ { | |
+ elog(ERROR, "unrecognized strategy number: %d", strategy); | |
+ entries = NULL; /* keep compiler quiet */ | |
+ } | |
+ | |
+ PG_RETURN_POINTER(entries); | |
+} | |
diff --git a/contrib/hstore/hstore_gist.c b/contrib/hstore/hstore_gist.c | |
index 9001180..04932ac 100644 | |
--- a/contrib/hstore/hstore_gist.c | |
+++ b/contrib/hstore/hstore_gist.c | |
@@ -6,8 +6,8 @@ | |
#include "access/gist.h" | |
#include "access/skey.h" | |
#include "catalog/pg_type.h" | |
+#include "utils/pg_crc.h" | |
-#include "crc32.h" | |
#include "hstore.h" | |
/* bigint defines */ | |
@@ -105,6 +105,66 @@ Datum ghstore_picksplit(PG_FUNCTION_ARGS); | |
Datum ghstore_union(PG_FUNCTION_ARGS); | |
Datum ghstore_same(PG_FUNCTION_ARGS); | |
+static int | |
+crc32_HStoreValue(HStoreValue *v, uint32 r) | |
+{ | |
+ int crc; | |
+ char flag = '\0'; | |
+ | |
+ INIT_CRC32(crc); | |
+ | |
+ switch(r) | |
+ { | |
+ case WHS_KEY: | |
+ flag = KEYFLAG; | |
+ break; | |
+ case WHS_VALUE: | |
+ flag = VALFLAG; | |
+ break; | |
+ case WHS_ELEM: | |
+ flag = ELEMFLAG; | |
+ break; | |
+ default: | |
+ break; | |
+ } | |
+ | |
+ COMP_CRC32(crc, &flag, 1); | |
+ | |
+ switch(v->type) | |
+ { | |
+ case hsvString: | |
+ COMP_CRC32(crc, v->string.val, v->string.len); | |
+ break; | |
+ case hsvBool: | |
+ flag = (v->boolean) ? 't' : 'f'; | |
+ COMP_CRC32(crc, &flag, 1); | |
+ break; | |
+ case hsvNumeric: | |
+ COMP_CRC32(crc, VARDATA_ANY(v->numeric), VARSIZE_ANY_EXHDR(v->numeric)); | |
+ break; | |
+ default: | |
+ elog(PANIC, "impossible value %d", v->type); | |
+ } | |
+ | |
+ FIN_CRC32(crc); | |
+ return crc; | |
+} | |
+ | |
+static int | |
+crc32_Key(char *buf, int sz) | |
+{ | |
+ int crc; | |
+ char flag = KEYFLAG; | |
+ | |
+ INIT_CRC32(crc); | |
+ | |
+ COMP_CRC32(crc, &flag, 1); | |
+ COMP_CRC32(crc, buf, sz); | |
+ | |
+ FIN_CRC32(crc); | |
+ return crc; | |
+} | |
+ | |
Datum | |
ghstore_compress(PG_FUNCTION_ARGS) | |
{ | |
@@ -113,25 +173,25 @@ ghstore_compress(PG_FUNCTION_ARGS) | |
if (entry->leafkey) | |
{ | |
- GISTTYPE *res = (GISTTYPE *) palloc0(CALCGTSIZE(0)); | |
- HStore *val = DatumGetHStoreP(entry->key); | |
- HEntry *hsent = ARRPTR(val); | |
- char *ptr = STRPTR(val); | |
- int count = HS_COUNT(val); | |
- int i; | |
+ GISTTYPE *res = (GISTTYPE *) palloc0(CALCGTSIZE(0)); | |
+ HStore *val = DatumGetHStoreP(entry->key); | |
SET_VARSIZE(res, CALCGTSIZE(0)); | |
- for (i = 0; i < count; ++i) | |
+ if (!HS_ISEMPTY(val)) | |
{ | |
- int h; | |
+ int r; | |
+ HStoreIterator *it = HStoreIteratorInit(VARDATA(val)); | |
+ HStoreValue v; | |
- h = crc32_sz((char *) HS_KEY(hsent, ptr, i), HS_KEYLEN(hsent, i)); | |
- HASH(GETSIGN(res), h); | |
- if (!HS_VALISNULL(hsent, i)) | |
+ while((r = HStoreIteratorGet(&it, &v, false)) != 0) | |
{ | |
- h = crc32_sz((char *) HS_VAL(hsent, ptr, i), HS_VALLEN(hsent, i)); | |
- HASH(GETSIGN(res), h); | |
+ if ((r == WHS_ELEM || r == WHS_KEY || r == WHS_VALUE) && v.type != hsvNull) | |
+ { | |
+ int h = crc32_HStoreValue(&v, r); | |
+ | |
+ HASH(GETSIGN(res), h); | |
+ } | |
} | |
} | |
@@ -490,7 +550,6 @@ ghstore_picksplit(PG_FUNCTION_ARGS) | |
PG_RETURN_POINTER(v); | |
} | |
- | |
Datum | |
ghstore_consistent(PG_FUNCTION_ARGS) | |
{ | |
@@ -513,82 +572,120 @@ ghstore_consistent(PG_FUNCTION_ARGS) | |
if (strategy == HStoreContainsStrategyNumber || | |
strategy == HStoreOldContainsStrategyNumber) | |
{ | |
- HStore *query = PG_GETARG_HS(1); | |
- HEntry *qe = ARRPTR(query); | |
- char *qv = STRPTR(query); | |
- int count = HS_COUNT(query); | |
+ BITVECP qe; | |
int i; | |
- for (i = 0; res && i < count; ++i) | |
+ qe = fcinfo->flinfo->fn_extra; | |
+ if (qe == NULL) | |
{ | |
- int crc = crc32_sz((char *) HS_KEY(qe, qv, i), HS_KEYLEN(qe, i)); | |
+ HStore *query = PG_GETARG_HS(1); | |
+ | |
+ qe = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(BITVEC)); | |
+ memset(qe, 0, sizeof(BITVEC)); | |
- if (GETBIT(sign, HASHVAL(crc))) | |
+ if (!HS_ISEMPTY(query)) | |
{ | |
- if (!HS_VALISNULL(qe, i)) | |
+ int r; | |
+ HStoreIterator *it = HStoreIteratorInit(VARDATA(query)); | |
+ HStoreValue v; | |
+ | |
+ while((r = HStoreIteratorGet(&it, &v, false)) != 0) | |
{ | |
- crc = crc32_sz((char *) HS_VAL(qe, qv, i), HS_VALLEN(qe, i)); | |
- if (!GETBIT(sign, HASHVAL(crc))) | |
- res = false; | |
+ if ((r == WHS_ELEM || r == WHS_KEY || r == WHS_VALUE) && v.type != hsvNull) | |
+ { | |
+ int crc = crc32_HStoreValue(&v, r); | |
+ | |
+ HASH(qe, crc); | |
+ } | |
} | |
} | |
- else | |
+ | |
+ fcinfo->flinfo->fn_extra = qe; | |
+ } | |
+ | |
+ LOOPBYTE | |
+ { | |
+ if ((sign[i] & qe[i]) != qe[i]) | |
+ { | |
res = false; | |
+ break; | |
+ } | |
} | |
} | |
else if (strategy == HStoreExistsStrategyNumber) | |
{ | |
- text *query = PG_GETARG_TEXT_PP(1); | |
- int crc = crc32_sz(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query)); | |
+ int *qval; | |
- res = (GETBIT(sign, HASHVAL(crc))) ? true : false; | |
- } | |
- else if (strategy == HStoreExistsAllStrategyNumber) | |
- { | |
- ArrayType *query = PG_GETARG_ARRAYTYPE_P(1); | |
- Datum *key_datums; | |
- bool *key_nulls; | |
- int key_count; | |
- int i; | |
- | |
- deconstruct_array(query, | |
- TEXTOID, -1, false, 'i', | |
- &key_datums, &key_nulls, &key_count); | |
- | |
- for (i = 0; res && i < key_count; ++i) | |
+ qval = fcinfo->flinfo->fn_extra; | |
+ if (qval == NULL) | |
{ | |
- int crc; | |
+ text *query = PG_GETARG_TEXT_PP(1); | |
+ int crc = crc32_Key(VARDATA_ANY(query), VARSIZE_ANY_EXHDR(query)); | |
+ | |
+ qval = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(*qval)); | |
+ *qval = HASHVAL(crc); | |
- if (key_nulls[i]) | |
- continue; | |
- crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ); | |
- if (!(GETBIT(sign, HASHVAL(crc)))) | |
- res = FALSE; | |
+ fcinfo->flinfo->fn_extra = qval; | |
} | |
+ | |
+ res = (GETBIT(sign, *qval)) ? true : false; | |
} | |
- else if (strategy == HStoreExistsAnyStrategyNumber) | |
+ else if (strategy == HStoreExistsAllStrategyNumber || strategy == HStoreExistsAnyStrategyNumber) | |
{ | |
- ArrayType *query = PG_GETARG_ARRAYTYPE_P(1); | |
- Datum *key_datums; | |
- bool *key_nulls; | |
- int key_count; | |
- int i; | |
+ BITVECP arrentry; | |
+ int i; | |
+ | |
+ arrentry = fcinfo->flinfo->fn_extra; | |
+ if (arrentry == NULL) | |
+ { | |
+ ArrayType *query = PG_GETARG_ARRAYTYPE_P(1); | |
+ Datum *key_datums; | |
+ bool *key_nulls; | |
+ int key_count; | |
+ | |
+ arrentry = MemoryContextAlloc(fcinfo->flinfo->fn_mcxt, sizeof(BITVEC)); | |
+ memset(arrentry, 0, sizeof(BITVEC)); | |
- deconstruct_array(query, | |
- TEXTOID, -1, false, 'i', | |
- &key_datums, &key_nulls, &key_count); | |
+ deconstruct_array(query, | |
+ TEXTOID, -1, false, 'i', | |
+ &key_datums, &key_nulls, &key_count); | |
- res = FALSE; | |
+ for (i = 0; i < key_count; ++i) | |
+ { | |
+ int crc; | |
+ | |
+ if (key_nulls[i]) | |
+ continue; | |
+ crc = crc32_Key(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ); | |
+ HASH(arrentry, crc); | |
+ } | |
- for (i = 0; !res && i < key_count; ++i) | |
+ fcinfo->flinfo->fn_extra = arrentry; | |
+ } | |
+ | |
+ if (strategy == HStoreExistsAllStrategyNumber) | |
{ | |
- int crc; | |
+ LOOPBYTE | |
+ { | |
+ if ((sign[i] & arrentry[i]) != arrentry[i]) | |
+ { | |
+ res = false; | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ else /* HStoreExistsAnyStrategyNumber */ | |
+ { | |
+ res = false; | |
- if (key_nulls[i]) | |
- continue; | |
- crc = crc32_sz(VARDATA(key_datums[i]), VARSIZE(key_datums[i]) - VARHDRSZ); | |
- if (GETBIT(sign, HASHVAL(crc))) | |
- res = TRUE; | |
+ LOOPBYTE | |
+ { | |
+ if (sign[i] & arrentry[i]) | |
+ { | |
+ res = true; | |
+ break; | |
+ } | |
+ } | |
} | |
} | |
else | |
diff --git a/contrib/hstore/hstore_gram.y b/contrib/hstore/hstore_gram.y | |
new file mode 100644 | |
index 0000000..c6afca2 | |
--- /dev/null | |
+++ b/contrib/hstore/hstore_gram.y | |
@@ -0,0 +1,295 @@ | |
+%{ | |
+#define YYPARSE_PARAM result /* need this to pass a pointer (void *) to yyparse */ | |
+ | |
+#include "postgres.h" | |
+ | |
+#include "fmgr.h" | |
+#include "utils/builtins.h" | |
+#include "hstore.h" | |
+ | |
+/* | |
+ * Bison doesn't allocate anything that needs to live across parser calls, | |
+ * so we can easily have it use palloc instead of malloc. This prevents | |
+ * memory leaks if we error out during parsing. Note this only works with | |
+ * bison >= 2.0. However, in bison 1.875 the default is to use alloca() | |
+ * if possible, so there's not really much problem anyhow, at least if | |
+ * you're building with gcc. | |
+ */ | |
+#define YYMALLOC palloc | |
+#define YYFREE pfree | |
+ | |
+/* Avoid exit() on fatal scanner errors (a bit ugly -- see yy_fatal_error) */ | |
+#undef fprintf | |
+#define fprintf(file, fmt, msg) fprintf_to_ereport(fmt, msg) | |
+ | |
+static void | |
+fprintf_to_ereport(const char *fmt, const char *msg) | |
+{ | |
+ ereport(ERROR, (errmsg_internal("%s", msg))); | |
+} | |
+ | |
+/* struct string is shared between scan and gram */ | |
+typedef struct string { | |
+ char *val; | |
+ int len; | |
+ int total; | |
+} string; | |
+#include <hstore_gram.h> | |
+ | |
+/* flex 2.5.4 doesn't bother with a decl for this */ | |
+int hstore_yylex(YYSTYPE * yylval_param); | |
+int hstore_yyparse(void *result); | |
+void hstore_yyerror(const char *message); | |
+ | |
+static HStoreValue* | |
+makeHStoreValueString(HStoreValue* v, string *s) | |
+{ | |
+ if (v == NULL) | |
+ v = palloc(sizeof(*v)); | |
+ | |
+ if (s == NULL) | |
+ { | |
+ v->type = hsvNull; | |
+ v->size = sizeof(HEntry); | |
+ } | |
+ else if (s->len > HENTRY_POSMASK) | |
+ { | |
+ elog(ERROR, "string is too long"); | |
+ } | |
+ else | |
+ { | |
+ v->type = hsvString; | |
+ v->string.val = s->val; | |
+ v->string.len = s->len; | |
+ v->size = sizeof(HEntry) + s->len; | |
+ | |
+ } | |
+ | |
+ return v; | |
+} | |
+ | |
+static HStoreValue* | |
+makeHStoreValueNumeric(string *s) | |
+{ | |
+ Numeric n = NULL; | |
+ HStoreValue *v; | |
+ | |
+ PG_TRY(); | |
+ { | |
+ n = DatumGetNumeric(DirectFunctionCall3(numeric_in, CStringGetDatum(s->val), 0, -1)); | |
+ } | |
+ PG_CATCH(); | |
+ { | |
+ n = NULL; | |
+ } | |
+ PG_END_TRY(); | |
+ | |
+ if (n != NULL) | |
+ { | |
+ v = palloc(sizeof(*v)); | |
+ v->type = hsvNumeric; | |
+ v->numeric = n; | |
+ v->size = 2*sizeof(HEntry) + VARSIZE_ANY(n); | |
+ } | |
+ else | |
+ { | |
+ v = makeHStoreValueString(NULL, s); | |
+ } | |
+ | |
+ return v; | |
+} | |
+ | |
+static HStoreValue* | |
+makeHStoreValueBool(bool val) { | |
+ HStoreValue *v = palloc(sizeof(*v)); | |
+ | |
+ v->type = hsvBool; | |
+ v->boolean = val; | |
+ v->size = sizeof(HEntry); | |
+ | |
+ return v; | |
+} | |
+ | |
+static HStoreValue* | |
+makeHStoreValueArray(List *list) | |
+{ | |
+ HStoreValue *v = palloc(sizeof(*v)); | |
+ | |
+ v->type = hsvArray; | |
+ v->array.scalar = false; | |
+ v->array.nelems = list_length(list); | |
+ v->size = sizeof(uint32) /* header */ + sizeof(HEntry) /* parent's entry */ + sizeof(HEntry) - 1 /*alignment*/; | |
+ | |
+ if (v->array.nelems > 0) | |
+ { | |
+ ListCell *cell; | |
+ int i = 0; | |
+ | |
+ v->array.elems = palloc(sizeof(HStoreValue) * v->array.nelems); | |
+ | |
+ foreach(cell, list) | |
+ { | |
+ HStoreValue *s = (HStoreValue*)lfirst(cell); | |
+ | |
+ v->size += s->size; | |
+ | |
+ v->array.elems[i++] = *s; | |
+ | |
+ if (v->size > HENTRY_POSMASK) | |
+ elog(ERROR, "array is too long"); | |
+ } | |
+ } | |
+ else | |
+ { | |
+ v->array.elems = NULL; | |
+ } | |
+ | |
+ return v; | |
+} | |
+ | |
+static HStoreValue* | |
+makeHStoreValuePairs(List *list) | |
+{ | |
+ HStoreValue *v = palloc(sizeof(*v)); | |
+ | |
+ v->type = hsvHash; | |
+ v->hash.npairs = list_length(list); | |
+ v->size = sizeof(uint32) /* header */ + sizeof(HEntry) /* parent's entry */ + sizeof(HEntry) - 1 /*alignment*/; | |
+ | |
+ if (v->hash.npairs > 0) | |
+ { | |
+ ListCell *cell; | |
+ int i = 0; | |
+ | |
+ v->hash.pairs = palloc(sizeof(HStorePair) * v->hash.npairs); | |
+ | |
+ foreach(cell, list) | |
+ { | |
+ HStorePair *s = (HStorePair*)lfirst(cell); | |
+ | |
+ v->size += s->key.size + s->value.size; | |
+ v->hash.pairs[i].order = i; | |
+ v->hash.pairs[i++] = *s; | |
+ | |
+ if (v->size > HENTRY_POSMASK) | |
+ elog(ERROR, "hstore is too long"); | |
+ } | |
+ | |
+ ORDER_PAIRS(v->hash.pairs, v->hash.npairs, v->size -= ptr->key.size + ptr->value.size); | |
+ } | |
+ else | |
+ { | |
+ v->hash.pairs = NULL; | |
+ } | |
+ | |
+ return v; | |
+} | |
+ | |
+static HStorePair* | |
+makeHStorePair(string *key, HStoreValue *value) { | |
+ HStorePair *v = palloc(sizeof(*v)); | |
+ | |
+ makeHStoreValueString(&v->key, key); | |
+ v->value = *value; | |
+ | |
+ return v; | |
+} | |
+ | |
+%} | |
+ | |
+/* BISON Declarations */ | |
+%pure-parser | |
+%expect 0 | |
+%name-prefix="hstore_yy" | |
+%error-verbose | |
+ | |
+%union { | |
+ string str; | |
+ Numeric numeric; | |
+ List *elems; /* list of HStoreValue */ | |
+ List *pairs; /* list of HStorePair */ | |
+ | |
+ HStoreValue *hvalue; | |
+ HStorePair *pair; | |
+} | |
+ | |
+%token <str> DELIMITER_P NULL_P STRING_P TRUE_P FALSE_P | |
+ NUMERIC_P | |
+ | |
+%type <hvalue> result hstore value scalar_value | |
+%type <str> key | |
+ | |
+%type <pair> pair | |
+ | |
+%type <elems> value_list | |
+%type <pairs> pair_list | |
+ | |
+/* Grammar follows */ | |
+%% | |
+ | |
+result: | |
+ pair_list { *((HStoreValue**)result) = makeHStoreValuePairs($1); } | |
+ | hstore { | |
+ if ($1->type == hsvNull) | |
+ *((HStoreValue**)result) = NULL; | |
+ else | |
+ *((HStoreValue**)result) = $1; | |
+ } | |
+ | scalar_value { | |
+ *((HStoreValue**)result) = makeHStoreValueArray(lappend(NIL, $1)); | |
+ (*((HStoreValue**)result))->array.scalar = true; | |
+ } | |
+ | /* EMPTY */ { *((HStoreValue**)result) = NULL; } | |
+ ; | |
+ | |
+hstore: | |
+ '{' pair_list '}' { $$ = makeHStoreValuePairs($2); } | |
+ | '{' value_list '}' { $$ = makeHStoreValueArray($2); } | |
+ | '[' value_list ']' { $$ = makeHStoreValueArray($2); } | |
+ | '{' value '}' { $$ = makeHStoreValueArray(lappend(NIL, $2)); } | |
+ | '[' value ']' { $$ = makeHStoreValueArray(lappend(NIL, $2)); } | |
+ | '{' '}' { $$ = makeHStoreValueString(NULL, NULL); } | |
+ | '[' ']' { $$ = makeHStoreValueString(NULL, NULL); } | |
+ ; | |
+ | |
+scalar_value: | |
+ NULL_P { $$ = makeHStoreValueString(NULL, NULL); } | |
+ | STRING_P { $$ = makeHStoreValueString(NULL, &$1); } | |
+ | TRUE_P { $$ = makeHStoreValueBool(true); } | |
+ | FALSE_P { $$ = makeHStoreValueBool(false); } | |
+ | NUMERIC_P { $$ = makeHStoreValueNumeric(&$1); } | |
+ ; | |
+ | |
+value: | |
+ scalar_value { $$ = $1; } | |
+ | hstore { $$ = $1; } | |
+ ; | |
+ | |
+value_list: | |
+ value ',' value { $$ = lappend(lappend(NIL, $1), $3); } | |
+ | value_list ',' value { $$ = lappend($1, $3); } | |
+ ; | |
+ | |
+/* | |
+ * key is always a string, not a bool or numeric | |
+ */ | |
+key: | |
+ STRING_P { $$ = $1; } | |
+ | TRUE_P { $$ = $1; } | |
+ | FALSE_P { $$ = $1; } | |
+ | NUMERIC_P { $$ = $1; } | |
+ | NULL_P { $$ = $1; } | |
+ ; | |
+ | |
+pair: | |
+ key DELIMITER_P value { $$ = makeHStorePair(&$1, $3); } | |
+ ; | |
+ | |
+pair_list: | |
+ pair { $$ = lappend(NIL, $1); } | |
+ | pair_list ',' pair { $$ = lappend($1, $3); } | |
+ ; | |
+ | |
+%% | |
+ | |
+#include "hstore_scan.c" | |
diff --git a/contrib/hstore/hstore_io.c b/contrib/hstore/hstore_io.c | |
index 772a5ca..d3f6857 100644 | |
--- a/contrib/hstore/hstore_io.c | |
+++ b/contrib/hstore/hstore_io.c | |
@@ -7,12 +7,15 @@ | |
#include "access/htup_details.h" | |
#include "catalog/pg_type.h" | |
+#include "catalog/pg_cast.h" | |
#include "funcapi.h" | |
-#include "lib/stringinfo.h" | |
#include "libpq/pqformat.h" | |
#include "utils/builtins.h" | |
+#include "utils/fmgroids.h" | |
#include "utils/json.h" | |
+#include "utils/guc.h" | |
#include "utils/lsyscache.h" | |
+#include "utils/syscache.h" | |
#include "utils/typcache.h" | |
#include "hstore.h" | |
@@ -22,507 +25,432 @@ PG_MODULE_MAGIC; | |
/* old names for C functions */ | |
HSTORE_POLLUTE(hstore_from_text, tconvert); | |
+/* GUC variables */ | |
+static bool pretty_print_var = false; | |
+#define SET_PRETTY_PRINT_VAR(x) ((pretty_print_var) ? ((x) | PrettyPrint) : (x)) | |
-typedef struct | |
+static void recvHStore(StringInfo buf, HStoreValue *v, uint32 level, uint32 header); | |
+ | |
+static size_t | |
+hstoreCheckKeyLen(size_t len) | |
{ | |
- char *begin; | |
- char *ptr; | |
- char *cur; | |
- char *word; | |
- int wordlen; | |
- | |
- Pairs *pairs; | |
- int pcur; | |
- int plen; | |
-} HSParser; | |
- | |
-#define RESIZEPRSBUF \ | |
-do { \ | |
- if ( state->cur - state->word + 1 >= state->wordlen ) \ | |
- { \ | |
- int32 clen = state->cur - state->word; \ | |
- state->wordlen *= 2; \ | |
- state->word = (char*)repalloc( (void*)state->word, state->wordlen ); \ | |
- state->cur = state->word + clen; \ | |
- } \ | |
-} while (0) | |
- | |
- | |
-#define GV_WAITVAL 0 | |
-#define GV_INVAL 1 | |
-#define GV_INESCVAL 2 | |
-#define GV_WAITESCIN 3 | |
-#define GV_WAITESCESCIN 4 | |
+ if (len > HSTORE_MAX_KEY_LEN) | |
+ ereport(ERROR, | |
+ (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), | |
+ errmsg("string too long for hstore key"))); | |
+ return len; | |
+} | |
-static bool | |
-get_val(HSParser *state, bool ignoreeq, bool *escaped) | |
+static size_t | |
+hstoreCheckValLen(size_t len) | |
{ | |
- int st = GV_WAITVAL; | |
+ if (len > HSTORE_MAX_VALUE_LEN) | |
+ ereport(ERROR, | |
+ (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), | |
+ errmsg("string too long for hstore value"))); | |
+ return len; | |
+} | |
+ | |
- state->wordlen = 32; | |
- state->cur = state->word = palloc(state->wordlen); | |
- *escaped = false; | |
+static HStore* | |
+hstoreDump(HStoreValue *p) | |
+{ | |
+ uint32 buflen; | |
+ HStore *out; | |
- while (1) | |
+ if (p == NULL || (p->type == hsvArray && p->array.nelems == 0) || (p->type == hsvHash && p->hash.npairs == 0)) | |
{ | |
- if (st == GV_WAITVAL) | |
- { | |
- if (*(state->ptr) == '"') | |
- { | |
- *escaped = true; | |
- st = GV_INESCVAL; | |
- } | |
- else if (*(state->ptr) == '\0') | |
- { | |
- return false; | |
- } | |
- else if (*(state->ptr) == '=' && !ignoreeq) | |
- { | |
- elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int32) (state->ptr - state->begin)); | |
- } | |
- else if (*(state->ptr) == '\\') | |
- { | |
- st = GV_WAITESCIN; | |
- } | |
- else if (!isspace((unsigned char) *(state->ptr))) | |
- { | |
- *(state->cur) = *(state->ptr); | |
- state->cur++; | |
- st = GV_INVAL; | |
- } | |
- } | |
- else if (st == GV_INVAL) | |
- { | |
- if (*(state->ptr) == '\\') | |
- { | |
- st = GV_WAITESCIN; | |
- } | |
- else if (*(state->ptr) == '=' && !ignoreeq) | |
- { | |
- state->ptr--; | |
- return true; | |
- } | |
- else if (*(state->ptr) == ',' && ignoreeq) | |
- { | |
- state->ptr--; | |
- return true; | |
- } | |
- else if (isspace((unsigned char) *(state->ptr))) | |
- { | |
- return true; | |
- } | |
- else if (*(state->ptr) == '\0') | |
- { | |
- state->ptr--; | |
- return true; | |
- } | |
- else | |
- { | |
- RESIZEPRSBUF; | |
- *(state->cur) = *(state->ptr); | |
- state->cur++; | |
- } | |
- } | |
- else if (st == GV_INESCVAL) | |
- { | |
- if (*(state->ptr) == '\\') | |
- { | |
- st = GV_WAITESCESCIN; | |
- } | |
- else if (*(state->ptr) == '"') | |
- { | |
- return true; | |
- } | |
- else if (*(state->ptr) == '\0') | |
- { | |
- elog(ERROR, "Unexpected end of string"); | |
- } | |
- else | |
- { | |
- RESIZEPRSBUF; | |
- *(state->cur) = *(state->ptr); | |
- state->cur++; | |
- } | |
- } | |
- else if (st == GV_WAITESCIN) | |
- { | |
- if (*(state->ptr) == '\0') | |
- elog(ERROR, "Unexpected end of string"); | |
- RESIZEPRSBUF; | |
- *(state->cur) = *(state->ptr); | |
- state->cur++; | |
- st = GV_INVAL; | |
- } | |
- else if (st == GV_WAITESCESCIN) | |
- { | |
- if (*(state->ptr) == '\0') | |
- elog(ERROR, "Unexpected end of string"); | |
- RESIZEPRSBUF; | |
- *(state->cur) = *(state->ptr); | |
- state->cur++; | |
- st = GV_INESCVAL; | |
- } | |
- else | |
- elog(ERROR, "Unknown state %d at position line %d in file '%s'", st, __LINE__, __FILE__); | |
+ buflen = 0; | |
+ out = palloc(VARHDRSZ); | |
+ } | |
+ else | |
+ { | |
+ buflen = VARHDRSZ + p->size; | |
+ out = palloc(buflen); | |
+ SET_VARSIZE(out, buflen); | |
- state->ptr++; | |
+ buflen = compressHStore(p, VARDATA(out)); | |
} | |
+ SET_VARSIZE(out, buflen + VARHDRSZ); | |
+ | |
+ return out; | |
+} | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_in); | |
+Datum hstore_in(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_in(PG_FUNCTION_ARGS) | |
+{ | |
+ PG_RETURN_POINTER(hstoreDump(parseHStore(PG_GETARG_CSTRING(0), -1, false))); | |
} | |
-#define WKEY 0 | |
-#define WVAL 1 | |
-#define WEQ 2 | |
-#define WGT 3 | |
-#define WDEL 4 | |
+static void | |
+recvHStoreValue(StringInfo buf, HStoreValue *v, uint32 level, int c) | |
+{ | |
+ uint32 hentry = c & HENTRY_TYPEMASK; | |
+ if (c == -1 /* compatibility */ || hentry == HENTRY_ISNULL) | |
+ { | |
+ v->type = hsvNull; | |
+ v->size = sizeof(HEntry); | |
+ } | |
+ else if (hentry == HENTRY_ISHASH || hentry == HENTRY_ISARRAY || hentry == HENTRY_ISCALAR) | |
+ { | |
+ recvHStore(buf, v, level + 1, (uint32)c); | |
+ } | |
+ else if (hentry == HENTRY_ISFALSE || hentry == HENTRY_ISTRUE) | |
+ { | |
+ v->type = hsvBool; | |
+ v->size = sizeof(HEntry); | |
+ v->boolean = (hentry == HENTRY_ISFALSE) ? false : true; | |
+ } | |
+ else if (hentry == HENTRY_ISNUMERIC) | |
+ { | |
+ v->type = hsvNumeric; | |
+ v->numeric = DatumGetNumeric(DirectFunctionCall3(numeric_recv, PointerGetDatum(buf), | |
+ Int32GetDatum(0), Int32GetDatum(-1))); | |
+ v->size = sizeof(HEntry) * 2 + VARSIZE_ANY(v->numeric); | |
+ } | |
+ else if (hentry == HENTRY_ISSTRING) | |
+ { | |
+ v->type = hsvString; | |
+ v->string.val = pq_getmsgtext(buf, c, &c); | |
+ v->string.len = hstoreCheckKeyLen(c); | |
+ v->size = sizeof(HEntry) + v->string.len; | |
+ } | |
+ else | |
+ { | |
+ elog(ERROR, "bogus input"); | |
+ } | |
+} | |
static void | |
-parse_hstore(HSParser *state) | |
+recvHStore(StringInfo buf, HStoreValue *v, uint32 level, uint32 header) | |
{ | |
- int st = WKEY; | |
- bool escaped = false; | |
+ uint32 hentry; | |
+ uint32 i; | |
+ | |
+ hentry = header & HENTRY_TYPEMASK; | |
- state->plen = 16; | |
- state->pairs = (Pairs *) palloc(sizeof(Pairs) * state->plen); | |
- state->pcur = 0; | |
- state->ptr = state->begin; | |
- state->word = NULL; | |
+ if (level == 0 && hentry == 0) | |
+ hentry = HENTRY_ISHASH; /* old version */ | |
- while (1) | |
+ v->size = 3 * sizeof(HEntry); | |
+ if (hentry == HENTRY_ISHASH) | |
{ | |
- if (st == WKEY) | |
- { | |
- if (!get_val(state, false, &escaped)) | |
- return; | |
- if (state->pcur >= state->plen) | |
- { | |
- state->plen *= 2; | |
- state->pairs = (Pairs *) repalloc(state->pairs, sizeof(Pairs) * state->plen); | |
- } | |
- state->pairs[state->pcur].key = state->word; | |
- state->pairs[state->pcur].keylen = hstoreCheckKeyLen(state->cur - state->word); | |
- state->pairs[state->pcur].val = NULL; | |
- state->word = NULL; | |
- st = WEQ; | |
- } | |
- else if (st == WEQ) | |
- { | |
- if (*(state->ptr) == '=') | |
- { | |
- st = WGT; | |
- } | |
- else if (*(state->ptr) == '\0') | |
- { | |
- elog(ERROR, "Unexpected end of string"); | |
- } | |
- else if (!isspace((unsigned char) *(state->ptr))) | |
- { | |
- elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int32) (state->ptr - state->begin)); | |
- } | |
- } | |
- else if (st == WGT) | |
- { | |
- if (*(state->ptr) == '>') | |
- { | |
- st = WVAL; | |
- } | |
- else if (*(state->ptr) == '\0') | |
- { | |
- elog(ERROR, "Unexpected end of string"); | |
- } | |
- else | |
- { | |
- elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int32) (state->ptr - state->begin)); | |
- } | |
- } | |
- else if (st == WVAL) | |
+ v->type = hsvHash; | |
+ v->hash.npairs = header & HS_COUNT_MASK; | |
+ if (v->hash.npairs > 0) | |
{ | |
- if (!get_val(state, true, &escaped)) | |
- elog(ERROR, "Unexpected end of string"); | |
- state->pairs[state->pcur].val = state->word; | |
- state->pairs[state->pcur].vallen = hstoreCheckValLen(state->cur - state->word); | |
- state->pairs[state->pcur].isnull = false; | |
- state->pairs[state->pcur].needfree = true; | |
- if (state->cur - state->word == 4 && !escaped) | |
+ v->hash.pairs = palloc(sizeof(*v->hash.pairs) * v->hash.npairs); | |
+ | |
+ for(i=0; i<v->hash.npairs; i++) | |
{ | |
- state->word[4] = '\0'; | |
- if (0 == pg_strcasecmp(state->word, "null")) | |
- state->pairs[state->pcur].isnull = true; | |
+ recvHStoreValue(buf, &v->hash.pairs[i].key, level, pq_getmsgint(buf, 4)); | |
+ if (v->hash.pairs[i].key.type != hsvString) | |
+ elog(ERROR, "hstore's key could be only a string"); | |
+ | |
+ recvHStoreValue(buf, &v->hash.pairs[i].value, level, pq_getmsgint(buf, 4)); | |
+ | |
+ v->size += v->hash.pairs[i].key.size + v->hash.pairs[i].value.size; | |
} | |
- state->word = NULL; | |
- state->pcur++; | |
- st = WDEL; | |
+ | |
+ ORDER_PAIRS(v->hash.pairs, v->hash.npairs, v->size -= ptr->key.size + ptr->value.size); | |
} | |
- else if (st == WDEL) | |
+ } | |
+ else if (hentry == HENTRY_ISARRAY || hentry == HENTRY_ISCALAR) | |
+ { | |
+ v->type = hsvArray; | |
+ v->array.nelems = header & HS_COUNT_MASK; | |
+ v->array.scalar = (hentry == HENTRY_ISCALAR) ? true : false; | |
+ | |
+ if (v->array.scalar && v->array.nelems != 1) | |
+ elog(ERROR, "bogus input"); | |
+ | |
+ if (v->array.nelems > 0) | |
{ | |
- if (*(state->ptr) == ',') | |
- { | |
- st = WKEY; | |
- } | |
- else if (*(state->ptr) == '\0') | |
- { | |
- return; | |
- } | |
- else if (!isspace((unsigned char) *(state->ptr))) | |
+ v->array.elems = palloc(sizeof(*v->array.elems) * v->array.nelems); | |
+ | |
+ for(i=0; i<v->array.nelems; i++) | |
{ | |
- elog(ERROR, "Syntax error near '%c' at position %d", *(state->ptr), (int32) (state->ptr - state->begin)); | |
+ recvHStoreValue(buf, v->array.elems + i, level, pq_getmsgint(buf, 4)); | |
+ v->size += v->array.elems[i].size; | |
} | |
} | |
- else | |
- elog(ERROR, "Unknown state %d at line %d in file '%s'", st, __LINE__, __FILE__); | |
- | |
- state->ptr++; | |
+ } | |
+ else | |
+ { | |
+ elog(ERROR, "bogus input"); | |
} | |
} | |
-static int | |
-comparePairs(const void *a, const void *b) | |
+PG_FUNCTION_INFO_V1(hstore_recv); | |
+Datum hstore_recv(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_recv(PG_FUNCTION_ARGS) | |
{ | |
- const Pairs *pa = a; | |
- const Pairs *pb = b; | |
- | |
- if (pa->keylen == pb->keylen) | |
- { | |
- int res = memcmp(pa->key, pb->key, pa->keylen); | |
+ StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); | |
+ HStoreValue v; | |
- if (res) | |
- return res; | |
+ recvHStore(buf, &v, 0, pq_getmsgint(buf, 4)); | |
- /* guarantee that needfree will be later */ | |
- if (pb->needfree == pa->needfree) | |
- return 0; | |
- else if (pa->needfree) | |
- return 1; | |
- else | |
- return -1; | |
- } | |
- return (pa->keylen > pb->keylen) ? 1 : -1; | |
+ PG_RETURN_POINTER(hstoreDump(&v)); | |
} | |
-/* | |
- * this code still respects pairs.needfree, even though in general | |
- * it should never be called in a context where anything needs freeing. | |
- * we keep it because (a) those calls are in a rare code path anyway, | |
- * and (b) who knows whether they might be needed by some caller. | |
- */ | |
-int | |
-hstoreUniquePairs(Pairs *a, int32 l, int32 *buflen) | |
+PG_FUNCTION_INFO_V1(hstore_from_text); | |
+Datum hstore_from_text(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_from_text(PG_FUNCTION_ARGS) | |
{ | |
- Pairs *ptr, | |
- *res; | |
+ text *key; | |
+ HStoreValue v; | |
+ HStorePair pair; | |
- *buflen = 0; | |
- if (l < 2) | |
+ if (PG_ARGISNULL(0)) | |
+ PG_RETURN_NULL(); | |
+ | |
+ key = PG_GETARG_TEXT_PP(0); | |
+ pair.key.type = hsvString; | |
+ pair.key.string.val = VARDATA_ANY(key); | |
+ pair.key.string.len = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key)); | |
+ pair.key.size = pair.key.string.len + sizeof(HEntry); | |
+ | |
+ if (PG_ARGISNULL(1)) | |
{ | |
- if (l == 1) | |
- *buflen = a->keylen + ((a->isnull) ? 0 : a->vallen); | |
- return l; | |
+ pair.value.type = hsvNull; | |
+ pair.value.size = sizeof(HEntry); | |
} | |
- | |
- qsort((void *) a, l, sizeof(Pairs), comparePairs); | |
- ptr = a + 1; | |
- res = a; | |
- while (ptr - a < l) | |
+ else | |
{ | |
- if (ptr->keylen == res->keylen && | |
- memcmp(ptr->key, res->key, res->keylen) == 0) | |
- { | |
- if (ptr->needfree) | |
- { | |
- pfree(ptr->key); | |
- pfree(ptr->val); | |
- } | |
- } | |
- else | |
- { | |
- *buflen += res->keylen + ((res->isnull) ? 0 : res->vallen); | |
- res++; | |
- memcpy(res, ptr, sizeof(Pairs)); | |
- } | |
+ text *val = NULL; | |
- ptr++; | |
+ val = PG_GETARG_TEXT_PP(1); | |
+ pair.value.type = hsvString; | |
+ pair.value.string.val = VARDATA_ANY(val); | |
+ pair.value.string.len = hstoreCheckValLen(VARSIZE_ANY_EXHDR(val)); | |
+ pair.value.size = pair.value.string.len + sizeof(HEntry); | |
} | |
- *buflen += res->keylen + ((res->isnull) ? 0 : res->vallen); | |
- return res + 1 - a; | |
-} | |
+ v.type = hsvHash; | |
+ v.size = sizeof(HEntry) + pair.key.size + pair.value.size; | |
+ v.hash.npairs = 1; | |
+ v.hash.pairs = &pair; | |
-size_t | |
-hstoreCheckKeyLen(size_t len) | |
-{ | |
- if (len > HSTORE_MAX_KEY_LEN) | |
- ereport(ERROR, | |
- (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), | |
- errmsg("string too long for hstore key"))); | |
- return len; | |
-} | |
- | |
-size_t | |
-hstoreCheckValLen(size_t len) | |
-{ | |
- if (len > HSTORE_MAX_VALUE_LEN) | |
- ereport(ERROR, | |
- (errcode(ERRCODE_STRING_DATA_RIGHT_TRUNCATION), | |
- errmsg("string too long for hstore value"))); | |
- return len; | |
+ PG_RETURN_POINTER(hstoreDump(&v)); | |
} | |
- | |
-HStore * | |
-hstorePairs(Pairs *pairs, int32 pcount, int32 buflen) | |
+PG_FUNCTION_INFO_V1(hstore_from_bool); | |
+Datum hstore_from_bool(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_from_bool(PG_FUNCTION_ARGS) | |
{ | |
- HStore *out; | |
- HEntry *entry; | |
- char *ptr; | |
- char *buf; | |
- int32 len; | |
- int32 i; | |
+ text *key; | |
+ HStoreValue v; | |
+ HStorePair pair; | |
- len = CALCDATASIZE(pcount, buflen); | |
- out = palloc(len); | |
- SET_VARSIZE(out, len); | |
- HS_SETCOUNT(out, pcount); | |
- | |
- if (pcount == 0) | |
- return out; | |
+ if (PG_ARGISNULL(0)) | |
+ PG_RETURN_NULL(); | |
- entry = ARRPTR(out); | |
- buf = ptr = STRPTR(out); | |
+ key = PG_GETARG_TEXT_PP(0); | |
+ pair.key.type = hsvString; | |
+ pair.key.string.val = VARDATA_ANY(key); | |
+ pair.key.string.len = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key)); | |
+ pair.key.size = pair.key.string.len + sizeof(HEntry); | |
- for (i = 0; i < pcount; i++) | |
- HS_ADDITEM(entry, buf, ptr, pairs[i]); | |
+ if (PG_ARGISNULL(1)) | |
+ { | |
+ pair.value.type = hsvNull; | |
+ pair.value.size = sizeof(HEntry); | |
+ } | |
+ else | |
+ { | |
+ pair.value.type = hsvBool; | |
+ pair.value.boolean = PG_GETARG_BOOL(1); | |
+ pair.value.size = sizeof(HEntry); | |
+ } | |
- HS_FINALIZE(out, pcount, buf, ptr); | |
+ v.type = hsvHash; | |
+ v.size = sizeof(HEntry) + pair.key.size + pair.value.size; | |
+ v.hash.npairs = 1; | |
+ v.hash.pairs = &pair; | |
- return out; | |
+ PG_RETURN_POINTER(hstoreDump(&v)); | |
} | |
- | |
-PG_FUNCTION_INFO_V1(hstore_in); | |
-Datum hstore_in(PG_FUNCTION_ARGS); | |
+PG_FUNCTION_INFO_V1(hstore_from_numeric); | |
+Datum hstore_from_numeric(PG_FUNCTION_ARGS); | |
Datum | |
-hstore_in(PG_FUNCTION_ARGS) | |
+hstore_from_numeric(PG_FUNCTION_ARGS) | |
{ | |
- HSParser state; | |
- int32 buflen; | |
- HStore *out; | |
+ text *key; | |
+ HStoreValue v; | |
+ HStorePair pair; | |
- state.begin = PG_GETARG_CSTRING(0); | |
+ if (PG_ARGISNULL(0)) | |
+ PG_RETURN_NULL(); | |
- parse_hstore(&state); | |
+ key = PG_GETARG_TEXT_PP(0); | |
+ pair.key.type = hsvString; | |
+ pair.key.string.val = VARDATA_ANY(key); | |
+ pair.key.string.len = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key)); | |
+ pair.key.size = pair.key.string.len + sizeof(HEntry); | |
- state.pcur = hstoreUniquePairs(state.pairs, state.pcur, &buflen); | |
+ if (PG_ARGISNULL(1)) | |
+ { | |
+ pair.value.type = hsvNull; | |
+ pair.value.size = sizeof(HEntry); | |
+ } | |
+ else | |
+ { | |
+ pair.value.type = hsvNumeric; | |
+ pair.value.numeric = PG_GETARG_NUMERIC(1); | |
+ pair.value.size = sizeof(HEntry) + sizeof(HEntry) + VARSIZE_ANY(pair.value.numeric); | |
+ } | |
- out = hstorePairs(state.pairs, state.pcur, buflen); | |
+ v.type = hsvHash; | |
+ v.size = sizeof(HEntry) + pair.key.size + pair.value.size; | |
+ v.hash.npairs = 1; | |
+ v.hash.pairs = &pair; | |
- PG_RETURN_POINTER(out); | |
+ PG_RETURN_POINTER(hstoreDump(&v)); | |
} | |
- | |
-PG_FUNCTION_INFO_V1(hstore_recv); | |
-Datum hstore_recv(PG_FUNCTION_ARGS); | |
+PG_FUNCTION_INFO_V1(hstore_from_th); | |
+Datum hstore_from_th(PG_FUNCTION_ARGS); | |
Datum | |
-hstore_recv(PG_FUNCTION_ARGS) | |
+hstore_from_th(PG_FUNCTION_ARGS) | |
{ | |
- int32 buflen; | |
- HStore *out; | |
- Pairs *pairs; | |
- int32 i; | |
- int32 pcount; | |
- StringInfo buf = (StringInfo) PG_GETARG_POINTER(0); | |
+ text *key; | |
+ HStoreValue v; | |
+ HStorePair pair; | |
- pcount = pq_getmsgint(buf, 4); | |
+ if (PG_ARGISNULL(0)) | |
+ PG_RETURN_NULL(); | |
- if (pcount == 0) | |
+ key = PG_GETARG_TEXT_PP(0); | |
+ pair.key.type = hsvString; | |
+ pair.key.string.val = VARDATA_ANY(key); | |
+ pair.key.string.len = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key)); | |
+ pair.key.size = pair.key.string.len + sizeof(HEntry); | |
+ | |
+ if (PG_ARGISNULL(1)) | |
{ | |
- out = hstorePairs(NULL, 0, 0); | |
- PG_RETURN_POINTER(out); | |
+ pair.value.type = hsvNull; | |
+ pair.value.size = sizeof(HEntry); | |
} | |
+ else | |
+ { | |
+ HStore *val = NULL; | |
- pairs = palloc(pcount * sizeof(Pairs)); | |
+ val = PG_GETARG_HS(1); | |
+ pair.value.type = hsvBinary; | |
+ pair.value.binary.data = VARDATA_ANY(val); | |
+ pair.value.binary.len = VARSIZE_ANY_EXHDR(val); | |
+ pair.value.size = pair.value.binary.len + sizeof(HEntry) * 2; | |
+ } | |
- for (i = 0; i < pcount; ++i) | |
- { | |
- int rawlen = pq_getmsgint(buf, 4); | |
- int len; | |
+ v.type = hsvHash; | |
+ v.size = sizeof(HEntry) + pair.key.size + pair.value.size; | |
+ v.hash.npairs = 1; | |
+ v.hash.pairs = &pair; | |
- if (rawlen < 0) | |
- ereport(ERROR, | |
- (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), | |
- errmsg("null value not allowed for hstore key"))); | |
+ PG_RETURN_POINTER(hstoreDump(&v)); | |
+} | |
- pairs[i].key = pq_getmsgtext(buf, rawlen, &len); | |
- pairs[i].keylen = hstoreCheckKeyLen(len); | |
- pairs[i].needfree = true; | |
+PG_FUNCTION_INFO_V1(hstore_from_arrays); | |
+PG_FUNCTION_INFO_V1(hstore_scalar_from_text); | |
+Datum hstore_scalar_from_text(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_scalar_from_text(PG_FUNCTION_ARGS) | |
+{ | |
+ HStoreValue a, v; | |
- rawlen = pq_getmsgint(buf, 4); | |
- if (rawlen < 0) | |
- { | |
- pairs[i].val = NULL; | |
- pairs[i].vallen = 0; | |
- pairs[i].isnull = true; | |
- } | |
- else | |
- { | |
- pairs[i].val = pq_getmsgtext(buf, rawlen, &len); | |
- pairs[i].vallen = hstoreCheckValLen(len); | |
- pairs[i].isnull = false; | |
- } | |
+ if (PG_ARGISNULL(0)) | |
+ { | |
+ v.type = hsvNull; | |
+ v.size = sizeof(HEntry); | |
} | |
+ else | |
+ { | |
+ text *scalar; | |
- pcount = hstoreUniquePairs(pairs, pcount, &buflen); | |
+ scalar = PG_GETARG_TEXT_PP(0); | |
+ v.type = hsvString; | |
+ v.string.val = VARDATA_ANY(scalar); | |
+ v.string.len = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(scalar)); | |
+ v.size = v.string.len + sizeof(HEntry); | |
+ } | |
- out = hstorePairs(pairs, pcount, buflen); | |
+ a.type = hsvArray; | |
+ a.size = sizeof(HEntry) + v.size; | |
+ a.array.nelems = 1; | |
+ a.array.elems = &v; | |
+ a.array.scalar = true; | |
- PG_RETURN_POINTER(out); | |
+ PG_RETURN_POINTER(hstoreDump(&a)); | |
} | |
- | |
-PG_FUNCTION_INFO_V1(hstore_from_text); | |
-Datum hstore_from_text(PG_FUNCTION_ARGS); | |
+PG_FUNCTION_INFO_V1(hstore_scalar_from_bool); | |
+Datum hstore_scalar_from_bool(PG_FUNCTION_ARGS); | |
Datum | |
-hstore_from_text(PG_FUNCTION_ARGS) | |
+hstore_scalar_from_bool(PG_FUNCTION_ARGS) | |
{ | |
- text *key; | |
- text *val = NULL; | |
- Pairs p; | |
- HStore *out; | |
+ HStoreValue a, v; | |
if (PG_ARGISNULL(0)) | |
- PG_RETURN_NULL(); | |
+ { | |
+ v.type = hsvNull; | |
+ v.size = sizeof(HEntry); | |
+ } | |
+ else | |
+ { | |
+ v.type = hsvBool; | |
+ v.boolean = PG_GETARG_BOOL(0); | |
+ v.size = sizeof(HEntry); | |
+ } | |
- p.needfree = false; | |
- key = PG_GETARG_TEXT_PP(0); | |
- p.key = VARDATA_ANY(key); | |
- p.keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key)); | |
+ a.type = hsvArray; | |
+ a.size = sizeof(HEntry) + v.size; | |
+ a.array.nelems = 1; | |
+ a.array.elems = &v; | |
+ a.array.scalar = true; | |
- if (PG_ARGISNULL(1)) | |
+ PG_RETURN_POINTER(hstoreDump(&a)); | |
+} | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_scalar_from_numeric); | |
+Datum hstore_scalar_from_numeric(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_scalar_from_numeric(PG_FUNCTION_ARGS) | |
+{ | |
+ HStoreValue a, v; | |
+ | |
+ if (PG_ARGISNULL(0)) | |
{ | |
- p.vallen = 0; | |
- p.isnull = true; | |
+ v.type = hsvNull; | |
+ v.size = sizeof(HEntry); | |
} | |
else | |
{ | |
- val = PG_GETARG_TEXT_PP(1); | |
- p.val = VARDATA_ANY(val); | |
- p.vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(val)); | |
- p.isnull = false; | |
+ v.type = hsvNumeric; | |
+ v.numeric = PG_GETARG_NUMERIC(0); | |
+ v.size = VARSIZE_ANY(v.numeric) + 2*sizeof(HEntry); | |
} | |
- out = hstorePairs(&p, 1, p.keylen + p.vallen); | |
+ a.type = hsvArray; | |
+ a.size = sizeof(HEntry) + v.size; | |
+ a.array.nelems = 1; | |
+ a.array.elems = &v; | |
+ a.array.scalar = true; | |
- PG_RETURN_POINTER(out); | |
+ PG_RETURN_POINTER(hstoreDump(&a)); | |
} | |
- | |
-PG_FUNCTION_INFO_V1(hstore_from_arrays); | |
Datum hstore_from_arrays(PG_FUNCTION_ARGS); | |
Datum | |
hstore_from_arrays(PG_FUNCTION_ARGS) | |
{ | |
- int32 buflen; | |
- HStore *out; | |
- Pairs *pairs; | |
+ HStoreValue v; | |
Datum *key_datums; | |
bool *key_nulls; | |
int key_count; | |
@@ -589,7 +517,10 @@ hstore_from_arrays(PG_FUNCTION_ARGS) | |
Assert(key_count == value_count); | |
} | |
- pairs = palloc(key_count * sizeof(Pairs)); | |
+ v.type = hsvHash; | |
+ v.size = 2 * sizeof(HEntry); | |
+ v.hash.pairs = palloc(key_count * sizeof(*v.hash.pairs)); | |
+ v.hash.npairs = key_count; | |
for (i = 0; i < key_count; ++i) | |
{ | |
@@ -598,31 +529,32 @@ hstore_from_arrays(PG_FUNCTION_ARGS) | |
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), | |
errmsg("null value not allowed for hstore key"))); | |
+ v.hash.pairs[i].key.type = hsvString; | |
+ v.hash.pairs[i].key.string.val = VARDATA_ANY(key_datums[i]); | |
+ v.hash.pairs[i].key.string.len = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i])); | |
+ v.hash.pairs[i].key.size = sizeof(HEntry) + v.hash.pairs[i].key.string.len; | |
+ | |
if (!value_nulls || value_nulls[i]) | |
{ | |
- pairs[i].key = VARDATA_ANY(key_datums[i]); | |
- pairs[i].val = NULL; | |
- pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i])); | |
- pairs[i].vallen = 4; | |
- pairs[i].isnull = true; | |
- pairs[i].needfree = false; | |
+ v.hash.pairs[i].value.type = hsvNull; | |
+ v.hash.pairs[i].value.size = sizeof(HEntry); | |
} | |
else | |
{ | |
- pairs[i].key = VARDATA_ANY(key_datums[i]); | |
- pairs[i].val = VARDATA_ANY(value_datums[i]); | |
- pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(key_datums[i])); | |
- pairs[i].vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(value_datums[i])); | |
- pairs[i].isnull = false; | |
- pairs[i].needfree = false; | |
+ v.hash.pairs[i].value.type = hsvString; | |
+ v.hash.pairs[i].value.size = sizeof(HEntry); | |
+ v.hash.pairs[i].value.string.val = VARDATA_ANY(value_datums[i]); | |
+ v.hash.pairs[i].value.string.len = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(value_datums[i])); | |
+ v.hash.pairs[i].value.size = sizeof(HEntry) + v.hash.pairs[i].value.string.len; | |
} | |
+ | |
+ v.size += v.hash.pairs[i].key.size + v.hash.pairs[i].value.size; | |
} | |
- key_count = hstoreUniquePairs(pairs, key_count, &buflen); | |
+ ORDER_PAIRS(v.hash.pairs, v.hash.npairs, v.size -= ptr->key.size + ptr->value.size); | |
- out = hstorePairs(pairs, key_count, buflen); | |
- PG_RETURN_POINTER(out); | |
+ PG_RETURN_POINTER(hstoreDump(&v)); | |
} | |
@@ -634,9 +566,7 @@ hstore_from_array(PG_FUNCTION_ARGS) | |
ArrayType *in_array = PG_GETARG_ARRAYTYPE_P(0); | |
int ndims = ARR_NDIM(in_array); | |
int count; | |
- int32 buflen; | |
- HStore *out; | |
- Pairs *pairs; | |
+ HStoreValue v; | |
Datum *in_datums; | |
bool *in_nulls; | |
int in_count; | |
@@ -647,8 +577,7 @@ hstore_from_array(PG_FUNCTION_ARGS) | |
switch (ndims) | |
{ | |
case 0: | |
- out = hstorePairs(NULL, 0, 0); | |
- PG_RETURN_POINTER(out); | |
+ PG_RETURN_POINTER(hstoreDump(NULL)); | |
case 1: | |
if ((ARR_DIMS(in_array)[0]) % 2) | |
@@ -676,7 +605,10 @@ hstore_from_array(PG_FUNCTION_ARGS) | |
count = in_count / 2; | |
- pairs = palloc(count * sizeof(Pairs)); | |
+ v.type = hsvHash; | |
+ v.size = 2*sizeof(HEntry); | |
+ v.hash.npairs = count; | |
+ v.hash.pairs = palloc(count * sizeof(HStorePair)); | |
for (i = 0; i < count; ++i) | |
{ | |
@@ -685,31 +617,31 @@ hstore_from_array(PG_FUNCTION_ARGS) | |
(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED), | |
errmsg("null value not allowed for hstore key"))); | |
+ v.hash.pairs[i].key.type = hsvString; | |
+ v.hash.pairs[i].key.string.val = VARDATA_ANY(in_datums[i * 2]); | |
+ v.hash.pairs[i].key.string.len = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(in_datums[i * 2])); | |
+ v.hash.pairs[i].key.size = sizeof(HEntry) + v.hash.pairs[i].key.string.len; | |
+ | |
if (in_nulls[i * 2 + 1]) | |
{ | |
- pairs[i].key = VARDATA_ANY(in_datums[i * 2]); | |
- pairs[i].val = NULL; | |
- pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(in_datums[i * 2])); | |
- pairs[i].vallen = 4; | |
- pairs[i].isnull = true; | |
- pairs[i].needfree = false; | |
+ v.hash.pairs[i].value.type = hsvNull; | |
+ v.hash.pairs[i].value.size = sizeof(HEntry); | |
} | |
else | |
{ | |
- pairs[i].key = VARDATA_ANY(in_datums[i * 2]); | |
- pairs[i].val = VARDATA_ANY(in_datums[i * 2 + 1]); | |
- pairs[i].keylen = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(in_datums[i * 2])); | |
- pairs[i].vallen = hstoreCheckValLen(VARSIZE_ANY_EXHDR(in_datums[i * 2 + 1])); | |
- pairs[i].isnull = false; | |
- pairs[i].needfree = false; | |
+ v.hash.pairs[i].value.type = hsvString; | |
+ v.hash.pairs[i].value.size = sizeof(HEntry); | |
+ v.hash.pairs[i].value.string.val = VARDATA_ANY(in_datums[i * 2 + 1]); | |
+ v.hash.pairs[i].value.string.len = hstoreCheckKeyLen(VARSIZE_ANY_EXHDR(in_datums[i * 2 + 1])); | |
+ v.hash.pairs[i].value.size = sizeof(HEntry) + v.hash.pairs[i].value.string.len; | |
} | |
- } | |
- count = hstoreUniquePairs(pairs, count, &buflen); | |
+ v.size += v.hash.pairs[i].key.size + v.hash.pairs[i].value.size; | |
+ } | |
- out = hstorePairs(pairs, count, buflen); | |
+ ORDER_PAIRS(v.hash.pairs, v.hash.npairs, v.size -= ptr->key.size + ptr->value.size); | |
- PG_RETURN_POINTER(out); | |
+ PG_RETURN_POINTER(hstoreDump(&v)); | |
} | |
/* most of hstore_from_record is shamelessly swiped from record_out */ | |
@@ -738,20 +670,18 @@ Datum hstore_from_record(PG_FUNCTION_ARGS); | |
Datum | |
hstore_from_record(PG_FUNCTION_ARGS) | |
{ | |
- HeapTupleHeader rec; | |
- int32 buflen; | |
- HStore *out; | |
- Pairs *pairs; | |
- Oid tupType; | |
- int32 tupTypmod; | |
- TupleDesc tupdesc; | |
- HeapTupleData tuple; | |
- RecordIOData *my_extra; | |
- int ncolumns; | |
- int i, | |
- j; | |
- Datum *values; | |
- bool *nulls; | |
+ HeapTupleHeader rec; | |
+ HStore *out; | |
+ HStoreValue v; | |
+ Oid tupType; | |
+ int32 tupTypmod; | |
+ TupleDesc tupdesc; | |
+ HeapTupleData tuple; | |
+ RecordIOData *my_extra; | |
+ int ncolumns; | |
+ int i; | |
+ Datum *values; | |
+ bool *nulls; | |
if (PG_ARGISNULL(0)) | |
{ | |
@@ -807,7 +737,10 @@ hstore_from_record(PG_FUNCTION_ARGS) | |
my_extra->ncolumns = ncolumns; | |
} | |
- pairs = palloc(ncolumns * sizeof(Pairs)); | |
+ v.type = hsvHash; | |
+ v.size = 2*sizeof(HEntry); | |
+ v.hash.npairs = ncolumns; | |
+ v.hash.pairs = palloc(ncolumns * sizeof(HStorePair)); | |
if (rec) | |
{ | |
@@ -829,7 +762,7 @@ hstore_from_record(PG_FUNCTION_ARGS) | |
nulls = NULL; | |
} | |
- for (i = 0, j = 0; i < ncolumns; ++i) | |
+ for (i = 0; i < ncolumns; ++i) | |
{ | |
ColumnIOData *column_info = &my_extra->columns[i]; | |
Oid column_type = tupdesc->attrs[i]->atttypid; | |
@@ -839,46 +772,62 @@ hstore_from_record(PG_FUNCTION_ARGS) | |
if (tupdesc->attrs[i]->attisdropped) | |
continue; | |
- pairs[j].key = NameStr(tupdesc->attrs[i]->attname); | |
- pairs[j].keylen = hstoreCheckKeyLen(strlen(NameStr(tupdesc->attrs[i]->attname))); | |
+ v.hash.pairs[i].key.type = hsvString; | |
+ v.hash.pairs[i].key.string.val = NameStr(tupdesc->attrs[i]->attname); | |
+ v.hash.pairs[i].key.string.len = hstoreCheckKeyLen(strlen(v.hash.pairs[i].key.string.val)); | |
+ v.hash.pairs[i].key.size = sizeof(HEntry) + v.hash.pairs[i].key.string.len; | |
if (!nulls || nulls[i]) | |
{ | |
- pairs[j].val = NULL; | |
- pairs[j].vallen = 4; | |
- pairs[j].isnull = true; | |
- pairs[j].needfree = false; | |
- ++j; | |
- continue; | |
+ v.hash.pairs[i].value.type = hsvNull; | |
+ v.hash.pairs[i].value.size = sizeof(HEntry); | |
} | |
- | |
- /* | |
- * Convert the column value to text | |
- */ | |
- if (column_info->column_type != column_type) | |
+ else | |
{ | |
- bool typIsVarlena; | |
+ /* | |
+ * Convert the column value to hstore's values | |
+ */ | |
+ if (column_type == BOOLOID) | |
+ { | |
+ v.hash.pairs[i].value.type = hsvBool; | |
+ v.hash.pairs[i].value.boolean = DatumGetBool(values[i]); | |
+ v.hash.pairs[i].value.size = sizeof(HEntry); | |
+ } | |
+ else if (column_type == NUMERICOID) | |
+ { /* XXX float... int... */ | |
+ v.hash.pairs[i].value.type = hsvNumeric; | |
+ v.hash.pairs[i].value.numeric = DatumGetNumeric(values[i]); | |
+ v.hash.pairs[i].value.size = 2*sizeof(HEntry) + VARSIZE_ANY(v.hash.pairs[i].value.numeric); | |
+ } | |
+ else | |
+ { | |
+ if (column_info->column_type != column_type) | |
+ { | |
+ bool typIsVarlena; | |
+ | |
+ getTypeOutputInfo(column_type, | |
+ &column_info->typiofunc, | |
+ &typIsVarlena); | |
+ fmgr_info_cxt(column_info->typiofunc, &column_info->proc, | |
+ fcinfo->flinfo->fn_mcxt); | |
+ column_info->column_type = column_type; | |
+ } | |
- getTypeOutputInfo(column_type, | |
- &column_info->typiofunc, | |
- &typIsVarlena); | |
- fmgr_info_cxt(column_info->typiofunc, &column_info->proc, | |
- fcinfo->flinfo->fn_mcxt); | |
- column_info->column_type = column_type; | |
- } | |
+ value = OutputFunctionCall(&column_info->proc, values[i]); | |
- value = OutputFunctionCall(&column_info->proc, values[i]); | |
+ v.hash.pairs[i].value.type = hsvString; | |
+ v.hash.pairs[i].value.string.val = value; | |
+ v.hash.pairs[i].value.string.len = hstoreCheckValLen(strlen(value)); | |
+ v.hash.pairs[i].value.size = sizeof(HEntry) + v.hash.pairs[i].value.string.len; | |
+ } | |
+ } | |
- pairs[j].val = value; | |
- pairs[j].vallen = hstoreCheckValLen(strlen(value)); | |
- pairs[j].isnull = false; | |
- pairs[j].needfree = false; | |
- ++j; | |
+ v.size += v.hash.pairs[i].key.size + v.hash.pairs[i].value.size; | |
} | |
- ncolumns = hstoreUniquePairs(pairs, j, &buflen); | |
+ ORDER_PAIRS(v.hash.pairs, v.hash.npairs, v.size -= ptr->key.size + ptr->value.size); | |
- out = hstorePairs(pairs, ncolumns, buflen); | |
+ out = hstoreDump(&v); | |
ReleaseTupleDesc(tupdesc); | |
@@ -893,8 +842,6 @@ hstore_populate_record(PG_FUNCTION_ARGS) | |
{ | |
Oid argtype = get_fn_expr_argtype(fcinfo->flinfo, 0); | |
HStore *hs; | |
- HEntry *entries; | |
- char *ptr; | |
HeapTupleHeader rec; | |
Oid tupType; | |
int32 tupTypmod; | |
@@ -940,8 +887,6 @@ hstore_populate_record(PG_FUNCTION_ARGS) | |
} | |
hs = PG_GETARG_HS(1); | |
- entries = ARRPTR(hs); | |
- ptr = STRPTR(hs); | |
/* | |
* if the input hstore is empty, we can only skip the rest if we were | |
@@ -949,7 +894,7 @@ hstore_populate_record(PG_FUNCTION_ARGS) | |
* domain nulls. | |
*/ | |
- if (HS_COUNT(hs) == 0 && rec) | |
+ if (HS_ISEMPTY(hs) && rec) | |
PG_RETURN_POINTER(rec); | |
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); | |
@@ -1013,9 +958,7 @@ hstore_populate_record(PG_FUNCTION_ARGS) | |
{ | |
ColumnIOData *column_info = &my_extra->columns[i]; | |
Oid column_type = tupdesc->attrs[i]->atttypid; | |
- char *value; | |
- int idx; | |
- int vallen; | |
+ HStoreValue *v = NULL; | |
/* Ignore dropped columns in datatype */ | |
if (tupdesc->attrs[i]->attisdropped) | |
@@ -1024,9 +967,12 @@ hstore_populate_record(PG_FUNCTION_ARGS) | |
continue; | |
} | |
- idx = hstoreFindKey(hs, 0, | |
- NameStr(tupdesc->attrs[i]->attname), | |
- strlen(NameStr(tupdesc->attrs[i]->attname))); | |
+ if (!HS_ISEMPTY(hs)) | |
+ { | |
+ char *key = NameStr(tupdesc->attrs[i]->attname); | |
+ | |
+ v = findUncompressedHStoreValue(VARDATA(hs), HS_FLAG_HSTORE, NULL, key, strlen(key)); | |
+ } | |
/* | |
* we can't just skip here if the key wasn't found since we might have | |
@@ -1036,7 +982,7 @@ hstore_populate_record(PG_FUNCTION_ARGS) | |
* then every field which we don't populate needs to be run through | |
* the input function just in case it's a domain type. | |
*/ | |
- if (idx < 0 && rec) | |
+ if (v == NULL && rec) | |
continue; | |
/* | |
@@ -1052,7 +998,7 @@ hstore_populate_record(PG_FUNCTION_ARGS) | |
column_info->column_type = column_type; | |
} | |
- if (idx < 0 || HS_VALISNULL(entries, idx)) | |
+ if (v == NULL || v->type == hsvNull) | |
{ | |
/* | |
* need InputFunctionCall to happen even for nulls, so that domain | |
@@ -1065,12 +1011,27 @@ hstore_populate_record(PG_FUNCTION_ARGS) | |
} | |
else | |
{ | |
- vallen = HS_VALLEN(entries, idx); | |
- value = palloc(1 + vallen); | |
- memcpy(value, HS_VAL(entries, ptr, idx), vallen); | |
- value[vallen] = 0; | |
+ char *s = NULL; | |
+ | |
+ if (v->type == hsvString) | |
+ s = pnstrdup(v->string.val, v->string.len); | |
+ else if (v->type == hsvBool) | |
+ s = pnstrdup((v->boolean) ? "t" : "f", 1); | |
+ else if (v->type == hsvNumeric) | |
+ s = DatumGetCString(DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric))); | |
+ else if (v->type == hsvBinary && column_type == JSONOID) | |
+ s = hstoreToCString(NULL, v->binary.data, v->binary.len, | |
+ SET_PRETTY_PRINT_VAR(JsonOutput | RootHashDecorated)); | |
+ else if (v->type == hsvBinary && type_is_array(column_type)) | |
+ s = hstoreToCString(NULL, v->binary.data, v->binary.len, | |
+ SET_PRETTY_PRINT_VAR(ArrayCurlyBraces)); | |
+ else if (v->type == hsvBinary) | |
+ s = hstoreToCString(NULL, v->binary.data, v->binary.len, | |
+ SET_PRETTY_PRINT_VAR(0)); | |
+ else | |
+ elog(PANIC, "Wrong hstore"); | |
- values[i] = InputFunctionCall(&column_info->proc, value, | |
+ values[i] = InputFunctionCall(&column_info->proc, s, | |
column_info->typioparam, | |
tupdesc->attrs[i]->atttypmod); | |
nulls[i] = false; | |
@@ -1084,129 +1045,451 @@ hstore_populate_record(PG_FUNCTION_ARGS) | |
PG_RETURN_DATUM(HeapTupleGetDatum(rettuple)); | |
} | |
+bool | |
+stringIsNumber(char *string, int len) { | |
+ enum { | |
+ SIN_FIRSTINT, | |
+ SIN_ZEROINT, | |
+ SIN_INT, | |
+ SIN_SCALE, | |
+ SIN_MSIGN, | |
+ SIN_MANTISSA | |
+ } sinState; | |
+ char *c; | |
+ bool r; | |
+ | |
+ if (*string == '-' || *string == '+') | |
+ { | |
+ string++; | |
+ len--; | |
+ } | |
+ | |
+ c = string; | |
+ r = true; | |
+ sinState = SIN_FIRSTINT; | |
+ | |
+ while(r && c - string < len) | |
+ { | |
+ switch(sinState) | |
+ { | |
+ case SIN_FIRSTINT: | |
+ if (*c == '0') | |
+ sinState = SIN_ZEROINT; | |
+ else if (*c == '.') | |
+ sinState = SIN_SCALE; | |
+ else if (isdigit(*c)) | |
+ sinState = SIN_INT; | |
+ else | |
+ r = false; | |
+ break; | |
+ case SIN_ZEROINT: | |
+ if (*c == '.') | |
+ sinState = SIN_SCALE; | |
+ else | |
+ r = false; | |
+ break; | |
+ case SIN_INT: | |
+ if (*c == '.') | |
+ sinState = SIN_SCALE; | |
+ else if (*c == 'e' || *c == 'E') | |
+ sinState = SIN_MSIGN; | |
+ else if (!isdigit(*c)) | |
+ r = false; | |
+ break; | |
+ case SIN_SCALE: | |
+ if (*c == 'e' || *c == 'E') | |
+ sinState = SIN_MSIGN; | |
+ else if (!isdigit(*c)) | |
+ r = false; | |
+ break; | |
+ case SIN_MSIGN: | |
+ if (*c == '-' || *c == '+' || isdigit(*c)) | |
+ sinState = SIN_MANTISSA; | |
+ else | |
+ r = false; | |
+ break; | |
+ case SIN_MANTISSA: | |
+ if (!isdigit(*c)) | |
+ r = false; | |
+ break; | |
+ default: | |
+ abort(); | |
+ } | |
+ | |
+ c++; | |
+ } | |
+ | |
+ if (sinState == SIN_MSIGN) | |
+ r = false; | |
+ | |
+ return r; | |
+} | |
+ | |
+static void | |
+printIndent(StringInfo out, bool isRootHash, HStoreOutputKind kind, int level) | |
+{ | |
+ if (kind & PrettyPrint) | |
+ { | |
+ int i; | |
+ | |
+ if (isRootHash && (kind & RootHashDecorated) == 0) | |
+ level--; | |
+ for(i=0; i<4*level; i++) | |
+ appendStringInfoCharMacro(out, ' '); | |
+ } | |
+} | |
+ | |
+static void | |
+printCR(StringInfo out, HStoreOutputKind kind) | |
+{ | |
+ if (kind & PrettyPrint) | |
+ appendStringInfoCharMacro(out, '\n'); | |
+} | |
-static char * | |
-cpw(char *dst, char *src, int len) | |
+static void | |
+escape_hstore(StringInfo out, char *string, uint32 len) | |
{ | |
- char *ptr = src; | |
+ char *ptr = string; | |
- while (ptr - src < len) | |
+ appendStringInfoCharMacro(out, '"'); | |
+ while (ptr - string < len) | |
{ | |
if (*ptr == '"' || *ptr == '\\') | |
- *dst++ = '\\'; | |
- *dst++ = *ptr++; | |
+ appendStringInfoCharMacro(out, '\\'); | |
+ appendStringInfoCharMacro(out, *ptr); | |
+ ptr++; | |
} | |
- return dst; | |
+ appendStringInfoCharMacro(out, '"'); | |
} | |
-PG_FUNCTION_INFO_V1(hstore_out); | |
-Datum hstore_out(PG_FUNCTION_ARGS); | |
-Datum | |
-hstore_out(PG_FUNCTION_ARGS) | |
+static void | |
+putEscapedString(StringInfo out, HStoreOutputKind kind, char *string, uint32 len) | |
{ | |
- HStore *in = PG_GETARG_HS(0); | |
- int buflen, | |
- i; | |
- int count = HS_COUNT(in); | |
- char *out, | |
- *ptr; | |
- char *base = STRPTR(in); | |
- HEntry *entries = ARRPTR(in); | |
+ if (kind & LooseOutput) | |
+ { | |
+ if (len == 1 && *string == 't') | |
+ appendStringInfoString(out, (kind & JsonOutput) ? "true" : "t" ); | |
+ else if (len == 1 && *string == 'f') | |
+ appendStringInfoString(out, (kind & JsonOutput) ? "false" : "f"); | |
+ else if (len > 0 && stringIsNumber(string, len)) | |
+ appendBinaryStringInfo(out, string, len); | |
+ else if (kind & JsonOutput) | |
+ escape_json(out, pnstrdup(string, len)); | |
+ else | |
+ escape_hstore(out, string, len); | |
+ } | |
+ else | |
+ { | |
+ if (kind & JsonOutput) | |
+ escape_json(out, pnstrdup(string, len)); | |
+ else | |
+ escape_hstore(out, string, len); | |
+ } | |
+} | |
- if (count == 0) | |
+static void | |
+putEscapedValue(StringInfo out, HStoreOutputKind kind, HStoreValue *v) | |
+{ | |
+ switch(v->type) | |
{ | |
- out = palloc(1); | |
- *out = '\0'; | |
- PG_RETURN_CSTRING(out); | |
+ case hsvNull: | |
+ appendBinaryStringInfo(out, (kind & JsonOutput) ? "null" : "NULL", 4); | |
+ break; | |
+ case hsvString: | |
+ putEscapedString(out, kind, v->string.val, v->string.len); | |
+ break; | |
+ case hsvBool: | |
+ if ((kind & JsonOutput) == 0) | |
+ appendBinaryStringInfo(out, (v->boolean) ? "t" : "f", 1); | |
+ else if (v->boolean) | |
+ appendBinaryStringInfo(out, "true", 4); | |
+ else | |
+ appendBinaryStringInfo(out, "false", 5); | |
+ break; | |
+ case hsvNumeric: | |
+ appendStringInfoString(out, DatumGetCString(DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric)))); | |
+ break; | |
+ default: | |
+ elog(PANIC, "Unknown type"); | |
} | |
+} | |
+ | |
+static bool | |
+needBrackets(int level, bool isArray, HStoreOutputKind kind, bool isScalar) | |
+{ | |
+ bool res; | |
- buflen = 0; | |
+ if (isArray && isScalar) | |
+ res = false; | |
+ else if (level == 0) | |
+ res = (isArray || (kind & RootHashDecorated)) ? true : false; | |
+ else | |
+ res = true; | |
- /* | |
- * this loop overestimates due to pessimistic assumptions about escaping, | |
- * so very large hstore values can't be output. this could be fixed, but | |
- * many other data types probably have the same issue. This replaced code | |
- * that used the original varlena size for calculations, which was wrong | |
- * in some subtle ways. | |
- */ | |
+ return res; | |
+} | |
+ | |
+static bool | |
+isArrayBrackets(HStoreOutputKind kind) | |
+{ | |
+ return ((kind & ArrayCurlyBraces) == 0) ? true : false; | |
+} | |
+ | |
+ | |
+char* | |
+hstoreToCString(StringInfo out, char *in, int len /* just estimation */, | |
+ HStoreOutputKind kind) | |
+{ | |
+ bool first = true; | |
+ HStoreIterator *it; | |
+ int type; | |
+ HStoreValue v; | |
+ int level = 0; | |
+ bool isRootHash = false; | |
- for (i = 0; i < count; i++) | |
+ if (out == NULL) | |
+ out = makeStringInfo(); | |
+ | |
+ if (in == NULL) | |
{ | |
- /* include "" and => and comma-space */ | |
- buflen += 6 + 2 * HS_KEYLEN(entries, i); | |
- /* include "" only if nonnull */ | |
- buflen += 2 + (HS_VALISNULL(entries, i) | |
- ? 2 | |
- : 2 * HS_VALLEN(entries, i)); | |
+ appendStringInfoString(out, ""); | |
+ return out->data; | |
} | |
- out = ptr = palloc(buflen); | |
+ enlargeStringInfo(out, (len >= 0) ? len : 64); | |
+ | |
+ it = HStoreIteratorInit(in); | |
- for (i = 0; i < count; i++) | |
+ while((type = HStoreIteratorGet(&it, &v, false)) != 0) | |
{ | |
- *ptr++ = '"'; | |
- ptr = cpw(ptr, HS_KEY(entries, base, i), HS_KEYLEN(entries, i)); | |
- *ptr++ = '"'; | |
- *ptr++ = '='; | |
- *ptr++ = '>'; | |
- if (HS_VALISNULL(entries, i)) | |
- { | |
- *ptr++ = 'N'; | |
- *ptr++ = 'U'; | |
- *ptr++ = 'L'; | |
- *ptr++ = 'L'; | |
- } | |
- else | |
+reout: | |
+ switch(type) | |
{ | |
- *ptr++ = '"'; | |
- ptr = cpw(ptr, HS_VAL(entries, base, i), HS_VALLEN(entries, i)); | |
- *ptr++ = '"'; | |
- } | |
+ case WHS_BEGIN_ARRAY: | |
+ if (first == false) | |
+ { | |
+ appendBinaryStringInfo(out, ", ", 2); | |
+ printCR(out, kind); | |
+ } | |
+ first = true; | |
- if (i + 1 != count) | |
- { | |
- *ptr++ = ','; | |
- *ptr++ = ' '; | |
+ if (needBrackets(level, true, kind, v.array.scalar)) | |
+ { | |
+ printIndent(out, isRootHash, kind, level); | |
+ appendStringInfoChar(out, isArrayBrackets(kind) ? '[' : '{'); | |
+ printCR(out, kind); | |
+ } | |
+ level++; | |
+ break; | |
+ case WHS_BEGIN_HASH: | |
+ if (first == false) | |
+ { | |
+ appendBinaryStringInfo(out, ", ", 2); | |
+ printCR(out, kind); | |
+ } | |
+ first = true; | |
+ | |
+ if (level == 0) | |
+ isRootHash = true; | |
+ | |
+ if (needBrackets(level, false, kind, false)) | |
+ { | |
+ printIndent(out, isRootHash, kind, level); | |
+ appendStringInfoCharMacro(out, '{'); | |
+ printCR(out, kind); | |
+ } | |
+ | |
+ level++; | |
+ break; | |
+ case WHS_KEY: | |
+ if (first == false) | |
+ { | |
+ appendBinaryStringInfo(out, ", ", 2); | |
+ printCR(out, kind); | |
+ } | |
+ first = true; | |
+ | |
+ printIndent(out, isRootHash, kind, level); | |
+ putEscapedValue(out, kind & ~LooseOutput /* key should not be loose */, &v); | |
+ appendBinaryStringInfo(out, (kind & JsonOutput) ? ": " : "=>", 2); | |
+ | |
+ type = HStoreIteratorGet(&it, &v, false); | |
+ if (type == WHS_VALUE) | |
+ { | |
+ first = false; | |
+ putEscapedValue(out, kind, &v); | |
+ } | |
+ else | |
+ { | |
+ Assert(type == WHS_BEGIN_HASH || type == WHS_BEGIN_ARRAY); | |
+ printCR(out, kind); | |
+ goto reout; | |
+ } | |
+ break; | |
+ case WHS_ELEM: | |
+ if (first == false) | |
+ { | |
+ appendBinaryStringInfo(out, ", ", 2); | |
+ printCR(out, kind); | |
+ } | |
+ else | |
+ { | |
+ first = false; | |
+ } | |
+ | |
+ printIndent(out, isRootHash, kind, level); | |
+ putEscapedValue(out, kind, &v); | |
+ break; | |
+ case WHS_END_ARRAY: | |
+ level--; | |
+ if (needBrackets(level, true, kind, v.array.scalar)) | |
+ { | |
+ printCR(out, kind); | |
+ printIndent(out, isRootHash, kind, level); | |
+ appendStringInfoChar(out, isArrayBrackets(kind) ? ']' : '}'); | |
+ } | |
+ first = false; | |
+ break; | |
+ case WHS_END_HASH: | |
+ level--; | |
+ if (needBrackets(level, false, kind, false)) | |
+ { | |
+ printCR(out, kind); | |
+ printIndent(out, isRootHash, kind, level); | |
+ appendStringInfoCharMacro(out, '}'); | |
+ } | |
+ first = false; | |
+ break; | |
+ default: | |
+ elog(PANIC, "Wrong flags"); | |
} | |
} | |
- *ptr = '\0'; | |
- PG_RETURN_CSTRING(out); | |
+ Assert(level == 0); | |
+ | |
+ return out->data; | |
+} | |
+ | |
+text* | |
+HStoreValueToText(HStoreValue *v) | |
+{ | |
+ text *out; | |
+ | |
+ if (v == NULL || v->type == hsvNull) | |
+ { | |
+ out = NULL; | |
+ } | |
+ else if (v->type == hsvString) | |
+ { | |
+ out = cstring_to_text_with_len(v->string.val, v->string.len); | |
+ } | |
+ else if (v->type == hsvBool) | |
+ { | |
+ out = cstring_to_text_with_len((v->boolean) ? "t" : "f", 1); | |
+ } | |
+ else if (v->type == hsvNumeric) | |
+ { | |
+ out = cstring_to_text(DatumGetCString(DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric)))); | |
+ } | |
+ else | |
+ { | |
+ StringInfo str; | |
+ | |
+ str = makeStringInfo(); | |
+ appendBinaryStringInfo(str, " ", 4); /* VARHDRSZ */ | |
+ | |
+ hstoreToCString(str, v->binary.data, v->binary.len, SET_PRETTY_PRINT_VAR(0)); | |
+ | |
+ out = (text*)str->data; | |
+ SET_VARSIZE(out, str->len); | |
+ } | |
+ | |
+ return out; | |
} | |
+PG_FUNCTION_INFO_V1(hstore_out); | |
+Datum hstore_out(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_out(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ char *out; | |
+ | |
+ out = hstoreToCString(NULL, (HS_ISEMPTY(hs)) ? NULL : VARDATA(hs), VARSIZE(hs), SET_PRETTY_PRINT_VAR(0)); | |
+ | |
+ PG_RETURN_CSTRING(out); | |
+} | |
PG_FUNCTION_INFO_V1(hstore_send); | |
Datum hstore_send(PG_FUNCTION_ARGS); | |
Datum | |
hstore_send(PG_FUNCTION_ARGS) | |
{ | |
- HStore *in = PG_GETARG_HS(0); | |
- int i; | |
- int count = HS_COUNT(in); | |
- char *base = STRPTR(in); | |
- HEntry *entries = ARRPTR(in); | |
- StringInfoData buf; | |
+ HStore *in = PG_GETARG_HS(0); | |
+ StringInfoData buf; | |
pq_begintypsend(&buf); | |
- pq_sendint(&buf, count, 4); | |
- | |
- for (i = 0; i < count; i++) | |
+ if (HS_ISEMPTY(in)) | |
+ { | |
+ pq_sendint(&buf, 0, 4); | |
+ } | |
+ else | |
{ | |
- int32 keylen = HS_KEYLEN(entries, i); | |
+ HStoreIterator *it; | |
+ int type; | |
+ HStoreValue v; | |
+ uint32 flag; | |
+ bytea *nbuf; | |
- pq_sendint(&buf, keylen, 4); | |
- pq_sendtext(&buf, HS_KEY(entries, base, i), keylen); | |
- if (HS_VALISNULL(entries, i)) | |
- { | |
- pq_sendint(&buf, -1, 4); | |
- } | |
- else | |
- { | |
- int32 vallen = HS_VALLEN(entries, i); | |
+ enlargeStringInfo(&buf, VARSIZE_ANY(in) /* just estimation */); | |
- pq_sendint(&buf, vallen, 4); | |
- pq_sendtext(&buf, HS_VAL(entries, base, i), vallen); | |
+ it = HStoreIteratorInit(VARDATA_ANY(in)); | |
+ | |
+ while((type = HStoreIteratorGet(&it, &v, false)) != 0) | |
+ { | |
+ switch(type) | |
+ { | |
+ case WHS_BEGIN_ARRAY: | |
+ flag = (v.array.scalar) ? HENTRY_ISCALAR : HENTRY_ISARRAY; | |
+ pq_sendint(&buf, v.array.nelems | flag, 4); | |
+ break; | |
+ case WHS_BEGIN_HASH: | |
+ pq_sendint(&buf, v.hash.npairs | HENTRY_ISHASH, 4); | |
+ break; | |
+ case WHS_KEY: | |
+ pq_sendint(&buf, v.string.len | HENTRY_ISSTRING, 4); | |
+ pq_sendtext(&buf, v.string.val, v.string.len); | |
+ break; | |
+ case WHS_ELEM: | |
+ case WHS_VALUE: | |
+ switch(v.type) | |
+ { | |
+ case hsvNull: | |
+ pq_sendint(&buf, HENTRY_ISNULL, 4); | |
+ break; | |
+ case hsvString: | |
+ pq_sendint(&buf, v.string.len | HENTRY_ISSTRING, 4); | |
+ pq_sendtext(&buf, v.string.val, v.string.len); | |
+ break; | |
+ case hsvBool: | |
+ pq_sendint(&buf, (v.boolean) ? HENTRY_ISTRUE : HENTRY_ISFALSE, 4); | |
+ break; | |
+ case hsvNumeric: | |
+ nbuf = DatumGetByteaP(DirectFunctionCall1(numeric_send, NumericGetDatum(v.numeric))); | |
+ pq_sendint(&buf, VARSIZE_ANY(nbuf) | HENTRY_ISNUMERIC, 4); | |
+ pq_sendbytes(&buf, (char*)nbuf, VARSIZE_ANY(nbuf)); | |
+ break; | |
+ default: | |
+ elog(PANIC, "Wrong type: %u", v.type); | |
+ } | |
+ break; | |
+ case WHS_END_ARRAY: | |
+ case WHS_END_HASH: | |
+ break; | |
+ default: | |
+ elog(PANIC, "Wrong flags"); | |
+ } | |
} | |
} | |
@@ -1228,124 +1511,28 @@ Datum | |
hstore_to_json_loose(PG_FUNCTION_ARGS) | |
{ | |
HStore *in = PG_GETARG_HS(0); | |
- int buflen, | |
- i; | |
- int count = HS_COUNT(in); | |
- char *out, | |
- *ptr; | |
- char *base = STRPTR(in); | |
- HEntry *entries = ARRPTR(in); | |
- bool is_number; | |
- StringInfo src, | |
- dst; | |
- | |
- if (count == 0) | |
- PG_RETURN_TEXT_P(cstring_to_text_with_len("{}",2)); | |
- | |
- buflen = 3; | |
+ text *out; | |
- /* | |
- * Formula adjusted slightly from the logic in hstore_out. We have to take | |
- * account of out treatment of booleans to be a bit more pessimistic about | |
- * the length of values. | |
- */ | |
+ if (HS_ISEMPTY(in)) | |
+ { | |
+ out = cstring_to_text_with_len("{}",2); | |
+ } | |
+ else | |
+ { | |
+ StringInfo str; | |
- for (i = 0; i < count; i++) | |
- { | |
- /* include "" and colon-space and comma-space */ | |
- buflen += 6 + 2 * HS_KEYLEN(entries, i); | |
- /* include "" only if nonnull */ | |
- buflen += 3 + (HS_VALISNULL(entries, i) | |
- ? 1 | |
- : 2 * HS_VALLEN(entries, i)); | |
- } | |
- | |
- out = ptr = palloc(buflen); | |
- | |
- src = makeStringInfo(); | |
- dst = makeStringInfo(); | |
- | |
- *ptr++ = '{'; | |
- | |
- for (i = 0; i < count; i++) | |
- { | |
- resetStringInfo(src); | |
- resetStringInfo(dst); | |
- appendBinaryStringInfo(src, HS_KEY(entries, base, i), HS_KEYLEN(entries, i)); | |
- escape_json(dst, src->data); | |
- strncpy(ptr, dst->data, dst->len); | |
- ptr += dst->len; | |
- *ptr++ = ':'; | |
- *ptr++ = ' '; | |
- resetStringInfo(dst); | |
- if (HS_VALISNULL(entries, i)) | |
- appendStringInfoString(dst, "null"); | |
- /* guess that values of 't' or 'f' are booleans */ | |
- else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 't') | |
- appendStringInfoString(dst, "true"); | |
- else if (HS_VALLEN(entries, i) == 1 && *(HS_VAL(entries, base, i)) == 'f') | |
- appendStringInfoString(dst, "false"); | |
- else | |
- { | |
- is_number = false; | |
- resetStringInfo(src); | |
- appendBinaryStringInfo(src, HS_VAL(entries, base, i), HS_VALLEN(entries, i)); | |
+ str = makeStringInfo(); | |
+ appendBinaryStringInfo(str, " ", 4); /* VARHDRSZ */ | |
- /* | |
- * don't treat something with a leading zero followed by another | |
- * digit as numeric - could be a zip code or similar | |
- */ | |
- if (src->len > 0 && | |
- !(src->data[0] == '0' && | |
- isdigit((unsigned char) src->data[1])) && | |
- strspn(src->data, "+-0123456789Ee.") == src->len) | |
- { | |
- /* | |
- * might be a number. See if we can input it as a numeric | |
- * value. Ignore any actual parsed value. | |
- */ | |
- char *endptr = "junk"; | |
- long lval; | |
- | |
- lval = strtol(src->data, &endptr, 10); | |
- (void) lval; | |
- if (*endptr == '\0') | |
- { | |
- /* | |
- * strol man page says this means the whole string is | |
- * valid | |
- */ | |
- is_number = true; | |
- } | |
- else | |
- { | |
- /* not an int - try a double */ | |
- double dval; | |
+ hstoreToCString(str, VARDATA_ANY(in), VARSIZE_ANY(in), | |
+ SET_PRETTY_PRINT_VAR(JsonOutput | RootHashDecorated | LooseOutput)); | |
- dval = strtod(src->data, &endptr); | |
- (void) dval; | |
- if (*endptr == '\0') | |
- is_number = true; | |
- } | |
- } | |
- if (is_number) | |
- appendBinaryStringInfo(dst, src->data, src->len); | |
- else | |
- escape_json(dst, src->data); | |
- } | |
- strncpy(ptr, dst->data, dst->len); | |
- ptr += dst->len; | |
+ out = (text*)str->data; | |
- if (i + 1 != count) | |
- { | |
- *ptr++ = ','; | |
- *ptr++ = ' '; | |
- } | |
+ SET_VARSIZE(out, str->len); | |
} | |
- *ptr++ = '}'; | |
- *ptr = '\0'; | |
- PG_RETURN_TEXT_P(cstring_to_text(out)); | |
+ PG_RETURN_TEXT_P(out); | |
} | |
PG_FUNCTION_INFO_V1(hstore_to_json); | |
@@ -1354,74 +1541,247 @@ Datum | |
hstore_to_json(PG_FUNCTION_ARGS) | |
{ | |
HStore *in = PG_GETARG_HS(0); | |
- int buflen, | |
- i; | |
- int count = HS_COUNT(in); | |
- char *out, | |
- *ptr; | |
- char *base = STRPTR(in); | |
- HEntry *entries = ARRPTR(in); | |
- StringInfo src, | |
- dst; | |
+ text *out; | |
- if (count == 0) | |
- PG_RETURN_TEXT_P(cstring_to_text_with_len("{}",2)); | |
+ if (HS_ISEMPTY(in)) | |
+ { | |
+ out = cstring_to_text_with_len("{}",2); | |
+ } | |
+ else | |
+ { | |
+ StringInfo str; | |
- buflen = 3; | |
+ str = makeStringInfo(); | |
+ appendBinaryStringInfo(str, " ", 4); /* VARHDRSZ */ | |
- /* | |
- * Formula adjusted slightly from the logic in hstore_out. We have to take | |
- * account of out treatment of booleans to be a bit more pessimistic about | |
- * the length of values. | |
- */ | |
+ hstoreToCString(str, HS_ISEMPTY(in) ? NULL : VARDATA_ANY(in), VARSIZE_ANY(in), | |
+ SET_PRETTY_PRINT_VAR(JsonOutput | RootHashDecorated)); | |
+ | |
+ out = (text*)str->data; | |
+ | |
+ SET_VARSIZE(out, str->len); | |
+ } | |
+ | |
+ PG_RETURN_TEXT_P(out); | |
+} | |
+ | |
+PG_FUNCTION_INFO_V1(json_to_hstore); | |
+Datum json_to_hstore(PG_FUNCTION_ARGS); | |
+Datum | |
+json_to_hstore(PG_FUNCTION_ARGS) | |
+{ | |
+ text *json = PG_GETARG_TEXT_PP(0); | |
- for (i = 0; i < count; i++) | |
+ PG_RETURN_POINTER(hstoreDump(parseHStore(VARDATA_ANY(json), VARSIZE_ANY_EXHDR(json), true))); | |
+} | |
+ | |
+static Oid | |
+searchCast(Oid src, Oid dst, CoercionMethod *method) | |
+{ | |
+ Oid funcOid = InvalidOid; | |
+ HeapTuple tuple; | |
+ | |
+ tuple = SearchSysCache2(CASTSOURCETARGET, | |
+ ObjectIdGetDatum(src), | |
+ ObjectIdGetDatum(dst)); | |
+ | |
+ | |
+ *method = 0; | |
+ | |
+ if (HeapTupleIsValid(tuple)) | |
+ { | |
+ Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple); | |
+ | |
+ if (castForm->castmethod == COERCION_METHOD_FUNCTION) | |
+ funcOid = castForm->castfunc; | |
+ | |
+ *method = castForm->castmethod; | |
+ | |
+ ReleaseSysCache(tuple); | |
+ } | |
+ | |
+ return funcOid; | |
+} | |
+ | |
+PG_FUNCTION_INFO_V1(array_to_hstore); | |
+Datum array_to_hstore(PG_FUNCTION_ARGS); | |
+Datum | |
+array_to_hstore(PG_FUNCTION_ARGS) | |
+{ | |
+ ArrayType *array = PG_GETARG_ARRAYTYPE_P(0); | |
+ ArrayIterator iterator; | |
+ int i = 0; | |
+ Datum datum; | |
+ bool isnull; | |
+ int ncounters = ARR_NDIM(array), | |
+ *counters = palloc0(sizeof(*counters) * ncounters), | |
+ *dims = ARR_DIMS(array); | |
+ ToHStoreState *state = NULL; | |
+ HStoreValue value, *result; | |
+ Oid castOid = InvalidOid; | |
+ int valueType = hsvString; | |
+ FmgrInfo castInfo; | |
+ CoercionMethod method; | |
+ | |
+ if (ArrayGetNItems(ARR_NDIM(array), ARR_DIMS(array)) == 0) | |
+ PG_RETURN_POINTER(hstoreDump(NULL)); | |
+ | |
+ switch(ARR_ELEMTYPE(array)) | |
{ | |
- /* include "" and colon-space and comma-space */ | |
- buflen += 6 + 2 * HS_KEYLEN(entries, i); | |
- /* include "" only if nonnull */ | |
- buflen += 3 + (HS_VALISNULL(entries, i) | |
- ? 1 | |
- : 2 * HS_VALLEN(entries, i)); | |
+ case BOOLOID: | |
+ valueType = hsvBool; | |
+ break; | |
+ case INT2OID: | |
+ case INT4OID: | |
+ case INT8OID: | |
+ case FLOAT4OID: | |
+ case FLOAT8OID: | |
+ castOid = searchCast(ARR_ELEMTYPE(array), NUMERICOID, &method); | |
+ Assert(castOid != InvalidOid); | |
+ case NUMERICOID: | |
+ valueType = hsvNumeric; | |
+ break; | |
+ default: | |
+ castOid = searchCast(ARR_ELEMTYPE(array), TEXTOID, &method); | |
+ | |
+ if (castOid == InvalidOid && method != COERCION_METHOD_BINARY) | |
+ elog(ERROR, "Could not cast array's element type to text"); | |
+ case TEXTOID: | |
+ valueType = hsvString; | |
+ break; | |
} | |
- out = ptr = palloc(buflen); | |
+ if (castOid != InvalidOid) | |
+ fmgr_info(castOid, &castInfo); | |
- src = makeStringInfo(); | |
- dst = makeStringInfo(); | |
+ iterator = array_create_iterator(array, 0); | |
- *ptr++ = '{'; | |
+ value.type = hsvArray; | |
+ value.array.scalar = false; | |
+ for(i=0; i<ncounters; i++) | |
+ { | |
+ value.array.nelems = dims[i]; | |
+ result = pushHStoreValue(&state, WHS_BEGIN_ARRAY, &value); | |
+ } | |
- for (i = 0; i < count; i++) | |
+ while(array_iterate(iterator, &datum, &isnull)) | |
{ | |
- resetStringInfo(src); | |
- resetStringInfo(dst); | |
- appendBinaryStringInfo(src, HS_KEY(entries, base, i), HS_KEYLEN(entries, i)); | |
- escape_json(dst, src->data); | |
- strncpy(ptr, dst->data, dst->len); | |
- ptr += dst->len; | |
- *ptr++ = ':'; | |
- *ptr++ = ' '; | |
- resetStringInfo(dst); | |
- if (HS_VALISNULL(entries, i)) | |
- appendStringInfoString(dst, "null"); | |
+ i = ncounters - 1; | |
+ | |
+ if (counters[i] >= dims[i]) | |
+ { | |
+ while(i>=0 && counters[i] >= dims[i]) | |
+ { | |
+ counters[i] = 0; | |
+ result = pushHStoreValue(&state, WHS_END_ARRAY, NULL); | |
+ i--; | |
+ } | |
+ | |
+ Assert(i>=0); | |
+ | |
+ counters[i]++; | |
+ | |
+ value.type = hsvArray; | |
+ value.array.scalar = false; | |
+ for(i = i + 1; i<ncounters; i++) | |
+ { | |
+ counters[i] = 1; | |
+ value.array.nelems = dims[i]; | |
+ result = pushHStoreValue(&state, WHS_BEGIN_ARRAY, &value); | |
+ } | |
+ } | |
else | |
{ | |
- resetStringInfo(src); | |
- appendBinaryStringInfo(src, HS_VAL(entries, base, i), HS_VALLEN(entries, i)); | |
- escape_json(dst, src->data); | |
+ counters[i]++; | |
} | |
- strncpy(ptr, dst->data, dst->len); | |
- ptr += dst->len; | |
- if (i + 1 != count) | |
+ if (isnull) | |
+ { | |
+ value.type = hsvNull; | |
+ value.size = sizeof(HEntry); | |
+ } | |
+ else | |
{ | |
- *ptr++ = ','; | |
- *ptr++ = ' '; | |
+ value.type = valueType; | |
+ switch(valueType) | |
+ { | |
+ case hsvBool: | |
+ value.boolean = DatumGetBool(datum); | |
+ value.size = sizeof(HEntry); | |
+ break; | |
+ case hsvString: | |
+ if (castOid != InvalidOid) | |
+ datum = FunctionCall1(&castInfo, datum); | |
+ value.string.val = VARDATA_ANY(datum); | |
+ value.string.len = VARSIZE_ANY_EXHDR(datum); | |
+ value.size = sizeof(HEntry) + value.string.len; | |
+ break; | |
+ case hsvNumeric: | |
+ if (castOid != InvalidOid) | |
+ datum = FunctionCall1(&castInfo, datum); | |
+ value.numeric = DatumGetNumeric(datum); | |
+ value.size = sizeof(HEntry)*2 + VARSIZE_ANY(value.numeric); | |
+ break; | |
+ default: | |
+ elog(ERROR, "Impossible state: %d", valueType); | |
+ } | |
} | |
+ | |
+ result = pushHStoreValue(&state, WHS_ELEM, &value); | |
} | |
- *ptr++ = '}'; | |
- *ptr = '\0'; | |
- PG_RETURN_TEXT_P(cstring_to_text(out)); | |
+ for(i=0; i<ncounters; i++) | |
+ result = pushHStoreValue(&state, WHS_END_ARRAY, NULL); | |
+ | |
+ PG_RETURN_POINTER(hstoreDump(result)); | |
+} | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_print); | |
+Datum hstore_print(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_print(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ int flags = 0; | |
+ text *out; | |
+ StringInfo str; | |
+ | |
+ if (PG_GETARG_BOOL(1)) | |
+ flags |= PrettyPrint; | |
+ if (PG_GETARG_BOOL(2)) | |
+ flags |= ArrayCurlyBraces; | |
+ if (PG_GETARG_BOOL(3)) | |
+ flags |= RootHashDecorated; | |
+ if (PG_GETARG_BOOL(4)) | |
+ flags |= JsonOutput; | |
+ if (PG_GETARG_BOOL(5)) | |
+ flags |= LooseOutput; | |
+ | |
+ str = makeStringInfo(); | |
+ appendBinaryStringInfo(str, " ", 4); /* VARHDRSZ */ | |
+ | |
+ hstoreToCString(str, (HS_ISEMPTY(hs)) ? NULL : VARDATA(hs), VARSIZE(hs), flags); | |
+ | |
+ out = (text*)str->data; | |
+ SET_VARSIZE(out, str->len); | |
+ | |
+ PG_RETURN_TEXT_P(out); | |
+} | |
+ | |
+void _PG_init(void); | |
+void | |
+_PG_init(void) | |
+{ | |
+ DefineCustomBoolVariable( | |
+ "hstore.pretty_print", | |
+ "Enable pretty print", | |
+ "Enable pretty print of hstore type", | |
+ &pretty_print_var, | |
+ pretty_print_var, | |
+ PGC_USERSET, | |
+ GUC_NOT_IN_SAMPLE, | |
+ NULL, | |
+ NULL, | |
+ NULL | |
+ ); | |
} | |
diff --git a/contrib/hstore/hstore_op.c b/contrib/hstore/hstore_op.c | |
index 45edb04..1693187 100644 | |
--- a/contrib/hstore/hstore_op.c | |
+++ b/contrib/hstore/hstore_op.c | |
@@ -25,154 +25,578 @@ HSTORE_POLLUTE(hstore_skeys, skeys); | |
HSTORE_POLLUTE(hstore_svals, svals); | |
HSTORE_POLLUTE(hstore_each, each); | |
+static HStoreValue* | |
+arrayToHStoreSortedArray(ArrayType *a) | |
+{ | |
+ Datum *key_datums; | |
+ bool *key_nulls; | |
+ int key_count; | |
+ HStoreValue *v; | |
+ int i, | |
+ j; | |
+ bool hasNonUniq = false; | |
-/* | |
- * We're often finding a sequence of keys in ascending order. The | |
- * "lowbound" parameter is used to cache lower bounds of searches | |
- * between calls, based on this assumption. Pass NULL for it for | |
- * one-off or unordered searches. | |
- */ | |
-int | |
-hstoreFindKey(HStore *hs, int *lowbound, char *key, int keylen) | |
+ deconstruct_array(a, | |
+ TEXTOID, -1, false, 'i', | |
+ &key_datums, &key_nulls, &key_count); | |
+ | |
+ if (key_count == 0) | |
+ return NULL; | |
+ | |
+ v = palloc(sizeof(*v)); | |
+ v->type = hsvArray; | |
+ v->array.scalar = false; | |
+ | |
+ v->array.elems = palloc(sizeof(*v->hash.pairs) * key_count); | |
+ | |
+ for (i = 0, j = 0; i < key_count; i++) | |
+ { | |
+ if (!key_nulls[i]) | |
+ { | |
+ v->array.elems[j].type = hsvString; | |
+ v->array.elems[j].string.val = VARDATA(key_datums[i]); | |
+ v->array.elems[j].string.len = VARSIZE(key_datums[i]) - VARHDRSZ; | |
+ j++; | |
+ } | |
+ } | |
+ v->array.nelems = j; | |
+ | |
+ if (v->array.nelems > 1) | |
+ qsort_arg(v->array.elems, v->array.nelems, sizeof(*v->array.elems), | |
+ compareHStoreStringValue, &hasNonUniq); | |
+ | |
+ if (hasNonUniq) | |
+ { | |
+ HStoreValue *ptr = v->array.elems + 1, | |
+ *res = v->array.elems; | |
+ | |
+ while (ptr - v->array.elems < v->array.nelems) | |
+ { | |
+ if (!(ptr->string.len == res->string.len && | |
+ memcmp(ptr->string.val, res->string.val, ptr->string.len) == 0)) | |
+ { | |
+ res++; | |
+ *res = *ptr; | |
+ } | |
+ | |
+ ptr++; | |
+ } | |
+ | |
+ v->array.nelems = res + 1 - v->array.elems; | |
+ } | |
+ | |
+ return v; | |
+} | |
+ | |
+static HStoreValue* | |
+findInHStoreSortedArray(HStoreValue *a, uint32 *lowbound, char *key, uint32 keylen) | |
{ | |
- HEntry *entries = ARRPTR(hs); | |
- int stopLow = lowbound ? *lowbound : 0; | |
- int stopHigh = HS_COUNT(hs); | |
- int stopMiddle; | |
- char *base = STRPTR(hs); | |
+ HStoreValue *stopLow = a->array.elems + ((lowbound) ? *lowbound : 0), | |
+ *stopHigh = a->array.elems + a->array.nelems, | |
+ *stopMiddle; | |
while (stopLow < stopHigh) | |
{ | |
- int difference; | |
+ int diff; | |
stopMiddle = stopLow + (stopHigh - stopLow) / 2; | |
- if (HS_KEYLEN(entries, stopMiddle) == keylen) | |
- difference = memcmp(HS_KEY(entries, base, stopMiddle), key, keylen); | |
+ if (keylen == stopMiddle->string.len) | |
+ diff = memcmp(stopMiddle->string.val, key, keylen); | |
else | |
- difference = (HS_KEYLEN(entries, stopMiddle) > keylen) ? 1 : -1; | |
+ diff = (stopMiddle->string.len > keylen) ? 1 : -1; | |
- if (difference == 0) | |
+ if (diff == 0) | |
{ | |
if (lowbound) | |
- *lowbound = stopMiddle + 1; | |
+ *lowbound = (stopMiddle - a->array.elems) + 1; | |
return stopMiddle; | |
} | |
- else if (difference < 0) | |
+ else if (diff < 0) | |
+ { | |
stopLow = stopMiddle + 1; | |
+ } | |
else | |
+ { | |
stopHigh = stopMiddle; | |
+ } | |
} | |
if (lowbound) | |
- *lowbound = stopLow; | |
- return -1; | |
+ *lowbound = (stopLow - a->array.elems) + 1; | |
+ | |
+ return NULL; | |
} | |
-Pairs * | |
-hstoreArrayToPairs(ArrayType *a, int *npairs) | |
+PG_FUNCTION_INFO_V1(hstore_fetchval); | |
+Datum hstore_fetchval(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_fetchval(PG_FUNCTION_ARGS) | |
{ | |
- Datum *key_datums; | |
- bool *key_nulls; | |
- int key_count; | |
- Pairs *key_pairs; | |
- int bufsiz; | |
- int i, | |
- j; | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ text *key = PG_GETARG_TEXT_PP(1); | |
+ HStoreValue *v = NULL; | |
+ text *out; | |
- deconstruct_array(a, | |
- TEXTOID, -1, false, 'i', | |
- &key_datums, &key_nulls, &key_count); | |
+ if (!HS_ISEMPTY(hs)) | |
+ v = findUncompressedHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, | |
+ NULL, VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key)); | |
- if (key_count == 0) | |
+ if ((out = HStoreValueToText(v)) == NULL) | |
+ PG_RETURN_NULL(); | |
+ else | |
+ PG_RETURN_TEXT_P(out); | |
+} | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_fetchval_numeric); | |
+Datum hstore_fetchval_numeric(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_fetchval_numeric(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ text *key = PG_GETARG_TEXT_PP(1); | |
+ HStoreValue *v = NULL; | |
+ | |
+ if (!HS_ISEMPTY(hs)) | |
+ v = findUncompressedHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, | |
+ NULL, VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key)); | |
+ | |
+ if (v && v->type == hsvNumeric) | |
{ | |
- *npairs = 0; | |
- return NULL; | |
+ Numeric out = palloc(VARSIZE_ANY(v->numeric)); | |
+ | |
+ memcpy(out, v->numeric, VARSIZE_ANY(v->numeric)); | |
+ PG_RETURN_NUMERIC(out); | |
} | |
- key_pairs = palloc(sizeof(Pairs) * key_count); | |
+ PG_RETURN_NULL(); | |
+} | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_fetchval_boolean); | |
+Datum hstore_fetchval_boolean(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_fetchval_boolean(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ text *key = PG_GETARG_TEXT_PP(1); | |
+ HStoreValue *v = NULL; | |
- for (i = 0, j = 0; i < key_count; i++) | |
+ if (!HS_ISEMPTY(hs)) | |
+ v = findUncompressedHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, | |
+ NULL, VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key)); | |
+ | |
+ if (v && v->type == hsvBool) | |
+ PG_RETURN_BOOL(v->boolean); | |
+ | |
+ PG_RETURN_NULL(); | |
+} | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_fetchval_n); | |
+Datum hstore_fetchval_n(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_fetchval_n(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ int i = PG_GETARG_INT32(1); | |
+ HStoreValue *v = NULL; | |
+ text *out; | |
+ | |
+ if (!HS_ISEMPTY(hs)) | |
+ v = getHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, i); | |
+ | |
+ if ((out = HStoreValueToText(v)) == NULL) | |
+ PG_RETURN_NULL(); | |
+ else | |
+ PG_RETURN_TEXT_P(out); | |
+} | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_fetchval_n_numeric); | |
+Datum hstore_fetchval_n_numeric(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_fetchval_n_numeric(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ int i = PG_GETARG_INT32(1); | |
+ HStoreValue *v = NULL; | |
+ | |
+ if (!HS_ISEMPTY(hs)) | |
+ v = getHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, i); | |
+ | |
+ if (v && v->type == hsvNumeric) | |
{ | |
- if (!key_nulls[i]) | |
+ Numeric out = palloc(VARSIZE_ANY(v->numeric)); | |
+ | |
+ memcpy(out, v->numeric, VARSIZE_ANY(v->numeric)); | |
+ PG_RETURN_NUMERIC(out); | |
+ } | |
+ | |
+ PG_RETURN_NULL(); | |
+} | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_fetchval_n_boolean); | |
+Datum hstore_fetchval_n_boolean(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_fetchval_n_boolean(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ int i = PG_GETARG_INT32(1); | |
+ HStoreValue *v = NULL; | |
+ | |
+ if (!HS_ISEMPTY(hs)) | |
+ v = getHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, i); | |
+ | |
+ if (v && v->type == hsvBool) | |
+ PG_RETURN_BOOL(v->boolean); | |
+ | |
+ PG_RETURN_NULL(); | |
+} | |
+ | |
+static bool | |
+h_atoi(char *c, int l, int *acc) | |
+{ | |
+ bool negative = false; | |
+ char *p = c; | |
+ | |
+ *acc = 0; | |
+ | |
+ while(isspace(*p) && p - c < l) | |
+ p++; | |
+ | |
+ if (p - c >= l) | |
+ return false; | |
+ | |
+ if (*p == '-') | |
+ { | |
+ negative = true; | |
+ p++; | |
+ } | |
+ else if (*p == '+') | |
+ { | |
+ p++; | |
+ } | |
+ | |
+ if (p - c >= l) | |
+ return false; | |
+ | |
+ | |
+ while(p - c < l) | |
+ { | |
+ if (!isdigit(*p)) | |
+ return false; | |
+ | |
+ *acc *= 10; | |
+ *acc += (*p - '0'); | |
+ p++; | |
+ } | |
+ | |
+ if (negative) | |
+ *acc = - *acc; | |
+ | |
+ return true; | |
+} | |
+ | |
+static HStoreValue* | |
+hstoreDeepFetch(HStore *in, ArrayType *path) | |
+{ | |
+ HStoreValue *v = NULL; | |
+ static HStoreValue init /* could be returned */; | |
+ Datum *path_elems; | |
+ bool *path_nulls; | |
+ int path_len, i; | |
+ | |
+ Assert(ARR_ELEMTYPE(path) == TEXTOID); | |
+ | |
+ if (ARR_NDIM(path) > 1) | |
+ ereport(ERROR, | |
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), | |
+ errmsg("wrong number of array subscripts"))); | |
+ | |
+ if (HS_ROOT_COUNT(in) == 0) | |
+ return NULL; | |
+ | |
+ deconstruct_array(path, TEXTOID, -1, false, 'i', | |
+ &path_elems, &path_nulls, &path_len); | |
+ | |
+ init.type = hsvBinary; | |
+ init.size = VARSIZE(in); | |
+ init.binary.data = VARDATA(in); | |
+ init.binary.len = VARSIZE_ANY_EXHDR(in); | |
+ | |
+ v = &init; | |
+ | |
+ if (path_len == 0) | |
+ return v; | |
+ | |
+ for(i=0; v != NULL && i<path_len; i++) | |
+ { | |
+ uint32 header; | |
+ | |
+ if (v->type != hsvBinary || path_nulls[i]) | |
+ return NULL; | |
+ | |
+ header = *(uint32*)v->binary.data; | |
+ | |
+ if (header & HS_FLAG_HSTORE) | |
{ | |
- key_pairs[j].key = VARDATA(key_datums[i]); | |
- key_pairs[j].keylen = VARSIZE(key_datums[i]) - VARHDRSZ; | |
- key_pairs[j].val = NULL; | |
- key_pairs[j].vallen = 0; | |
- key_pairs[j].needfree = 0; | |
- key_pairs[j].isnull = 1; | |
- j++; | |
+ v = findUncompressedHStoreValue(v->binary.data, HS_FLAG_HSTORE, | |
+ NULL, VARDATA_ANY(path_elems[i]), VARSIZE_ANY_EXHDR(path_elems[i])); | |
+ } | |
+ else if (header & HS_FLAG_ARRAY) | |
+ { | |
+ int ith; | |
+ | |
+ if (h_atoi(VARDATA_ANY(path_elems[i]), VARSIZE_ANY_EXHDR(path_elems[i]), &ith) == false) | |
+ return NULL; | |
+ | |
+ if (ith < 0) | |
+ { | |
+ if (-ith > (int)(header & HS_COUNT_MASK)) | |
+ return NULL; | |
+ else | |
+ ith = ((int)(header & HS_COUNT_MASK)) + ith; | |
+ } | |
+ else | |
+ { | |
+ if (ith >= (int)(header & HS_COUNT_MASK)) | |
+ return NULL; | |
+ } | |
+ | |
+ v = getHStoreValue(v->binary.data, HS_FLAG_ARRAY, ith); | |
+ } | |
+ else | |
+ { | |
+ elog(PANIC,"wrong header type: %08x", header); | |
} | |
} | |
- *npairs = hstoreUniquePairs(key_pairs, j, &bufsiz); | |
+ return v; | |
+} | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_fetchval_path); | |
+Datum hstore_fetchval_path(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_fetchval_path(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ ArrayType *path = PG_GETARG_ARRAYTYPE_P(1); | |
+ text *out; | |
- return key_pairs; | |
+ if ((out = HStoreValueToText(hstoreDeepFetch(hs, path))) == NULL) | |
+ PG_RETURN_NULL(); | |
+ else | |
+ PG_RETURN_TEXT_P(out); | |
} | |
+PG_FUNCTION_INFO_V1(hstore_fetchval_path_numeric); | |
+Datum hstore_fetchval_path_numeric(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_fetchval_path_numeric(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ ArrayType *path = PG_GETARG_ARRAYTYPE_P(1); | |
+ HStoreValue *v = NULL; | |
+ | |
+ if (!HS_ISEMPTY(hs)) | |
+ v = hstoreDeepFetch(hs, path); | |
-PG_FUNCTION_INFO_V1(hstore_fetchval); | |
-Datum hstore_fetchval(PG_FUNCTION_ARGS); | |
+ if (v && v->type == hsvNumeric) | |
+ { | |
+ Numeric out = palloc(VARSIZE_ANY(v->numeric)); | |
+ | |
+ memcpy(out, v->numeric, VARSIZE_ANY(v->numeric)); | |
+ PG_RETURN_NUMERIC(out); | |
+ } | |
+ | |
+ PG_RETURN_NULL(); | |
+} | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_fetchval_path_boolean); | |
+Datum hstore_fetchval_path_boolean(PG_FUNCTION_ARGS); | |
Datum | |
-hstore_fetchval(PG_FUNCTION_ARGS) | |
+hstore_fetchval_path_boolean(PG_FUNCTION_ARGS) | |
{ | |
- HStore *hs = PG_GETARG_HS(0); | |
- text *key = PG_GETARG_TEXT_PP(1); | |
- HEntry *entries = ARRPTR(hs); | |
- text *out; | |
- int idx = hstoreFindKey(hs, NULL, | |
- VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key)); | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ ArrayType *path = PG_GETARG_ARRAYTYPE_P(1); | |
+ HStoreValue *v = NULL; | |
+ | |
+ if (!HS_ISEMPTY(hs)) | |
+ v = hstoreDeepFetch(hs, path); | |
+ | |
+ if (v && v->type == hsvBool) | |
+ PG_RETURN_BOOL(v->boolean); | |
+ | |
+ PG_RETURN_NULL(); | |
+} | |
+ | |
+static HStore * | |
+HStoreValueToHStore(HStoreValue *v) | |
+{ | |
+ HStore *out; | |
+ | |
+ if (v == NULL || v->type == hsvNull) | |
+ { | |
+ out = NULL; | |
+ } | |
+ else if (v->type == hsvString || v->type == hsvBool || v->type == hsvNumeric) | |
+ { | |
+ ToHStoreState *state = NULL; | |
+ HStoreValue *res; | |
+ int r; | |
+ HStoreValue scalarArray; | |
+ | |
+ scalarArray.type = hsvArray; | |
+ scalarArray.array.scalar = true; | |
+ scalarArray.array.nelems = 1; | |
+ | |
+ pushHStoreValue(&state, WHS_BEGIN_ARRAY, &scalarArray); | |
+ pushHStoreValue(&state, WHS_ELEM, v); | |
+ res = pushHStoreValue(&state, WHS_END_ARRAY, NULL); | |
+ | |
+ out = palloc(VARHDRSZ + res->size); | |
+ SET_VARSIZE(out, VARHDRSZ + res->size); | |
+ r = compressHStore(res, VARDATA(out)); | |
+ Assert(r <= res->size); | |
+ SET_VARSIZE(out, r + VARHDRSZ); | |
+ } | |
+ else | |
+ { | |
+ out = palloc(VARHDRSZ + v->size); | |
+ | |
+ Assert(v->type == hsvBinary); | |
+ SET_VARSIZE(out, VARHDRSZ + v->binary.len); | |
+ memcpy(VARDATA(out), v->binary.data, v->binary.len); | |
+ } | |
+ | |
+ return out; | |
+} | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_fetchval_hstore); | |
+Datum hstore_fetchval_hstore(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_fetchval_hstore(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ text *key = PG_GETARG_TEXT_PP(1); | |
+ HStoreValue *v = NULL; | |
+ HStore *out; | |
+ | |
+ if (!HS_ISEMPTY(hs)) | |
+ v = findUncompressedHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, | |
+ NULL, VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key)); | |
- if (idx < 0 || HS_VALISNULL(entries, idx)) | |
+ | |
+ if ((out = HStoreValueToHStore(v)) == NULL) | |
PG_RETURN_NULL(); | |
+ else | |
+ PG_RETURN_POINTER(out); | |
+} | |
- out = cstring_to_text_with_len(HS_VAL(entries, STRPTR(hs), idx), | |
- HS_VALLEN(entries, idx)); | |
+PG_FUNCTION_INFO_V1(hstore_fetchval_n_hstore); | |
+Datum hstore_fetchval_n_hstore(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_fetchval_n_hstore(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ int i = PG_GETARG_INT32(1); | |
+ HStoreValue *v = NULL; | |
+ HStore *out; | |
- PG_RETURN_TEXT_P(out); | |
+ if (!HS_ISEMPTY(hs)) | |
+ v = getHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, i); | |
+ | |
+ if ((out = HStoreValueToHStore(v)) == NULL) | |
+ PG_RETURN_NULL(); | |
+ else | |
+ PG_RETURN_POINTER(out); | |
} | |
+PG_FUNCTION_INFO_V1(hstore_fetchval_path_hstore); | |
+Datum hstore_fetchval_path_hstore(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_fetchval_path_hstore(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ ArrayType *path = PG_GETARG_ARRAYTYPE_P(1); | |
+ HStore *out; | |
+ | |
+ if ((out = HStoreValueToHStore(hstoreDeepFetch(hs, path))) == NULL) | |
+ PG_RETURN_NULL(); | |
+ else | |
+ PG_RETURN_POINTER(out); | |
+} | |
PG_FUNCTION_INFO_V1(hstore_exists); | |
Datum hstore_exists(PG_FUNCTION_ARGS); | |
Datum | |
hstore_exists(PG_FUNCTION_ARGS) | |
{ | |
- HStore *hs = PG_GETARG_HS(0); | |
- text *key = PG_GETARG_TEXT_PP(1); | |
- int idx = hstoreFindKey(hs, NULL, | |
- VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key)); | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ text *key = PG_GETARG_TEXT_PP(1); | |
+ HStoreValue *v = NULL; | |
+ | |
+ if (!HS_ISEMPTY(hs)) | |
+ v = findUncompressedHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, | |
+ NULL, VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key)); | |
+ | |
+ PG_RETURN_BOOL(v != NULL); | |
+} | |
+ | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_exists_idx); | |
+Datum hstore_exists_idx(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_exists_idx(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ int ith = PG_GETARG_INT32(1); | |
+ HStoreValue *v = NULL; | |
+ | |
+ if (!HS_ISEMPTY(hs)) | |
+ v = getHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, ith); | |
+ | |
+ PG_RETURN_BOOL(v != NULL); | |
+} | |
- PG_RETURN_BOOL(idx >= 0); | |
+PG_FUNCTION_INFO_V1(hstore_exists_path); | |
+Datum hstore_exists_path(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_exists_path(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ ArrayType *path = PG_GETARG_ARRAYTYPE_P(1); | |
+ | |
+ PG_RETURN_BOOL(hstoreDeepFetch(hs, path) != NULL); | |
} | |
+ | |
PG_FUNCTION_INFO_V1(hstore_exists_any); | |
Datum hstore_exists_any(PG_FUNCTION_ARGS); | |
Datum | |
hstore_exists_any(PG_FUNCTION_ARGS) | |
{ | |
- HStore *hs = PG_GETARG_HS(0); | |
- ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1); | |
- int nkeys; | |
- Pairs *key_pairs = hstoreArrayToPairs(keys, &nkeys); | |
- int i; | |
- int lowbound = 0; | |
- bool res = false; | |
- | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1); | |
+ HStoreValue *v = arrayToHStoreSortedArray(keys); | |
+ int i; | |
+ uint32 *plowbound = NULL, lowbound = 0; | |
+ bool res = false; | |
+ | |
+ if (HS_ISEMPTY(hs) || v == NULL || v->hash.npairs == 0) | |
+ PG_RETURN_BOOL(false); | |
+ | |
+ if (HS_ROOT_IS_HASH(hs)) | |
+ plowbound = &lowbound; | |
/* | |
* we exploit the fact that the pairs list is already sorted into strictly | |
- * increasing order to narrow the hstoreFindKey search; each search can | |
+ * increasing order to narrow the findUncompressedHStoreValue search; each search can | |
* start one entry past the previous "found" entry, or at the lower bound | |
* of the last search. | |
*/ | |
- for (i = 0; i < nkeys; i++) | |
+ for (i = 0; i < v->array.nelems; i++) | |
{ | |
- int idx = hstoreFindKey(hs, &lowbound, | |
- key_pairs[i].key, key_pairs[i].keylen); | |
- | |
- if (idx >= 0) | |
+ if (findUncompressedHStoreValueByValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, plowbound, | |
+ v->array.elems + i) != NULL) | |
{ | |
res = true; | |
break; | |
@@ -188,26 +612,34 @@ Datum hstore_exists_all(PG_FUNCTION_ARGS); | |
Datum | |
hstore_exists_all(PG_FUNCTION_ARGS) | |
{ | |
- HStore *hs = PG_GETARG_HS(0); | |
- ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1); | |
- int nkeys; | |
- Pairs *key_pairs = hstoreArrayToPairs(keys, &nkeys); | |
- int i; | |
- int lowbound = 0; | |
- bool res = true; | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ ArrayType *keys = PG_GETARG_ARRAYTYPE_P(1); | |
+ HStoreValue *v = arrayToHStoreSortedArray(keys); | |
+ int i; | |
+ uint32 *plowbound = NULL, lowbound = 0; | |
+ bool res = true; | |
+ | |
+ if (HS_ISEMPTY(hs) || v == NULL || v->array.nelems == 0) | |
+ { | |
+ | |
+ if (v == NULL || v->array.nelems == 0) | |
+ PG_RETURN_BOOL(true); /* compatibility */ | |
+ else | |
+ PG_RETURN_BOOL(false); | |
+ } | |
+ if (HS_ROOT_IS_HASH(hs)) | |
+ plowbound = &lowbound; | |
/* | |
* we exploit the fact that the pairs list is already sorted into strictly | |
- * increasing order to narrow the hstoreFindKey search; each search can | |
+ * increasing order to narrow the findUncompressedHStoreValue search; each search can | |
* start one entry past the previous "found" entry, or at the lower bound | |
* of the last search. | |
*/ | |
- for (i = 0; i < nkeys; i++) | |
+ for (i = 0; i < v->array.nelems; i++) | |
{ | |
- int idx = hstoreFindKey(hs, &lowbound, | |
- key_pairs[i].key, key_pairs[i].keylen); | |
- | |
- if (idx < 0) | |
+ if (findUncompressedHStoreValueByValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, plowbound, | |
+ v->array.elems + i) == NULL) | |
{ | |
res = false; | |
break; | |
@@ -223,14 +655,15 @@ Datum hstore_defined(PG_FUNCTION_ARGS); | |
Datum | |
hstore_defined(PG_FUNCTION_ARGS) | |
{ | |
- HStore *hs = PG_GETARG_HS(0); | |
- text *key = PG_GETARG_TEXT_PP(1); | |
- HEntry *entries = ARRPTR(hs); | |
- int idx = hstoreFindKey(hs, NULL, | |
- VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key)); | |
- bool res = (idx >= 0 && !HS_VALISNULL(entries, idx)); | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ text *key = PG_GETARG_TEXT_PP(1); | |
+ HStoreValue *v = NULL; | |
- PG_RETURN_BOOL(res); | |
+ if (!HS_ISEMPTY(hs)) | |
+ v = findUncompressedHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, | |
+ NULL, VARDATA_ANY(key), VARSIZE_ANY_EXHDR(key)); | |
+ | |
+ PG_RETURN_BOOL(!(v == NULL || v->type == hsvNull)); | |
} | |
@@ -239,483 +672,1300 @@ Datum hstore_delete(PG_FUNCTION_ARGS); | |
Datum | |
hstore_delete(PG_FUNCTION_ARGS) | |
{ | |
- HStore *hs = PG_GETARG_HS(0); | |
- text *key = PG_GETARG_TEXT_PP(1); | |
- char *keyptr = VARDATA_ANY(key); | |
- int keylen = VARSIZE_ANY_EXHDR(key); | |
- HStore *out = palloc(VARSIZE(hs)); | |
- char *bufs, | |
- *bufd, | |
- *ptrd; | |
- HEntry *es, | |
- *ed; | |
- int i; | |
- int count = HS_COUNT(hs); | |
- int outcount = 0; | |
- | |
- SET_VARSIZE(out, VARSIZE(hs)); | |
- HS_SETCOUNT(out, count); /* temporary! */ | |
+ HStore *in = PG_GETARG_HS(0); | |
+ text *key = PG_GETARG_TEXT_PP(1); | |
+ char *keyptr = VARDATA_ANY(key); | |
+ int keylen = VARSIZE_ANY_EXHDR(key); | |
+ HStore *out = palloc(VARSIZE(in)); | |
+ ToHStoreState *toState = NULL; | |
+ HStoreIterator *it; | |
+ uint32 r; | |
+ HStoreValue v, *res = NULL; | |
+ bool skipNested = false; | |
+ | |
+ SET_VARSIZE(out, VARSIZE(in)); | |
+ | |
+ if (HS_ISEMPTY(in)) | |
+ PG_RETURN_POINTER(out); | |
- bufs = STRPTR(hs); | |
- es = ARRPTR(hs); | |
- bufd = ptrd = STRPTR(out); | |
- ed = ARRPTR(out); | |
+ it = HStoreIteratorInit(VARDATA(in)); | |
- for (i = 0; i < count; ++i) | |
+ while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0) | |
{ | |
- int len = HS_KEYLEN(es, i); | |
- char *ptrs = HS_KEY(es, bufs, i); | |
+ skipNested = true; | |
- if (!(len == keylen && memcmp(ptrs, keyptr, keylen) == 0)) | |
+ if ((r == WHS_ELEM || r == WHS_KEY) && | |
+ (v.type == hsvString && keylen == v.string.len && memcmp(keyptr, v.string.val, keylen) == 0)) | |
{ | |
- int vallen = HS_VALLEN(es, i); | |
+ if (r == WHS_KEY) | |
+ /* skip corresponding value */ | |
+ HStoreIteratorGet(&it, &v, true); | |
- HS_COPYITEM(ed, bufd, ptrd, ptrs, len, vallen, HS_VALISNULL(es, i)); | |
- ++outcount; | |
+ continue; | |
} | |
+ | |
+ res = pushHStoreValue(&toState, r, &v); | |
} | |
- HS_FINALIZE(out, outcount, bufd, ptrd); | |
+ if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) || | |
+ (res->type == hsvHash && res->hash.npairs == 0) ) | |
+ { | |
+ SET_VARSIZE(out, VARHDRSZ); | |
+ } | |
+ else | |
+ { | |
+ r = compressHStore(res, VARDATA(out)); | |
+ SET_VARSIZE(out, r + VARHDRSZ); | |
+ } | |
PG_RETURN_POINTER(out); | |
} | |
- | |
PG_FUNCTION_INFO_V1(hstore_delete_array); | |
Datum hstore_delete_array(PG_FUNCTION_ARGS); | |
Datum | |
hstore_delete_array(PG_FUNCTION_ARGS) | |
{ | |
- HStore *hs = PG_GETARG_HS(0); | |
- HStore *out = palloc(VARSIZE(hs)); | |
- int hs_count = HS_COUNT(hs); | |
- char *ps, | |
- *bufd, | |
- *pd; | |
- HEntry *es, | |
- *ed; | |
- int i, | |
- j; | |
- int outcount = 0; | |
- ArrayType *key_array = PG_GETARG_ARRAYTYPE_P(1); | |
- int nkeys; | |
- Pairs *key_pairs = hstoreArrayToPairs(key_array, &nkeys); | |
- | |
- SET_VARSIZE(out, VARSIZE(hs)); | |
- HS_SETCOUNT(out, hs_count); /* temporary! */ | |
- | |
- ps = STRPTR(hs); | |
- es = ARRPTR(hs); | |
- bufd = pd = STRPTR(out); | |
- ed = ARRPTR(out); | |
- | |
- if (nkeys == 0) | |
+ HStore *in = PG_GETARG_HS(0); | |
+ HStore *out = palloc(VARSIZE(in)); | |
+ HStoreValue *a = arrayToHStoreSortedArray(PG_GETARG_ARRAYTYPE_P(1)); | |
+ HStoreIterator *it; | |
+ ToHStoreState *toState = NULL; | |
+ uint32 r, i = 0; | |
+ HStoreValue v, *res = NULL; | |
+ bool skipNested = false; | |
+ bool isHash = false; | |
+ | |
+ | |
+ if (HS_ISEMPTY(in) || a == NULL || a->array.nelems == 0) | |
{ | |
- /* return a copy of the input, unchanged */ | |
- memcpy(out, hs, VARSIZE(hs)); | |
- HS_FIXSIZE(out, hs_count); | |
- HS_SETCOUNT(out, hs_count); | |
+ memcpy(out, in, VARSIZE(in)); | |
PG_RETURN_POINTER(out); | |
} | |
- /* | |
- * this is in effect a merge between hs and key_pairs, both of which are | |
- * already sorted by (keylen,key); we take keys from hs only | |
- */ | |
+ it = HStoreIteratorInit(VARDATA(in)); | |
- for (i = j = 0; i < hs_count;) | |
+ while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0) | |
{ | |
- int difference; | |
- if (j >= nkeys) | |
- difference = -1; | |
- else | |
+ if (skipNested == false) | |
{ | |
- int skeylen = HS_KEYLEN(es, i); | |
- | |
- if (skeylen == key_pairs[j].keylen) | |
- difference = memcmp(HS_KEY(es, ps, i), | |
- key_pairs[j].key, | |
- key_pairs[j].keylen); | |
- else | |
- difference = (skeylen > key_pairs[j].keylen) ? 1 : -1; | |
+ Assert(v.type == hsvArray || v.type == hsvHash); | |
+ isHash = (v.type == hsvArray) ? false : true; | |
+ skipNested = true; | |
} | |
- if (difference > 0) | |
- ++j; | |
- else if (difference == 0) | |
- ++i, ++j; | |
- else | |
+ if ((r == WHS_ELEM || r == WHS_KEY) && v.type == hsvString && i < a->array.nelems) | |
{ | |
- HS_COPYITEM(ed, bufd, pd, | |
- HS_KEY(es, ps, i), HS_KEYLEN(es, i), | |
- HS_VALLEN(es, i), HS_VALISNULL(es, i)); | |
- ++outcount; | |
- ++i; | |
- } | |
- } | |
+ int diff; | |
- HS_FINALIZE(out, outcount, bufd, pd); | |
+ if (isHash) | |
+ { | |
+ do { | |
+ diff = compareHStoreStringValue(&v, a->array.elems + i, NULL); | |
- PG_RETURN_POINTER(out); | |
-} | |
+ if (diff >= 0) | |
+ i++; | |
+ } while(diff > 0 && i < a->array.nelems); | |
+ } | |
+ else | |
+ { | |
+ diff = (findInHStoreSortedArray(a, NULL, v.string.val, v.string.len) == NULL) ? 1 : 0; | |
+ } | |
+ if (diff == 0) | |
+ { | |
+ if (r == WHS_KEY) | |
+ /* skip corresponding value */ | |
+ HStoreIteratorGet(&it, &v, true); | |
-PG_FUNCTION_INFO_V1(hstore_delete_hstore); | |
+ continue; | |
+ } | |
+ } | |
+ | |
+ res = pushHStoreValue(&toState, r, &v); | |
+ } | |
+ | |
+ if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) || | |
+ (res->type == hsvHash && res->hash.npairs == 0) ) | |
+ { | |
+ SET_VARSIZE(out, VARHDRSZ); | |
+ } | |
+ else | |
+ { | |
+ r = compressHStore(res, VARDATA(out)); | |
+ SET_VARSIZE(out, r + VARHDRSZ); | |
+ } | |
+ | |
+ PG_RETURN_POINTER(out); | |
+} | |
+ | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_delete_hstore); | |
Datum hstore_delete_hstore(PG_FUNCTION_ARGS); | |
Datum | |
hstore_delete_hstore(PG_FUNCTION_ARGS) | |
{ | |
- HStore *hs = PG_GETARG_HS(0); | |
- HStore *hs2 = PG_GETARG_HS(1); | |
- HStore *out = palloc(VARSIZE(hs)); | |
- int hs_count = HS_COUNT(hs); | |
- int hs2_count = HS_COUNT(hs2); | |
- char *ps, | |
- *ps2, | |
- *bufd, | |
- *pd; | |
- HEntry *es, | |
- *es2, | |
- *ed; | |
- int i, | |
- j; | |
- int outcount = 0; | |
- | |
- SET_VARSIZE(out, VARSIZE(hs)); | |
- HS_SETCOUNT(out, hs_count); /* temporary! */ | |
- | |
- ps = STRPTR(hs); | |
- es = ARRPTR(hs); | |
- ps2 = STRPTR(hs2); | |
- es2 = ARRPTR(hs2); | |
- bufd = pd = STRPTR(out); | |
- ed = ARRPTR(out); | |
- | |
- if (hs2_count == 0) | |
- { | |
- /* return a copy of the input, unchanged */ | |
- memcpy(out, hs, VARSIZE(hs)); | |
- HS_FIXSIZE(out, hs_count); | |
- HS_SETCOUNT(out, hs_count); | |
+ HStore *hs1 = PG_GETARG_HS(0); | |
+ HStore *hs2 = PG_GETARG_HS(1); | |
+ HStore *out = palloc(VARSIZE(hs1)); | |
+ HStoreIterator *it1, *it2; | |
+ ToHStoreState *toState = NULL; | |
+ uint32 r1, r2; | |
+ HStoreValue v1, v2, *res = NULL; | |
+ bool isHash1, isHash2; | |
+ | |
+ if (HS_ISEMPTY(hs1) || HS_ISEMPTY(hs2)) | |
+ { | |
+ memcpy(out, hs1, VARSIZE(hs1)); | |
PG_RETURN_POINTER(out); | |
} | |
- /* | |
- * this is in effect a merge between hs and hs2, both of which are already | |
- * sorted by (keylen,key); we take keys from hs only; for equal keys, we | |
- * take the value from hs unless the values are equal | |
- */ | |
+ it1 = HStoreIteratorInit(VARDATA(hs1)); | |
+ r1 = HStoreIteratorGet(&it1, &v1, false); | |
+ isHash1 = (v1.type == hsvArray) ? false : true; | |
+ | |
+ it2 = HStoreIteratorInit(VARDATA(hs2)); | |
+ r2 = HStoreIteratorGet(&it2, &v2, false); | |
+ isHash2 = (v2.type == hsvArray) ? false : true; | |
+ | |
+ res = pushHStoreValue(&toState, r1, &v1); | |
- for (i = j = 0; i < hs_count;) | |
+ if (isHash1 == true && isHash2 == true) | |
{ | |
- int difference; | |
+ bool fin2 = false, | |
+ keyIsDef = false; | |
- if (j >= hs2_count) | |
- difference = -1; | |
- else | |
+ while((r1 = HStoreIteratorGet(&it1, &v1, true)) != 0) | |
+ { | |
+ if (r1 == WHS_KEY && fin2 == false) | |
+ { | |
+ int diff = 1; | |
+ | |
+ if (keyIsDef) | |
+ r2 = WHS_KEY; | |
+ | |
+ while(keyIsDef || (r2 = HStoreIteratorGet(&it2, &v2, true)) != 0) | |
+ { | |
+ if (r2 != WHS_KEY) | |
+ continue; | |
+ | |
+ diff = compareHStoreStringValue(&v1, &v2, NULL); | |
+ | |
+ if (diff > 0 && keyIsDef) | |
+ keyIsDef = false; | |
+ if (diff <= 0) | |
+ break; | |
+ } | |
+ | |
+ if (r2 == 0) | |
+ { | |
+ fin2 = true; | |
+ } | |
+ else if (diff == 0) | |
+ { | |
+ HStoreValue vk; | |
+ | |
+ keyIsDef = false; | |
+ | |
+ r1 = HStoreIteratorGet(&it1, &vk, true); | |
+ r2 = HStoreIteratorGet(&it2, &v2, true); | |
+ | |
+ Assert(r1 == WHS_VALUE && r2 == WHS_VALUE); | |
+ | |
+ if (compareHStoreValue(&vk, &v2) != 0) | |
+ { | |
+ res = pushHStoreValue(&toState, WHS_KEY, &v1); | |
+ res = pushHStoreValue(&toState, WHS_VALUE, &vk); | |
+ } | |
+ | |
+ continue; | |
+ } | |
+ else | |
+ { | |
+ keyIsDef = true; | |
+ } | |
+ } | |
+ | |
+ res = pushHStoreValue(&toState, r1, &v1); | |
+ } | |
+ } | |
+ else | |
+ { | |
+ while((r1 = HStoreIteratorGet(&it1, &v1, true)) != 0) | |
{ | |
- int skeylen = HS_KEYLEN(es, i); | |
- int s2keylen = HS_KEYLEN(es2, j); | |
- if (skeylen == s2keylen) | |
- difference = memcmp(HS_KEY(es, ps, i), | |
- HS_KEY(es2, ps2, j), | |
- skeylen); | |
+ if (r1 == WHS_ELEM || r1 == WHS_KEY) | |
+ { | |
+ int diff = 1; | |
+ | |
+ it2 = HStoreIteratorInit(VARDATA(hs2)); | |
+ | |
+ r2 = HStoreIteratorGet(&it2, &v2, false); | |
+ | |
+ while(diff && (r2 = HStoreIteratorGet(&it2, &v2, true)) != 0) | |
+ { | |
+ if (r2 == WHS_KEY || r2 == WHS_VALUE || r2 == WHS_ELEM) | |
+ diff = compareHStoreValue(&v1, &v2); | |
+ } | |
+ | |
+ if (diff == 0) | |
+ { | |
+ if (r1 == WHS_KEY) | |
+ HStoreIteratorGet(&it1, &v1, true); | |
+ continue; | |
+ } | |
+ } | |
+ | |
+ res = pushHStoreValue(&toState, r1, &v1); | |
+ } | |
+ } | |
+ | |
+ if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) || | |
+ (res->type == hsvHash && res->hash.npairs == 0) ) | |
+ { | |
+ SET_VARSIZE(out, VARHDRSZ); | |
+ } | |
+ else | |
+ { | |
+ int r = compressHStore(res, VARDATA(out)); | |
+ SET_VARSIZE(out, r + VARHDRSZ); | |
+ } | |
+ | |
+ PG_RETURN_POINTER(out); | |
+} | |
+ | |
+static HStoreValue* | |
+deletePathDo(HStoreIterator **it, Datum *path_elems, bool *path_nulls, int path_len, | |
+ ToHStoreState **st, int level) | |
+{ | |
+ HStoreValue v, *res = NULL; | |
+ int r; | |
+ | |
+ r = HStoreIteratorGet(it, &v, false); | |
+ | |
+ if (r == WHS_BEGIN_ARRAY) | |
+ { | |
+ int skipIdx, i; | |
+ uint32 n = v.array.nelems; | |
+ | |
+ skipIdx = n; | |
+ if (level >= path_len || path_nulls[level] || | |
+ h_atoi(VARDATA_ANY(path_elems[level]), VARSIZE_ANY_EXHDR(path_elems[level]), &skipIdx) == false) | |
+ { | |
+ skipIdx = n; | |
+ } | |
+ else if (skipIdx < 0) | |
+ { | |
+ if (-skipIdx > n) | |
+ skipIdx = n; | |
else | |
- difference = (skeylen > s2keylen) ? 1 : -1; | |
+ skipIdx = n + skipIdx; | |
+ } | |
+ | |
+ if (skipIdx > n) | |
+ skipIdx = n; | |
+ | |
+ if (skipIdx == 0 && n == 1) | |
+ { | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_ELEM); | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_END_ARRAY); | |
+ return NULL; | |
+ } | |
+ | |
+ pushHStoreValue(st, r, &v); | |
+ | |
+ for(i=0; i<skipIdx; i++) { | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_ELEM); | |
+ res = pushHStoreValue(st, r, &v); | |
+ } | |
+ | |
+ if (level >= path_len || skipIdx == n) { | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_END_ARRAY); | |
+ res = pushHStoreValue(st, r, &v); | |
+ return res; | |
+ } | |
+ | |
+ if (level == path_len - 1) | |
+ { | |
+ /* last level in path, skip all elem */ | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_ELEM); | |
+ } | |
+ else | |
+ { | |
+ res = deletePathDo(it, path_elems, path_nulls, path_len, st, level + 1); | |
+ } | |
+ | |
+ for(i = skipIdx + 1; i<n; i++) { | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_ELEM); | |
+ res = pushHStoreValue(st, r, &v); | |
+ } | |
+ | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_END_ARRAY); | |
+ res = pushHStoreValue(st, r, &v); | |
+ } | |
+ else if (r == WHS_BEGIN_HASH) | |
+ { | |
+ int i; | |
+ uint32 n = v.hash.npairs; | |
+ HStoreValue k; | |
+ bool done = false; | |
+ | |
+ if (n == 1 && level == path_len - 1) | |
+ { | |
+ r = HStoreIteratorGet(it, &k, false); | |
+ Assert(r == WHS_KEY); | |
+ | |
+ if ( path_nulls[level] == false && k.string.len == VARSIZE_ANY_EXHDR(path_elems[level]) && | |
+ memcmp(k.string.val, VARDATA_ANY(path_elems[level]), k.string.len) == 0) | |
+ { | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_VALUE); | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_END_HASH); | |
+ return NULL; | |
+ } | |
+ | |
+ pushHStoreValue(st, WHS_BEGIN_HASH, &v); | |
+ pushHStoreValue(st, WHS_KEY, &k); | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_VALUE); | |
+ pushHStoreValue(st, r, &v); | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_END_HASH); | |
+ return pushHStoreValue(st, r, &v); | |
+ } | |
+ | |
+ pushHStoreValue(st, WHS_BEGIN_HASH, &v); | |
+ | |
+ if (level >= path_len || path_nulls[level]) | |
+ done = true; | |
+ | |
+ for(i=0; i<n; i++) | |
+ { | |
+ r = HStoreIteratorGet(it, &k, false); | |
+ Assert(r == WHS_KEY); | |
+ | |
+ if (done == false && | |
+ k.string.len == VARSIZE_ANY_EXHDR(path_elems[level]) && | |
+ memcmp(k.string.val, VARDATA_ANY(path_elems[level]), k.string.len) == 0) | |
+ { | |
+ done = true; | |
+ | |
+ if (level == path_len - 1) | |
+ { | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_VALUE); | |
+ } | |
+ else | |
+ { | |
+ pushHStoreValue(st, r, &k); | |
+ res = deletePathDo(it, path_elems, path_nulls, path_len, st, level + 1); | |
+ if (res == NULL) | |
+ { | |
+ v.type = hsvNull; | |
+ pushHStoreValue(st, WHS_VALUE, &v); | |
+ } | |
+ } | |
+ | |
+ continue; | |
+ } | |
+ | |
+ pushHStoreValue(st, r, &k); | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_VALUE); | |
+ pushHStoreValue(st, r, &v); | |
+ } | |
+ | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_END_HASH); | |
+ res = pushHStoreValue(st, r, &v); | |
+ } | |
+ else if (r == WHS_ELEM || r == WHS_VALUE) /* just a string or null */ | |
+ { | |
+ pushHStoreValue(st, r, &v); | |
+ res = (void*)0x01; /* dummy value */ | |
+ } | |
+ else | |
+ { | |
+ elog(PANIC, "impossible state"); | |
+ } | |
+ | |
+ return res; | |
+} | |
+ | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_delete_path); | |
+Datum hstore_delete_path(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_delete_path(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *in = PG_GETARG_HS(0); | |
+ HStore *out = palloc(VARSIZE(in)); | |
+ ArrayType *path = PG_GETARG_ARRAYTYPE_P(1); | |
+ HStoreValue *res = NULL; | |
+ Datum *path_elems; | |
+ bool *path_nulls; | |
+ int path_len; | |
+ HStoreIterator *it; | |
+ ToHStoreState *st = NULL; | |
+ | |
+ Assert(ARR_ELEMTYPE(path) == TEXTOID); | |
+ | |
+ if (ARR_NDIM(path) > 1) | |
+ ereport(ERROR, | |
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), | |
+ errmsg("wrong number of array subscripts"))); | |
+ | |
+ if (HS_ROOT_COUNT(in) == 0) | |
+ { | |
+ memcpy(out, in, VARSIZE(in)); | |
+ PG_RETURN_POINTER(out); | |
+ } | |
+ | |
+ deconstruct_array(path, TEXTOID, -1, false, 'i', | |
+ &path_elems, &path_nulls, &path_len); | |
+ | |
+ if (path_len == 0) | |
+ { | |
+ memcpy(out, in, VARSIZE(in)); | |
+ PG_RETURN_POINTER(out); | |
+ } | |
+ | |
+ it = HStoreIteratorInit(VARDATA(in)); | |
+ | |
+ res = deletePathDo(&it, path_elems, path_nulls, path_len, &st, 0); | |
+ | |
+ if (res == NULL) | |
+ { | |
+ SET_VARSIZE(out, VARHDRSZ); | |
+ } | |
+ else | |
+ { | |
+ int sz; | |
+ | |
+ sz = compressHStore(res, VARDATA(out)); | |
+ SET_VARSIZE(out, sz + VARHDRSZ); | |
+ } | |
+ | |
+ PG_RETURN_POINTER(out); | |
+} | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_delete_idx); | |
+Datum hstore_delete_idx(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_delete_idx(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *in = PG_GETARG_HS(0); | |
+ int idx = PG_GETARG_INT32(1); | |
+ HStore *out = palloc(VARSIZE(in)); | |
+ ToHStoreState *toState = NULL; | |
+ HStoreIterator *it; | |
+ uint32 r, i = 0, n; | |
+ HStoreValue v, *res = NULL; | |
+ | |
+ if (HS_ISEMPTY(in)) | |
+ { | |
+ memcpy(out, in, VARSIZE(in)); | |
+ PG_RETURN_POINTER(out); | |
+ } | |
+ | |
+ it = HStoreIteratorInit(VARDATA(in)); | |
+ | |
+ r = HStoreIteratorGet(&it, &v, false); | |
+ if (r == WHS_BEGIN_ARRAY) | |
+ n = v.array.nelems; | |
+ else | |
+ n = v.hash.npairs; | |
+ | |
+ if (idx < 0) | |
+ { | |
+ if (-idx > n) | |
+ idx = n; | |
+ else | |
+ idx = n + idx; | |
+ } | |
+ | |
+ if (idx >= n) | |
+ { | |
+ memcpy(out, in, VARSIZE(in)); | |
+ PG_RETURN_POINTER(out); | |
+ } | |
+ | |
+ pushHStoreValue(&toState, r, &v); | |
+ | |
+ while((r = HStoreIteratorGet(&it, &v, true)) != 0) | |
+ { | |
+ if (r == WHS_ELEM || r == WHS_KEY) | |
+ { | |
+ if (i++ == idx) | |
+ { | |
+ if (r == WHS_KEY) | |
+ HStoreIteratorGet(&it, &v, true); /* skip value */ | |
+ continue; | |
+ } | |
+ } | |
+ | |
+ res = pushHStoreValue(&toState, r, &v); | |
+ } | |
+ | |
+ if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) || | |
+ (res->type == hsvHash && res->hash.npairs == 0) ) | |
+ { | |
+ SET_VARSIZE(out, VARHDRSZ); | |
+ } | |
+ else | |
+ { | |
+ r = compressHStore(res, VARDATA(out)); | |
+ SET_VARSIZE(out, r + VARHDRSZ); | |
+ } | |
+ | |
+ PG_RETURN_POINTER(out); | |
+} | |
+ | |
+static void | |
+convertScalarToString(HStoreValue *v) | |
+{ | |
+ switch(v->type) { | |
+ case hsvNull: | |
+ elog(ERROR, "key in hstore type could not be a NULL"); | |
+ break; | |
+ case hsvBool: | |
+ v->type = hsvString; | |
+ v->string.val = pnstrdup((v->boolean) ? "t" : "f", 1); | |
+ v->string.len = 1; | |
+ v->size = sizeof(HEntry) + v->string.len; | |
+ break; | |
+ case hsvNumeric: | |
+ v->type = hsvString; | |
+ v->string.val = DatumGetCString( | |
+ DirectFunctionCall1(numeric_out, PointerGetDatum(v->numeric))); | |
+ v->string.len = strlen(v->string.val); | |
+ v->size = sizeof(HEntry) + v->string.len; | |
+ break; | |
+ case hsvString: | |
+ break; | |
+ default: | |
+ elog(PANIC,"Could not convert to string"); | |
+ } | |
+} | |
+ | |
+static HStoreValue * | |
+IteratorConcat(HStoreIterator **it1, HStoreIterator **it2, ToHStoreState **toState) | |
+{ | |
+ uint32 r1, r2, rk1, rk2; | |
+ HStoreValue v1, v2, *res = NULL; | |
+ | |
+ r1 = rk1 = HStoreIteratorGet(it1, &v1, false); | |
+ r2 = rk2 = HStoreIteratorGet(it2, &v2, false); | |
+ | |
+ if (rk1 == WHS_BEGIN_HASH && rk2 == WHS_BEGIN_HASH) | |
+ { | |
+ bool fin2 = false, | |
+ keyIsDef = false; | |
+ | |
+ res = pushHStoreValue(toState, r1, &v1); | |
+ | |
+ for(;;) | |
+ { | |
+ r1 = HStoreIteratorGet(it1, &v1, true); | |
+ | |
+ Assert(r1 == WHS_KEY || r1 == WHS_VALUE || r1 == WHS_END_HASH); | |
+ | |
+ if (r1 == WHS_KEY && fin2 == false) | |
+ { | |
+ int diff = 1; | |
+ | |
+ if (keyIsDef) | |
+ r2 = WHS_KEY; | |
+ | |
+ while(keyIsDef || (r2 = HStoreIteratorGet(it2, &v2, true)) != 0) | |
+ { | |
+ if (r2 != WHS_KEY) | |
+ continue; | |
+ | |
+ diff = compareHStoreStringValue(&v1, &v2, NULL); | |
+ | |
+ if (diff > 0) | |
+ { | |
+ if (keyIsDef) | |
+ keyIsDef = false; | |
+ | |
+ pushHStoreValue(toState, r2, &v2); | |
+ r2 = HStoreIteratorGet(it2, &v2, true); | |
+ Assert(r2 == WHS_VALUE); | |
+ pushHStoreValue(toState, r2, &v2); | |
+ } | |
+ else if (diff <= 0) | |
+ { | |
+ break; | |
+ } | |
+ } | |
+ | |
+ if (r2 == 0) | |
+ { | |
+ fin2 = true; | |
+ } | |
+ else if (diff == 0) | |
+ { | |
+ keyIsDef = false; | |
+ | |
+ pushHStoreValue(toState, r1, &v1); | |
+ | |
+ r1 = HStoreIteratorGet(it1, &v1, true); /* ignore */ | |
+ r2 = HStoreIteratorGet(it2, &v2, true); /* new val */ | |
+ | |
+ Assert(r1 == WHS_VALUE && r2 == WHS_VALUE); | |
+ pushHStoreValue(toState, r2, &v2); | |
+ | |
+ continue; | |
+ } | |
+ else | |
+ { | |
+ keyIsDef = true; | |
+ } | |
+ } | |
+ else if (r1 == WHS_END_HASH) | |
+ { | |
+ if (r2 != 0) | |
+ { | |
+ if (keyIsDef) | |
+ r2 = WHS_KEY; | |
+ | |
+ while(keyIsDef || (r2 = HStoreIteratorGet(it2, &v2, true)) != 0) | |
+ { | |
+ if (r2 != WHS_KEY) | |
+ continue; | |
+ | |
+ pushHStoreValue(toState, r2, &v2); | |
+ r2 = HStoreIteratorGet(it2, &v2, true); | |
+ Assert(r2 == WHS_VALUE); | |
+ pushHStoreValue(toState, r2, &v2); | |
+ keyIsDef = false; | |
+ } | |
+ } | |
+ | |
+ res = pushHStoreValue(toState, r1, &v1); | |
+ break; | |
+ } | |
+ | |
+ res = pushHStoreValue(toState, r1, &v1); | |
} | |
+ } | |
+ else if ((rk1 == WHS_BEGIN_HASH || rk1 == WHS_BEGIN_ARRAY) && (rk2 == WHS_BEGIN_HASH || rk2 == WHS_BEGIN_ARRAY)) | |
+ { | |
+ if (rk1 == WHS_BEGIN_HASH && rk2 == WHS_BEGIN_ARRAY && v2.array.nelems % 2 != 0) | |
+ elog(ERROR, "hstore's array must have even number of elements"); | |
- if (difference > 0) | |
- ++j; | |
- else if (difference == 0) | |
+ res = pushHStoreValue(toState, r1, &v1); | |
+ | |
+ for(;;) | |
{ | |
- int svallen = HS_VALLEN(es, i); | |
- int snullval = HS_VALISNULL(es, i); | |
+ r1 = HStoreIteratorGet(it1, &v1, true); | |
+ if (r1 == WHS_END_HASH || r1 == WHS_END_ARRAY) | |
+ break; | |
+ Assert(r1 == WHS_KEY || r1 == WHS_VALUE || r1 == WHS_ELEM); | |
+ pushHStoreValue(toState, r1, &v1); | |
+ } | |
- if (snullval != HS_VALISNULL(es2, j) | |
- || (!snullval | |
- && (svallen != HS_VALLEN(es2, j) | |
- || memcmp(HS_VAL(es, ps, i), HS_VAL(es2, ps2, j), svallen) != 0))) | |
+ while((r2 = HStoreIteratorGet(it2, &v2, true)) != 0) | |
+ { | |
+ if (!(r2 == WHS_END_HASH || r2 == WHS_END_ARRAY)) | |
{ | |
- HS_COPYITEM(ed, bufd, pd, | |
- HS_KEY(es, ps, i), HS_KEYLEN(es, i), | |
- svallen, snullval); | |
- ++outcount; | |
+ if (rk1 == WHS_BEGIN_HASH) | |
+ { | |
+ convertScalarToString(&v2); | |
+ pushHStoreValue(toState, WHS_KEY, &v2); | |
+ r2 = HStoreIteratorGet(it2, &v2, true); | |
+ Assert(r2 == WHS_ELEM); | |
+ pushHStoreValue(toState, WHS_VALUE, &v2); | |
+ } | |
+ else | |
+ { | |
+ pushHStoreValue(toState, WHS_ELEM, &v2); | |
+ } | |
} | |
- ++i, ++j; | |
+ } | |
+ | |
+ res = pushHStoreValue(toState, (rk1 == WHS_BEGIN_HASH) ? WHS_END_HASH : WHS_END_ARRAY, NULL/* signal to sort */); | |
+ } | |
+ else if ((rk1 & (WHS_VALUE | WHS_ELEM)) != 0) | |
+ { | |
+ if (v2.type == hsvArray && v2.array.scalar) | |
+ { | |
+ Assert(v2.array.nelems == 1); | |
+ r2 = HStoreIteratorGet(it2, &v2, false); | |
+ pushHStoreValue(toState, r1, &v2); | |
} | |
else | |
{ | |
- HS_COPYITEM(ed, bufd, pd, | |
- HS_KEY(es, ps, i), HS_KEYLEN(es, i), | |
- HS_VALLEN(es, i), HS_VALISNULL(es, i)); | |
- ++outcount; | |
- ++i; | |
+ res = pushHStoreValue(toState, r2, &v2); | |
+ while((r2 = HStoreIteratorGet(it2, &v2, true)) != 0) | |
+ res = pushHStoreValue(toState, r2, &v2); | |
} | |
} | |
+ else | |
+ { | |
+ elog(ERROR, "invalid concatnation of hstores"); | |
+ } | |
+ | |
+ return res; | |
+} | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_concat); | |
+Datum hstore_concat(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_concat(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *hs1 = PG_GETARG_HS(0); | |
+ HStore *hs2 = PG_GETARG_HS(1); | |
+ HStore *out = palloc(VARSIZE(hs1) + VARSIZE(hs2)); | |
+ ToHStoreState *toState = NULL; | |
+ HStoreValue *res; | |
+ HStoreIterator *it1, *it2; | |
+ | |
+ if (HS_ISEMPTY(hs1)) | |
+ { | |
+ memcpy(out, hs2, VARSIZE(hs2)); | |
+ PG_RETURN_POINTER(out); | |
+ } | |
+ else if (HS_ISEMPTY(hs2)) | |
+ { | |
+ memcpy(out, hs1, VARSIZE(hs1)); | |
+ PG_RETURN_POINTER(out); | |
+ } | |
+ | |
+ it1 = HStoreIteratorInit(VARDATA(hs1)); | |
+ it2 = HStoreIteratorInit(VARDATA(hs2)); | |
+ | |
+ res = IteratorConcat(&it1, &it2, &toState); | |
+ | |
+ if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) || | |
+ (res->type == hsvHash && res->hash.npairs == 0) ) | |
+ { | |
+ SET_VARSIZE(out, VARHDRSZ); | |
+ } | |
+ else | |
+ { | |
+ int r = compressHStore(res, VARDATA(out)); | |
+ SET_VARSIZE(out, r + VARHDRSZ); | |
+ } | |
+ | |
+ PG_RETURN_POINTER(out); | |
+} | |
+ | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_slice_to_array); | |
+Datum hstore_slice_to_array(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_slice_to_array(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ ArrayType *key_array = PG_GETARG_ARRAYTYPE_P(1); | |
+ ArrayType *aout; | |
+ Datum *key_datums; | |
+ bool *key_nulls; | |
+ Datum *out_datums; | |
+ bool *out_nulls; | |
+ int key_count; | |
+ int i; | |
+ | |
+ deconstruct_array(key_array, | |
+ TEXTOID, -1, false, 'i', | |
+ &key_datums, &key_nulls, &key_count); | |
+ | |
+ if (key_count == 0 || HS_ISEMPTY(hs)) | |
+ { | |
+ aout = construct_empty_array(TEXTOID); | |
+ PG_RETURN_POINTER(aout); | |
+ } | |
+ | |
+ out_datums = palloc(sizeof(Datum) * key_count); | |
+ out_nulls = palloc(sizeof(bool) * key_count); | |
+ | |
+ for (i = 0; i < key_count; ++i) | |
+ { | |
+ text *key = (text *) DatumGetPointer(key_datums[i]); | |
+ HStoreValue *v = NULL; | |
+ | |
+ if (key_nulls[i] == false) | |
+ v = findUncompressedHStoreValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, NULL, | |
+ VARDATA(key), VARSIZE(key) - VARHDRSZ); | |
+ | |
+ out_datums[i] = PointerGetDatum(HStoreValueToText(v)); | |
+ out_nulls[i] = (DatumGetPointer(out_datums[i]) == NULL) ? true : false; | |
+ } | |
+ | |
+ aout = construct_md_array(out_datums, out_nulls, | |
+ ARR_NDIM(key_array), | |
+ ARR_DIMS(key_array), | |
+ ARR_LBOUND(key_array), | |
+ TEXTOID, -1, false, 'i'); | |
+ | |
+ PG_RETURN_POINTER(aout); | |
+} | |
+ | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_slice_to_hstore); | |
+Datum hstore_slice_to_hstore(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_slice_to_hstore(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ HStoreValue *a = arrayToHStoreSortedArray(PG_GETARG_ARRAYTYPE_P(1)); | |
+ uint32 lowbound = 0, | |
+ *plowbound; | |
+ HStoreValue *res = NULL; | |
+ ToHStoreState *state = NULL; | |
+ text *out; | |
+ uint32 i; | |
+ | |
+ out = palloc(VARSIZE(hs)); | |
+ | |
+ if (a == NULL || a->array.nelems == 0 || HS_ISEMPTY(hs)) | |
+ { | |
+ memcpy(out, hs, VARSIZE(hs)); | |
+ PG_RETURN_POINTER(out); | |
+ } | |
+ | |
+ if (HS_ROOT_IS_HASH(hs)) | |
+ { | |
+ plowbound = &lowbound; | |
+ pushHStoreValue(&state, WHS_BEGIN_HASH, NULL); | |
+ } | |
+ else | |
+ { | |
+ plowbound = NULL; | |
+ pushHStoreValue(&state, WHS_BEGIN_ARRAY, NULL); | |
+ } | |
+ | |
+ for (i = 0; i < a->array.nelems; ++i) | |
+ { | |
+ HStoreValue *v = findUncompressedHStoreValueByValue(VARDATA(hs), HS_FLAG_HSTORE | HS_FLAG_ARRAY, plowbound, | |
+ a->array.elems + i); | |
+ | |
+ if (v) | |
+ { | |
+ if (plowbound) | |
+ { | |
+ pushHStoreValue(&state, WHS_KEY, a->array.elems + i); | |
+ pushHStoreValue(&state, WHS_VALUE, v); | |
+ } | |
+ else | |
+ { | |
+ pushHStoreValue(&state, WHS_ELEM, v); | |
+ } | |
+ } | |
+ } | |
+ | |
+ if (plowbound) | |
+ res = pushHStoreValue(&state, WHS_END_HASH, a /* any non-null value */); | |
+ else | |
+ res = pushHStoreValue(&state, WHS_END_ARRAY, NULL); | |
+ | |
+ | |
+ if (res == NULL || (res->type == hsvArray && res->array.nelems == 0) || | |
+ (res->type == hsvHash && res->hash.npairs == 0) ) | |
+ { | |
+ SET_VARSIZE(out, VARHDRSZ); | |
+ } | |
+ else | |
+ { | |
+ int r = compressHStore(res, VARDATA(out)); | |
+ SET_VARSIZE(out, r + VARHDRSZ); | |
+ } | |
+ | |
+ PG_RETURN_POINTER(out); | |
+} | |
+ | |
+static HStoreValue* | |
+replacePathDo(HStoreIterator **it, Datum *path_elems, bool *path_nulls, int path_len, | |
+ ToHStoreState **st, int level, HStoreValue *newval) | |
+{ | |
+ HStoreValue v, *res = NULL; | |
+ int r; | |
+ | |
+ r = HStoreIteratorGet(it, &v, false); | |
+ | |
+ if (r == WHS_BEGIN_ARRAY) | |
+ { | |
+ int idx, i; | |
+ uint32 n = v.array.nelems; | |
+ | |
+ idx = n; | |
+ if (level >= path_len || path_nulls[level] || | |
+ h_atoi(VARDATA_ANY(path_elems[level]), VARSIZE_ANY_EXHDR(path_elems[level]), &idx) == false) | |
+ { | |
+ idx = n; | |
+ } | |
+ else if (idx < 0) | |
+ { | |
+ if (-idx > n) | |
+ idx = n; | |
+ else | |
+ idx = n + idx; | |
+ } | |
+ | |
+ if (idx > n) | |
+ idx = n; | |
+ | |
+ pushHStoreValue(st, r, &v); | |
+ | |
+ for(i=0; i<n; i++) | |
+ { | |
+ if (i == idx && level < path_len) | |
+ { | |
+ if (level == path_len - 1) | |
+ { | |
+ r = HStoreIteratorGet(it, &v, true); /* skip */ | |
+ Assert(r == WHS_ELEM); | |
+ res = pushHStoreValue(st, r, newval); | |
+ } | |
+ else | |
+ { | |
+ res = replacePathDo(it, path_elems, path_nulls, path_len, st, level + 1, newval); | |
+ } | |
+ } | |
+ else | |
+ { | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_ELEM); | |
+ res = pushHStoreValue(st, r, &v); | |
+ } | |
+ } | |
+ | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_END_ARRAY); | |
+ res = pushHStoreValue(st, r, &v); | |
+ } | |
+ else if (r == WHS_BEGIN_HASH) | |
+ { | |
+ int i; | |
+ uint32 n = v.hash.npairs; | |
+ HStoreValue k; | |
+ bool done = false; | |
+ | |
+ pushHStoreValue(st, WHS_BEGIN_HASH, &v); | |
+ | |
+ if (level >= path_len || path_nulls[level]) | |
+ done = true; | |
+ | |
+ for(i=0; i<n; i++) | |
+ { | |
+ r = HStoreIteratorGet(it, &k, false); | |
+ Assert(r == WHS_KEY); | |
+ res = pushHStoreValue(st, r, &k); | |
+ | |
+ if (done == false && | |
+ k.string.len == VARSIZE_ANY_EXHDR(path_elems[level]) && | |
+ memcmp(k.string.val, VARDATA_ANY(path_elems[level]), k.string.len) == 0) | |
+ { | |
+ if (level == path_len - 1) | |
+ { | |
+ r = HStoreIteratorGet(it, &v, true); /* skip */ | |
+ Assert(r == WHS_VALUE); | |
+ res = pushHStoreValue(st, r, newval); | |
+ } | |
+ else | |
+ { | |
+ res = replacePathDo(it, path_elems, path_nulls, path_len, st, level + 1, newval); | |
+ } | |
+ } | |
+ else | |
+ { | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_VALUE); | |
+ res = pushHStoreValue(st, r, &v); | |
+ } | |
+ } | |
+ | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_END_HASH); | |
+ res = pushHStoreValue(st, r, &v); | |
+ } | |
+ else if (r == WHS_ELEM || r == WHS_VALUE) | |
+ { | |
+ pushHStoreValue(st, r, &v); | |
+ res = (void*)0x01; /* dummy value */ | |
+ } | |
+ else | |
+ { | |
+ elog(PANIC, "impossible state"); | |
+ } | |
- HS_FINALIZE(out, outcount, bufd, pd); | |
- | |
- PG_RETURN_POINTER(out); | |
+ return res; | |
} | |
- | |
-PG_FUNCTION_INFO_V1(hstore_concat); | |
-Datum hstore_concat(PG_FUNCTION_ARGS); | |
+PG_FUNCTION_INFO_V1(hstore_replace); | |
+Datum hstore_replace(PG_FUNCTION_ARGS); | |
Datum | |
-hstore_concat(PG_FUNCTION_ARGS) | |
+hstore_replace(PG_FUNCTION_ARGS) | |
{ | |
- HStore *s1 = PG_GETARG_HS(0); | |
- HStore *s2 = PG_GETARG_HS(1); | |
- HStore *out = palloc(VARSIZE(s1) + VARSIZE(s2)); | |
- char *ps1, | |
- *ps2, | |
- *bufd, | |
- *pd; | |
- HEntry *es1, | |
- *es2, | |
- *ed; | |
- int s1idx; | |
- int s2idx; | |
- int s1count = HS_COUNT(s1); | |
- int s2count = HS_COUNT(s2); | |
- int outcount = 0; | |
- | |
- SET_VARSIZE(out, VARSIZE(s1) + VARSIZE(s2) - HSHRDSIZE); | |
- HS_SETCOUNT(out, s1count + s2count); | |
- | |
- if (s1count == 0) | |
- { | |
- /* return a copy of the input, unchanged */ | |
- memcpy(out, s2, VARSIZE(s2)); | |
- HS_FIXSIZE(out, s2count); | |
- HS_SETCOUNT(out, s2count); | |
+ HStore *in = PG_GETARG_HS(0); | |
+ ArrayType *path = PG_GETARG_ARRAYTYPE_P(1); | |
+ HStore *newval = PG_GETARG_HS(2); | |
+ HStore *out = palloc(VARSIZE(in) + VARSIZE(newval)); | |
+ HStoreValue *res = NULL; | |
+ HStoreValue value; | |
+ Datum *path_elems; | |
+ bool *path_nulls; | |
+ int path_len; | |
+ HStoreIterator *it; | |
+ ToHStoreState *st = NULL; | |
+ | |
+ Assert(ARR_ELEMTYPE(path) == TEXTOID); | |
+ | |
+ if (ARR_NDIM(path) > 1) | |
+ ereport(ERROR, | |
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), | |
+ errmsg("wrong number of array subscripts"))); | |
+ | |
+ if (HS_ROOT_COUNT(in) == 0) | |
+ { | |
+ memcpy(out, in, VARSIZE(in)); | |
PG_RETURN_POINTER(out); | |
} | |
- if (s2count == 0) | |
+ deconstruct_array(path, TEXTOID, -1, false, 'i', | |
+ &path_elems, &path_nulls, &path_len); | |
+ | |
+ if (path_len == 0) | |
{ | |
- /* return a copy of the input, unchanged */ | |
- memcpy(out, s1, VARSIZE(s1)); | |
- HS_FIXSIZE(out, s1count); | |
- HS_SETCOUNT(out, s1count); | |
+ memcpy(out, in, VARSIZE(in)); | |
PG_RETURN_POINTER(out); | |
} | |
- ps1 = STRPTR(s1); | |
- ps2 = STRPTR(s2); | |
- bufd = pd = STRPTR(out); | |
- es1 = ARRPTR(s1); | |
- es2 = ARRPTR(s2); | |
- ed = ARRPTR(out); | |
- | |
- /* | |
- * this is in effect a merge between s1 and s2, both of which are already | |
- * sorted by (keylen,key); we take s2 for equal keys | |
- */ | |
- | |
- for (s1idx = s2idx = 0; s1idx < s1count || s2idx < s2count; ++outcount) | |
+ if (HS_ROOT_COUNT(newval) == 0) | |
{ | |
- int difference; | |
+ value.type = hsvNull; | |
+ value.size = sizeof(HEntry); | |
+ } | |
+ else | |
+ { | |
+ value.type = hsvBinary; | |
+ value.binary.data = VARDATA(newval); | |
+ value.binary.len = VARSIZE_ANY_EXHDR(newval); | |
+ value.size = value.binary.len + sizeof(HEntry); | |
+ } | |
- if (s1idx >= s1count) | |
- difference = 1; | |
- else if (s2idx >= s2count) | |
- difference = -1; | |
- else | |
- { | |
- int s1keylen = HS_KEYLEN(es1, s1idx); | |
- int s2keylen = HS_KEYLEN(es2, s2idx); | |
+ it = HStoreIteratorInit(VARDATA(in)); | |
- if (s1keylen == s2keylen) | |
- difference = memcmp(HS_KEY(es1, ps1, s1idx), | |
- HS_KEY(es2, ps2, s2idx), | |
- s1keylen); | |
- else | |
- difference = (s1keylen > s2keylen) ? 1 : -1; | |
- } | |
+ res = replacePathDo(&it, path_elems, path_nulls, path_len, &st, 0, &value); | |
- if (difference >= 0) | |
- { | |
- HS_COPYITEM(ed, bufd, pd, | |
- HS_KEY(es2, ps2, s2idx), HS_KEYLEN(es2, s2idx), | |
- HS_VALLEN(es2, s2idx), HS_VALISNULL(es2, s2idx)); | |
- ++s2idx; | |
- if (difference == 0) | |
- ++s1idx; | |
- } | |
- else | |
- { | |
- HS_COPYITEM(ed, bufd, pd, | |
- HS_KEY(es1, ps1, s1idx), HS_KEYLEN(es1, s1idx), | |
- HS_VALLEN(es1, s1idx), HS_VALISNULL(es1, s1idx)); | |
- ++s1idx; | |
- } | |
+ if (res == NULL) | |
+ { | |
+ SET_VARSIZE(out, VARHDRSZ); | |
} | |
+ else | |
+ { | |
+ int sz; | |
- HS_FINALIZE(out, outcount, bufd, pd); | |
+ sz = compressHStore(res, VARDATA(out)); | |
+ SET_VARSIZE(out, sz + VARHDRSZ); | |
+ } | |
PG_RETURN_POINTER(out); | |
} | |
- | |
-PG_FUNCTION_INFO_V1(hstore_slice_to_array); | |
-Datum hstore_slice_to_array(PG_FUNCTION_ARGS); | |
-Datum | |
-hstore_slice_to_array(PG_FUNCTION_ARGS) | |
+static HStoreValue* | |
+concatPathDo(HStoreIterator **it, Datum *path_elems, bool *path_nulls, int path_len, | |
+ ToHStoreState **st, int level, HStoreIterator *toConcat) | |
{ | |
- HStore *hs = PG_GETARG_HS(0); | |
- HEntry *entries = ARRPTR(hs); | |
- char *ptr = STRPTR(hs); | |
- ArrayType *key_array = PG_GETARG_ARRAYTYPE_P(1); | |
- ArrayType *aout; | |
- Datum *key_datums; | |
- bool *key_nulls; | |
- Datum *out_datums; | |
- bool *out_nulls; | |
- int key_count; | |
- int i; | |
+ HStoreValue v, *res = NULL; | |
+ int r; | |
- deconstruct_array(key_array, | |
- TEXTOID, -1, false, 'i', | |
- &key_datums, &key_nulls, &key_count); | |
+ r = HStoreIteratorGet(it, &v, false); | |
- if (key_count == 0) | |
+ if (r == WHS_BEGIN_ARRAY) | |
{ | |
- aout = construct_empty_array(TEXTOID); | |
- PG_RETURN_POINTER(aout); | |
- } | |
+ int idx, i; | |
+ uint32 n = v.array.nelems; | |
- out_datums = palloc(sizeof(Datum) * key_count); | |
- out_nulls = palloc(sizeof(bool) * key_count); | |
+ idx = n; | |
+ if (level >= path_len || path_nulls[level] || | |
+ h_atoi(VARDATA_ANY(path_elems[level]), VARSIZE_ANY_EXHDR(path_elems[level]), &idx) == false) | |
+ { | |
+ idx = n; | |
+ } | |
+ else if (idx < 0) | |
+ { | |
+ if (-idx > n) | |
+ idx = n; | |
+ else | |
+ idx = n + idx; | |
+ } | |
- for (i = 0; i < key_count; ++i) | |
- { | |
- text *key = (text *) DatumGetPointer(key_datums[i]); | |
- int idx; | |
+ if (idx > n) | |
+ idx = n; | |
- if (key_nulls[i]) | |
- idx = -1; | |
- else | |
- idx = hstoreFindKey(hs, NULL, VARDATA(key), VARSIZE(key) - VARHDRSZ); | |
+ pushHStoreValue(st, r, &v); | |
- if (idx < 0 || HS_VALISNULL(entries, idx)) | |
+ for(i=0; i<n; i++) | |
{ | |
- out_nulls[i] = true; | |
- out_datums[i] = (Datum) 0; | |
+ if (i == idx && level < path_len) | |
+ { | |
+ if (level == path_len - 1) | |
+ res = IteratorConcat(it, &toConcat, st); | |
+ else | |
+ res = concatPathDo(it, path_elems, path_nulls, path_len, st, level + 1, toConcat); | |
+ } | |
+ else | |
+ { | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_ELEM); | |
+ res = pushHStoreValue(st, r, &v); | |
+ } | |
} | |
- else | |
+ | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_END_ARRAY); | |
+ res = pushHStoreValue(st, r, &v); | |
+ } | |
+ else if (r == WHS_BEGIN_HASH) | |
+ { | |
+ int i; | |
+ uint32 n = v.hash.npairs; | |
+ HStoreValue k; | |
+ bool done = false; | |
+ | |
+ pushHStoreValue(st, WHS_BEGIN_HASH, &v); | |
+ | |
+ if (level >= path_len || path_nulls[level]) | |
+ done = true; | |
+ | |
+ for(i=0; i<n; i++) | |
{ | |
- out_datums[i] = PointerGetDatum( | |
- cstring_to_text_with_len(HS_VAL(entries, ptr, idx), | |
- HS_VALLEN(entries, idx))); | |
- out_nulls[i] = false; | |
+ r = HStoreIteratorGet(it, &k, false); | |
+ Assert(r == WHS_KEY); | |
+ res = pushHStoreValue(st, r, &k); | |
+ | |
+ if (done == false && level < path_len && k.string.len == VARSIZE_ANY_EXHDR(path_elems[level]) && | |
+ memcmp(k.string.val, VARDATA_ANY(path_elems[level]), k.string.len) == 0) | |
+ { | |
+ if (level == path_len - 1) | |
+ res = IteratorConcat(it, &toConcat, st); | |
+ else | |
+ res = concatPathDo(it, path_elems, path_nulls, path_len, st, level + 1, toConcat); | |
+ } | |
+ else | |
+ { | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_VALUE); | |
+ res = pushHStoreValue(st, r, &v); | |
+ } | |
} | |
- } | |
- aout = construct_md_array(out_datums, out_nulls, | |
- ARR_NDIM(key_array), | |
- ARR_DIMS(key_array), | |
- ARR_LBOUND(key_array), | |
- TEXTOID, -1, false, 'i'); | |
+ r = HStoreIteratorGet(it, &v, true); | |
+ Assert(r == WHS_END_HASH); | |
+ res = pushHStoreValue(st, r, &v); | |
+ } | |
+ else if (r == WHS_ELEM || r == WHS_VALUE) | |
+ { | |
+ pushHStoreValue(st, r, &v); | |
+ res = (void*)0x01; /* dummy value */ | |
+ } | |
+ else | |
+ { | |
+ elog(PANIC, "impossible state"); | |
+ } | |
- PG_RETURN_POINTER(aout); | |
+ return res; | |
} | |
- | |
-PG_FUNCTION_INFO_V1(hstore_slice_to_hstore); | |
-Datum hstore_slice_to_hstore(PG_FUNCTION_ARGS); | |
+PG_FUNCTION_INFO_V1(hstore_deep_concat); | |
+Datum hstore_deep_concat(PG_FUNCTION_ARGS); | |
Datum | |
-hstore_slice_to_hstore(PG_FUNCTION_ARGS) | |
+hstore_deep_concat(PG_FUNCTION_ARGS) | |
{ | |
- HStore *hs = PG_GETARG_HS(0); | |
- HEntry *entries = ARRPTR(hs); | |
- char *ptr = STRPTR(hs); | |
- ArrayType *key_array = PG_GETARG_ARRAYTYPE_P(1); | |
- HStore *out; | |
- int nkeys; | |
- Pairs *key_pairs = hstoreArrayToPairs(key_array, &nkeys); | |
- Pairs *out_pairs; | |
- int bufsiz; | |
- int lastidx = 0; | |
- int i; | |
- int out_count = 0; | |
- | |
- if (nkeys == 0) | |
+ HStore *in = PG_GETARG_HS(0); | |
+ ArrayType *path = PG_GETARG_ARRAYTYPE_P(1); | |
+ HStore *newval = PG_GETARG_HS(2); | |
+ HStore *out = palloc(VARSIZE(in) + VARSIZE(newval)); | |
+ HStoreValue *res = NULL; | |
+ Datum *path_elems; | |
+ bool *path_nulls; | |
+ int path_len; | |
+ HStoreIterator *it1, *it2; | |
+ ToHStoreState *st = NULL; | |
+ | |
+ Assert(ARR_ELEMTYPE(path) == TEXTOID); | |
+ | |
+ if (ARR_NDIM(path) > 1) | |
+ ereport(ERROR, | |
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), | |
+ errmsg("wrong number of array subscripts"))); | |
+ | |
+ if (HS_ROOT_COUNT(in) == 0 || HS_ROOT_COUNT(newval) == 0) | |
{ | |
- out = hstorePairs(NULL, 0, 0); | |
+ memcpy(out, in, VARSIZE(in)); | |
PG_RETURN_POINTER(out); | |
} | |
- out_pairs = palloc(sizeof(Pairs) * nkeys); | |
- bufsiz = 0; | |
+ deconstruct_array(path, TEXTOID, -1, false, 'i', | |
+ &path_elems, &path_nulls, &path_len); | |
- /* | |
- * we exploit the fact that the pairs list is already sorted into strictly | |
- * increasing order to narrow the hstoreFindKey search; each search can | |
- * start one entry past the previous "found" entry, or at the lower bound | |
- * of the last search. | |
- */ | |
+ it1 = HStoreIteratorInit(VARDATA(in)); | |
+ it2 = HStoreIteratorInit(VARDATA(newval)); | |
- for (i = 0; i < nkeys; ++i) | |
- { | |
- int idx = hstoreFindKey(hs, &lastidx, | |
- key_pairs[i].key, key_pairs[i].keylen); | |
+ if (path_len == 0) | |
+ res = IteratorConcat(&it1, &it2, &st); | |
+ else | |
+ res = concatPathDo(&it1, path_elems, path_nulls, path_len, &st, 0, it2); | |
- if (idx >= 0) | |
- { | |
- out_pairs[out_count].key = key_pairs[i].key; | |
- bufsiz += (out_pairs[out_count].keylen = key_pairs[i].keylen); | |
- out_pairs[out_count].val = HS_VAL(entries, ptr, idx); | |
- bufsiz += (out_pairs[out_count].vallen = HS_VALLEN(entries, idx)); | |
- out_pairs[out_count].isnull = HS_VALISNULL(entries, idx); | |
- out_pairs[out_count].needfree = false; | |
- ++out_count; | |
- } | |
+ if (res == NULL) | |
+ { | |
+ SET_VARSIZE(out, VARHDRSZ); | |
} | |
+ else | |
+ { | |
+ int sz; | |
- /* | |
- * we don't use uniquePairs here because we know that the pairs list is | |
- * already sorted and uniq'ed. | |
- */ | |
- | |
- out = hstorePairs(out_pairs, out_count, bufsiz); | |
+ sz = compressHStore(res, VARDATA(out)); | |
+ SET_VARSIZE(out, sz + VARHDRSZ); | |
+ } | |
PG_RETURN_POINTER(out); | |
} | |
- | |
PG_FUNCTION_INFO_V1(hstore_akeys); | |
Datum hstore_akeys(PG_FUNCTION_ARGS); | |
Datum | |
hstore_akeys(PG_FUNCTION_ARGS) | |
{ | |
- HStore *hs = PG_GETARG_HS(0); | |
- Datum *d; | |
- ArrayType *a; | |
- HEntry *entries = ARRPTR(hs); | |
- char *base = STRPTR(hs); | |
- int count = HS_COUNT(hs); | |
- int i; | |
- | |
- if (count == 0) | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ Datum *d; | |
+ ArrayType *a; | |
+ int i = 0, r = 0; | |
+ HStoreIterator *it; | |
+ HStoreValue v; | |
+ bool skipNested = false; | |
+ | |
+ if (HS_ISEMPTY(hs)) | |
{ | |
a = construct_empty_array(TEXTOID); | |
PG_RETURN_POINTER(a); | |
} | |
- d = (Datum *) palloc(sizeof(Datum) * count); | |
+ d = (Datum *) palloc(sizeof(Datum) * HS_ROOT_COUNT(hs)); | |
- for (i = 0; i < count; ++i) | |
+ it = HStoreIteratorInit(VARDATA(hs)); | |
+ | |
+ while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0) | |
{ | |
- text *item = cstring_to_text_with_len(HS_KEY(entries, base, i), | |
- HS_KEYLEN(entries, i)); | |
+ skipNested = true; | |
- d[i] = PointerGetDatum(item); | |
+ if ((r == WHS_ELEM && v.type != hsvNull) || r == WHS_KEY) | |
+ d[i++] = PointerGetDatum(HStoreValueToText(&v)); | |
} | |
- a = construct_array(d, count, | |
+ a = construct_array(d, i, | |
TEXTOID, -1, false, 'i'); | |
PG_RETURN_POINTER(a); | |
@@ -727,43 +1977,40 @@ Datum hstore_avals(PG_FUNCTION_ARGS); | |
Datum | |
hstore_avals(PG_FUNCTION_ARGS) | |
{ | |
- HStore *hs = PG_GETARG_HS(0); | |
- Datum *d; | |
- bool *nulls; | |
- ArrayType *a; | |
- HEntry *entries = ARRPTR(hs); | |
- char *base = STRPTR(hs); | |
- int count = HS_COUNT(hs); | |
- int lb = 1; | |
- int i; | |
- | |
- if (count == 0) | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ Datum *d; | |
+ ArrayType *a; | |
+ int i = 0, r = 0; | |
+ HStoreIterator *it; | |
+ HStoreValue v; | |
+ bool skipNested = false; | |
+ bool *nulls; | |
+ int lb = 1; | |
+ | |
+ if (HS_ISEMPTY(hs)) | |
{ | |
a = construct_empty_array(TEXTOID); | |
PG_RETURN_POINTER(a); | |
} | |
- d = (Datum *) palloc(sizeof(Datum) * count); | |
- nulls = (bool *) palloc(sizeof(bool) * count); | |
+ d = (Datum *) palloc(sizeof(Datum) * HS_ROOT_COUNT(hs)); | |
+ nulls = (bool *) palloc(sizeof(bool) * HS_ROOT_COUNT(hs)); | |
- for (i = 0; i < count; ++i) | |
+ it = HStoreIteratorInit(VARDATA(hs)); | |
+ | |
+ while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0) | |
{ | |
- if (HS_VALISNULL(entries, i)) | |
- { | |
- d[i] = (Datum) 0; | |
- nulls[i] = true; | |
- } | |
- else | |
- { | |
- text *item = cstring_to_text_with_len(HS_VAL(entries, base, i), | |
- HS_VALLEN(entries, i)); | |
+ skipNested = true; | |
- d[i] = PointerGetDatum(item); | |
- nulls[i] = false; | |
+ if (r == WHS_ELEM || r == WHS_VALUE) | |
+ { | |
+ d[i] = PointerGetDatum(HStoreValueToText(&v)); | |
+ nulls[i] = (DatumGetPointer(d[i]) == NULL) ? true : false; | |
+ i++; | |
} | |
} | |
- a = construct_md_array(d, nulls, 1, &count, &lb, | |
+ a = construct_md_array(d, nulls, 1, &i, &lb, | |
TEXTOID, -1, false, 'i'); | |
PG_RETURN_POINTER(a); | |
@@ -773,44 +2020,53 @@ hstore_avals(PG_FUNCTION_ARGS) | |
static ArrayType * | |
hstore_to_array_internal(HStore *hs, int ndims) | |
{ | |
- HEntry *entries = ARRPTR(hs); | |
- char *base = STRPTR(hs); | |
- int count = HS_COUNT(hs); | |
- int out_size[2] = {0, 2}; | |
- int lb[2] = {1, 1}; | |
- Datum *out_datums; | |
- bool *out_nulls; | |
- int i; | |
+ int count = HS_ROOT_COUNT(hs); | |
+ int out_size[2] = {0, 2}; | |
+ int lb[2] = {1, 1}; | |
+ Datum *out_datums; | |
+ bool *out_nulls; | |
+ bool isHash = HS_ROOT_IS_HASH(hs) ? true : false; | |
+ int i = 0, r = 0; | |
+ HStoreIterator *it; | |
+ HStoreValue v; | |
+ bool skipNested = false; | |
Assert(ndims < 3); | |
if (count == 0 || ndims == 0) | |
return construct_empty_array(TEXTOID); | |
- out_size[0] = count * 2 / ndims; | |
+ if (isHash == false && ndims == 2 && count % 2 != 0) | |
+ elog(ERROR, "hstore's array should have even number of elements"); | |
+ | |
+ out_size[0] = count * (isHash ? 2 : 1) / ndims; | |
out_datums = palloc(sizeof(Datum) * count * 2); | |
out_nulls = palloc(sizeof(bool) * count * 2); | |
- for (i = 0; i < count; ++i) | |
- { | |
- text *key = cstring_to_text_with_len(HS_KEY(entries, base, i), | |
- HS_KEYLEN(entries, i)); | |
+ it = HStoreIteratorInit(VARDATA(hs)); | |
- out_datums[i * 2] = PointerGetDatum(key); | |
- out_nulls[i * 2] = false; | |
+ while((r = HStoreIteratorGet(&it, &v, skipNested)) != 0) | |
+ { | |
+ skipNested = true; | |
- if (HS_VALISNULL(entries, i)) | |
+ switch(r) | |
{ | |
- out_datums[i * 2 + 1] = (Datum) 0; | |
- out_nulls[i * 2 + 1] = true; | |
- } | |
- else | |
- { | |
- text *item = cstring_to_text_with_len(HS_VAL(entries, base, i), | |
- HS_VALLEN(entries, i)); | |
- | |
- out_datums[i * 2 + 1] = PointerGetDatum(item); | |
- out_nulls[i * 2 + 1] = false; | |
+ case WHS_ELEM: | |
+ out_datums[i] = PointerGetDatum(HStoreValueToText(&v)); | |
+ out_nulls[i] = (DatumGetPointer(out_datums[i]) == NULL) ? true : false; | |
+ i++; | |
+ break; | |
+ case WHS_KEY: | |
+ out_datums[i * 2] = PointerGetDatum(HStoreValueToText(&v)); | |
+ out_nulls[i * 2] = (DatumGetPointer(out_datums[i * 2]) == NULL) ? true : false; | |
+ break; | |
+ case WHS_VALUE: | |
+ out_datums[i * 2 + 1] = PointerGetDatum(HStoreValueToText(&v)); | |
+ out_nulls[i * 2 + 1] = (DatumGetPointer(out_datums[i * 2 + 1]) == NULL) ? true : false; | |
+ i++; | |
+ break; | |
+ default: | |
+ break; | |
} | |
} | |
@@ -850,222 +2106,518 @@ hstore_to_matrix(PG_FUNCTION_ARGS) | |
* there was no explanatory comment in the original code. --AG) | |
*/ | |
-static void | |
-setup_firstcall(FuncCallContext *funcctx, HStore *hs, | |
- FunctionCallInfoData *fcinfo) | |
-{ | |
- MemoryContext oldcontext; | |
- HStore *st; | |
+typedef struct SetReturningState | |
+{ | |
+ HStore *hs; | |
+ HStoreIterator *it; | |
+ MemoryContext ctx; | |
+ | |
+ HStoreValue init; | |
+ int path_len; | |
+ int level; | |
+ struct { | |
+ HStoreValue v; | |
+ Datum varStr; | |
+ int varInt; | |
+ enum { | |
+ pathStr, | |
+ pathInt, | |
+ pathAny | |
+ } varKind; | |
+ int i; | |
+ } *path; | |
+} SetReturningState; | |
+ | |
+static SetReturningState* | |
+setup_firstcall(FuncCallContext *funcctx, HStore *hs, ArrayType *path, | |
+ FunctionCallInfoData *fcinfo) | |
+{ | |
+ MemoryContext oldcontext; | |
+ SetReturningState *st; | |
+ | |
+ oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); | |
+ | |
+ st = palloc(sizeof(*st)); | |
+ | |
+ st->ctx = funcctx->multi_call_memory_ctx; | |
+ | |
+ st->hs = (HStore *) palloc(VARSIZE(hs)); | |
+ memcpy(st->hs, hs, VARSIZE(hs)); | |
+ if (HS_ISEMPTY(hs) || path) | |
+ st->it = NULL; | |
+ else | |
+ st->it = HStoreIteratorInit(VARDATA(st->hs)); | |
+ | |
+ funcctx->user_fctx = (void *) st; | |
+ | |
+ if (fcinfo) | |
+ { | |
+ TupleDesc tupdesc; | |
+ | |
+ /* Build a tuple descriptor for our result type */ | |
+ if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) | |
+ elog(ERROR, "return type must be a row type"); | |
+ | |
+ funcctx->tuple_desc = BlessTupleDesc(tupdesc); | |
+ } | |
+ | |
+ st->path_len = st->level = 0; | |
+ if (path) | |
+ { | |
+ Datum *path_elems; | |
+ bool *path_nulls; | |
+ int i; | |
+ | |
+ Assert(ARR_ELEMTYPE(path) == TEXTOID); | |
+ if (ARR_NDIM(path) > 1) | |
+ ereport(ERROR, | |
+ (errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR), | |
+ errmsg("wrong number of array subscripts"))); | |
+ | |
+ deconstruct_array(path, TEXTOID, -1, false, 'i', | |
+ &path_elems, &path_nulls, &st->path_len); | |
+ | |
+ st->init.type = hsvBinary; | |
+ st->init.size = VARSIZE(st->hs); | |
+ st->init.binary.data = VARDATA(st->hs); | |
+ st->init.binary.len = VARSIZE_ANY_EXHDR(st->hs); | |
+ | |
+ if (st->path_len > 0) | |
+ { | |
+ st->path = palloc(sizeof(*st->path) * st->path_len); | |
+ st->path[0].v = st->init; | |
+ } | |
+ | |
+ for(i=0; i<st->path_len; i++) | |
+ { | |
+ st->path[i].varStr = path_elems[i]; | |
+ st->path[i].i = 0; | |
+ | |
+ if (path_nulls[i]) | |
+ st->path[i].varKind = pathAny; | |
+ else if (h_atoi(VARDATA_ANY(path_elems[i]), VARSIZE_ANY_EXHDR(path_elems[i]), &st->path[i].varInt)) | |
+ st->path[i].varKind = pathInt; | |
+ else | |
+ st->path[i].varKind = pathStr; | |
+ } | |
+ } | |
+ | |
+ MemoryContextSwitchTo(oldcontext); | |
+ | |
+ return st; | |
+} | |
+ | |
+static uint32 | |
+HStoreIteratorGetCtx(SetReturningState *st, HStoreValue *v, bool skipNested) | |
+{ | |
+ int r; | |
+ MemoryContext oldctx; | |
+ | |
+ oldctx = MemoryContextSwitchTo(st->ctx); | |
+ r = HStoreIteratorGet(&st->it, v, skipNested); | |
+ MemoryContextSwitchTo(oldctx); | |
+ | |
+ return r; | |
+} | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_skeys); | |
+Datum hstore_skeys(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_skeys(PG_FUNCTION_ARGS) | |
+{ | |
+ FuncCallContext *funcctx; | |
+ SetReturningState *st; | |
+ int r; | |
+ HStoreValue v; | |
+ | |
+ if (SRF_IS_FIRSTCALL()) | |
+ { | |
+ funcctx = SRF_FIRSTCALL_INIT(); | |
+ st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, NULL); | |
+ } | |
+ | |
+ funcctx = SRF_PERCALL_SETUP(); | |
+ st = (SetReturningState *) funcctx->user_fctx; | |
+ | |
+ while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0) | |
+ { | |
+ if (r == WHS_KEY || r == WHS_ELEM) | |
+ { | |
+ text *item = HStoreValueToText(&v); | |
+ | |
+ if (item == NULL) | |
+ SRF_RETURN_NEXT_NULL(funcctx); | |
+ else | |
+ SRF_RETURN_NEXT(funcctx, PointerGetDatum(item)); | |
+ } | |
+ } | |
+ | |
+ SRF_RETURN_DONE(funcctx); | |
+} | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_svals); | |
+Datum hstore_svals(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_svals(PG_FUNCTION_ARGS) | |
+{ | |
+ FuncCallContext *funcctx; | |
+ SetReturningState *st; | |
+ int r; | |
+ HStoreValue v; | |
+ | |
+ if (SRF_IS_FIRSTCALL()) | |
+ { | |
+ funcctx = SRF_FIRSTCALL_INIT(); | |
+ st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, NULL); | |
+ } | |
+ | |
+ funcctx = SRF_PERCALL_SETUP(); | |
+ st = (SetReturningState *) funcctx->user_fctx; | |
+ | |
+ while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0) | |
+ { | |
+ if (r == WHS_VALUE || r == WHS_ELEM) | |
+ { | |
+ text *item = HStoreValueToText(&v); | |
+ | |
+ if (item == NULL) | |
+ SRF_RETURN_NEXT_NULL(funcctx); | |
+ else | |
+ SRF_RETURN_NEXT(funcctx, PointerGetDatum(item)); | |
+ } | |
+ } | |
+ | |
+ SRF_RETURN_DONE(funcctx); | |
+} | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_hvals); | |
+Datum hstore_hvals(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_hvals(PG_FUNCTION_ARGS) | |
+{ | |
+ FuncCallContext *funcctx; | |
+ SetReturningState *st; | |
+ int r; | |
+ HStoreValue v; | |
+ | |
+ if (SRF_IS_FIRSTCALL()) | |
+ { | |
+ funcctx = SRF_FIRSTCALL_INIT(); | |
+ st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, NULL); | |
+ } | |
+ | |
+ funcctx = SRF_PERCALL_SETUP(); | |
+ st = (SetReturningState *) funcctx->user_fctx; | |
+ | |
+ while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0) | |
+ { | |
+ if (r == WHS_VALUE || r == WHS_ELEM) | |
+ { | |
+ HStore *item = HStoreValueToHStore(&v); | |
+ | |
+ if (item == NULL) | |
+ SRF_RETURN_NEXT_NULL(funcctx); | |
+ else | |
+ SRF_RETURN_NEXT(funcctx, PointerGetDatum(item)); | |
+ } | |
+ } | |
+ | |
+ SRF_RETURN_DONE(funcctx); | |
+} | |
+ | |
+static HStoreValue* | |
+getNextValsPath(SetReturningState *st) | |
+{ | |
+ HStoreValue *v = NULL; | |
+ | |
+ if (st->path_len == 0) | |
+ { | |
+ /* empty path */ | |
+ if (st->level == 0) | |
+ { | |
+ v = &st->init; | |
+ st->level ++; | |
+ } | |
+ | |
+ return v; | |
+ } | |
+ | |
+ while(st->level >= 0) | |
+ { | |
+ uint32 header; | |
- oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); | |
+ v = NULL; | |
+ if (st->path[st->level].v.type != hsvBinary) | |
+ { | |
+ st->level--; | |
+ continue; | |
+ } | |
- st = (HStore *) palloc(VARSIZE(hs)); | |
- memcpy(st, hs, VARSIZE(hs)); | |
+ header = *(uint32*)st->path[st->level].v.binary.data; | |
- funcctx->user_fctx = (void *) st; | |
+ if (header & HS_FLAG_HSTORE) | |
+ { | |
+ if (st->path[st->level].varKind == pathAny) | |
+ { | |
+ v = getHStoreValue(st->path[st->level].v.binary.data, HS_FLAG_HSTORE, st->path[st->level].i++); | |
+ } | |
+ else | |
+ { | |
+ v = findUncompressedHStoreValue(st->path[st->level].v.binary.data, HS_FLAG_HSTORE, NULL, | |
+ VARDATA_ANY(st->path[st->level].varStr), | |
+ VARSIZE_ANY_EXHDR(st->path[st->level].varStr)); | |
+ } | |
+ } | |
+ else if (header & HS_FLAG_ARRAY) | |
+ { | |
+ if (st->path[st->level].varKind == pathAny) | |
+ { | |
+ v = getHStoreValue(st->path[st->level].v.binary.data, HS_FLAG_ARRAY, st->path[st->level].i++); | |
+ } | |
+ else if (st->path[st->level].varKind == pathInt) | |
+ { | |
+ int ith = st->path[st->level].varInt; | |
- if (fcinfo) | |
- { | |
- TupleDesc tupdesc; | |
+ if (ith < 0) | |
+ { | |
+ if (-ith > (int)(header & HS_COUNT_MASK)) | |
+ { | |
+ st->level--; | |
+ continue; | |
+ } | |
+ else | |
+ { | |
+ ith = ((int)(header & HS_COUNT_MASK)) + ith; | |
+ } | |
+ } | |
+ else | |
+ { | |
+ if (ith >= (int)(header & HS_COUNT_MASK)) | |
+ { | |
+ st->level--; | |
+ continue; | |
+ } | |
+ } | |
- /* Build a tuple descriptor for our result type */ | |
- if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE) | |
- elog(ERROR, "return type must be a row type"); | |
+ v = getHStoreValue(st->path[st->level].v.binary.data, HS_FLAG_ARRAY, ith); | |
+ } | |
+ else | |
+ { | |
+ st->level--; | |
+ continue; | |
+ } | |
+ } | |
+ else | |
+ { | |
+ elog(PANIC, "impossible state"); | |
+ } | |
- funcctx->tuple_desc = BlessTupleDesc(tupdesc); | |
+ if (v == NULL) | |
+ { | |
+ st->level--; | |
+ } | |
+ else if (st->level == st->path_len - 1) | |
+ { | |
+ if (st->path[st->level].varKind != pathAny) | |
+ { | |
+ st->path[st->level].v.type = hsvNull; | |
+ st->level--; | |
+ } | |
+ break; | |
+ } | |
+ else | |
+ { | |
+ if (st->path[st->level].varKind != pathAny) | |
+ st->path[st->level].v.type = hsvNull; | |
+ st->level++; | |
+ st->path[st->level].v = *v; | |
+ st->path[st->level].i = 0; | |
+ } | |
} | |
- MemoryContextSwitchTo(oldcontext); | |
+ return v; | |
} | |
- | |
-PG_FUNCTION_INFO_V1(hstore_skeys); | |
-Datum hstore_skeys(PG_FUNCTION_ARGS); | |
+PG_FUNCTION_INFO_V1(hstore_svals_path); | |
+Datum hstore_svals_path(PG_FUNCTION_ARGS); | |
Datum | |
-hstore_skeys(PG_FUNCTION_ARGS) | |
+hstore_svals_path(PG_FUNCTION_ARGS) | |
{ | |
- FuncCallContext *funcctx; | |
- HStore *hs; | |
- int i; | |
+ FuncCallContext *funcctx; | |
+ SetReturningState *st; | |
+ HStoreValue *v; | |
if (SRF_IS_FIRSTCALL()) | |
{ | |
- hs = PG_GETARG_HS(0); | |
funcctx = SRF_FIRSTCALL_INIT(); | |
- setup_firstcall(funcctx, hs, NULL); | |
+ st = setup_firstcall(funcctx, PG_GETARG_HS(0), PG_GETARG_ARRAYTYPE_P(1), NULL); | |
} | |
funcctx = SRF_PERCALL_SETUP(); | |
- hs = (HStore *) funcctx->user_fctx; | |
- i = funcctx->call_cntr; | |
+ st = (SetReturningState *) funcctx->user_fctx; | |
- if (i < HS_COUNT(hs)) | |
+ if ((v = getNextValsPath(st)) != NULL) | |
{ | |
- HEntry *entries = ARRPTR(hs); | |
- text *item; | |
- | |
- item = cstring_to_text_with_len(HS_KEY(entries, STRPTR(hs), i), | |
- HS_KEYLEN(entries, i)); | |
- | |
- SRF_RETURN_NEXT(funcctx, PointerGetDatum(item)); | |
+ text *item = HStoreValueToText(v); | |
+ | |
+ if (item == NULL) | |
+ SRF_RETURN_NEXT_NULL(funcctx); | |
+ else | |
+ SRF_RETURN_NEXT(funcctx, PointerGetDatum(item)); | |
} | |
SRF_RETURN_DONE(funcctx); | |
} | |
- | |
-PG_FUNCTION_INFO_V1(hstore_svals); | |
-Datum hstore_svals(PG_FUNCTION_ARGS); | |
+PG_FUNCTION_INFO_V1(hstore_hvals_path); | |
+Datum hstore_hvals_path(PG_FUNCTION_ARGS); | |
Datum | |
-hstore_svals(PG_FUNCTION_ARGS) | |
+hstore_hvals_path(PG_FUNCTION_ARGS) | |
{ | |
- FuncCallContext *funcctx; | |
- HStore *hs; | |
- int i; | |
+ FuncCallContext *funcctx; | |
+ SetReturningState *st; | |
+ HStoreValue *v; | |
if (SRF_IS_FIRSTCALL()) | |
{ | |
- hs = PG_GETARG_HS(0); | |
funcctx = SRF_FIRSTCALL_INIT(); | |
- setup_firstcall(funcctx, hs, NULL); | |
+ st = setup_firstcall(funcctx, PG_GETARG_HS(0), PG_GETARG_ARRAYTYPE_P(1), NULL); | |
} | |
funcctx = SRF_PERCALL_SETUP(); | |
- hs = (HStore *) funcctx->user_fctx; | |
- i = funcctx->call_cntr; | |
+ st = (SetReturningState *) funcctx->user_fctx; | |
- if (i < HS_COUNT(hs)) | |
+ if ((v = getNextValsPath(st)) != NULL) | |
{ | |
- HEntry *entries = ARRPTR(hs); | |
- | |
- if (HS_VALISNULL(entries, i)) | |
- { | |
- ReturnSetInfo *rsi; | |
- | |
- /* ugly ugly ugly. why no macro for this? */ | |
- (funcctx)->call_cntr++; | |
- rsi = (ReturnSetInfo *) fcinfo->resultinfo; | |
- rsi->isDone = ExprMultipleResult; | |
- PG_RETURN_NULL(); | |
- } | |
+ HStore *item = HStoreValueToHStore(v); | |
+ | |
+ if (item == NULL) | |
+ SRF_RETURN_NEXT_NULL(funcctx); | |
else | |
- { | |
- text *item; | |
- | |
- item = cstring_to_text_with_len(HS_VAL(entries, STRPTR(hs), i), | |
- HS_VALLEN(entries, i)); | |
- | |
SRF_RETURN_NEXT(funcctx, PointerGetDatum(item)); | |
- } | |
} | |
SRF_RETURN_DONE(funcctx); | |
} | |
- | |
-PG_FUNCTION_INFO_V1(hstore_contains); | |
-Datum hstore_contains(PG_FUNCTION_ARGS); | |
+PG_FUNCTION_INFO_V1(hstore_each); | |
+Datum hstore_each(PG_FUNCTION_ARGS); | |
Datum | |
-hstore_contains(PG_FUNCTION_ARGS) | |
+hstore_each(PG_FUNCTION_ARGS) | |
{ | |
- HStore *val = PG_GETARG_HS(0); | |
- HStore *tmpl = PG_GETARG_HS(1); | |
- bool res = true; | |
- HEntry *te = ARRPTR(tmpl); | |
- char *tstr = STRPTR(tmpl); | |
- HEntry *ve = ARRPTR(val); | |
- char *vstr = STRPTR(val); | |
- int tcount = HS_COUNT(tmpl); | |
- int lastidx = 0; | |
- int i; | |
+ FuncCallContext *funcctx; | |
+ SetReturningState *st; | |
+ int r; | |
+ HStoreValue v; | |
- /* | |
- * we exploit the fact that keys in "tmpl" are in strictly increasing | |
- * order to narrow the hstoreFindKey search; each search can start one | |
- * entry past the previous "found" entry, or at the lower bound of the | |
- * search | |
- */ | |
+ if (SRF_IS_FIRSTCALL()) | |
+ { | |
+ funcctx = SRF_FIRSTCALL_INIT(); | |
+ st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, fcinfo); | |
+ } | |
- for (i = 0; res && i < tcount; ++i) | |
+ funcctx = SRF_PERCALL_SETUP(); | |
+ st = (SetReturningState *) funcctx->user_fctx; | |
+ | |
+ while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0) | |
{ | |
- int idx = hstoreFindKey(val, &lastidx, | |
- HS_KEY(te, tstr, i), HS_KEYLEN(te, i)); | |
+ Datum res, | |
+ dvalues[2] = {0, 0}; | |
+ bool nulls[2] = {false, false}; | |
+ text *item; | |
+ HeapTuple tuple; | |
- if (idx >= 0) | |
+ if (r == WHS_ELEM) | |
{ | |
- bool nullval = HS_VALISNULL(te, i); | |
- int vallen = HS_VALLEN(te, i); | |
+ nulls[0] = true; | |
- if (nullval != HS_VALISNULL(ve, idx) | |
- || (!nullval | |
- && (vallen != HS_VALLEN(ve, idx) | |
- || memcmp(HS_VAL(te, tstr, i), HS_VAL(ve, vstr, idx), vallen)))) | |
- res = false; | |
+ item = HStoreValueToText(&v); | |
+ if (item == NULL) | |
+ nulls[1] = true; | |
+ else | |
+ dvalues[1] = PointerGetDatum(item); | |
+ } | |
+ else if (r == WHS_KEY) | |
+ { | |
+ item = HStoreValueToText(&v); | |
+ dvalues[0] = PointerGetDatum(item); | |
+ | |
+ r = HStoreIteratorGetCtx(st, &v, true); | |
+ Assert(r == WHS_VALUE); | |
+ item = HStoreValueToText(&v); | |
+ if (item == NULL) | |
+ nulls[1] = true; | |
+ else | |
+ dvalues[1] = PointerGetDatum(item); | |
} | |
else | |
- res = false; | |
- } | |
+ { | |
+ continue; | |
+ } | |
- PG_RETURN_BOOL(res); | |
-} | |
+ tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls); | |
+ res = HeapTupleGetDatum(tuple); | |
+ SRF_RETURN_NEXT(funcctx, PointerGetDatum(res)); | |
+ } | |
-PG_FUNCTION_INFO_V1(hstore_contained); | |
-Datum hstore_contained(PG_FUNCTION_ARGS); | |
-Datum | |
-hstore_contained(PG_FUNCTION_ARGS) | |
-{ | |
- PG_RETURN_DATUM(DirectFunctionCall2(hstore_contains, | |
- PG_GETARG_DATUM(1), | |
- PG_GETARG_DATUM(0) | |
- )); | |
+ SRF_RETURN_DONE(funcctx); | |
} | |
- | |
-PG_FUNCTION_INFO_V1(hstore_each); | |
-Datum hstore_each(PG_FUNCTION_ARGS); | |
+PG_FUNCTION_INFO_V1(hstore_each_hstore); | |
+Datum hstore_each_hstore(PG_FUNCTION_ARGS); | |
Datum | |
-hstore_each(PG_FUNCTION_ARGS) | |
+hstore_each_hstore(PG_FUNCTION_ARGS) | |
{ | |
- FuncCallContext *funcctx; | |
- HStore *hs; | |
- int i; | |
+ FuncCallContext *funcctx; | |
+ SetReturningState *st; | |
+ int r; | |
+ HStoreValue v; | |
if (SRF_IS_FIRSTCALL()) | |
{ | |
- hs = PG_GETARG_HS(0); | |
funcctx = SRF_FIRSTCALL_INIT(); | |
- setup_firstcall(funcctx, hs, fcinfo); | |
+ st = setup_firstcall(funcctx, PG_GETARG_HS(0), NULL, fcinfo); | |
} | |
funcctx = SRF_PERCALL_SETUP(); | |
- hs = (HStore *) funcctx->user_fctx; | |
- i = funcctx->call_cntr; | |
+ st = (SetReturningState *) funcctx->user_fctx; | |
- if (i < HS_COUNT(hs)) | |
+ while(st->it && (r = HStoreIteratorGetCtx(st, &v, true)) != 0) | |
{ | |
- HEntry *entries = ARRPTR(hs); | |
- char *ptr = STRPTR(hs); | |
Datum res, | |
- dvalues[2]; | |
+ dvalues[2] = {0, 0}; | |
bool nulls[2] = {false, false}; | |
text *item; | |
+ HStore *hitem; | |
HeapTuple tuple; | |
- item = cstring_to_text_with_len(HS_KEY(entries, ptr, i), | |
- HS_KEYLEN(entries, i)); | |
- dvalues[0] = PointerGetDatum(item); | |
+ if (r == WHS_ELEM) | |
+ { | |
+ nulls[0] = true; | |
- if (HS_VALISNULL(entries, i)) | |
+ hitem = HStoreValueToHStore(&v); | |
+ if (hitem == NULL) | |
+ nulls[1] = true; | |
+ else | |
+ dvalues[1] = PointerGetDatum(hitem); | |
+ } | |
+ else if (r == WHS_KEY) | |
{ | |
- dvalues[1] = (Datum) 0; | |
- nulls[1] = true; | |
+ item = HStoreValueToText(&v); | |
+ dvalues[0] = PointerGetDatum(item); | |
+ | |
+ r = HStoreIteratorGetCtx(st, &v, true); | |
+ Assert(r == WHS_VALUE); | |
+ hitem = HStoreValueToHStore(&v); | |
+ if (hitem == NULL) | |
+ nulls[1] = true; | |
+ else | |
+ dvalues[1] = PointerGetDatum(hitem); | |
} | |
else | |
{ | |
- item = cstring_to_text_with_len(HS_VAL(entries, ptr, i), | |
- HS_VALLEN(entries, i)); | |
- dvalues[1] = PointerGetDatum(item); | |
+ continue; | |
} | |
tuple = heap_form_tuple(funcctx->tuple_desc, dvalues, nulls); | |
@@ -1077,6 +2629,176 @@ hstore_each(PG_FUNCTION_ARGS) | |
SRF_RETURN_DONE(funcctx); | |
} | |
+static bool | |
+deepContains(HStoreIterator **it1, HStoreIterator **it2) | |
+{ | |
+ uint32 r1, r2; | |
+ HStoreValue v1, v2; | |
+ bool res = true; | |
+ | |
+ r1 = HStoreIteratorGet(it1, &v1, false); | |
+ r2 = HStoreIteratorGet(it2, &v2, false); | |
+ | |
+ if (r1 != r2) | |
+ { | |
+ res = false; | |
+ } | |
+ else if (r1 == WHS_BEGIN_HASH) | |
+ { | |
+ uint32 lowbound = 0; | |
+ HStoreValue *v; | |
+ | |
+ for(;;) { | |
+ r2 = HStoreIteratorGet(it2, &v2, false); | |
+ if (r2 == WHS_END_HASH) | |
+ break; | |
+ | |
+ Assert(r2 == WHS_KEY); | |
+ | |
+ v = findUncompressedHStoreValueByValue((*it1)->buffer, HS_FLAG_HSTORE, &lowbound, &v2); | |
+ | |
+ if (v == NULL) | |
+ { | |
+ res = false; | |
+ break; | |
+ } | |
+ | |
+ r2 = HStoreIteratorGet(it2, &v2, true); | |
+ Assert(r2 == WHS_VALUE); | |
+ | |
+ if (v->type != v2.type) | |
+ { | |
+ res = false; | |
+ break; | |
+ } | |
+ else if (v->type == hsvString || v->type == hsvNull || v->type == hsvBool || v->type == hsvNumeric) | |
+ { | |
+ if (compareHStoreValue(v, &v2) != 0) | |
+ { | |
+ res = false; | |
+ break; | |
+ } | |
+ } | |
+ else | |
+ { | |
+ HStoreIterator *it1a, *it2a; | |
+ | |
+ Assert(v2.type == hsvBinary); | |
+ Assert(v->type == hsvBinary); | |
+ | |
+ it1a = HStoreIteratorInit(v->binary.data); | |
+ it2a = HStoreIteratorInit(v2.binary.data); | |
+ | |
+ if ((res = deepContains(&it1a, &it2a)) == false) | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ else if (r1 == WHS_BEGIN_ARRAY) | |
+ { | |
+ HStoreValue *v; | |
+ HStoreValue *av = NULL; | |
+ uint32 nelems = v1.array.nelems; | |
+ | |
+ for(;;) { | |
+ r2 = HStoreIteratorGet(it2, &v2, true); | |
+ if (r2 == WHS_END_ARRAY) | |
+ break; | |
+ | |
+ Assert(r2 == WHS_ELEM); | |
+ | |
+ if (v2.type == hsvString || v2.type == hsvNull || v2.type == hsvBool || v2.type == hsvNumeric) | |
+ { | |
+ v = findUncompressedHStoreValueByValue((*it1)->buffer, HS_FLAG_ARRAY, NULL, &v2); | |
+ if (v == NULL) | |
+ { | |
+ res = false; | |
+ break; | |
+ } | |
+ } | |
+ else | |
+ { | |
+ uint32 i; | |
+ | |
+ if (av == NULL) | |
+ { | |
+ uint32 j = 0; | |
+ | |
+ av = palloc(sizeof(*av) * nelems); | |
+ | |
+ for(i=0; i<nelems; i++) | |
+ { | |
+ r2 = HStoreIteratorGet(it1, &v1, true); | |
+ Assert(r2 == WHS_ELEM); | |
+ | |
+ if (v1.type == hsvBinary) | |
+ av[j++] = v1; | |
+ } | |
+ | |
+ if (j == 0) | |
+ { | |
+ res = false; | |
+ break; | |
+ } | |
+ | |
+ nelems = j; | |
+ } | |
+ | |
+ res = false; | |
+ for(i = 0; res == false && i<nelems; i++) | |
+ { | |
+ HStoreIterator *it1a, *it2a; | |
+ | |
+ it1a = HStoreIteratorInit(av[i].binary.data); | |
+ it2a = HStoreIteratorInit(v2.binary.data); | |
+ | |
+ res = deepContains(&it1a, &it2a); | |
+ } | |
+ | |
+ if (res == false) | |
+ break; | |
+ } | |
+ } | |
+ } | |
+ else | |
+ { | |
+ elog(PANIC, "impossible state"); | |
+ } | |
+ | |
+ return res; | |
+} | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_contains); | |
+Datum hstore_contains(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_contains(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *val = PG_GETARG_HS(0); | |
+ HStore *tmpl = PG_GETARG_HS(1); | |
+ bool res = true; | |
+ HStoreIterator *it1, *it2; | |
+ | |
+ if (HS_ROOT_COUNT(val) < HS_ROOT_COUNT(tmpl) || HS_ROOT_IS_HASH(val) != HS_ROOT_IS_HASH(tmpl)) | |
+ PG_RETURN_BOOL(false); | |
+ | |
+ it1 = HStoreIteratorInit(VARDATA(val)); | |
+ it2 = HStoreIteratorInit(VARDATA(tmpl)); | |
+ res = deepContains(&it1, &it2); | |
+ | |
+ PG_RETURN_BOOL(res); | |
+} | |
+ | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_contained); | |
+Datum hstore_contained(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_contained(PG_FUNCTION_ARGS) | |
+{ | |
+ PG_RETURN_DATUM(DirectFunctionCall2(hstore_contains, | |
+ PG_GETARG_DATUM(1), | |
+ PG_GETARG_DATUM(0) | |
+ )); | |
+} | |
/* | |
* btree sort order for hstores isn't intended to be useful; we really only | |
@@ -1089,72 +2811,28 @@ Datum hstore_cmp(PG_FUNCTION_ARGS); | |
Datum | |
hstore_cmp(PG_FUNCTION_ARGS) | |
{ | |
- HStore *hs1 = PG_GETARG_HS(0); | |
- HStore *hs2 = PG_GETARG_HS(1); | |
- int hcount1 = HS_COUNT(hs1); | |
- int hcount2 = HS_COUNT(hs2); | |
- int res = 0; | |
+ HStore *hs1 = PG_GETARG_HS(0); | |
+ HStore *hs2 = PG_GETARG_HS(1); | |
+ int res; | |
- if (hcount1 == 0 || hcount2 == 0) | |
- { | |
- /* | |
- * if either operand is empty, and the other is nonempty, the nonempty | |
- * one is larger. If both are empty they are equal. | |
- */ | |
- if (hcount1 > 0) | |
- res = 1; | |
- else if (hcount2 > 0) | |
- res = -1; | |
- } | |
- else | |
+ if (HS_ISEMPTY(hs1) || HS_ISEMPTY(hs2)) | |
{ | |
- /* here we know both operands are nonempty */ | |
- char *str1 = STRPTR(hs1); | |
- char *str2 = STRPTR(hs2); | |
- HEntry *ent1 = ARRPTR(hs1); | |
- HEntry *ent2 = ARRPTR(hs2); | |
- size_t len1 = HSE_ENDPOS(ent1[2 * hcount1 - 1]); | |
- size_t len2 = HSE_ENDPOS(ent2[2 * hcount2 - 1]); | |
- | |
- res = memcmp(str1, str2, Min(len1, len2)); | |
- | |
- if (res == 0) | |
+ if (HS_ISEMPTY(hs1)) | |
{ | |
- if (len1 > len2) | |
- res = 1; | |
- else if (len1 < len2) | |
- res = -1; | |
- else if (hcount1 > hcount2) | |
- res = 1; | |
- else if (hcount2 > hcount1) | |
- res = -1; | |
+ if (HS_ISEMPTY(hs2)) | |
+ res = 0; | |
else | |
- { | |
- int count = hcount1 * 2; | |
- int i; | |
- | |
- for (i = 0; i < count; ++i) | |
- if (HSE_ENDPOS(ent1[i]) != HSE_ENDPOS(ent2[i]) || | |
- HSE_ISNULL(ent1[i]) != HSE_ISNULL(ent2[i])) | |
- break; | |
- if (i < count) | |
- { | |
- if (HSE_ENDPOS(ent1[i]) < HSE_ENDPOS(ent2[i])) | |
- res = -1; | |
- else if (HSE_ENDPOS(ent1[i]) > HSE_ENDPOS(ent2[i])) | |
- res = 1; | |
- else if (HSE_ISNULL(ent1[i])) | |
- res = 1; | |
- else if (HSE_ISNULL(ent2[i])) | |
- res = -1; | |
- } | |
- } | |
+ res = -1; | |
} | |
else | |
{ | |
- res = (res > 0) ? 1 : -1; | |
+ res = 1; | |
} | |
} | |
+ else | |
+ { | |
+ res = compareHStoreBinaryValue(VARDATA(hs1), VARDATA(hs2)); | |
+ } | |
/* | |
* this is a btree support function; this is one of the few places where | |
@@ -1248,17 +2926,61 @@ hstore_hash(PG_FUNCTION_ARGS) | |
Datum hval = hash_any((unsigned char *) VARDATA(hs), | |
VARSIZE(hs) - VARHDRSZ); | |
- /* | |
- * this is the only place in the code that cares whether the overall | |
- * varlena size exactly matches the true data size; this assertion should | |
- * be maintained by all the other code, but we make it explicit here. | |
- */ | |
- Assert(VARSIZE(hs) == | |
- (HS_COUNT(hs) != 0 ? | |
- CALCDATASIZE(HS_COUNT(hs), | |
- HSE_ENDPOS(ARRPTR(hs)[2 * HS_COUNT(hs) - 1])) : | |
- HSHRDSIZE)); | |
- | |
PG_FREE_IF_COPY(hs, 0); | |
PG_RETURN_DATUM(hval); | |
} | |
+ | |
+PG_FUNCTION_INFO_V1(hstore_typeof); | |
+Datum hstore_typeof(PG_FUNCTION_ARGS); | |
+Datum | |
+hstore_typeof(PG_FUNCTION_ARGS) | |
+{ | |
+ HStore *hs = PG_GETARG_HS(0); | |
+ HStoreIterator *it; | |
+ HStoreValue v; | |
+ uint32 r; | |
+ | |
+ if (HS_ISEMPTY(hs)) | |
+ PG_RETURN_NULL(); | |
+ | |
+ it = HStoreIteratorInit(VARDATA(hs)); | |
+ r = HStoreIteratorGet(&it, &v, false); | |
+ | |
+ switch(r) | |
+ { | |
+ case WHS_BEGIN_ARRAY: | |
+ if (v.array.scalar) | |
+ { | |
+ Assert(v.array.nelems == 1); | |
+ r = HStoreIteratorGet(&it, &v, false); | |
+ Assert(r == WHS_ELEM); | |
+ | |
+ switch(v.type) | |
+ { | |
+ case hsvNull: | |
+ PG_RETURN_TEXT_P(cstring_to_text("null")); | |
+ case hsvBool: | |
+ PG_RETURN_TEXT_P(cstring_to_text("bool")); | |
+ case hsvNumeric: | |
+ PG_RETURN_TEXT_P(cstring_to_text("numeric")); | |
+ case hsvString: | |
+ PG_RETURN_TEXT_P(cstring_to_text("string")); | |
+ default: | |
+ elog(ERROR, "bogus hstore"); | |
+ } | |
+ } | |
+ else | |
+ { | |
+ PG_RETURN_TEXT_P(cstring_to_text("array")); | |
+ } | |
+ case WHS_BEGIN_HASH: | |
+ PG_RETURN_TEXT_P(cstring_to_text("hash")); | |
+ case 0: | |
+ PG_RETURN_NULL(); | |
+ default: | |
+ elog(ERROR, "bogus hstore"); | |
+ } | |
+ | |
+ PG_RETURN_NULL(); | |
+} | |
+ | |
diff --git a/contrib/hstore/hstore_scan.l b/contrib/hstore/hstore_scan.l | |
new file mode 100644 | |
index 0000000..752ab93 | |
--- /dev/null | |
+++ b/contrib/hstore/hstore_scan.l | |
@@ -0,0 +1,291 @@ | |
+%{ | |
+static string scanstring; | |
+ | |
+/* No reason to constrain amount of data slurped */ | |
+/* #define YY_READ_BUF_SIZE 16777216 */ | |
+ | |
+/* Handles to the buffer that the lexer uses internally */ | |
+static YY_BUFFER_STATE scanbufhandle; | |
+static char *scanbuf; | |
+static int scanbuflen; | |
+ | |
+static void addstring(bool init, char *s, int l); | |
+static void addchar(bool init, char s); | |
+static int checkSpecialVal(void); /* examine scanstring for the special value */ | |
+ | |
+static bool inputJSON = false; | |
+ | |
+ | |
+%} | |
+ | |
+%option 8bit | |
+%option never-interactive | |
+%option nodefault | |
+%option noinput | |
+%option nounput | |
+%option noyywrap | |
+%option warn | |
+%option prefix="hstore_yy" | |
+%option bison-bridge | |
+ | |
+%x xQUOTED | |
+%x xNONQUOTED | |
+ | |
+any [^\,\[\]\{\}\"\=\> \t\n\r\f\\\:] | |
+ | |
+ | |
+%% | |
+ | |
+<INITIAL>[\,\{\}\[\]] { return *yytext; } | |
+ | |
+<INITIAL>\=\> { return DELIMITER_P; } | |
+ | |
+<INITIAL>\: { | |
+ if (inputJSON) | |
+ { | |
+ return DELIMITER_P; | |
+ } | |
+ else | |
+ { | |
+ addchar(true, ':'); | |
+ BEGIN xNONQUOTED; | |
+ } | |
+ } | |
+ | |
+<INITIAL>[ \t\n\r\f]+ { /* ignore */ } | |
+ | |
+<INITIAL>\=/[^\>] { | |
+ addchar(true, '='); | |
+ BEGIN xNONQUOTED; | |
+ } | |
+ | |
+<INITIAL>\> { | |
+ addchar(true, yytext[0]); | |
+ BEGIN xNONQUOTED; | |
+ } | |
+<INITIAL>\\. { | |
+ addchar(true, yytext[1]); | |
+ BEGIN xNONQUOTED; | |
+ } | |
+ | |
+<INITIAL>({any}|\>)+ { | |
+ addstring(true, yytext, yyleng); | |
+ BEGIN xNONQUOTED; | |
+ } | |
+ | |
+<INITIAL>\" { | |
+ addchar(true, '\0'); | |
+ BEGIN xQUOTED; | |
+ } | |
+ | |
+<INITIAL>\= { /* =<<EOF>> */ | |
+ addchar(true, '='); | |
+ yylval->str = scanstring; | |
+ return STRING_P; | |
+ } | |
+ | |
+<xNONQUOTED>({any}|[\>\"\:])+ { | |
+ addstring(false, yytext, yyleng); | |
+ } | |
+ | |
+<xNONQUOTED>\=/[^\>] { addchar(false, *yytext); } | |
+ | |
+<xNONQUOTED>[ \t\n\r\f]+ { | |
+ yylval->str = scanstring; | |
+ BEGIN INITIAL; | |
+ return checkSpecialVal(); | |
+ } | |
+ | |
+<xNONQUOTED>\= { /* =<<EOF>> */ | |
+ addchar(false, '='); | |
+ yylval->str = scanstring; | |
+ BEGIN INITIAL; | |
+ return STRING_P; | |
+ } | |
+ | |
+<xNONQUOTED>[\,\{\}\[\]] { | |
+ yylval->str = scanstring; | |
+ yyless(0); | |
+ BEGIN INITIAL; | |
+ return checkSpecialVal(); | |
+ } | |
+ | |
+<xNONQUOTED><<EOF>> { | |
+ yylval->str = scanstring; | |
+ BEGIN INITIAL; | |
+ return checkSpecialVal(); | |
+ } | |
+ | |
+<xNONQUOTED>\=\> { | |
+ yylval->str = scanstring; | |
+ yyless(0); | |
+ BEGIN INITIAL; | |
+ return checkSpecialVal(); | |
+ } | |
+ | |
+ | |
+<xNONQUOTED,xQUOTED>\\. { addchar(false, yytext[1]); } | |
+ | |
+<INITIAL,xNONQUOTED,xQUOTED>\\ { yyerror("Unexpected end after backslesh"); } | |
+ | |
+<xQUOTED><<EOF>> { yyerror("Unexpected end of quoted string"); } | |
+ | |
+<xQUOTED>\" { | |
+ yylval->str = scanstring; | |
+ BEGIN INITIAL; | |
+ return STRING_P; | |
+ } | |
+ | |
+<xQUOTED>[^\\\"]+ { addstring(false, yytext, yyleng); } | |
+ | |
+<INITIAL><<EOF>> { yyterminate(); } | |
+ | |
+%% | |
+ | |
+void | |
+yyerror(const char *message) | |
+{ | |
+ if (*yytext == YY_END_OF_BUFFER_CHAR) | |
+ { | |
+ ereport(ERROR, | |
+ (errcode(ERRCODE_SYNTAX_ERROR), | |
+ errmsg("bad hstore representation"), | |
+ /* translator: %s is typically "syntax error" */ | |
+ errdetail("%s at end of input", message))); | |
+ } | |
+ else | |
+ { | |
+ ereport(ERROR, | |
+ (errcode(ERRCODE_SYNTAX_ERROR), | |
+ errmsg("bad hstore representation"), | |
+ /* translator: first %s is typically "syntax error" */ | |
+ errdetail("%s at or near \"%s\"", message, yytext))); | |
+ } | |
+} | |
+ | |
+static int | |
+checkSpecialVal() | |
+{ | |
+ int res = STRING_P; | |
+ | |
+ if (stringIsNumber(scanstring.val, scanstring.len)) | |
+ { | |
+ /* for numeric_in() call we need to make a correct C-string */ | |
+ addchar(false, '\0'); | |
+ res = NUMERIC_P; | |
+ } | |
+ else if (scanstring.len == 1) | |
+ { | |
+ if (*scanstring.val == 't') | |
+ res = TRUE_P; | |
+ else if (*scanstring.val == 'f') | |
+ res = FALSE_P; | |
+ } | |
+ else if (scanstring.len == 4) | |
+ { | |
+ if (pg_strncasecmp("null", scanstring.val, scanstring.len) == 0) | |
+ res = NULL_P; | |
+ else if (pg_strncasecmp("true", scanstring.val, scanstring.len) == 0) | |
+ res = TRUE_P; | |
+ } | |
+ else if (scanstring.len == 5) | |
+ { | |
+ if (pg_strncasecmp("false", scanstring.val, scanstring.len) == 0) | |
+ res = FALSE_P; | |
+ } | |
+ | |
+ return res; | |
+} | |
+/* | |
+ * Called before any actual parsing is done | |
+ */ | |
+static void | |
+hstore_scanner_init(const char *str, int slen) | |
+{ | |
+ if (slen <= 0) | |
+ slen = strlen(str); | |
+ | |
+ /* | |
+ * Might be left over after ereport() | |
+ */ | |
+ if (YY_CURRENT_BUFFER) | |
+ yy_delete_buffer(YY_CURRENT_BUFFER); | |
+ | |
+ /* | |
+ * Make a scan buffer with special termination needed by flex. | |
+ */ | |
+ | |
+ scanbuflen = slen; | |
+ scanbuf = palloc(slen + 2); | |
+ memcpy(scanbuf, str, slen); | |
+ scanbuf[slen] = scanbuf[slen + 1] = YY_END_OF_BUFFER_CHAR; | |
+ scanbufhandle = yy_scan_buffer(scanbuf, slen + 2); | |
+ | |
+ BEGIN(INITIAL); | |
+} | |
+ | |
+ | |
+/* | |
+ * Called after parsing is done to clean up after hstore_scanner_init() | |
+ */ | |
+static void | |
+hstore_scanner_finish(void) | |
+{ | |
+ yy_delete_buffer(scanbufhandle); | |
+ pfree(scanbuf); | |
+} | |
+ | |
+static void | |
+addstring(bool init, char *s, int l) { | |
+ if (init) { | |
+ scanstring.total = 32; | |
+ scanstring.val = palloc(scanstring.total); | |
+ scanstring.len = 0; | |
+ } | |
+ | |
+ if (s && l) { | |
+ while(scanstring.len + l + 1 >= scanstring.total) { | |
+ scanstring.total *= 2; | |
+ scanstring.val = repalloc(scanstring.val, scanstring.total); | |
+ } | |
+ | |
+ memcpy(scanstring.val+scanstring.len, s, l); | |
+ scanstring.len+=l; | |
+ } | |
+} | |
+ | |
+static void | |
+addchar(bool init, char s) { | |
+ if (init) | |
+ { | |
+ scanstring.total = 32; | |
+ scanstring.val = palloc(scanstring.total); | |
+ scanstring.len = 0; | |
+ } | |
+ else if(scanstring.len + 1 >= scanstring.total) | |
+ { | |
+ scanstring.total*=2; | |
+ scanstring.val=repalloc(scanstring.val, scanstring.total); | |
+ } | |
+ | |
+ scanstring.val[ scanstring.len ] = s; | |
+ if (s != '\0') | |
+ scanstring.len++; | |
+} | |
+ | |
+HStoreValue* | |
+parseHStore(const char *str, int len, bool json) { | |
+ HStoreValue *parseresult; | |
+ | |
+ inputJSON = json; | |
+ | |
+ hstore_scanner_init(str, len); | |
+ | |
+ if (hstore_yyparse((void*)&parseresult) != 0) | |
+ hstore_yyerror("bugus input"); | |
+ | |
+ hstore_scanner_finish(); | |
+ | |
+ return parseresult; | |
+} | |
+ | |
diff --git a/contrib/hstore/hstore_support.c b/contrib/hstore/hstore_support.c | |
new file mode 100644 | |
index 0000000..d7b7107 | |
--- /dev/null | |
+++ b/contrib/hstore/hstore_support.c | |
@@ -0,0 +1,1055 @@ | |
+#include "postgres.h" | |
+#include "utils/builtins.h" | |
+ | |
+#include "hstore.h" | |
+ | |
+/**************************************************************************** | |
+ * Compare Functions * | |
+ ****************************************************************************/ | |
+int | |
+compareHStoreStringValue(const void *a, const void *b, void *arg) | |
+{ | |
+ const HStoreValue *va = a; | |
+ const HStoreValue *vb = b; | |
+ int res; | |
+ | |
+ Assert(va->type == hsvString); | |
+ Assert(vb->type == hsvString); | |
+ | |
+ if (va->string.len == vb->string.len) | |
+ { | |
+ res = memcmp(va->string.val, vb->string.val, va->string.len); | |
+ if (res == 0 && arg) | |
+ *(bool*)arg = true; | |
+ } | |
+ else | |
+ { | |
+ res = (va->string.len > vb->string.len) ? 1 : -1; | |
+ } | |
+ | |
+ return res; | |
+} | |
+ | |
+int | |
+compareHStorePair(const void *a, const void *b, void *arg) | |
+{ | |
+ const HStorePair *pa = a; | |
+ const HStorePair *pb = b; | |
+ int res; | |
+ | |
+ res = compareHStoreStringValue(&pa->key, &pb->key, arg); | |
+ | |
+ /* | |
+ * guarantee keeping order of equal pair. Unique algorithm will | |
+ * prefer first element as value | |
+ */ | |
+ | |
+ if (res == 0) | |
+ res = (pa->order > pb->order) ? -1 : 1; | |
+ | |
+ return res; | |
+} | |
+ | |
+int | |
+compareHStoreValue(HStoreValue *a, HStoreValue *b) | |
+{ | |
+ if (a->type == b->type) | |
+ { | |
+ switch(a->type) | |
+ { | |
+ case hsvNull: | |
+ return 0; | |
+ case hsvString: | |
+ return compareHStoreStringValue(a, b, NULL); | |
+ case hsvBool: | |
+ if (a->boolean == b->boolean) | |
+ return 0; | |
+ return (a->boolean > b->boolean) ? 1 : -1; | |
+ case hsvNumeric: | |
+ return DatumGetInt32(DirectFunctionCall2(numeric_cmp, | |
+ PointerGetDatum(a->numeric), | |
+ PointerGetDatum(b->numeric))); | |
+ case hsvArray: | |
+ if (a->array.nelems == b->array.nelems) | |
+ { | |
+ int i, r; | |
+ | |
+ for(i=0; i<a->array.nelems; i++) | |
+ if ((r = compareHStoreValue(a->array.elems + i, b->array.elems + i)) != 0) | |
+ return r; | |
+ | |
+ return 0; | |
+ } | |
+ | |
+ return (a->array.nelems > b->array.nelems) ? 1 : -1; | |
+ case hsvHash: | |
+ if (a->hash.npairs == b->hash.npairs) | |
+ { | |
+ int i, r; | |
+ | |
+ for(i=0; i<a->hash.npairs; i++) | |
+ { | |
+ if ((r = compareHStoreStringValue(&a->hash.pairs[i].key, &b->hash.pairs[i].key, NULL)) != 0) | |
+ return r; | |
+ if ((r = compareHStoreValue(&a->hash.pairs[i].value, &b->hash.pairs[i].value)) != 0) | |
+ return r; | |
+ } | |
+ | |
+ return 0; | |
+ } | |
+ | |
+ return (a->hash.npairs > b->hash.npairs) ? 1 : -1; | |
+ case hsvBinary: | |
+ return compareHStoreBinaryValue(a->binary.data, b->binary.data); | |
+ default: | |
+ elog(PANIC, "unknown HStoreValue->type: %d", a->type); | |
+ } | |
+ } | |
+ | |
+ return (a->type > b->type) ? 1 : -1; | |
+} | |
+ | |
+int | |
+compareHStoreBinaryValue(char *a, char *b) | |
+{ | |
+ HStoreIterator *it1, *it2; | |
+ int res = 0; | |
+ | |
+ it1 = HStoreIteratorInit(a); | |
+ it2 = HStoreIteratorInit(b); | |
+ | |
+ while(res == 0) | |
+ { | |
+ HStoreValue v1, v2; | |
+ int r1, r2; | |
+ | |
+ r1 = HStoreIteratorGet(&it1, &v1, false); | |
+ r2 = HStoreIteratorGet(&it2, &v2, false); | |
+ | |
+ if (r1 == r2) | |
+ { | |
+ if (r1 == 0) | |
+ break; /* equal */ | |
+ | |
+ if (v1.type == v2.type) | |
+ { | |
+ switch(v1.type) | |
+ { | |
+ case hsvString: | |
+ res = compareHStoreStringValue(&v1, &v2, NULL); | |
+ break; | |
+ case hsvBool: | |
+ if (v1.boolean == v2.boolean) | |
+ res = 0; | |
+ else | |
+ res = (v1.boolean > v2.boolean) ? 1 : -1; | |
+ break; | |
+ case hsvNumeric: | |
+ res = DatumGetInt32(DirectFunctionCall2(numeric_cmp, | |
+ PointerGetDatum(v1.numeric), | |
+ PointerGetDatum(v2.numeric))); | |
+ break; | |
+ case hsvArray: | |
+ if (v1.array.nelems != v2.array.nelems) | |
+ res = (v1.array.nelems > v2.array.nelems) ? 1 : -1; | |
+ break; | |
+ case hsvHash: | |
+ if (v1.hash.npairs != v2.hash.npairs) | |
+ res = (v1.hash.npairs > v2.hash.npairs) ? 1 : -1; | |
+ break; | |
+ default: | |
+ break; | |
+ } | |
+ } | |
+ else | |
+ { | |
+ res = (v1.type > v2.type) ? 1 : -1; /* dummy order */ | |
+ } | |
+ } | |
+ else | |
+ { | |
+ res = (r1 > r2) ? 1 : -1; /* dummy order */ | |
+ } | |
+ } | |
+ | |
+ return res; | |
+} | |
+ | |
+/**************************************************************************** | |
+ * find string key in hash or array * | |
+ ****************************************************************************/ | |
+HStoreValue* | |
+findUncompressedHStoreValueByValue(char *buffer, uint32 flags, uint32 *lowbound, HStoreValue *key) | |
+{ | |
+ uint32 header = *(uint32*)buffer; | |
+ static HStoreValue r; | |
+ | |
+ Assert((header & (HS_FLAG_ARRAY | HS_FLAG_HSTORE)) != (HS_FLAG_ARRAY | HS_FLAG_HSTORE)); | |
+ | |
+ if (flags & HS_FLAG_ARRAY & header) | |
+ { | |
+ HEntry *array = (HEntry*)(buffer + sizeof(header)); | |
+ char *data = (char*)(array + (header & HS_COUNT_MASK)); | |
+ int i; | |
+ | |
+ for(i=(lowbound) ? *lowbound : 0; i<(header & HS_COUNT_MASK); i++) { | |
+ HEntry *e = array + i; | |
+ | |
+ if (HSE_ISNULL(*e) && key->type == hsvNull) | |
+ { | |
+ r.type = hsvNull; | |
+ if (lowbound) | |
+ *lowbound = i; | |
+ r.size = sizeof(HEntry); | |
+ | |
+ return &r; | |
+ } | |
+ else if (HSE_ISSTRING(*e) && key->type == hsvString ) | |
+ { | |
+ if (key->string.len == HSE_LEN(*e) && memcmp(key->string.val, data + HSE_OFF(*e), key->string.len) == 0) | |
+ { | |
+ r.type = hsvString; | |
+ r.string.val = data + HSE_OFF(*e); | |
+ r.string.len = key->string.len; | |
+ r.size = sizeof(HEntry) + r.string.len; | |
+ if (lowbound) | |
+ *lowbound = i; | |
+ | |
+ return &r; | |
+ } | |
+ } | |
+ else if (HSE_ISBOOL(*e) && key->type == hsvBool) | |
+ { | |
+ if ((HSE_ISBOOL_TRUE(*e) && key->boolean == true) || (HSE_ISBOOL_FALSE(*e) && key->boolean == false)) | |
+ { | |
+ r = *key; | |
+ r.size = sizeof(HEntry); | |
+ if (lowbound) | |
+ *lowbound = i; | |
+ | |
+ return &r; | |
+ } | |
+ } | |
+ else if (HSE_ISNUMERIC(*e) && key->type == hsvNumeric) | |
+ { | |
+ if (DatumGetBool(DirectFunctionCall2(numeric_eq, | |
+ PointerGetDatum(data + INTALIGN(HSE_OFF(*e))), | |
+ PointerGetDatum(key->numeric))) == true) | |
+ { | |
+ r.type = hsvNumeric; | |
+ r.numeric = (Numeric)(data + INTALIGN(HSE_OFF(*e))); | |
+ if (lowbound) | |
+ *lowbound = i; | |
+ | |
+ return &r; | |
+ } | |
+ } | |
+ } | |
+ } | |
+ else if (flags & HS_FLAG_HSTORE & header) | |
+ { | |
+ HEntry *array = (HEntry*)(buffer + sizeof(header)); | |
+ char *data = (char*)(array + (header & HS_COUNT_MASK) * 2); | |
+ uint32 stopLow = lowbound ? *lowbound : 0, | |
+ stopHigh = (header & HS_COUNT_MASK), | |
+ stopMiddle; | |
+ | |
+ if (key->type != hsvString) | |
+ return NULL; | |
+ | |
+ while (stopLow < stopHigh) | |
+ { | |
+ int difference; | |
+ HEntry *e; | |
+ | |
+ stopMiddle = stopLow + (stopHigh - stopLow) / 2; | |
+ | |
+ e = array + stopMiddle * 2; | |
+ | |
+ if (key->string.len == HSE_LEN(*e)) | |
+ difference = memcmp(data + HSE_OFF(*e), key->string.val, key->string.len); | |
+ else | |
+ difference = (HSE_LEN(*e) > key->string.len) ? 1 : -1; | |
+ | |
+ if (difference == 0) | |
+ { | |
+ HEntry *v = e + 1; | |
+ | |
+ if (lowbound) | |
+ *lowbound = stopMiddle + 1; | |
+ | |
+ if (HSE_ISSTRING(*v)) | |
+ { | |
+ r.type = hsvString; | |
+ r.string.val = data + HSE_OFF(*v); | |
+ r.string.len = HSE_LEN(*v); | |
+ r.size = sizeof(HEntry) + r.string.len; | |
+ } | |
+ else if (HSE_ISBOOL(*v)) | |
+ { | |
+ r.type = hsvBool; | |
+ r.boolean = (HSE_ISBOOL_TRUE(*v)) ? true : false; | |
+ r.size = sizeof(HEntry); | |
+ } | |
+ else if (HSE_ISNUMERIC(*v)) | |
+ { | |
+ r.type = hsvNumeric; | |
+ r.numeric = (Numeric)(data + INTALIGN(HSE_OFF(*v))); | |
+ r.size = 2*sizeof(HEntry) + VARSIZE_ANY(r.numeric); | |
+ } | |
+ else if (HSE_ISNULL(*v)) | |
+ { | |
+ r.type = hsvNull; | |
+ r.size = sizeof(HEntry); | |
+ } | |
+ else | |
+ { | |
+ r.type = hsvBinary; | |
+ r.binary.data = data + INTALIGN(HSE_OFF(*v)); | |
+ r.binary.len = HSE_LEN(*v) - (INTALIGN(HSE_OFF(*v)) - HSE_OFF(*v)); | |
+ r.size = 2*sizeof(HEntry) + r.binary.len; | |
+ } | |
+ | |
+ return &r; | |
+ } | |
+ else if (difference < 0) | |
+ { | |
+ stopLow = stopMiddle + 1; | |
+ } | |
+ else | |
+ { | |
+ stopHigh = stopMiddle; | |
+ } | |
+ } | |
+ | |
+ if (lowbound) | |
+ *lowbound = stopLow; | |
+ } | |
+ | |
+ return NULL; | |
+} | |
+ | |
+HStoreValue* | |
+findUncompressedHStoreValue(char *buffer, uint32 flags, uint32 *lowbound, char *key, uint32 keylen) | |
+{ | |
+ HStoreValue v; | |
+ | |
+ if (key == NULL) | |
+ { | |
+ v.type = hsvNull; | |
+ } | |
+ else | |
+ { | |
+ v.type = hsvString; | |
+ v.string.val = key; | |
+ v.string.len = keylen; | |
+ } | |
+ | |
+ return findUncompressedHStoreValueByValue(buffer, flags, lowbound, &v); | |
+} | |
+ | |
+HStoreValue* | |
+getHStoreValue(char *buffer, uint32 flags, int32 i) | |
+{ | |
+ uint32 header = *(uint32*)buffer; | |
+ static HStoreValue r; | |
+ HEntry *array, *e; | |
+ char *data; | |
+ | |
+ Assert((header & (HS_FLAG_ARRAY | HS_FLAG_HSTORE)) != (HS_FLAG_ARRAY | HS_FLAG_HSTORE)); | |
+ | |
+ if (i >= 0) | |
+ { | |
+ if (i >= (header & HS_COUNT_MASK)) | |
+ return NULL; | |
+ } | |
+ else | |
+ { | |
+ if (-i > (header & HS_COUNT_MASK)) | |
+ return NULL; | |
+ | |
+ i = (header & HS_COUNT_MASK) + i; | |
+ } | |
+ | |
+ array = (HEntry*)(buffer + sizeof(header)); | |
+ | |
+ if (flags & HS_FLAG_ARRAY & header) | |
+ { | |
+ e = array + i; | |
+ data = (char*)(array + (header & HS_COUNT_MASK)); | |
+ } | |
+ else if (flags & HS_FLAG_HSTORE & header) | |
+ { | |
+ e = array + i * 2 + 1; | |
+ data = (char*)(array + (header & HS_COUNT_MASK) * 2); | |
+ } | |
+ else | |
+ { | |
+ return NULL; | |
+ } | |
+ | |
+ if (HSE_ISSTRING(*e)) | |
+ { | |
+ r.type = hsvString; | |
+ r.string.val = data + HSE_OFF(*e); | |
+ r.string.len = HSE_LEN(*e); | |
+ r.size = sizeof(HEntry) + r.string.len; | |
+ } | |
+ else if (HSE_ISBOOL(*e)) | |
+ { | |
+ r.type = hsvBool; | |
+ r.boolean = (HSE_ISBOOL_TRUE(*e)) ? true : false; | |
+ r.size = sizeof(HEntry); | |
+ } | |
+ else if (HSE_ISNUMERIC(*e)) | |
+ { | |
+ r.type = hsvNumeric; | |
+ r.numeric = (Numeric)(data + INTALIGN(HSE_OFF(*e))); | |
+ r.size = 2*sizeof(HEntry) + VARSIZE_ANY(r.numeric); | |
+ } | |
+ else if (HSE_ISNULL(*e)) | |
+ { | |
+ r.type = hsvNull; | |
+ r.size = sizeof(HEntry); | |
+ } | |
+ else | |
+ { | |
+ r.type = hsvBinary; | |
+ r.binary.data = data + INTALIGN(HSE_OFF(*e)); | |
+ r.binary.len = HSE_LEN(*e) - (INTALIGN(HSE_OFF(*e)) - HSE_OFF(*e)); | |
+ r.size = r.binary.len + 2*sizeof(HEntry); | |
+ } | |
+ | |
+ return &r; | |
+} | |
+ | |
+/**************************************************************************** | |
+ * Walk on tree representation of hstore * | |
+ ****************************************************************************/ | |
+static void | |
+walkUncompressedHStoreDo(HStoreValue *v, walk_hstore_cb cb, void *cb_arg, uint32 level) | |
+{ | |
+ int i; | |
+ | |
+ switch(v->type) | |
+ { | |
+ case hsvArray: | |
+ cb(cb_arg, v, WHS_BEGIN_ARRAY, level); | |
+ for(i=0; i<v->array.nelems; i++) | |
+ { | |
+ if (v->array.elems[i].type == hsvNull || v->array.elems[i].type == hsvString || | |
+ v->array.elems[i].type == hsvBool || v->array.elems[i].type == hsvNumeric || | |
+ v->array.elems[i].type == hsvBinary) | |
+ cb(cb_arg, v->array.elems + i, WHS_ELEM, level); | |
+ else | |
+ walkUncompressedHStoreDo(v->array.elems + i, cb, cb_arg, level + 1); | |
+ } | |
+ cb(cb_arg, v, WHS_END_ARRAY, level); | |
+ break; | |
+ case hsvHash: | |
+ cb(cb_arg, v, WHS_BEGIN_HASH, level); | |
+ | |
+ for(i=0; i<v->hash.npairs; i++) | |
+ { | |
+ cb(cb_arg, &v->hash.pairs[i].key, WHS_KEY, level); | |
+ | |
+ if (v->hash.pairs[i].value.type == hsvNull || v->hash.pairs[i].value.type == hsvString || | |
+ v->hash.pairs[i].value.type == hsvBool || v->hash.pairs[i].value.type == hsvNumeric || | |
+ v->hash.pairs[i].value.type == hsvBinary) | |
+ cb(cb_arg, &v->hash.pairs[i].value, WHS_VALUE, level); | |
+ else | |
+ walkUncompressedHStoreDo(&v->hash.pairs[i].value, cb, cb_arg, level + 1); | |
+ } | |
+ | |
+ cb(cb_arg, v, WHS_END_HASH, level); | |
+ break; | |
+ default: | |
+ elog(PANIC, "impossible HStoreValue->type: %d", v->type); | |
+ } | |
+} | |
+ | |
+void | |
+walkUncompressedHStore(HStoreValue *v, walk_hstore_cb cb, void *cb_arg) | |
+{ | |
+ if (v) | |
+ walkUncompressedHStoreDo(v, cb, cb_arg, 0); | |
+} | |
+ | |
+/**************************************************************************** | |
+ * Iteration over binary hstore * | |
+ ****************************************************************************/ | |
+static void | |
+parseBuffer(HStoreIterator *it, char *buffer) | |
+{ | |
+ uint32 header = *(uint32*)buffer; | |
+ | |
+ it->type = header & (HS_FLAG_ARRAY | HS_FLAG_HSTORE); | |
+ it->nelems = header & HS_COUNT_MASK; | |
+ it->buffer = buffer; | |
+ | |
+ | |
+ buffer += sizeof(uint32); | |
+ it->array = (HEntry*)buffer; | |
+ | |
+ it->state = hsi_start; | |
+ | |
+ switch(it->type) | |
+ { | |
+ case HS_FLAG_ARRAY: | |
+ it->data = buffer + it->nelems * sizeof(HEntry); | |
+ it->isScalar = (header & HS_FLAG_SCALAR) ? true : false; | |
+ Assert(it->isScalar == false || it->nelems == 1); | |
+ break; | |
+ case HS_FLAG_HSTORE: | |
+ it->data = buffer + it->nelems * sizeof(HEntry) * 2; | |
+ break; | |
+ default: | |
+ elog(PANIC, "impossible type: %08x", it->type); | |
+ } | |
+} | |
+ | |
+HStoreIterator* | |
+HStoreIteratorInit(char *buffer) | |
+{ | |
+ HStoreIterator *it = palloc(sizeof(*it)); | |
+ | |
+ parseBuffer(it, buffer); | |
+ it->next = NULL; | |
+ | |
+ return it; | |
+} | |
+ | |
+static bool | |
+formAnswer(HStoreIterator **it, HStoreValue *v, HEntry *e, bool skipNested) | |
+{ | |
+ if (HSE_ISSTRING(*e)) | |
+ { | |
+ v->type = hsvString; | |
+ v->string.val = (*it)->data + HSE_OFF(*e); | |
+ v->string.len = HSE_LEN(*e); | |
+ v->size = sizeof(HEntry) + v->string.len; | |
+ | |
+ return false; | |
+ } | |
+ else if (HSE_ISBOOL(*e)) | |
+ { | |
+ v->type = hsvBool; | |
+ v->boolean = (HSE_ISBOOL_TRUE(*e)) ? true : false; | |
+ v->size = sizeof(HEntry); | |
+ | |
+ return false; | |
+ } | |
+ else if (HSE_ISNUMERIC(*e)) | |
+ { | |
+ v->type = hsvNumeric; | |
+ v->numeric = (Numeric)((*it)->data + INTALIGN(HSE_OFF(*e))); | |
+ v->size = 2*sizeof(HEntry) + VARSIZE_ANY(v->numeric); | |
+ | |
+ return false; | |
+ } | |
+ else if (HSE_ISNULL(*e)) | |
+ { | |
+ v->type = hsvNull; | |
+ v->size = sizeof(HEntry); | |
+ | |
+ return false; | |
+ } | |
+ else if (skipNested) | |
+ { | |
+ v->type = hsvBinary; | |
+ v->binary.data = (*it)->data + INTALIGN(HSE_OFF(*e)); | |
+ v->binary.len = HSE_LEN(*e) - (INTALIGN(HSE_OFF(*e)) - HSE_OFF(*e)); | |
+ v->size = v->binary.len + 2*sizeof(HEntry); | |
+ | |
+ return false; | |
+ } | |
+ else | |
+ { | |
+ HStoreIterator *nit = palloc(sizeof(*nit)); | |
+ | |
+ parseBuffer(nit, (*it)->data + INTALIGN(HSE_OFF(*e))); | |
+ nit->next = *it; | |
+ *it = nit; | |
+ | |
+ return true; | |
+ } | |
+} | |
+ | |
+static HStoreIterator* | |
+up(HStoreIterator *it) | |
+{ | |
+ HStoreIterator *v = it->next; | |
+ | |
+ pfree(it); | |
+ | |
+ return v; | |
+} | |
+ | |
+int | |
+HStoreIteratorGet(HStoreIterator **it, HStoreValue *v, bool skipNested) | |
+{ | |
+ int res; | |
+ | |
+ if (*it == NULL) | |
+ return 0; | |
+ | |
+ /* | |
+ * Encode all possible states by one integer. That's possible | |
+ * because enum members of HStoreIterator->state uses different bits | |
+ * than HS_FLAG_ARRAY/HS_FLAG_HSTORE. See definition of HStoreIterator | |
+ */ | |
+ | |
+ switch((*it)->type | (*it)->state) | |
+ { | |
+ case HS_FLAG_ARRAY | hsi_start: | |
+ (*it)->state = hsi_elem; | |
+ (*it)->i = 0; | |
+ v->type = hsvArray; | |
+ v->array.nelems = (*it)->nelems; | |
+ res = WHS_BEGIN_ARRAY; | |
+ v->array.scalar = (*it)->isScalar; | |
+ break; | |
+ case HS_FLAG_ARRAY | hsi_elem: | |
+ if ((*it)->i >= (*it)->nelems) | |
+ { | |
+ *it = up(*it); | |
+ res = WHS_END_ARRAY; | |
+ } | |
+ else if (formAnswer(it, v, &(*it)->array[ (*it)->i++ ], skipNested)) | |
+ { | |
+ res = HStoreIteratorGet(it, v, skipNested); | |
+ } | |
+ else | |
+ { | |
+ res = WHS_ELEM; | |
+ } | |
+ break; | |
+ case HS_FLAG_HSTORE | hsi_start: | |
+ (*it)->state = hsi_key; | |
+ (*it)->i = 0; | |
+ v->type = hsvHash; | |
+ v->hash.npairs = (*it)->nelems; | |
+ res = WHS_BEGIN_HASH; | |
+ break; | |
+ case HS_FLAG_HSTORE | hsi_key: | |
+ if ((*it)->i >= (*it)->nelems) | |
+ { | |
+ *it = up(*it); | |
+ res = WHS_END_HASH; | |
+ } | |
+ else | |
+ { | |
+ formAnswer(it, v, &(*it)->array[ (*it)->i * 2 ], false); | |
+ (*it)->state = hsi_value; | |
+ res = WHS_KEY; | |
+ } | |
+ break; | |
+ case HS_FLAG_HSTORE | hsi_value: | |
+ (*it)->state = hsi_key; | |
+ if (formAnswer(it, v, &(*it)->array[ ((*it)->i++) * 2 + 1], skipNested)) | |
+ res = HStoreIteratorGet(it, v, skipNested); | |
+ else | |
+ res = WHS_VALUE; | |
+ break; | |
+ default: | |
+ elog(PANIC,"unknown state %08x", (*it)->type & (*it)->state); | |
+ } | |
+ | |
+ return res; | |
+} | |
+ | |
+/**************************************************************************** | |
+ * Transformation from tree to binary representation of hstore * | |
+ ****************************************************************************/ | |
+typedef struct CompressState | |
+{ | |
+ char *begin; | |
+ char *ptr; | |
+ | |
+ struct { | |
+ uint32 i; | |
+ uint32 *header; | |
+ HEntry *array; | |
+ char *begin; | |
+ } *levelstate, *lptr, *pptr; | |
+ | |
+ uint32 maxlevel; | |
+ | |
+} CompressState; | |
+ | |
+#define curLevelState state->lptr | |
+#define prevLevelState state->pptr | |
+ | |
+static void | |
+putHEntryString(CompressState *state, HStoreValue* value, uint32 level, uint32 i) | |
+{ | |
+ curLevelState = state->levelstate + level; | |
+ | |
+ if (i == 0) | |
+ curLevelState->array[0].entry = HENTRY_ISFIRST; | |
+ else | |
+ curLevelState->array[i].entry = 0; | |
+ | |
+ switch(value->type) | |
+ { | |
+ case hsvNull: | |
+ curLevelState->array[i].entry |= HENTRY_ISNULL; | |
+ | |
+ if (i>0) | |
+ curLevelState->array[i].entry |= | |
+ curLevelState->array[i - 1].entry & HENTRY_POSMASK; | |
+ break; | |
+ case hsvString: | |
+ memcpy(state->ptr, value->string.val, value->string.len); | |
+ state->ptr += value->string.len; | |
+ | |
+ if (i == 0) | |
+ curLevelState->array[i].entry |= value->string.len; | |
+ else | |
+ curLevelState->array[i].entry |= | |
+ (curLevelState->array[i - 1].entry & HENTRY_POSMASK) + value->string.len; | |
+ break; | |
+ case hsvBool: | |
+ curLevelState->array[i].entry |= (value->boolean) ? HENTRY_ISTRUE : HENTRY_ISFALSE; | |
+ | |
+ if (i>0) | |
+ curLevelState->array[i].entry |= | |
+ curLevelState->array[i - 1].entry & HENTRY_POSMASK; | |
+ break; | |
+ case hsvNumeric: | |
+ { | |
+ int addlen = INTALIGN(state->ptr - state->begin) - (state->ptr - state->begin); | |
+ int numlen = VARSIZE_ANY(value->numeric); | |
+ | |
+ switch(addlen) | |
+ { | |
+ case 3: | |
+ *state->ptr = '\0'; state->ptr++; | |
+ case 2: | |
+ *state->ptr = '\0'; state->ptr++; | |
+ case 1: | |
+ *state->ptr = '\0'; state->ptr++; | |
+ case 0: | |
+ default: | |
+ break; | |
+ } | |
+ | |
+ memcpy(state->ptr, value->numeric, numlen); | |
+ state->ptr += numlen; | |
+ | |
+ curLevelState->array[i].entry |= HENTRY_ISNUMERIC; | |
+ if (i == 0) | |
+ curLevelState->array[i].entry |= addlen + numlen; | |
+ else | |
+ curLevelState->array[i].entry |= | |
+ (curLevelState->array[i - 1].entry & HENTRY_POSMASK) + addlen + numlen; | |
+ break; | |
+ } | |
+ case hsvBinary: | |
+ { | |
+ int addlen = INTALIGN(state->ptr - state->begin) - (state->ptr - state->begin); | |
+ | |
+ switch(addlen) | |
+ { | |
+ case 3: | |
+ *state->ptr = '\0'; state->ptr++; | |
+ case 2: | |
+ *state->ptr = '\0'; state->ptr++; | |
+ case 1: | |
+ *state->ptr = '\0'; state->ptr++; | |
+ case 0: | |
+ default: | |
+ break; | |
+ } | |
+ | |
+ memcpy(state->ptr, value->binary.data, value->binary.len); | |
+ state->ptr += value->binary.len; | |
+ | |
+ curLevelState->array[i].entry |= HENTRY_ISNEST; | |
+ | |
+ if (i == 0) | |
+ curLevelState->array[i].entry |= addlen + value->binary.len; | |
+ else | |
+ curLevelState->array[i].entry |= | |
+ (curLevelState->array[i - 1].entry & HENTRY_POSMASK) + addlen + value->binary.len; | |
+ } | |
+ break; | |
+ default: | |
+ elog(PANIC,"Unsupported HStoreValue type: %d", value->type); | |
+ } | |
+} | |
+ | |
+static void | |
+compressCallback(void *arg, HStoreValue* value, uint32 flags, uint32 level) | |
+{ | |
+ CompressState *state = arg; | |
+ | |
+ if (level == state->maxlevel) { | |
+ state->maxlevel *= 2; | |
+ state->levelstate = repalloc(state->levelstate, sizeof(*state->levelstate) * state->maxlevel); | |
+ } | |
+ | |
+ curLevelState = state->levelstate + level; | |
+ | |
+ if (flags & (WHS_BEGIN_ARRAY | WHS_BEGIN_HASH)) | |
+ { | |
+ Assert(((flags & WHS_BEGIN_ARRAY) && value->type == hsvArray) || | |
+ ((flags & WHS_BEGIN_HASH) && value->type == hsvHash)); | |
+ | |
+ curLevelState->begin = state->ptr; | |
+ | |
+ switch(INTALIGN(state->ptr - state->begin) - (state->ptr - state->begin)) | |
+ { | |
+ case 3: | |
+ *state->ptr = '\0'; state->ptr++; | |
+ case 2: | |
+ *state->ptr = '\0'; state->ptr++; | |
+ case 1: | |
+ *state->ptr = '\0'; state->ptr++; | |
+ case 0: | |
+ default: | |
+ break; | |
+ } | |
+ | |
+ curLevelState->header = (uint32*)state->ptr; | |
+ state->ptr += sizeof(*curLevelState->header); | |
+ | |
+ curLevelState->array = (HEntry*)state->ptr; | |
+ curLevelState->i = 0; | |
+ | |
+ if (value->type == hsvArray) | |
+ { | |
+ *curLevelState->header = value->array.nelems | HS_FLAG_ARRAY ; | |
+ state->ptr += sizeof(HEntry) * value->array.nelems; | |
+ | |
+ if (value->array.scalar) | |
+ { | |
+ Assert(value->array.nelems == 1); | |
+ Assert(level == 0); | |
+ *curLevelState->header |= HS_FLAG_SCALAR; | |
+ } | |
+ } | |
+ else | |
+ { | |
+ *curLevelState->header = value->hash.npairs | HS_FLAG_HSTORE ; | |
+ state->ptr += sizeof(HEntry) * value->hash.npairs * 2; | |
+ } | |
+ | |
+ if (level == 0) | |
+ *curLevelState->header |= HS_FLAG_NEWVERSION; | |
+ } | |
+ else if (flags & WHS_ELEM) | |
+ { | |
+ putHEntryString(state, value, level, curLevelState->i); | |
+ curLevelState->i++; | |
+ } | |
+ else if (flags & WHS_KEY) | |
+ { | |
+ Assert(value->type == hsvString); | |
+ | |
+ putHEntryString(state, value, level, curLevelState->i * 2); | |
+ } | |
+ else if (flags & WHS_VALUE) | |
+ { | |
+ putHEntryString(state, value, level, curLevelState->i * 2 + 1); | |
+ curLevelState->i++; | |
+ } | |
+ else if (flags & (WHS_END_ARRAY | WHS_END_HASH)) | |
+ { | |
+ uint32 len, i; | |
+ | |
+ Assert(((flags & WHS_END_ARRAY) && value->type == hsvArray) || | |
+ ((flags & WHS_END_HASH) && value->type == hsvHash)); | |
+ if (level == 0) | |
+ return; | |
+ | |
+ len = state->ptr - (char*)curLevelState->begin; | |
+ | |
+ prevLevelState = curLevelState - 1; | |
+ | |
+ if (*prevLevelState->header & HS_FLAG_ARRAY) { | |
+ i = prevLevelState->i; | |
+ | |
+ prevLevelState->array[i].entry = HENTRY_ISNEST; | |
+ | |
+ if (i == 0) | |
+ prevLevelState->array[0].entry |= HENTRY_ISFIRST | len; | |
+ else | |
+ prevLevelState->array[i].entry |= | |
+ (prevLevelState->array[i - 1].entry & HENTRY_POSMASK) + len; | |
+ } | |
+ else if (*prevLevelState->header & HS_FLAG_HSTORE) | |
+ { | |
+ i = 2 * prevLevelState->i + 1; /* VALUE, not a KEY */ | |
+ | |
+ prevLevelState->array[i].entry = HENTRY_ISNEST; | |
+ | |
+ prevLevelState->array[i].entry |= | |
+ (prevLevelState->array[i - 1].entry & HENTRY_POSMASK) + len; | |
+ } | |
+ else | |
+ { | |
+ elog(PANIC, "Wrong parent"); | |
+ } | |
+ | |
+ Assert(state->ptr - curLevelState->begin <= value->size); | |
+ prevLevelState->i++; | |
+ } | |
+ else | |
+ { | |
+ elog(PANIC, "Wrong flags"); | |
+ } | |
+} | |
+ | |
+uint32 | |
+compressHStore(HStoreValue *v, char *buffer) { | |
+ uint32 l = 0; | |
+ CompressState state; | |
+ | |
+ state.begin = state.ptr = buffer; | |
+ state.maxlevel = 8; | |
+ state.levelstate = palloc(sizeof(*state.levelstate) * state.maxlevel); | |
+ | |
+ walkUncompressedHStore(v, compressCallback, &state); | |
+ | |
+ l = state.ptr - buffer; | |
+ Assert(l <= v->size); | |
+ | |
+ return l; | |
+} | |
+ | |
+/**************************************************************************** | |
+ * Iteration-like forming hstore * | |
+ * Note: it believ by default in already sorted keys in hash, * | |
+ * although with r == WHS_END_HASH and v == NULL it will sort itself * | |
+ ****************************************************************************/ | |
+static ToHStoreState* | |
+pushState(ToHStoreState **state) | |
+{ | |
+ ToHStoreState *ns = palloc(sizeof(*ns)); | |
+ | |
+ ns->next = *state; | |
+ return ns; | |
+} | |
+ | |
+static void | |
+appendArray(ToHStoreState *state, HStoreValue *v) | |
+{ | |
+ HStoreValue *a = &state->v; | |
+ | |
+ Assert(a->type == hsvArray); | |
+ | |
+ if (a->array.nelems >= state->size) | |
+ { | |
+ state->size *= 2; | |
+ a->array.elems = repalloc(a->array.elems, | |
+ sizeof(*a->array.elems) * state->size); | |
+ } | |
+ | |
+ a->array.elems[a->array.nelems ++] = *v; | |
+ | |
+ a->size += v->size; | |
+} | |
+ | |
+static void | |
+appendKey(ToHStoreState *state, HStoreValue *v) | |
+{ | |
+ HStoreValue *h = &state->v; | |
+ | |
+ Assert(h->type == hsvHash); | |
+ | |
+ if (h->hash.npairs >= state->size) | |
+ { | |
+ state->size *= 2; | |
+ h->hash.pairs = repalloc(h->hash.pairs, | |
+ sizeof(*h->hash.pairs) * state->size); | |
+ } | |
+ | |
+ h->hash.pairs[h->hash.npairs].key = *v; | |
+ h->hash.pairs[h->hash.npairs].order = h->hash.npairs; | |
+ | |
+ h->size += v->size; | |
+} | |
+ | |
+static void | |
+appendValue(ToHStoreState *state, HStoreValue *v) | |
+{ | |
+ | |
+ HStoreValue *h = &state->v; | |
+ | |
+ Assert(h->type == hsvHash); | |
+ | |
+ h->hash.pairs[h->hash.npairs++].value = *v; | |
+ | |
+ h->size += v->size; | |
+} | |
+ | |
+ | |
+HStoreValue* | |
+pushHStoreValue(ToHStoreState **state, int r /* WHS_* */, HStoreValue *v) { | |
+ HStoreValue *h = NULL; | |
+ | |
+ switch(r) | |
+ { | |
+ case WHS_BEGIN_ARRAY: | |
+ *state = pushState(state); | |
+ h = &(*state)->v; | |
+ (*state)->v.type = hsvArray; | |
+ (*state)->v.size = 3*sizeof(HEntry); | |
+ (*state)->v.array.nelems = 0; | |
+ (*state)->v.array.scalar = (v && v->array.scalar) ? true : false; | |
+ (*state)->size = (v && v->type == hsvArray && v->array.nelems > 0) ? v->array.nelems : 4; | |
+ (*state)->v.array.elems = palloc(sizeof(*(*state)->v.array.elems) * (*state)->size); | |
+ break; | |
+ case WHS_BEGIN_HASH: | |
+ *state = pushState(state); | |
+ h = &(*state)->v; | |
+ (*state)->v.type = hsvHash; | |
+ (*state)->v.size = 3*sizeof(HEntry); | |
+ (*state)->v.hash.npairs = 0; | |
+ (*state)->size = (v && v->type == hsvHash && v->hash.npairs > 0) ? v->hash.npairs : 4; | |
+ (*state)->v.hash.pairs = palloc(sizeof(*(*state)->v.hash.pairs) * (*state)->size); | |
+ break; | |
+ case WHS_ELEM: | |
+ Assert(v->type == hsvNull || v->type == hsvString || v->type == hsvBool || v->type == hsvNumeric || | |
+ v->type == hsvBinary); | |
+ appendArray(*state, v); | |
+ break; | |
+ case WHS_KEY: | |
+ Assert(v->type == hsvString); | |
+ appendKey(*state, v); | |
+ break; | |
+ case WHS_VALUE: | |
+ Assert(v->type == hsvNull || v->type == hsvString || v->type == hsvBool || v->type == hsvNumeric || | |
+ v->type == hsvBinary); | |
+ appendValue(*state, v); | |
+ break; | |
+ case WHS_END_HASH: | |
+ h = &(*state)->v; | |
+ if (v == NULL) | |
+ ORDER_PAIRS(h->hash.pairs, h->hash.npairs, (void)0); | |
+ case WHS_END_ARRAY: | |
+ h = &(*state)->v; | |
+ *state = (*state)->next; | |
+ if (*state) | |
+ { | |
+ switch((*state)->v.type) | |
+ { | |
+ case hsvArray: | |
+ appendArray(*state, h); | |
+ break; | |
+ case hsvHash: | |
+ appendValue(*state, h); | |
+ break; | |
+ default: | |
+ elog(PANIC, "wrong parent type: %d", (*state)->v.type); | |
+ } | |
+ } | |
+ break; | |
+ default: | |
+ elog(PANIC, "wrong type: %08x", r); | |
+ } | |
+ | |
+ return h; | |
+} | |
+ | |
diff --git a/contrib/hstore/sql/hstore.sql b/contrib/hstore/sql/hstore.sql | |
index 9518f56..10afc45 100644 | |
--- a/contrib/hstore/sql/hstore.sql | |
+++ b/contrib/hstore/sql/hstore.sql | |
@@ -39,6 +39,9 @@ select 'aa=>"bb" ,cc=>dd'::hstore; | |
select 'aa=>null'::hstore; | |
select 'aa=>NuLl'::hstore; | |
select 'aa=>"NuLl"'::hstore; | |
+select 'aa=>nul'::hstore; | |
+select 'aa=>NuL'::hstore; | |
+select 'aa=>"NuL"'::hstore; | |
select e'\\=a=>q=w'::hstore; | |
select e'"=a"=>q\\=w'::hstore; | |
@@ -213,7 +216,7 @@ select hstore(v) from testhstore1 v; | |
select hstore(null::testhstore0); | |
select hstore(null::testhstore1); | |
select pg_column_size(hstore(v)) | |
- = pg_column_size('a=>1, b=>"foo", c=>"1.2", d=>"3", e=>"0"'::hstore) | |
+ = pg_column_size('a=>"1", b=>"foo", c=>1.2, d=>"3", e=>"0"'::hstore) | |
from testhstore1 v; | |
select populate_record(v, hstore('c', '3.45')) from testhstore1 v; | |
select populate_record(v, hstore('d', '3.45')) from testhstore1 v; | |
@@ -299,6 +302,8 @@ select count(*) from testhstore where h ? 'public'; | |
select count(*) from testhstore where h ?| ARRAY['public','disabled']; | |
select count(*) from testhstore where h ?& ARRAY['public','disabled']; | |
+RESET enable_seqscan; | |
+ | |
drop index hidx; | |
create index hidx on testhstore using gin (h); | |
set enable_seqscan=off; | |
@@ -310,6 +315,8 @@ select count(*) from testhstore where h ? 'public'; | |
select count(*) from testhstore where h ?| ARRAY['public','disabled']; | |
select count(*) from testhstore where h ?& ARRAY['public','disabled']; | |
+RESET enable_seqscan; | |
+ | |
select count(*) from (select (each(h)).key from testhstore) as wow ; | |
select key, count(*) from (select (each(h)).key from testhstore) as wow group by key order by count desc, key; | |
@@ -323,6 +330,9 @@ select count(*) from (select h from (select * from testhstore union all select * | |
select distinct * from (values (hstore '' || ''),('')) v(h); | |
set enable_sort = true; | |
+RESET enable_hashagg; | |
+RESET enable_sort; | |
+ | |
-- btree | |
drop index hidx; | |
create index hidx on testhstore using btree (h); | |
@@ -331,6 +341,18 @@ set enable_seqscan=off; | |
select count(*) from testhstore where h #># 'p=>1'; | |
select count(*) from testhstore where h = 'pos=>98, line=>371, node=>CBA, indexed=>t'; | |
+--gin hash | |
+drop index hidx; | |
+create index hidx on testhstore using gin (h gin_hstore_hash_ops); | |
+set enable_seqscan=off; | |
+ | |
+select count(*) from testhstore where h @> 'wait=>NULL'; | |
+select count(*) from testhstore where h @> 'wait=>CC'; | |
+select count(*) from testhstore where h @> 'wait=>CC, public=>t'; | |
+ | |
+RESET enable_seqscan; | |
+drop index hidx; | |
+ | |
-- json | |
select hstore_to_json('"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4'); | |
select cast( hstore '"a key" =>1, b => t, c => null, d=> 12345, e => 012345, f=> 1.234, g=> 2.345e+4' as json); | |
diff --git a/contrib/hstore/sql/nested.sql b/contrib/hstore/sql/nested.sql | |
new file mode 100644 | |
index 0000000..71a692e | |
--- /dev/null | |
+++ b/contrib/hstore/sql/nested.sql | |
@@ -0,0 +1,478 @@ | |
+ | |
+SELECT 'ff => {a=>12, b=>16}'::hstore; | |
+ | |
+SELECT 'ff => {a=>12, b=>16}, qq=> 123'::hstore; | |
+ | |
+SELECT 'aa => {a,aaa}, qq=>{ a=>12, b=>16 , c=> { c1, c2}, d=>{d1=>d1, d2=>d2, d1=>d3} }'::hstore; | |
+ | |
+SELECT '"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore; | |
+ | |
+SELECT '"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2,{c3},{c4=>4}}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore; | |
+ | |
+SELECT 'ff => {a,aaa}'::hstore; | |
+ | |
+ | |
+select 'null'::hstore; | |
+select '{null}'::hstore; | |
+select ''::hstore; | |
+select '{}'::hstore; | |
+ | |
+--test optional outer braces | |
+SELECT 'a=>1'::hstore; | |
+SELECT '{a=>1}'::hstore; | |
+SELECT '{a,b}'::hstore; | |
+SELECT '{a,{b}}'::hstore; | |
+SELECT '{{a},b}'::hstore; | |
+SELECT '{a,{b},{c}}'::hstore; | |
+SELECT '{{a},{b},c}'::hstore; | |
+SELECT '{{a},b,{c}}'::hstore; | |
+SELECT '{a,{b=>1}}'::hstore; | |
+SELECT '{{a},{b=>1}}'::hstore; | |
+SELECT 'a'::hstore; | |
+SELECT '{a}'::hstore; | |
+SELECT ''::hstore; | |
+SELECT '{}'::hstore; | |
+ | |
+--nested json | |
+ | |
+SELECT hstore_to_json('a=>1'); | |
+SELECT hstore_to_json('{a=>1}'); | |
+SELECT hstore_to_json('{a,b}'); | |
+SELECT hstore_to_json('{a,{b}}'); | |
+SELECT hstore_to_json('{{a},b}'); | |
+SELECT hstore_to_json('{a,{b},{c}}'); | |
+SELECT hstore_to_json('{{a},{b},c}'); | |
+SELECT hstore_to_json('{{a},b,{c}}'); | |
+SELECT hstore_to_json('{a,{b=>1}}'); | |
+SELECT hstore_to_json('{{a},{b=>1}}'); | |
+SELECT hstore_to_json('{{a},{b=>1},{c}}'); | |
+SELECT hstore_to_json('a'); | |
+SELECT hstore_to_json('{a}'); | |
+SELECT hstore_to_json(''); | |
+SELECT hstore_to_json('{}'); | |
+ | |
+SELECT hstore_to_json('"aa"=>{a,aaa}, "qq"=>{"a"=>"12", "b"=>"16", "c"=>{c1,c2,{c3},{c4=>4}}, "d"=>{"d1"=>"d1", "d2"=>"d2"}}'::hstore); | |
+ | |
+-- | |
+ | |
+SELECT 'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'ff', | |
+ 'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'qq', | |
+ ('ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'Y') IS NULL AS t, | |
+ 'ff => {a=>12, b=>16}, qq=> 123, x=>{1,2}, Y=>NULL'::hstore -> 'x'; | |
+ | |
+SELECT '[ a, b, c, d]'::hstore -> 'a'; | |
+-- | |
+ | |
+CREATE TABLE testtype (i int, h hstore, a int[], j json); | |
+INSERT INTO testtype VALUES (1, 'a=>1', '{1,2,3}', '{"x": 2}'); | |
+ | |
+SELECT populate_record(v, 'i=>2'::hstore) FROM testtype v; | |
+SELECT populate_record(v, 'i=>2, a=>{7,8,9}'::hstore) FROM testtype v; | |
+SELECT populate_record(v, 'i=>2, h=>{b=>3}, a=>{7,8,9}'::hstore) FROM testtype v; | |
+SELECT populate_record(v, 'i=>2, h=>{b=>3}, a=>{7,8,9}, j=>{a=>{1,2,3}}'::hstore) FROM testtype v; | |
+ | |
+--complex delete | |
+ | |
+SELECT 'b=>{a,c}'::hstore - 'a'::text; | |
+SELECT 'b=>{a,c}, a=>1'::hstore - 'a'::text; | |
+SELECT 'b=>{a,c}, a=>[2,3]'::hstore - 'a'::text; | |
+SELECT 'b=>{a,c}, a=>[2,3]'::hstore - 'a'::text; | |
+SELECT '[2,3,a]'::hstore - 'a'::text; | |
+SELECT '[a,2,3,a]'::hstore - 'a'::text; | |
+SELECT '[a,a]'::hstore - 'a'::text; | |
+SELECT '[a]'::hstore - 'a'::text; | |
+SELECT 'a=>1'::hstore - 'a'::text; | |
+SELECT ''::hstore - 'a'::text; | |
+ | |
+SELECT '{a, 1 , b,2, c,3}'::hstore - ARRAY['d','b']; | |
+ | |
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v'::hstore; | |
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v=>23'::hstore; | |
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'v=>{1,2}'::hstore; | |
+SELECT '{a=>{1,2}, v=>23, b=>c}'::hstore - 'a=>{1,2}'::hstore; | |
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - 'v'::hstore; | |
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - 'v=>23'::hstore; | |
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - '[v,23]'::hstore; | |
+SELECT '{a, {1,2}, v, 23, b, c}'::hstore - '[v,{1,2}]'::hstore; | |
+ | |
+--joining | |
+ | |
+SELECT 'aa=>1 , b=>2, cq=>3'::hstore || '{cq,l, b,g, fg,f, 1,2}'::hstore; | |
+SELECT '{aa,1 , b,2, cq,3}'::hstore || '{cq,l, b,g, fg,f, 1,2}'::hstore; | |
+ | |
+--slice | |
+SELECT slice_array(hstore 'aa=>1, b=>2, c=>3', ARRAY['g','h','i']); | |
+SELECT slice_array(hstore '{aa,1, b,2, c,3}', ARRAY['g','h','i']); | |
+SELECT slice_array(hstore 'aa=>1, b=>2, c=>3', ARRAY['b','c']); | |
+SELECT slice_array(hstore '{aa,1, b,2, c,3}', ARRAY['b','c']); | |
+SELECT slice_array(hstore 'aa=>1, b=>{2=>1}, c=>{1,2}', ARRAY['b','c']); | |
+ | |
+SELECT slice(hstore '{aa=>1, b=>2, c=>3}', ARRAY['g','h','i']); | |
+SELECT slice(hstore '{aa,1, b,2, c,3}', ARRAY['g','h','i']); | |
+SELECT slice(hstore '{aa=>1, b=>2, c=>3}', ARRAY['b','c']); | |
+SELECT slice(hstore '{aa,1, b,2, c,3}', ARRAY['b','c']); | |
+SELECT slice(hstore '{aa=>1, b=>{2=>1}, c=>{1,2}}', ARRAY['b','c']); | |
+ | |
+--to array | |
+SELECT %% 'aa=>1, cq=>l, b=>{a,n}, fg=>NULL'; | |
+SELECT %% '{aa,1, cq,l, b,g, fg,NULL}'; | |
+SELECT hstore_to_matrix( 'aa=>1, cq=>l, b=>{a,n}, fg=>NULL'); | |
+SELECT hstore_to_matrix( '{aa,1, cq,l, b,g, fg,NULL}'); | |
+ | |
+ | |
+--contains | |
+SELECT 'a=>b'::hstore @> 'a=>b, c=>b'; | |
+SELECT 'a=>b, c=>b'::hstore @> 'a=>b'; | |
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{1,2}'; | |
+SELECT 'a=>{2,1}, c=>b'::hstore @> 'a=>{1,2}'; | |
+SELECT 'a=>{1=>2}, c=>b'::hstore @> 'a=>{1,2}'; | |
+SELECT 'a=>{2=>1}, c=>b'::hstore @> 'a=>{1,2}'; | |
+SELECT 'a=>{1=>2}, c=>b'::hstore @> 'a=>{1=>2}'; | |
+SELECT 'a=>{2=>1}, c=>b'::hstore @> 'a=>{1=>2}'; | |
+SELECT '{a,b}'::hstore @> '{a,b, c,b}'; | |
+SELECT '{a,b, c,b}'::hstore @> '{a,b}'; | |
+SELECT '{a,b, c,{1,2}}'::hstore @> '{a,{1,2}}'; | |
+SELECT '{a,b, c,{1,2}}'::hstore @> '{b,{1,2}}'; | |
+ | |
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{1}'; | |
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{2}'; | |
+SELECT 'a=>{1,2}, c=>b'::hstore @> 'a=>{3}'; | |
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{c=>3}}'; | |
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4}}'; | |
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4},3}'; | |
+SELECT 'a=>{1,2,{c=>3, x=>4}}, c=>b'::hstore @> 'a=>{{x=>4},1}'; | |
+ | |
+-- %> | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'n'; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'a'; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'b'; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'c'; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'd'; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 'd' %> '1'; | |
+ | |
+SELECT '[1,2,3,{a,b}]'::hstore %> '1'; | |
+SELECT '["1",2,3,{a,b}]'::hstore %> '1'; | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 5; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 4; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 3; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 2; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 1; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore %> 0; | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 5; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 4; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 3; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 2; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 1; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore %> 0; | |
+ | |
+-- -> | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 5; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 4; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 3; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 2; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 1; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> 0; | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 5; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 4; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 3; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 2; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 1; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> 0; | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -6; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -5; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -4; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -3; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -2; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore -> -1; | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -6; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -5; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -4; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -3; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -2; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore -> -1; | |
+ | |
+-- #> | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{0}'; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{a}'; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c}'; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 0}'; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 1}'; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 2}'; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, 3}'; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -1}'; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -2}'; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -3}'; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #> '{c, -4}'; | |
+ | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{0}'; | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{3}'; | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{4}'; | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #> '{4,5}'; | |
+ | |
+-- #%> | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{0}'; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{a}'; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c}'; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 0}'; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 1}'; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 2}'; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, 3}'; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -1}'; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -2}'; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -3}'; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #%> '{c, -4}'; | |
+ | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{0}'; | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{3}'; | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{4}'; | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #%> '{4,5}'; | |
+ | |
+-- ? | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 5; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 4; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 3; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 2; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 1; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? 0; | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 5; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 4; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 3; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 2; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 1; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? 0; | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -6; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -5; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -4; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -3; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -2; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore ? -1; | |
+ | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -6; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -5; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -4; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -3; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -2; | |
+SELECT '[a,b, c,{1,2}, NULL]'::hstore ? -1; | |
+ | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{0}'::text[]; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{a}'::text[]; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c}'::text[]; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{b}'::text[]; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 0}'::text[]; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 1}'::text[]; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 2}'::text[]; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, 3}'::text[]; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -1}'::text[]; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -2}'::text[]; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -3}'::text[]; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -4}'::text[]; | |
+SELECT 'a=>b, c=>{1,2,3}'::hstore #? '{c, -5}'::text[]; | |
+ | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{0}'::text[]; | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{3}'::text[]; | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{4}'::text[]; | |
+SELECT '[0, 1, 2, {3,4}, {5=>five}]'::hstore #? '{4,5}'::text[]; | |
+ | |
+--deep delete | |
+ | |
+SELECT 'a=>1'::hstore #- '{x}'; | |
+SELECT 'a=>1'::hstore #- '{a}'; | |
+SELECT 'a=>1'::hstore #- '{NULL}'; | |
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{x}'; | |
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{a}'; | |
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{b}'; | |
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{c}'; | |
+SELECT 'a=>1'::hstore #- '{x,1}'; | |
+SELECT 'a=>1'::hstore #- '{a,1}'; | |
+SELECT 'a=>1'::hstore #- '{NULL,1}'; | |
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{x,1}'; | |
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{a,1}'; | |
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{b,1}'; | |
+SELECT 'a=>1, b=>2, c=>3'::hstore #- '{c,1}'; | |
+ | |
+SELECT '[a]'::hstore #- '{2}'; | |
+SELECT '[a]'::hstore #- '{1}'; | |
+SELECT '[a]'::hstore #- '{0}'; | |
+SELECT '[a]'::hstore #- '{-1}'; | |
+SELECT '[a]'::hstore #- '{-2}'; | |
+ | |
+SELECT '[a,b,c]'::hstore #- '{3}'; | |
+SELECT '[a,b,c]'::hstore #- '{2}'; | |
+SELECT '[a,b,c]'::hstore #- '{1}'; | |
+SELECT '[a,b,c]'::hstore #- '{0}'; | |
+SELECT '[a,b,c]'::hstore #- '{-1}'; | |
+SELECT '[a,b,c]'::hstore #- '{-2}'; | |
+SELECT '[a,b,c]'::hstore #- '{-3}'; | |
+SELECT '[a,b,c]'::hstore #- '{-4}'; | |
+ | |
+SELECT '[a,b,c]'::hstore #- '{0,0}'; | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{x}'; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{a}'; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b}'; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c}'; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d}'; | |
+ | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, 0}'; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, -1}'; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{b, -1}' #- '{b, -1}'; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c, 1}'; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{c, 2}'; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, -2}'; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 1}'; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}'; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}' #- '{d, 1, 0}'; | |
+SELECT 'n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore #- '{d, 1, 0}' #- '{d, 1, 0}' #- '{d, 1, 0}'; | |
+ | |
+-- delete(int) | |
+ | |
+SELECT '[a,b,c]'::hstore - 3; | |
+SELECT '[a,b,c]'::hstore - 2; | |
+SELECT '[a,b,c]'::hstore - 1; | |
+SELECT '[a,b,c]'::hstore - 0; | |
+SELECT '[a,b,c]'::hstore - -1; | |
+SELECT '[a,b,c]'::hstore - -2; | |
+SELECT '[a,b,c]'::hstore - -3; | |
+SELECT '[a,b,c]'::hstore - -4; | |
+ | |
+SELECT 'a=>1, b=>2, c=>3'::hstore - 3; | |
+SELECT 'a=>1, b=>2, c=>3'::hstore - 2; | |
+SELECT 'a=>1, b=>2, c=>3'::hstore - 1; | |
+SELECT 'a=>1, b=>2, c=>3'::hstore - 0; | |
+SELECT 'a=>1, b=>2, c=>3'::hstore - -1; | |
+SELECT 'a=>1, b=>2, c=>3'::hstore - -2; | |
+SELECT 'a=>1, b=>2, c=>3'::hstore - -3; | |
+SELECT 'a=>1, b=>2, c=>3'::hstore - -4; | |
+ | |
+--replace | |
+ | |
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', '{1,2,3}'); | |
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{b,-1}', '{1,2,3}'); | |
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1,0}', '{1,2,3}'); | |
+SELECT replace('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,NULL,0}', '{1,2,3}'); | |
+ | |
+--deep concat | |
+ | |
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{}', 'n=>not_null'); | |
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', 'n=>not_null'); | |
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', 'not_null'); | |
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{n}', '{not_null}'); | |
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{}', 'b=>{3,4}'); | |
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{b}', '{3,4}'); | |
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1}', '{4,5}'); | |
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d,1}', '4=>5'); | |
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{d}', '2=>{4,5}'); | |
+SELECT concat_path('n=>NULL, a=>1, b=>{1,2}, c=>{1=>2}, d=>{1=>{2,3}}'::hstore, '{NULL,1}', '4=>5'); | |
+ | |
+--cast | |
+ | |
+SELECT ('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::text)::hstore AS err; | |
+SELECT ('{"f2":{"f3":1},"f4":{"f5":99,"f6":"stringy"}}'::json)::hstore AS ok; | |
+ | |
+--hvals | |
+ | |
+SELECT q->'tags' FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore) AS q; | |
+ | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{}') AS q; | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3}') AS q; | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2}') AS q; | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1}') AS q; | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0}') AS q; | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1}') AS q; | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2}') AS q; | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL}') AS q; | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3,tags}') AS q; | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2,tags}') AS q; | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1,tags}') AS q; | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0,tags}') AS q; | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1,tags}') AS q; | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2,tags}') AS q; | |
+SELECT q FROM hvals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL,tags}') AS q; | |
+ | |
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{}') AS q; | |
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{1}') AS q; | |
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a}') AS q; | |
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b}') AS q; | |
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{c}') AS q; | |
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL}') AS q; | |
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a,c}') AS q; | |
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,c}') AS q; | |
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b,NULL}') AS q; | |
+SELECT q FROM hvals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q; | |
+SELECT q FROM hvals('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q; | |
+ | |
+--svals path | |
+ | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{}') AS q; | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3}') AS q; | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2}') AS q; | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1}') AS q; | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0}') AS q; | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1}') AS q; | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2}') AS q; | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL}') AS q; | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-3,tags}') AS q; | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-2,tags}') AS q; | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{-1,tags}') AS q; | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{0,tags}') AS q; | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{1,tags}') AS q; | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{2,tags}') AS q; | |
+SELECT q FROM svals('{{tags=>1, sh=>2}, {tags=>3, sh=>4}}'::hstore, '{NULL,tags}') AS q; | |
+ | |
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{}') AS q; | |
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{1}') AS q; | |
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a}') AS q; | |
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b}') AS q; | |
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{c}') AS q; | |
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL}') AS q; | |
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{a,c}') AS q; | |
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,c}') AS q; | |
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{b,NULL}') AS q; | |
+SELECT q FROM svals('a=>{b=>c, c=>b}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q; | |
+SELECT q FROM svals('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first'::hstore, '{NULL,1}') AS q; | |
+ | |
+--each | |
+ | |
+SELECT * FROM each('a=>b, c=>cc'::hstore) AS q; | |
+SELECT * FROM each('[a, b, c, cc]'::hstore) AS q; | |
+SELECT * FROM each('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first, n=>null'::hstore) AS q; | |
+ | |
+SELECT * FROM each_hstore('a=>b, c=>cc'::hstore) AS q; | |
+SELECT * FROM each_hstore('[a, b, c, cc]'::hstore) AS q; | |
+SELECT * FROM each_hstore('a=>{b=>c, c=>b, 1=>first}, b=>{1,2}, c=>cc, 1=>first, n=>null'::hstore) AS q; | |
+ | |
+--decoration | |
+ | |
+SELECT 'a=>1, b=>{c=>3}, d=>[4,[5]]'::hstore AS h, '[a, {b=>c}, [c, d, e]]'::hstore AS a; | |
+ | |
+SET hstore.pretty_print = true; | |
+SELECT 'a=>1, b=>{c=>3}, d=>[4,[5]], e=>[1,2,3,4], f=>g, g=>j'::hstore AS h, | |
+ '[a, {b=>c, c=>d}, [c, d, e, [1,2], h, {f=>g, g=>f}]]'::hstore AS a; | |
+RESET hstore.pretty_print; | |
+ | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore); | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true ); | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true ); | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, array_curly_braces := true ); | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true ); | |
+ | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, loose := true ); | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, root_hash_decorated := true ); | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, json := true, array_curly_braces := true ); | |
+ | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true, root_hash_decorated := true ); | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, loose := true, array_curly_braces := true ); | |
+ | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true, array_curly_braces := true ); | |
+SELECT hstore_print('a=>t, f=>t, t=>"f", arr=>[1,2,3,"3",x], 123=>string'::hstore, root_hash_decorated := true, array_curly_braces := true, loose := true); | |
diff --git a/contrib/hstore/sql/types.sql b/contrib/hstore/sql/types.sql | |
new file mode 100644 | |
index 0000000..ac0b206 | |
--- /dev/null | |
+++ b/contrib/hstore/sql/types.sql | |
@@ -0,0 +1,147 @@ | |
+SELECT '"foo"=>true'::hstore; | |
+SELECT 'foo=>true'::hstore; | |
+SELECT '"true"=>true'::hstore; | |
+SELECT 'true=>true'::hstore; | |
+SELECT '"t"=>true'::hstore; | |
+SELECT 't=>true'::hstore; | |
+SELECT '"false"=>true'::hstore; | |
+SELECT 'false=>true'::hstore; | |
+SELECT '"f"=>true'::hstore; | |
+SELECT 'f=>true'::hstore; | |
+ | |
+SELECT '"foo"=>false'::hstore; | |
+SELECT 'foo=>false'::hstore; | |
+SELECT '"false"=>false'::hstore; | |
+SELECT 'false=>false'::hstore; | |
+SELECT '"t"=>false'::hstore; | |
+SELECT 't=>false'::hstore; | |
+SELECT '"false"=>false'::hstore; | |
+SELECT 'false=>false'::hstore; | |
+SELECT '"f"=>false'::hstore; | |
+SELECT 'f=>false'::hstore; | |
+ | |
+SELECT '"1"=>x'::hstore; | |
+SELECT '1=>x'::hstore; | |
+SELECT 'foo=>1'::hstore; | |
+SELECT 'foo=>1.'::hstore; | |
+SELECT 'foo=>1.0'::hstore; | |
+SELECT 'foo=>1.01'::hstore; | |
+SELECT 'foo=>1.01e'::hstore; | |
+SELECT 'foo=>1.01e1'::hstore; | |
+SELECT 'foo=>1.01e+1'::hstore; | |
+SELECT 'foo=>1.01e-1'::hstore; | |
+SELECT 'foo=>.1'::hstore; | |
+SELECT 'foo=>.1e'::hstore; | |
+SELECT 'foo=>.1e1'::hstore; | |
+SELECT 'foo=>.1e+1'::hstore; | |
+SELECT 'foo=>.1e-1'::hstore; | |
+ | |
+SELECT 'foo=>+1'::hstore; | |
+SELECT 'foo=>+1.'::hstore; | |
+SELECT 'foo=>+1.0'::hstore; | |
+SELECT 'foo=>+1.01'::hstore; | |
+SELECT 'foo=>+1.01e'::hstore; | |
+SELECT 'foo=>+1.01e1'::hstore; | |
+SELECT 'foo=>+1.01e+1'::hstore; | |
+SELECT 'foo=>+1.01e-1'::hstore; | |
+SELECT 'foo=>+.1'::hstore; | |
+SELECT 'foo=>+.1e'::hstore; | |
+SELECT 'foo=>+.1e1'::hstore; | |
+SELECT 'foo=>+.1e+1'::hstore; | |
+SELECT 'foo=>+.1e-1'::hstore; | |
+ | |
+SELECT 'foo=>-1'::hstore; | |
+SELECT 'foo=>-1.'::hstore; | |
+SELECT 'foo=>-1.0'::hstore; | |
+SELECT 'foo=>-1.01'::hstore; | |
+SELECT 'foo=>-1.01e'::hstore; | |
+SELECT 'foo=>-1.01e1'::hstore; | |
+SELECT 'foo=>-1.01e+1'::hstore; | |
+SELECT 'foo=>-1.01e-1'::hstore; | |
+SELECT 'foo=>-.1'::hstore; | |
+SELECT 'foo=>-.1e'::hstore; | |
+SELECT 'foo=>-.1e1'::hstore; | |
+SELECT 'foo=>-.1e+1'::hstore; | |
+SELECT 'foo=>-.1e-1'::hstore; | |
+ | |
+SELECT 'foo=>1e2000'::hstore; | |
+ | |
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 'foo'; | |
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 'bar'; | |
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 0; | |
+SELECT 'foo=>1e12, bar=>x'::hstore ^> 1; | |
+ | |
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 'foo'; | |
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 'bar'; | |
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 0; | |
+SELECT '[foo, 1e12, bar, x]'::hstore ^> 1; | |
+ | |
+SELECT 'foo=>{x, 1e-12}'::hstore #^> '{foo, 0}'; | |
+SELECT 'foo=>{x, 1e-12}'::hstore #^> '{foo, 1}'; | |
+ | |
+SELECT 'foo=>t, bar=>x'::hstore ?> 'foo'; | |
+SELECT 'foo=>t, bar=>x'::hstore ?> 'bar'; | |
+SELECT 'foo=>t, bar=>x'::hstore ?> 0; | |
+SELECT 'foo=>t, bar=>x'::hstore ?> 1; | |
+ | |
+SELECT '[foo, t, bar, x]'::hstore ?> 'foo'; | |
+SELECT '[foo, t, bar, x]'::hstore ?> 'bar'; | |
+SELECT '[foo, t, bar, x]'::hstore ?> 0; | |
+SELECT '[foo, t, bar, x]'::hstore ?> 1; | |
+ | |
+SELECT 'foo=>{x, t}'::hstore #?> '{foo, 0}'; | |
+SELECT 'foo=>{x, t}'::hstore #?> '{foo, 1}'; | |
+ | |
+SELECT 'foo=>f, bar=>x'::hstore ?> 'foo'; | |
+SELECT 'foo=>f, bar=>x'::hstore ?> 'bar'; | |
+SELECT 'foo=>f, bar=>x'::hstore ?> 0; | |
+SELECT 'foo=>f, bar=>x'::hstore ?> 1; | |
+ | |
+SELECT '[foo, f, bar, x]'::hstore ?> 'foo'; | |
+SELECT '[foo, f, bar, x]'::hstore ?> 'bar'; | |
+SELECT '[foo, f, bar, x]'::hstore ?> 0; | |
+SELECT '[foo, f, bar, x]'::hstore ?> 1; | |
+ | |
+SELECT 'foo=>{x, f}'::hstore #?> '{foo, 0}'; | |
+SELECT 'foo=>{x, f}'::hstore #?> '{foo, 1}'; | |
+ | |
+ | |
+SELECT hstore_typeof('a=>b') AS hash; | |
+SELECT hstore_typeof('{a=>b}') AS hash; | |
+SELECT hstore_typeof('{a, b}') AS array; | |
+SELECT hstore_typeof('{{a=>b}}') AS array; | |
+SELECT hstore_typeof('[a, b]') AS array; | |
+SELECT hstore_typeof('') AS "NULL"; | |
+SELECT hstore_typeof('NULL') AS "null"; | |
+SELECT hstore_typeof('1.0') AS numeric; | |
+SELECT hstore_typeof('t') AS bool; | |
+SELECT hstore_typeof('f') AS bool; | |
+ | |
+SELECT hstore('xxx', 't'::bool); | |
+SELECT hstore('xxx', 'f'::bool); | |
+ | |
+SELECT hstore('xxx', 3.14); | |
+SELECT hstore('xxx', 3.14::numeric); | |
+SELECT hstore('xxx', '3.14'::numeric); | |
+ | |
+SELECT hstore(NULL); | |
+SELECT hstore('NULL'); | |
+ | |
+SELECT hstore('t'::bool) AS "true", hstore('f'::bool) AS "false"; | |
+ | |
+SELECT hstore(3.14), hstore(3.14::numeric), hstore('3.14'::numeric); | |
+ | |
+SELECT hstore('xxx', 'foo=>t, bar=>3.14, zzz=>xxx'::hstore); | |
+ | |
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int2[]); | |
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int4[]); | |
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::int8[]); | |
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::float4[]); | |
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::float8[]); | |
+SELECT array_to_hstore('{{1,1,f},{f,t,NULL}}'::bool[]); | |
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::text[]); | |
+SELECT array_to_hstore('{{1,1,4},{23,3,5}}'::varchar[]); | |
+ | |
+SELECT array_to_hstore('{{{1,11},{1,1},{4,41}},{{23,231},{3,31},{5,51}}}'::int4[]); | |
+SELECT hstore('array', array_to_hstore('{{{1,11},{1,1},{4,41}},{{23,231},{3,31},{5,51}}}'::int4[])); | |
+ | |
diff --git a/doc/src/sgml/hstore.sgml b/doc/src/sgml/hstore.sgml | |
index fbe9543..b2fe9e9 100644 | |
--- a/doc/src/sgml/hstore.sgml | |
+++ b/doc/src/sgml/hstore.sgml | |
@@ -8,39 +8,108 @@ | |
</indexterm> | |
<para> | |
- This module implements the <type>hstore</> data type for storing sets of | |
- key/value pairs within a single <productname>PostgreSQL</> value. | |
+ This module implements the <type>hstore</> data type for storing arbitrarily | |
+ nested key/value pairs and arrays within a single <productname>PostgreSQL</> value. | |
This can be useful in various scenarios, such as rows with many attributes | |
- that are rarely examined, or semi-structured data. Keys and values are | |
- simply text strings. | |
+ that are rarely examined, or semi-structured data. Keys are strings, while values | |
+ can be strings, numbers, booleans, or <literal>NULL</>. | |
+ </para> | |
+ | |
+ <para> | |
+ The <type>hstore</> type is similar to the core <type>json</> data type, but, | |
+ in the current implementation, differs in a few key ways: | |
+ </para> | |
+ | |
+ <itemizedlist> | |
+ | |
+ <listitem> | |
+ <para> | |
+ It's faster. <type>hstore</> is stored in a binary representation, whereas | |
+ <type>json</> is stored as text, and so needs to be parsed every time it's | |
+ accessed. | |
+ </para> | |
+ </listitem> | |
+ | |
+ <listitem> | |
+ <para> | |
+ Better index support. <type>hstore</> can be used in | |
+ <link linkend="GiST"><acronym>GiST</></link> and | |
+ <link linkend="GIN"><acronym>GIN</></link> indexes to allow searches | |
+ on keys or even key paths. | |
+ </para> | |
+ </listitem> | |
+ | |
+ </itemizedlist> | |
+ | |
+ <para> | |
+ That said, <type>hstore</> includes interfaces to transparently convert values | |
+ to and from <type>json</>. These allow the best of both worlds: store and | |
+ query <type>hstore</> values, but convert them to <type>json</> when fetching | |
+ them, for easy parsing in your client application code. | |
</para> | |
<sect2> | |
<title><type>hstore</> External Representation</title> | |
<para> | |
- | |
The text representation of an <type>hstore</>, used for input and output, | |
- includes zero or more <replaceable>key</> <literal>=></> | |
- <replaceable>value</> pairs separated by commas. Some examples: | |
+ may be formatted as scalar values, hash-like values, array-like values, and | |
+ nested array and hash values. Scalar values are simply strings, numeric | |
+ values, booleans, or <literal>NULL</>. Strings continaining whitespace, | |
+ commas, <literal>=</>s or <literal>></>s must be double-quoted. To | |
+ include a double quote or a backslash in a key or value, escape it with a | |
+ backslash. Boolean values may be represnted as <literal>true</>, <literal>t</>, | |
+ <literal>false</>, or <literal>f</>. Use quotation marks to represent thes | |
+ values as strings. The <literal>NULL</> keyword is case-insensitive. | |
+ Double-quote the <literal>NULL</> to treat it as the ordinary string | |
+ <quote>NULL</quote>. Some examples: | |
+ | |
+<programlisting> | |
+=% SELECT 'foo'::hstore, '"hi \"bob\""'::hstore, '1.0'::hstore, 'true'::hstore, NULL::hstore; | |
+ hstore | hstore | hstore | hstore | hstore | |
+--------+--------------+--------+--------+-------- | |
+ "foo" | "hi \"bob\"" | 1.0 | t | | |
+</programlisting> | |
+ | |
+ </para> | |
+ | |
+ <para> | |
+ Arrays of values of any supported type may be constructed as | |
+ square-bracketed comma-separated lists. Some examples: | |
+ | |
+<programlisting> | |
+=% SELECT '[k,v]'::hstore, '[1.0, "hi there", false, null]'::hstore; | |
+ hstore | hstore | |
+------------+---------------------------- | |
+ ["k", "v"] | [1.0, "hi there", f, NULL] | |
+</programlisting> | |
+ | |
+ </para> | |
+ | |
+ <para> | |
+ Hashes includes zero or more | |
+ <replaceable>key</> <literal>=></> <replaceable>value</> pairs separated | |
+ by commas, optionally brackted by curly braces. Keys must be strings and | |
+ may not be <literal>NULL</>; values may be any <type>hstore</> type, | |
+ including <literal>NULL</>. Examples: | |
-<synopsis> | |
-k => v | |
-foo => bar, baz => whatever | |
-"1-a" => "anything at all" | |
-</synopsis> | |
+<programlisting> | |
+=% SELECT 'k => v'::hstore | |
+-% , '{foo => "hi there"}'::hstore | |
+-% , '{one => 1, two => 2.0, three => true, four => null}'::hstore; | |
+ hstore | hstore | hstore | |
+----------+-------------------+------------------------------------------------ | |
+ "k"=>"v" | "foo"=>"hi there" | "one"=>1, "two"=>2.0, "four"=>NULL, "three"=>t | |
+</programlisting> | |
The order of the pairs is not significant (and may not be reproduced on | |
- output). Whitespace between pairs or around the <literal>=></> sign is | |
- ignored. Double-quote keys and values that include whitespace, commas, | |
- <literal>=</>s or <literal>></>s. To include a double quote or a | |
- backslash in a key or value, escape it with a backslash. | |
+ output). | |
</para> | |
<para> | |
- Each key in an <type>hstore</> is unique. If you declare an <type>hstore</> | |
- with duplicate keys, only one will be stored in the <type>hstore</> and | |
- there is no guarantee as to which will be kept: | |
+ Each key in an <type>hstore</> hash is unique. If you declare an | |
+ <type>hstore</> hash with duplicate keys, only one will be stored in | |
+ the <type>hstore</> and there is no guarantee as to which will be kept: | |
<programlisting> | |
SELECT 'a=>1,a=>2'::hstore; | |
@@ -51,14 +120,58 @@ SELECT 'a=>1,a=>2'::hstore; | |
</para> | |
<para> | |
- A value (but not a key) can be an SQL <literal>NULL</>. For example: | |
+ Hashes and arrays may be arbitrarily nested. In this case, brackets are | |
+ required for hash values. Here's an example adapted from the | |
+ <ulink url="http://geojson.org/geojson-spec.html">GeoJSON spec</ulink>: | |
<programlisting> | |
-key => NULL | |
-</programlisting> | |
- | |
- The <literal>NULL</> keyword is case-insensitive. Double-quote the | |
- <literal>NULL</> to treat it as the ordinary string <quote>NULL</quote>. | |
+=% SET hstore.pretty_print=true; | |
+=% SELECT '{ | |
+ "type" => "Feature", | |
+ "bbox" => [-180.0, -90.0, 180.0, 90.0], | |
+ "geometry" => { | |
+ "type" => "Polygon", | |
+ "coordinates" => [[ | |
+ [-180.0, 10.0], [20.0, 90.0], [180.0, -5.0], [-30.0, -90.0] | |
+ ]] | |
+ } | |
+}'::hstore; | |
+ hstore | |
+-------------------------- | |
+ "bbox"=> + | |
+ [ + | |
+ -180.0, + | |
+ -90.0, + | |
+ 180.0, + | |
+ 90.0 + | |
+ ], + | |
+ "type"=>"Feature", + | |
+ "geometry"=> + | |
+ { + | |
+ "type"=>"Polygon", + | |
+ "coordinates"=> + | |
+ [ + | |
+ [ + | |
+ [ + | |
+ -180.0, + | |
+ 10.0 + | |
+ ], + | |
+ [ + | |
+ 20.0, + | |
+ 90.0 + | |
+ ], + | |
+ [ + | |
+ 180.0, + | |
+ -5.0 + | |
+ ], + | |
+ [ + | |
+ -30.0, + | |
+ -90.0 + | |
+ ] + | |
+ ] + | |
+ ] + | |
+ } | |
+ </programlisting> | |
</para> | |
<note> | |
@@ -83,6 +196,35 @@ key => NULL | |
</sect2> | |
<sect2> | |
+ <title>Ouput Format Configuration Parameters</title> | |
+ | |
+ <para> | |
+ There are several configuration parameters that control the output formatting of | |
+ <type>hstore</> values. | |
+ </para> | |
+ | |
+ <varlistentry> | |
+ <term> | |
+ <varname>hstore.pretty_print</varname> (<type>boolean</type>) | |
+ </term> | |
+ <indexterm> | |
+ <primary><varname>hstore.pretty_print</> configuration parameter</primary> | |
+ </indexterm> | |
+ <listitem> | |
+ <para> | |
+ By default, the text representation of <type>hstore</> values includes no | |
+ whitespace between the values it contains. Set <varname>hstore.pretty_print</varname> | |
+ to <literal>true</> to add newlines between values and to indent nested | |
+ hashes and arrays. | |
+ </para> | |
+ </listitem> | |
+ </varlistentry> | |
+ | |
+ </variablelist> | |
+ | |
+ </sect2> | |
+ | |
+ <sect2> | |
<title><type>hstore</> Operators and Functions</title> | |
<para> | |
@@ -98,6 +240,7 @@ key => NULL | |
<thead> | |
<row> | |
<entry>Operator</entry> | |
+ <entry>Returns</entry> | |
<entry>Description</entry> | |
<entry>Example</entry> | |
<entry>Result</entry> | |
@@ -107,13 +250,103 @@ key => NULL | |
<tbody> | |
<row> | |
<entry><type>hstore</> <literal>-></> <type>text</></entry> | |
+ <entry><type>text</></entry> | |
<entry>get value for key (<literal>NULL</> if not present)</entry> | |
<entry><literal>'a=>x, b=>y'::hstore -> 'a'</literal></entry> | |
<entry><literal>x</literal></entry> | |
</row> | |
<row> | |
+ <entry><type>hstore</> <literal>-></> <type>integer</></entry> | |
+ <entry><type>text</></entry> | |
+ <entry>get value for array index (<literal>NULL</> if not present)</entry> | |
+ <entry><literal>'[foo,bar,baz]'::hstore -> 1</literal></entry> | |
+ <entry><literal>bar</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><type>hstore</> <literal>^></> <type>text</></entry> | |
+ <entry><type>numeric</></entry> | |
+ <entry>get numeric value for key (<literal>NULL</> if not numeric or not present)</entry> | |
+ <entry><literal>'a=>42.0, b=>y'::hstore ^> 'a'</literal></entry> | |
+ <entry><literal>42.0</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><type>hstore</> <literal>^></> <type>integer</></entry> | |
+ <entry><type>numeric</></entry> | |
+ <entry>get numeric value for array index (<literal>NULL</> if not numeric or not present)</entry> | |
+ <entry><literal>'[foo,null,44]'::hstore ^> 2</literal></entry> | |
+ <entry><literal>44</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><type>hstore</> <literal>?></> <type>text</></entry> | |
+ <entry><type>boolean</></entry> | |
+ <entry>get boolean value for key (<literal>NULL</> if not boolean or not present)</entry> | |
+ <entry><literal>'a => 42.0, b => true'::hstore ?> 'b'</literal></entry> | |
+ <entry><literal>t</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><type>hstore</> <literal>?></> <type>integer</></entry> | |
+ <entry><type>bolean</></entry> | |
+ <entry>get boolean value for array index (<literal>NULL</> if not boolean or not present)</entry> | |
+ <entry><literal>'[false,null,44]'::hstore ?> 0</literal></entry> | |
+ <entry><literal>f</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><type>hstore</> <literal>#></> <type>text[]</></entry> | |
+ <entry><type>text</></entry> | |
+ <entry>get value for key path (<literal>NULL</> if not present)</entry> | |
+ <entry><literal>'foo => {bar => yellow}'::hstore #> '[foo,bar]'</literal></entry> | |
+ <entry><literal>yellow</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><type>hstore</> <literal>#^></> <type>text[]</></entry> | |
+ <entry><type>numeric</></entry> | |
+ <entry>get numeric value for key path (<literal>NULL</> if not numeric or not present)</entry> | |
+ <entry><literal>'foo => {bar => 99}'::hstore #^> '[foo,bar]'</literal></entry> | |
+ <entry><literal>99</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><type>hstore</> <literal>#?></> <type>text[]</></entry> | |
+ <entry><type>boolean</></entry> | |
+ <entry>get boolean value for key path (<literal>NULL</> if not booelan or not present)</entry> | |
+ <entry><literal>'foo => {bar => true}'::hstore #?> '[foo,bar]'</literal></entry> | |
+ <entry><literal>t</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><type>hstore</> <literal>%></> <type>text</></entry> | |
+ <entry><type>hstore</></entry> | |
+ <entry>get hstore value for key (<literal>NULL</> if not present)</entry> | |
+ <entry><literal>'foo => {bar => 99}'::hstore %> 'foo'</literal></entry> | |
+ <entry><literal>"bar"=>99</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><type>hstore</> <literal>%></> <type>integer</></entry> | |
+ <entry><type>hstore</></entry> | |
+ <entry>get hstore value array index (<literal>NULL</> if not present)</entry> | |
+ <entry><literal>'[1, 2, {foo=>hi}]'::hstore %> 2'</literal></entry> | |
+ <entry><literal>"foo"=>"hi"</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><type>hstore</> <literal>#%></> <type>text[]</></entry> | |
+ <entry><type>hstore</></entry> | |
+ <entry>get hstore value for key path (<literal>NULL</> if not present)</entry> | |
+ <entry><literal>'a => 1, b => {c => [44,44]}'::hstore #%> '[b,c]'</literal></entry> | |
+ <entry><literal>[44, 44]</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
<entry><type>hstore</> <literal>-></> <type>text[]</></entry> | |
+ <entry><type>text[]</></entry> | |
<entry>get values for keys (<literal>NULL</> if not present)</entry> | |
<entry><literal>'a=>x, b=>y, c=>z'::hstore -> ARRAY['c','a']</literal></entry> | |
<entry><literal>{"z","x"}</literal></entry> | |
@@ -121,6 +354,7 @@ key => NULL | |
<row> | |
<entry><type>hstore</> <literal>||</> <type>hstore</></entry> | |
+ <entry><type>hstore</></entry> | |
<entry>concatenate <type>hstore</>s</entry> | |
<entry><literal>'a=>b, c=>d'::hstore || 'c=>x, d=>q'::hstore</literal></entry> | |
<entry><literal>"a"=>"b", "c"=>"x", "d"=>"q"</literal></entry> | |
@@ -128,13 +362,31 @@ key => NULL | |
<row> | |
<entry><type>hstore</> <literal>?</> <type>text</></entry> | |
+ <entry><type>boolean</></entry> | |
<entry>does <type>hstore</> contain key?</entry> | |
<entry><literal>'a=>1'::hstore ? 'a'</literal></entry> | |
<entry><literal>t</literal></entry> | |
</row> | |
<row> | |
+ <entry><type>hstore</> <literal>?</> <type>integer</></entry> | |
+ <entry><type>boolean</></entry> | |
+ <entry>does <type>hstore</> contain array index?</entry> | |
+ <entry><literal>'a,b,c'::hstore ? 2</literal></entry> | |
+ <entry><literal>t</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><type>hstore</> <literal>#?</> <type>text[]</></entry> | |
+ <entry><type>boolean</></entry> | |
+ <entry>does <type>hstore</> contain key path?</entry> | |
+ <entry><literal>'[1, 2, {foo=>hi}]'::hstore #? '[2,foo]'</literal></entry> | |
+ <entry><literal>t</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
<entry><type>hstore</> <literal>?&</> <type>text[]</></entry> | |
+ <entry><type>boolean</></entry> | |
<entry>does <type>hstore</> contain all specified keys?</entry> | |
<entry><literal>'a=>1,b=>2'::hstore ?& ARRAY['a','b']</literal></entry> | |
<entry><literal>t</literal></entry> | |
@@ -142,6 +394,7 @@ key => NULL | |
<row> | |
<entry><type>hstore</> <literal>?|</> <type>text[]</></entry> | |
+ <entry><type>boolean</></entry> | |
<entry>does <type>hstore</> contain any of the specified keys?</entry> | |
<entry><literal>'a=>1,b=>2'::hstore ?| ARRAY['b','c']</literal></entry> | |
<entry><literal>t</literal></entry> | |
@@ -149,6 +402,7 @@ key => NULL | |
<row> | |
<entry><type>hstore</> <literal>@></> <type>hstore</></entry> | |
+ <entry><type>boolean</></entry> | |
<entry>does left operand contain right?</entry> | |
<entry><literal>'a=>b, b=>1, c=>NULL'::hstore @> 'b=>1'</literal></entry> | |
<entry><literal>t</literal></entry> | |
@@ -156,6 +410,7 @@ key => NULL | |
<row> | |
<entry><type>hstore</> <literal><@</> <type>hstore</></entry> | |
+ <entry><type>boolean</></entry> | |
<entry>is left operand contained in right?</entry> | |
<entry><literal>'a=>c'::hstore <@ 'a=>b, b=>1, c=>NULL'</literal></entry> | |
<entry><literal>f</literal></entry> | |
@@ -163,13 +418,23 @@ key => NULL | |
<row> | |
<entry><type>hstore</> <literal>-</> <type>text</></entry> | |
+ <entry><type>hstore</></entry> | |
<entry>delete key from left operand</entry> | |
<entry><literal>'a=>1, b=>2, c=>3'::hstore - 'b'::text</literal></entry> | |
<entry><literal>"a"=>"1", "c"=>"3"</literal></entry> | |
</row> | |
<row> | |
+ <entry><type>hstore</> <literal>-</> <type>integer</></entry> | |
+ <entry><type>hstore</></entry> | |
+ <entry>delete index from left operand</entry> | |
+ <entry><literal>'[2, 3, 4, 6, 8]'::hstore - 1;</literal></entry> | |
+ <entry><literal>[2, 4, 6, 8]</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
<entry><type>hstore</> <literal>-</> <type>text[]</></entry> | |
+ <entry><type>hstore</></entry> | |
<entry>delete keys from left operand</entry> | |
<entry><literal>'a=>1, b=>2, c=>3'::hstore - ARRAY['a','b']</literal></entry> | |
<entry><literal>"c"=>"3"</literal></entry> | |
@@ -177,13 +442,23 @@ key => NULL | |
<row> | |
<entry><type>hstore</> <literal>-</> <type>hstore</></entry> | |
+ <entry><type>hstore</></entry> | |
<entry>delete matching pairs from left operand</entry> | |
<entry><literal>'a=>1, b=>2, c=>3'::hstore - 'a=>4, b=>2'::hstore</literal></entry> | |
<entry><literal>"a"=>"1", "c"=>"3"</literal></entry> | |
</row> | |
<row> | |
+ <entry><type>hstore</> <literal>#-</> <type>text[]</></entry> | |
+ <entry><type>hstore</></entry> | |
+ <entry>delete key path from left operand</entry> | |
+ <entry><literal>'{a => {b => { c => [1,2]}}}'::hstore #- '[a,b,c,0]'</literal></entry> | |
+ <entry><literal>"a"=>{"b"=>{"c"=>[2]}}</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
<entry><type>record</> <literal>#=</> <type>hstore</></entry> | |
+ <entry><type>record</></entry> | |
<entry>replace fields in <type>record</> with matching values from <type>hstore</></entry> | |
<entry>see Examples section</entry> | |
<entry></entry> | |
@@ -191,6 +466,7 @@ key => NULL | |
<row> | |
<entry><literal>%%</> <type>hstore</></entry> | |
+ <entry><type>text[]</></entry> | |
<entry>convert <type>hstore</> to array of alternating keys and values</entry> | |
<entry><literal>%% 'a=>foo, b=>bar'::hstore</literal></entry> | |
<entry><literal>{a,foo,b,bar}</literal></entry> | |
@@ -198,6 +474,7 @@ key => NULL | |
<row> | |
<entry><literal>%#</> <type>hstore</></entry> | |
+ <entry><type>text[]</></entry> | |
<entry>convert <type>hstore</> to two-dimensional key/value array</entry> | |
<entry><literal>%# 'a=>foo, b=>bar'::hstore</literal></entry> | |
<entry><literal>{{a,foo},{b,bar}}</literal></entry> | |
@@ -208,12 +485,22 @@ key => NULL | |
</table> | |
<note> | |
- <para> | |
- Prior to PostgreSQL 8.2, the containment operators <literal>@></> | |
- and <literal><@</> were called <literal>@</> and <literal>~</>, | |
- respectively. These names are still available, but are deprecated and will | |
- eventually be removed. Notice that the old names are reversed from the | |
- convention formerly followed by the core geometric data types! | |
+ <para> | |
+ As of PostgreSQL 8.4, the <literal>@></> and <literal>@<</> operators can go deep: | |
+<programlisting> | |
+postgres=# SELECT 'a=>[1,2,{c=>3, x=>4}], c=>b'::hstore @> 'a=>[{c=>3}]'; | |
+ ?column? | |
+---------- | |
+ t | |
+</programlisting> | |
+ </para> | |
+ | |
+ <para> | |
+ Prior to PostgreSQL 8.2, the containment operators <literal>@></> | |
+ and <literal><@</> were called <literal>@</> and <literal>~</>, | |
+ respectively. These names are still available, but are deprecated and will | |
+ eventually be removed. Notice that the old names are reversed from the | |
+ convention formerly followed by the core geometric data types! | |
</para> | |
</note> | |
@@ -258,6 +545,14 @@ key => NULL | |
</row> | |
<row> | |
+ <entry><function>hstore(text, hstore)</function></entry> | |
+ <entry><type>hstore</type></entry> | |
+ <entry>make a nested <type>hstore</></entry> | |
+ <entry><literal>hstore('xxx', 'foo=>t, bar=>3.14'::hstore)</literal></entry> | |
+ <entry><literal>"xxx"=>{"bar"=>3.14, "foo"=>t}</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
<entry><function>hstore(text, text)</function></entry> | |
<entry><type>hstore</type></entry> | |
<entry>make single-item <type>hstore</></entry> | |
@@ -266,6 +561,54 @@ key => NULL | |
</row> | |
<row> | |
+ <entry><function>hstore(text, numeric)</function></entry> | |
+ <entry><type>hstore</type></entry> | |
+ <entry>make single-item <type>hstore</></entry> | |
+ <entry><literal>hstore('a', 3.14)</literal></entry> | |
+ <entry><literal>"a"=>3.14</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><function>hstore(text, boolean)</function></entry> | |
+ <entry><type>hstore</type></entry> | |
+ <entry>make single-item <type>hstore</></entry> | |
+ <entry><literal>hstore('a', true)</literal></entry> | |
+ <entry><literal>"a"=>t</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><function>hstore(text)</function></entry> | |
+ <entry><type>hstore</type></entry> | |
+ <entry>make scalar string <type>hstore</></entry> | |
+ <entry><literal>hstore('foo')</literal></entry> | |
+ <entry><literal>"foo"</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><function>hstore(numeric)</function></entry> | |
+ <entry><type>hstore</type></entry> | |
+ <entry>make scalar numeric <type>hstore</></entry> | |
+ <entry><literal>hstore(42)</literal></entry> | |
+ <entry><literal>42</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><function>hstore(boolean)</function></entry> | |
+ <entry><type>hstore</type></entry> | |
+ <entry>make scalar boolean <type>hstore</></entry> | |
+ <entry><literal>hstore(false)</literal></entry> | |
+ <entry><literal>f</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><function>array_to_hstore(anyarray)</function></entry> | |
+ <entry><type>hstore</type></entry> | |
+ <entry>construct an array <type>hstore</> from an array</entry> | |
+ <entry><literal>array_to_hstore('{{1,1,4},{23,3,5}}'::int[])</literal></entry> | |
+ <entry><literal>[[1, 1, 4], [23, 3, 5]]</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
<entry><function>akeys(hstore)</function><indexterm><primary>akeys</primary></indexterm></entry> | |
<entry><type>text[]</type></entry> | |
<entry>get <type>hstore</>'s keys as an array</entry> | |
@@ -306,6 +649,18 @@ b | |
</row> | |
<row> | |
+ <entry><function>hvals(hstore)</function><indexterm><primary>hvals</primary></indexterm></entry> | |
+ <entry><type>setof hstore</type></entry> | |
+ <entry>get <type>hstore</>'s values as a set of <type>hstore</>s</entry> | |
+ <entry><literal>hvals('a=>[1,2],b=>{foo=>1}')</literal></entry> | |
+ <entry> | |
+<programlisting> | |
+[1, 2] | |
+"foo"=>1 | |
+</programlisting></entry> | |
+ </row> | |
+ | |
+ <row> | |
<entry><function>hstore_to_array(hstore)</function><indexterm><primary>hstore_to_array</primary></indexterm></entry> | |
<entry><type>text[]</type></entry> | |
<entry>get <type>hstore</>'s keys and values as an array of alternating | |
@@ -339,6 +694,14 @@ b | |
</row> | |
<row> | |
+ <entry><function>json_to_hstore(json)</function><indexterm><primary>json_to_hstore</primary></indexterm></entry> | |
+ <entry><type>hstore</type></entry> | |
+ <entry>get <type>json</type> as an <type>hstore</type> value</entry> | |
+ <entry><literal>json_to_hstore('{"a key": "1", "b": "t", "c": null, "d": "12345", "e": "012345", "f": "1.234", "g": "2.345e+4"}')</literal></entry> | |
+ <entry><literal>"b"=>"t", "c"=>NULL, "d"=>"12345", "e"=>"012345", "f"=>"1.234", "g"=>"2.345e+4", "a key"=>"1"</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
<entry><function>slice(hstore, text[])</function><indexterm><primary>slice</primary></indexterm></entry> | |
<entry><type>hstore</type></entry> | |
<entry>extract a subset of an <type>hstore</></entry> | |
@@ -361,6 +724,20 @@ b | |
</row> | |
<row> | |
+ <entry><function>each_hstore(hstore)</function><indexterm><primary>each_hstore</primary></indexterm></entry> | |
+ <entry><type>setof(key text, value text)</type></entry> | |
+ <entry>get <type>hstore</>'s keys and values as a set</entry> | |
+ <entry><literal>select * from each_hstore('a=>1,b=>2')</literal></entry> | |
+ <entry> | |
+<programlisting> | |
+ key | value | |
+-----+------- | |
+ a | 1 | |
+ b | 2 | |
+</programlisting></entry> | |
+ </row> | |
+ | |
+ <row> | |
<entry><function>exist(hstore,text)</function><indexterm><primary>exist</primary></indexterm></entry> | |
<entry><type>boolean</type></entry> | |
<entry>does <type>hstore</> contain key?</entry> | |
@@ -377,6 +754,30 @@ b | |
</row> | |
<row> | |
+ <entry><function>hstore_typeof(hstore)</function><indexterm><primary>hstore_typeof</primary></indexterm></entry> | |
+ <entry><type>text</type></entry> | |
+ <entry>get the type of an <type>hstore</> value, one of <literal>hash</>, <literal>array</>, <literal>string</>, <literal>numeric</>, <literal>bool</>, or <literal>null</></entry> | |
+ <entry><literal>hstore_typeof('[1]')</literal></entry> | |
+ <entry><literal>array</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><function>replace(hstore,text[],hstore)</function><indexterm><primary>replace</primary></indexterm></entry> | |
+ <entry><type>hstore</type></entry> | |
+ <entry>replace value at the specified path</entry> | |
+ <entry><literal>replace('a=>1,b=>{c=>3,d=>[4,5,6]}'::hstore,'[b,d]', '1')</literal></entry> | |
+ <entry><literal>"a"=>1, "b"=>{"c"=>3, "d"=>}</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><function>concat_path(hstore,text[],hstore)</function><indexterm><primary>concat_path</primary></indexterm></entry> | |
+ <entry><type>hstore</type></entry> | |
+ <entry>concatenate <type>hstore</> value at the specified path</entry> | |
+ <entry><literal>concat_path('b=>{c=>3,d=>[4,5,6]}'::hstore,'[b,d]', '1')</literal></entry> | |
+ <entry><literal>"b"=>{"c"=>3, "d"=>[4, 5, 6, 1]}</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
<entry><function>delete(hstore,text)</function><indexterm><primary>delete</primary></indexterm></entry> | |
<entry><type>hstore</type></entry> | |
<entry>delete pair with matching key</entry> | |
@@ -404,7 +805,15 @@ b | |
<entry><function>populate_record(record,hstore)</function><indexterm><primary>populate_record</primary></indexterm></entry> | |
<entry><type>record</type></entry> | |
<entry>replace fields in <type>record</> with matching values from <type>hstore</></entry> | |
- <entry>see Examples section</entry> | |
+ <entry>see Populating Records section</entry> | |
+ <entry></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><function>hstore_print(hstore,bool,bool,bool,bool,bool)</function></entry> | |
+ <entry><type>text</type></entry> | |
+ <entry>Format an <type>hstore</> value as text with various formatting options</entry> | |
+ <entry>see Printing section</entry> | |
<entry></entry> | |
</row> | |
@@ -415,7 +824,8 @@ b | |
<note> | |
<para> | |
The function <function>hstore_to_json</function> is used when an <type>hstore</type> | |
- value is cast to <type>json</type>. | |
+ value is cast to <type>json</type>. Conversely, the function <function>json_to_hstore</function> | |
+ is used when a <type>json</type> value is cast to <type>hstore</type>. | |
</para> | |
</note> | |
@@ -426,6 +836,14 @@ b | |
but it will reject non-record types with a run-time error. | |
</para> | |
</note> | |
+ | |
+ <note> | |
+ <para> | |
+ The <literal>hstore_typeof</> function's <literal>null</> return value should not be confused | |
+ with a SQL NULL. While calling <literal>hstore_typeof('null'::hstore)</> will return | |
+ <literal>null</>, calling <literal>hstore_typeof(NULL::hstore)</> will return a SQL NULL. | |
+ </para> | |
+ </note> | |
</sect2> | |
<sect2> | |
@@ -458,6 +876,155 @@ CREATE INDEX hidx ON testhstore USING HASH (h); | |
</sect2> | |
<sect2> | |
+ <title>Printing</title> | |
+ | |
+ <para> | |
+ The <literal>hstore_print()</> function takes a single <type>hstore</> | |
+ value and formats it as text. By default, the returned value is identical | |
+ to the text format used to return <type>hstore</> values in queries. | |
+ However, <literal>hstore_print()</> also accepts a number of optional | |
+ parameters, passed as <type>boolean</> values, to format an <type>hstore</> | |
+ in various ways. The parameters include: | |
+ </para> | |
+ | |
+ <table id="hstore-print-table"> | |
+ <title><literal>hstore_print()</> Parameters</title> | |
+ | |
+ <tgroup cols="4"> | |
+ <thead> | |
+ <row> | |
+ <entry>Parameter</entry> | |
+ <entry>Description</entry> | |
+ <entry>Example</entry> | |
+ <entry>Result</entry> | |
+ </row> | |
+ </thead> | |
+ | |
+ <tbody> | |
+ | |
+ <row> | |
+ <entry><literal>pretty_print</></entry> | |
+ <entry>Adds add newlines between values and indents nested hashes and arrays.</entry> | |
+ <entry><literal>hstore_print('a=>t, t=>"f", arr=>[1,2,"3"]', pretty_print := true)</literal></entry> | |
+ <entry> | |
+<programlisting> | |
+ hstore_print | |
+-------------- | |
+ "a"=>t, + | |
+ "t"=>"f", + | |
+ "arr"=> + | |
+ [ + | |
+ 1, + | |
+ 2, + | |
+ "3" + | |
+ ] | |
+</programlisting></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><literal>array_curly_braces</></entry> | |
+ <entry>Wraps arrays in curly braces instead of brackets</entry> | |
+ <entry><literal>hstore_print('arr=>[1,2,"3"]', array_curly_braces := true)</literal></entry> | |
+ <entry><literal>"arr"=>{1, 2, "3"}</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><literal>root_hash_decorated</></entry> | |
+ <entry>Wraps the root has object, if three is one, in curly braces</entry> | |
+ <entry><literal>hstore_print('arr=>[1,2,"3"]', root_hash_decorated := true)</literal></entry> | |
+ <entry><literal>{"arr"=>[1, 2, "3"]}</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><literal>json</></entry> | |
+ <entry>Retuns the value as a JSON string</entry> | |
+ <entry><literal>hstore_print('arr=>[1,2,"3"]', json := true)</literal></entry> | |
+ <entry><literal>"arr": [1, 2, "3"]</literal></entry> | |
+ </row> | |
+ | |
+ <row> | |
+ <entry><literal>loose</></entry> | |
+ <entry>Try to parse numbers and booleans</entry> | |
+ <entry><literal>hstore_print('arr=>[1,"2","t"]', loose := true)</literal></entry> | |
+ <entry><literal>"arr"=>[1, 2, t]</literal></entry> | |
+ </row> | |
+ | |
+ </tbody> | |
+ </tgroup> | |
+ </table> | |
+ | |
+ <para> | |
+ These options can be combined for different effects. For example, to pretty-print | |
+ an <type>hstore</> value with the root hash decorated and array curly braces, | |
+ simply pass all three values: | |
+<programlisting> | |
+# SELECT hstore_print( | |
+ 'arr=>[1,2,"3"]', | |
+ root_hash_decorated := true, | |
+ array_curly_braces := true, | |
+ pretty_print := true | |
+); | |
+ hstore_print | |
+-------------- | |
+ { + | |
+ "arr"=> + | |
+ { + | |
+ 1, + | |
+ 2, + | |
+ "3" + | |
+ } + | |
+ } | |
+(1 row) | |
+</programlisting> | |
+ </para> | |
+ | |
+ </sect2> | |
+ | |
+ | |
+ <sect2> | |
+ <title>Populating Records</title> | |
+ | |
+ <para> | |
+ The <literal>populate_record()</> converts an <type>hstore</> hash value to | |
+ a pre-defined record type. Pass any record value (even <literal>NULL</>) as | |
+ the first argument and the <type>hstore</> to convert to that type as the | |
+ second argument. At its simplest <literal>populate_record()</> simply maps | |
+ keys to column names and values to record values: | |
+<programlisting> | |
+CREATE TABLE test (col1 integer, col2 text, col3 text); | |
+ | |
+SELECT * FROM populate_record( | |
+ null::test, | |
+ '"col1"=>"456", "col2"=>"zzz"' | |
+); | |
+ col1 | col2 | col3 | |
+------+------+------ | |
+ 456 | zzz | | |
+(1 row) | |
+</programlisting> | |
+ </para> | |
+ | |
+ <para> | |
+ But <literal>populate_record()</> supports more complicated records and nested | |
+ <type>hstore</a> values, as well. It makes an effort to convert | |
+ from <type>hstore</> data types to PostgreSQL types, including arrays, | |
+ <type>json</>, and <type>hstore</> values: | |
+<programlisting> | |
+CREATE type stuff AS (i int, h hstore, a int[], j json); | |
+ | |
+SELECT * FROM populate_record( | |
+ null::stuff, | |
+ 'i=>2, h=>{b=>3}, a=>{7,8,9}, j=>{a=>{1,2,3}}' | |
+); | |
+ i | h | a | j | |
+---+--------+---------+------------------ | |
+ 2 | "b"=>3 | {7,8,9} | {"a": [1, 2, 3]} | |
+</programlisting> | |
+ </para> | |
+ | |
+ </sect2> | |
+ | |
+ <sect2> | |
<title>Examples</title> | |
<para> | |
@@ -489,20 +1056,6 @@ SELECT hstore(t) FROM test AS t; | |
</para> | |
<para> | |
- Convert an <type>hstore</> to a predefined <type>record</> type: | |
-<programlisting> | |
-CREATE TABLE test (col1 integer, col2 text, col3 text); | |
- | |
-SELECT * FROM populate_record(null::test, | |
- '"col1"=>"456", "col2"=>"zzz"'); | |
- col1 | col2 | col3 | |
-------+------+------ | |
- 456 | zzz | | |
-(1 row) | |
-</programlisting> | |
- </para> | |
- | |
- <para> | |
Modify an existing record using the values from an <type>hstore</>: | |
<programlisting> | |
CREATE TABLE test (col1 integer, col2 text, col3 text); | |
@@ -567,11 +1120,14 @@ SELECT key, count(*) FROM | |
<sect2> | |
<title>Compatibility</title> | |
- <para> | |
- As of PostgreSQL 9.0, <type>hstore</> uses a different internal | |
- representation than previous versions. This presents no obstacle for | |
+ <para>The internal representation of <type>hstore</> has been updated | |
+ a couple of times in its history. Data types and nested structures were | |
+ added in PostgreSQL 9.4, while capacity and improved index support were | |
+ introduced in Postgrsql 9.0. These changes present no obstacle for | |
dump/restore upgrades since the text representation (used in the dump) is | |
- unchanged. | |
+ unchanged. However, <type>hstore</> values dumped from 9.4 cannot be | |
+ loaded into earlier versions of PostgreSQL if they contain nested values | |
+ or typed data. | |
</para> | |
<para> | |
diff --git a/src/include/funcapi.h b/src/include/funcapi.h | |
index 716555d..5b9808f 100644 | |
--- a/src/include/funcapi.h | |
+++ b/src/include/funcapi.h | |
@@ -293,6 +293,15 @@ extern void end_MultiFuncCall(PG_FUNCTION_ARGS, FuncCallContext *funcctx); | |
PG_RETURN_DATUM(_result); \ | |
} while (0) | |
+#define SRF_RETURN_NEXT_NULL(_funcctx) \ | |
+ do { \ | |
+ ReturnSetInfo *rsi; \ | |
+ (_funcctx)->call_cntr++; \ | |
+ rsi = (ReturnSetInfo *) fcinfo->resultinfo; \ | |
+ rsi->isDone = ExprMultipleResult; \ | |
+ PG_RETURN_NULL(); \ | |
+ } while (0) | |
+ | |
#define SRF_RETURN_DONE(_funcctx) \ | |
do { \ | |
ReturnSetInfo *rsi; \ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment