]> git.8kb.co.uk Git - postgresql/jsonb_delete/blob - jsonb_delete.c
Initial commit
[postgresql/jsonb_delete] / jsonb_delete.c
1 /* 
2  * jsonb_delete.c 
3  *     Test jsonb delete operator functions for 9.4+
4  *
5  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
6  * Portions Copyright (c) 1994, Regents of the University of California
7  * Author: Glyn Astill <glyn@8kb.co.uk>
8  *
9  */
10
11 #include "postgres.h"
12 #include "fmgr.h"
13 #include "utils/jsonb.h"
14 #include "utils/builtins.h"
15 #include "jsonb_delete.h"
16
17 #ifdef PG_MODULE_MAGIC
18         PG_MODULE_MAGIC;
19 #endif 
20
21 Datum jsonb_delete_jsonb(PG_FUNCTION_ARGS);
22
23 PG_FUNCTION_INFO_V1(jsonb_delete_jsonb);
24
25 /*
26  * Operator function to delete top level keys and values from left operand 
27  * where a match is found in the right operand.
28  *
29  * jsonb, jsonb -> jsonb
30  *
31  */
32 Datum 
33 jsonb_delete_jsonb(PG_FUNCTION_ARGS)
34 {
35     Jsonb                  *jb1 = PG_GETARG_JSONB(0);
36     Jsonb                  *jb2 = PG_GETARG_JSONB(1);
37     JsonbValue             *res = NULL;
38     JsonbParseState        *state = NULL;
39     JsonbIterator          *it, *it2;
40     JsonbValue              v, v2;
41     JsonbValue              key;
42     JsonbValue             *lookup = NULL;
43     int32                   r, r2;
44     bool                    push = true;
45
46     /* check if right jsonb is empty and return left if so */
47     if (JB_ROOT_COUNT(jb2) == 0)
48         PG_RETURN_JSONB(jb1);
49
50     it = JsonbIteratorInit(&jb1->root);
51
52     while ((r = JsonbIteratorNext(&it, &v, true)) != WJB_DONE)
53     {
54         push = true;
55
56         switch (r)
57         {
58             case WJB_BEGIN_ARRAY:
59             case WJB_BEGIN_OBJECT:
60             case WJB_END_ARRAY:
61             case WJB_END_OBJECT:
62                 res = pushJsonbValue(&state, r, NULL);
63                 break;
64             case WJB_ELEM:
65                 if (v.type == jbvBinary)
66                 {
67                     it2 = JsonbIteratorInit(&jb2->root);
68                     while ((r2 = JsonbIteratorNext(&it2, &v2, true)) != WJB_DONE)
69                     {
70                         if (v2.type == jbvBinary && v2.val.binary.len == v.val.binary.len &&
71                             memcmp(v2.val.binary.data, v.val.binary.data, v2.val.binary.len) == 0)
72                         {
73                                 push = false;
74                                 break;
75                         }
76                     }
77                 }
78                 else if (findJsonbValueFromContainer(&jb2->root, JB_FARRAY, &v) != NULL)
79                     push = false;
80
81                 if (push) 
82                 {
83                     if (v.type == jbvBinary) 
84                         res = pushJsonbBinary(&state, v.val.binary.data);
85                     else 
86                         res = pushJsonbValue(&state, WJB_ELEM, &v);
87                 }
88                 break;
89             case WJB_KEY:
90                 lookup = findJsonbValueFromContainer(&jb2->root, JB_FOBJECT, &v);
91
92                 key = v;
93                 r = JsonbIteratorNext(&it, &v, true);
94
95                 Assert(r == WJB_VALUE);
96
97                 if (lookup != NULL && lookup->type == v.type) 
98                 {
99                     switch (lookup->type) 
100                     {
101                         /* Nulls within json are equal, but should not be equal to SQL nulls */
102                         case jbvNull:
103                             push = false;
104                             break;
105                         case jbvNumeric:
106                             if (DatumGetBool(DirectFunctionCall2(numeric_eq, 
107                                                                  PointerGetDatum(lookup->val.numeric), 
108                                                                  PointerGetDatum(v.val.numeric))))
109                                 push = false;
110                             break;
111                         case jbvString:
112                             if ((lookup->val.string.len == v.val.string.len) &&
113                                 (memcmp(lookup->val.string.val, 
114                                         v.val.string.val, 
115                                         lookup->val.string.len) == 0))
116                                 push = false;
117                             break;
118                         case jbvBinary:
119                             if ((lookup->val.binary.len == v.val.binary.len) &&
120                                 (memcmp(lookup->val.binary.data, 
121                                         v.val.binary.data, 
122                                         lookup->val.binary.len) == 0))
123                                 push = false;
124                                 break;
125                         case jbvBool:
126                             if (lookup->val.boolean == v.val.boolean)
127                                 push = false;
128                             break;
129                         default:
130                             ereport(ERROR, (errcode(ERRCODE_SUCCESSFUL_COMPLETION), errmsg("unrecognized lookup type: %d", (int) lookup->type)));
131                         /* inner switch end */
132                     }
133                 }
134
135                 if (push) 
136                 {                
137                     res = pushJsonbValue(&state, WJB_KEY, &key);
138
139                     /* if our value is nested binary data, iterate separately pushing each val */
140                     if (v.type == jbvBinary) 
141                         res = pushJsonbBinary(&state, v.val.binary.data);
142                     else 
143                         res = pushJsonbValue(&state, r, &v);
144                 }
145                 break;
146             case WJB_VALUE:
147                 /* should not be possible */
148             default:
149                 elog(ERROR, "impossible state: %d", r);
150             /* switch end */ 
151         }
152
153     }
154
155     if (JB_ROOT_IS_SCALAR(jb1) && !res->val.array.rawScalar && res->val.array.nElems == 1)
156         res->val.array.rawScalar = true;
157
158     PG_FREE_IF_COPY(jb1, 0); 
159     PG_FREE_IF_COPY(jb2, 1); 
160
161     PG_RETURN_JSONB(JsonbValueToJsonb(res));
162 }
163