v12 support
[smlar.git] / smlar_cache.c
1 #include "smlar.h"
2
3 #include "fmgr.h"
4 #include "utils/array.h"
5 #include "utils/memutils.h"
6
7 /*
8  * Deconstructed array cache
9  */
10
11 typedef struct ArrayCacheEntry {
12         Datum                   toastedArray;
13         ArrayType               *da;
14         SimpleArray             *sa;
15         struct SmlSign  *ss;
16
17         struct ArrayCacheEntry  *prev;
18         struct ArrayCacheEntry  *next;
19 } ArrayCacheEntry;
20
21 #define NENTRIES        (16)
22
23 typedef struct ArrayCache {
24         MemoryContext           ctx;
25         int32                           nentries;
26         ArrayCacheEntry         *head;
27         ArrayCacheEntry         *tail;
28         ArrayCacheEntry*        entries[ NENTRIES ];
29         StatCache                       *DocStat;
30 } ArrayCache;
31
32 static void
33 moveFirst(ArrayCache *ac, ArrayCacheEntry *entry)
34 {
35         /*
36          * delete entry form a list
37          */
38         Assert( entry != ac->head );
39         if ( entry == ac->tail )
40         {
41                 Assert( entry->next == NULL );
42                 ac->tail = entry->prev;
43                 if ( ac->tail )
44                         ac->tail->next = NULL;
45                 else
46                         ac->head = NULL;
47         }
48         else
49         {
50                 entry->prev->next = entry->next;
51                 entry->next->prev = entry->prev;
52         }
53
54         /*
55          * Install into head
56          */
57
58         Assert( ac->head != NULL );
59         Assert( ac->tail != NULL );
60
61         entry->next = ac->head;
62         entry->prev = NULL;
63         ac->head->prev = entry;
64         ac->head = entry;
65 }
66
67 #define DATUMSIZE(d)    VARSIZE_ANY(DatumGetPointer(d))
68 static int
69 cmpDatum(Datum a, Datum b)
70 {
71         int32   la = DATUMSIZE(a);
72         int32   lb = DATUMSIZE(b);
73
74         if ( la == lb )
75                 return memcmp( DatumGetPointer(a), DatumGetPointer(b), la );
76
77         return (la > lb) ? 1 : -1;
78 }
79
80 static void
81 fetchData(ArrayCache *ac, ArrayCacheEntry *entry, ArrayType **da, SimpleArray **sa,  struct SmlSign  **ss )
82 {
83         ProcTypeInfo    info;
84
85         info = findProcs( ARR_ELEMTYPE(entry->da) );
86
87         if ( sa )
88         {
89                 if ( entry->sa == NULL )
90                 {
91                         MemoryContext   old;
92
93                         getFmgrInfoCmp(info);
94
95                         old = MemoryContextSwitchTo( ac->ctx );
96                         entry->sa = Array2SimpleArrayU(info, entry->da, (getSmlType() == ST_TFIDF) ? ac : NULL);
97                         MemoryContextSwitchTo(old);
98                 }
99
100                 *sa = entry->sa;
101         }
102
103         if ( ss )
104         {
105                 if ( entry->ss == NULL )
106                 {
107                         MemoryContext   old;
108
109                         getFmgrInfoHash(info);
110
111                         old = MemoryContextSwitchTo( ac->ctx );
112                         entry->ss = Array2HashedArray(info, entry->da);
113                         MemoryContextSwitchTo(old);
114                 }
115
116                 *ss = entry->ss;
117         }
118
119         if (da)
120                 *da = entry->da;
121 }
122
123 static void
124 cleanupData(ArrayCacheEntry *entry)
125 {
126         if ( entry->sa )
127         {
128                 if ( entry->sa->elems )
129                         pfree( entry->sa->elems );
130                 if ( entry->sa->df )
131                         pfree( entry->sa->df );
132                 if ( entry->sa->hash )
133                         pfree( entry->sa->hash );
134                 pfree( entry->sa );
135         }
136         entry->sa = NULL;
137
138         if ( entry->ss )
139                 pfree(entry->ss);
140         entry->ss = NULL;
141
142         pfree( DatumGetPointer(entry->toastedArray) );
143         pfree( entry->da );
144 }
145
146 static void
147 makeEntry(ArrayCache *ac, ArrayCacheEntry *entry, Datum a)
148 {
149         ArrayType *detoastedArray;
150
151         entry->toastedArray = (Datum)MemoryContextAlloc( ac->ctx, DATUMSIZE(a) );
152         memcpy( DatumGetPointer(entry->toastedArray), DatumGetPointer(a), DATUMSIZE(a) );
153
154         detoastedArray = (ArrayType*)PG_DETOAST_DATUM(entry->toastedArray);
155         entry->da = MemoryContextAlloc( ac->ctx, VARSIZE(detoastedArray));
156         memcpy( entry->da, detoastedArray, VARSIZE(detoastedArray));
157 }
158
159 static int
160 cmpEntry(const void *a, const void *b)
161 {
162         return cmpDatum( (*(ArrayCacheEntry**)a)->toastedArray, (*(ArrayCacheEntry**)b)->toastedArray ); 
163 }
164
165 void*
166 SearchArrayCache( void *cache, MemoryContext ctx, Datum a, ArrayType **da, SimpleArray **sa,  struct SmlSign  **ss )
167 {
168         ArrayCache              *ac;
169         ArrayCacheEntry *entry;
170
171         if ( cache == NULL )
172                 cache = MemoryContextAllocZero(ctx, sizeof(ArrayCache));
173
174         ac = (ArrayCache*) cache;
175         ac->ctx = ctx;
176
177         /*
178          * Fast check of resent used value
179          */
180         if ( ac->head && cmpDatum(ac->head->toastedArray, a) == 0 )
181         {
182                 fetchData(ac, ac->head, da, sa, ss);
183                 return cache;
184         }
185
186         if ( ac->head == NULL  )
187         {
188                 ac->entries[0] = ac->head = ac->tail = MemoryContextAllocZero(ctx, sizeof(ArrayCacheEntry));
189                 ac->nentries = 1;
190                 makeEntry(ac, ac->head, a);
191                 fetchData(ac, ac->head, da, sa, ss);
192                 return cache;
193         }
194
195         do {
196                 ArrayCacheEntry **StopLow = ac->entries;
197                 ArrayCacheEntry **StopHigh = ac->entries + ac->nentries;
198                 ArrayCacheEntry **StopMiddle;
199                 int cmp;
200
201                 while (StopLow < StopHigh) {
202                         StopMiddle = StopLow + ((StopHigh - StopLow) >> 1);
203                         entry = *StopMiddle;
204                         cmp = cmpDatum(entry->toastedArray, a);
205
206                         if ( cmp == 0 )
207                         {
208                                 moveFirst(ac, entry);
209                                 fetchData(ac, ac->head, da, sa, ss);
210                                 return cache;
211                         }
212                         else if ( cmp < 0 )
213                                 StopLow = StopMiddle + 1;
214                         else
215                                 StopHigh = StopMiddle;
216                 }
217         } while(0);
218
219         /*
220          * Not found
221          */
222
223         if ( ac->nentries < NENTRIES )
224         {
225                 entry = ac->entries[ ac->nentries ] = MemoryContextAllocZero(ctx, sizeof(ArrayCacheEntry));
226
227                 /* install first */
228                 entry->next = ac->head;
229                 entry->prev = NULL;
230                 ac->head->prev = entry;
231                 ac->head = entry;
232
233                 ac->nentries ++;
234
235                 makeEntry(ac, ac->head, a);
236                 fetchData(ac, ac->head, da, sa, ss);
237         }
238         else
239         {
240                 cleanupData( ac->tail );
241                 moveFirst(ac, ac->tail );
242                 makeEntry(ac, ac->head, a);
243                 fetchData(ac, ac->head, da, sa, ss);
244         }
245
246         qsort(ac->entries, ac->nentries, sizeof(ArrayCacheEntry*), cmpEntry);
247         return cache;
248 }
249
250 StatElem  *
251 fingArrayStat(void *cache, Oid typoid, Datum query, StatElem *low)
252 {
253         ArrayCache              *ac;
254
255         if ( cache == NULL )
256                 return NULL;
257
258         ac = (ArrayCache*) cache;
259         if ( ac->DocStat == NULL )
260         {
261                 ac->DocStat = initStatCache(ac->ctx);
262                 low = NULL;
263         }
264
265         if ( typoid != ac->DocStat->info->typid )
266                 elog(ERROR,"Types of stat table and actual arguments are different");
267
268         return findStat(ac->DocStat, query, low);
269 }
270
271 StatCache *
272 getStat(void *cache, size_t n)
273 {
274         ArrayCache      *ac;
275
276         if ( cache == NULL )
277                 return NULL;
278
279         ac = (ArrayCache*) cache;
280         if ( ac->DocStat == NULL )
281                 ac->DocStat = initStatCache(ac->ctx);
282
283         getHashStatCache(ac->DocStat, ac->ctx, n);
284
285         return ac->DocStat;
286 }
287
288 void
289 allocateHash(void *cache, SimpleArray *a)
290 {
291         ArrayCache      *ac;
292
293         if ( cache == NULL )
294                 return;
295         ac = (ArrayCache*) cache;
296
297         if (a->hash)
298                 return;
299
300         a->hash = MemoryContextAlloc( ac->ctx, sizeof(uint32) * a->nelems );
301 }