]> granicus.if.org Git - postgresql/blob - src/backend/access/common/indextuple.c
d3fbe0fef4d42fad4cc14d9d4bbff23534831c52
[postgresql] / src / backend / access / common / indextuple.c
1 /*-------------------------------------------------------------------------
2  *
3  * indextuple.c--
4  *         This file contains index tuple accessor and mutator routines,
5  *         as well as a few various tuple utilities.
6  *
7  * Copyright (c) 1994, Regents of the University of California
8  *
9  *
10  * IDENTIFICATION
11  *        $Header: /cvsroot/pgsql/src/backend/access/common/indextuple.c,v 1.26 1998/02/06 20:17:51 momjian Exp $
12  *
13  *-------------------------------------------------------------------------
14  */
15
16 #include <postgres.h>
17
18 #include <access/heapam.h>
19 #include <access/ibit.h>
20 #include <access/itup.h>
21 #include <access/tupmacs.h>
22 #include <catalog/pg_type.h>
23
24 #ifndef HAVE_MEMMOVE
25 #include <regex/utils.h>
26 #else
27 #include <string.h>
28 #endif
29
30 /* ----------------------------------------------------------------
31  *                                index_ tuple interface routines
32  * ----------------------------------------------------------------
33  */
34
35 /* ----------------
36  *              index_formtuple
37  * ----------------
38  */
39 IndexTuple
40 index_formtuple(TupleDesc tupleDescriptor,
41                                 Datum value[],
42                                 char null[])
43 {
44         register char *tp;                      /* tuple pointer */
45         IndexTuple      tuple;                  /* return tuple */
46         Size            size,
47                                 hoff;
48         int                     i;
49         unsigned short infomask = 0;
50         bool            hasnull = false;
51         uint16          tupmask = 0;
52         int                     numberOfAttributes = tupleDescriptor->natts;
53
54         if (numberOfAttributes > MaxIndexAttributeNumber)
55                 elog(ERROR, "index_formtuple: numberOfAttributes of %d > %d",
56                          numberOfAttributes, MaxIndexAttributeNumber);
57
58
59         for (i = 0; i < numberOfAttributes && !hasnull; i++)
60         {
61                 if (null[i] != ' ')
62                         hasnull = true;
63         }
64
65         if (hasnull)
66                 infomask |= INDEX_NULL_MASK;
67
68         hoff = IndexInfoFindDataOffset(infomask);
69         size = hoff
70                 + ComputeDataSize(tupleDescriptor,
71                                                   value, null);
72         size = DOUBLEALIGN(size);       /* be conservative */
73
74         tp = (char *) palloc(size);
75         tuple = (IndexTuple) tp;
76         MemSet(tp, 0, (int) size);
77
78         DataFill((char *) tp + hoff,
79                          tupleDescriptor,
80                          value,
81                          null,
82                          &tupmask,
83                          (hasnull ? (bits8 *) tp + sizeof(*tuple) : NULL));
84
85         /*
86          * We do this because DataFill wants to initialize a "tupmask" which
87          * is used for HeapTuples, but we want an indextuple infomask.  The
88          * only "relevent" info is the "has variable attributes" field, which
89          * is in mask position 0x02.  We have already set the null mask above.
90          */
91
92         if (tupmask & 0x02)
93                 infomask |= INDEX_VAR_MASK;
94
95         /*
96          * Here we make sure that we can actually hold the size.  We also want
97          * to make sure that size is not aligned oddly.  This actually is a
98          * rather odd way to make sure the size is not too large overall.
99          */
100
101         if (size & 0xE000)
102                 elog(ERROR, "index_formtuple: data takes %d bytes: too big", size);
103
104
105         infomask |= size;
106
107         /* ----------------
108          * initialize metadata
109          * ----------------
110          */
111         tuple->t_info = infomask;
112         return (tuple);
113 }
114
115 /* ----------------
116  *              nocache_index_getattr
117  *
118  *              This gets called from index_getattr() macro, and only in cases
119  *              where we can't use cacheoffset and the value is not null.
120  *
121  *              This caches attribute offsets in the attribute descriptor.
122  *
123  *              an alternate way to speed things up would be to cache offsets
124  *              with the tuple, but that seems more difficult unless you take
125  *              the storage hit of actually putting those offsets into the
126  *              tuple you send to disk.  Yuck.
127  *
128  *              This scheme will be slightly slower than that, but should
129  *              preform well for queries which hit large #'s of tuples.  After
130  *              you cache the offsets once, examining all the other tuples using
131  *              the same attribute descriptor will go much quicker. -cim 5/4/91
132  * ----------------
133  */
134 Datum
135 nocache_index_getattr(IndexTuple tup,
136                          int attnum,
137                          TupleDesc tupleDesc,
138                          bool *isnull)
139 {
140         register char *tp;                      /* ptr to att in tuple */
141         register char *bp = NULL;       /* ptr to att in tuple */
142         int                     slow;                   /* do we have to walk nulls? */
143         register int data_off;          /* tuple data offset */
144         AttributeTupleForm *att = tupleDesc->attrs;
145
146         /* ----------------
147          *      sanity checks
148          * ----------------
149          */
150
151         /* ----------------
152          *       Three cases:
153          *
154          *       1: No nulls and no variable length attributes.
155          *       2: Has a null or a varlena AFTER att.
156          *       3: Has nulls or varlenas BEFORE att.
157          * ----------------
158          */
159
160 #ifdef IN_MACRO
161 /* This is handled in the macro */
162         Assert(PointerIsValid(isnull));
163         Assert(attnum > 0);
164
165         *isnull = false;
166 #endif
167
168         data_off = IndexTupleHasMinHeader(tup) ? sizeof *tup :
169                 IndexInfoFindDataOffset(tup->t_info);
170
171         if (IndexTupleNoNulls(tup))
172         {
173                 attnum--;
174
175 #ifdef IN_MACRO
176 /* This is handled in the macro */
177         
178                 /* first attribute is always at position zero */
179
180                 if (attnum == 1)
181                 {
182                         return (Datum) fetchatt(&(att[0]), (char *) tup + data_off);
183                 }
184                 if (att[attnum]->attcacheoff != -1)
185                 {
186                         return (Datum) fetchatt(&(att[attnum]),
187                                                          (char *) tup + data_off +
188                                                          att[attnum]->attcacheoff);
189                 }
190 #endif
191
192                 slow = 0;
193         }
194         else
195         {                                                       /* there's a null somewhere in the tuple */
196
197                 slow = 0;
198                 /* ----------------
199                  *              check to see if desired att is null
200                  * ----------------
201                  */
202
203                 attnum--;
204
205                 bp = (char *) tup + sizeof(*tup);               /* "knows" t_bits are
206                                                                                                  * here! */
207 #ifdef IN_MACRO
208 /* This is handled in the macro */
209                 
210                 if (att_isnull(attnum, bp))
211                 {
212                         *isnull = true;
213                         return (Datum)NULL;
214                 }
215 #endif
216
217                 /* ----------------
218                  *              Now check to see if any preceeding bits are null...
219                  * ----------------
220                  */
221                 {
222                         register int i = 0; /* current offset in bp */
223                         register int mask;      /* bit in byte we're looking at */
224                         register char n;        /* current byte in bp */
225                         register int byte,
226                                                  finalbit;
227
228                         byte = attnum >> 3;
229                         finalbit = attnum & 0x07;
230
231                         for (; i <= byte && !slow; i++)
232                         {
233                                 n = bp[i];
234                                 if (i < byte)
235                                 {
236                                         /* check for nulls in any "earlier" bytes */
237                                         if ((~n) != 0)
238                                                 slow=1;
239                                 }
240                                 else
241                                 {
242                                         /* check for nulls "before" final bit of last byte */
243                                         mask = (1 << finalbit) - 1;
244                                         if ((~n) & mask)
245                                                 slow=1;
246                                 }
247                         }
248                 }
249         }
250
251         tp = (char *) tup + data_off;
252
253         /* now check for any non-fixed length attrs before our attribute */
254
255         if (!slow)
256         {
257                 if (att[attnum]->attcacheoff != -1)
258                 {
259                         return (Datum) fetchatt(&(att[attnum]),
260                                                          tp + att[attnum]->attcacheoff);
261                 }
262                 else if (attnum == 0)
263                 {
264                         return ((Datum) fetchatt(&(att[0]), (char *) tp));
265                 }
266                 else if (!IndexTupleAllFixed(tup))
267                 {
268                         register int j = 0;
269
270                         for (j = 0; j < attnum && !slow; j++)
271                                 if (att[j]->attlen < 1 && !VARLENA_FIXED_SIZE(att[j]))
272                                         slow = 1;
273                 }
274         }
275
276         /*
277          * if slow is zero, and we got here, we know that we have a tuple with
278          * no nulls.  We also know that we have to initialize the remainder of
279          * the attribute cached offset values.
280          */
281
282         if (!slow)
283         {
284                 register int j = 1;
285                 register long off;
286
287                 /*
288                  * need to set cache for some atts
289                  */
290
291                 att[0]->attcacheoff = 0;
292
293                 while (att[j]->attcacheoff != -1)
294                         j++;
295
296                 if (!VARLENA_FIXED_SIZE(att[j-1]))
297                         off = att[j - 1]->attcacheoff + att[j - 1]->attlen;
298                 else
299                         off = att[j - 1]->attcacheoff + att[j - 1]->atttypmod;
300
301                 for (; j < attnum + 1; j++)
302                 {
303                         /*
304                          * Fix me when going to a machine with more than a four-byte
305                          * word!
306                          */
307
308                         switch (att[j]->attlen)
309                         {
310                                 case -1:
311                                         off = (att[j]->attalign == 'd') ?
312                                                 DOUBLEALIGN(off) : INTALIGN(off);
313                                         break;
314                                 case sizeof(char):
315                                         break;
316                                 case sizeof(short):
317                                         off = SHORTALIGN(off);
318                                         break;
319                                 case sizeof(int32):
320                                         off = INTALIGN(off);
321                                         break;
322                                 default:
323                                         if (att[j]->attlen > sizeof(int32))
324                                                 off = (att[j]->attalign == 'd') ?
325                                                         DOUBLEALIGN(off) : LONGALIGN(off);
326                                         else
327                                                 elog(ERROR, "nocache_index_getattr: attribute %d has len %d",
328                                                          j, att[j]->attlen);
329                                         break;
330
331                         }
332
333                         att[j]->attcacheoff = off;
334
335                         /* The only varlena/-1 length value to get here is this */
336                         if (!VARLENA_FIXED_SIZE(att[j]))
337                                 off += att[j]->attlen;
338                         else
339                         {
340                                 Assert(att[j]->atttypmod == VARSIZE(tp + off));
341                                 off += att[j]->atttypmod;
342                         }
343                 }
344
345                 return (Datum) fetchatt(&(att[attnum]), tp + att[attnum]->attcacheoff);
346         }
347         else
348         {
349                 register bool usecache = true;
350                 register int off = 0;
351                 register int i;
352
353                 /*
354                  * Now we know that we have to walk the tuple CAREFULLY.
355                  */
356
357                 for (i = 0; i < attnum; i++)
358                 {
359                         if (!IndexTupleNoNulls(tup))
360                         {
361                                 if (att_isnull(i, bp))
362                                 {
363                                         usecache = false;
364                                         continue;
365                                 }
366                         }
367
368                         /* If we know the next offset, we can skip the rest */
369                         if (usecache && att[i]->attcacheoff != -1)
370                                 off = att[i]->attcacheoff;
371                         else
372                         {
373                                 switch (att[i]->attlen)
374                                 {
375                                         case -1:
376                                                 off = (att[i]->attalign == 'd') ?
377                                                         DOUBLEALIGN(off) : INTALIGN(off);
378                                                 break;
379                                         case sizeof(char):
380                                                 break;
381                                         case sizeof(short):
382                                                 off = SHORTALIGN(off);
383                                                 break;
384                                         case sizeof(int32):
385                                                 off = INTALIGN(off);
386                                                 break;
387                                         default:
388                                                 if (att[i]->attlen < sizeof(int32))
389                                                         elog(ERROR,
390                                                                  "nocachegetiattr2: attribute %d has len %d",
391                                                                  i, att[i]->attlen);
392                                                 if (att[i]->attalign == 'd')
393                                                         off = DOUBLEALIGN(off);
394                                                 else
395                                                         off = LONGALIGN(off);
396                                                 break;
397                                 }
398                                 if (usecache)
399                                         att[i]->attcacheoff = off;
400                         }
401
402                         switch (att[i]->attlen)
403                         {
404                                 case sizeof(char):
405                                         off++;
406                                         break;
407                                 case sizeof(short):
408                                         off += sizeof(short);
409                                         break;
410                                 case sizeof(int32):
411                                         off += sizeof(int32);
412                                         break;
413                                 case -1:
414                                         Assert(!VARLENA_FIXED_SIZE(att[i]) ||
415                                                         att[i]->atttypmod == VARSIZE(tp + off));
416                                         off += VARSIZE(tp + off);
417                                         if (!VARLENA_FIXED_SIZE(att[i]))
418                                                 usecache = false;
419                                         break;
420                                 default:
421                                         off += att[i]->attlen;
422                                         break;
423                         }
424                 }
425
426                 switch (att[attnum]->attlen)
427                 {
428                         case -1:
429                                 off = (att[attnum]->attalign == 'd') ?
430                                         DOUBLEALIGN(off) : INTALIGN(off);
431                                 break;
432                         case sizeof(char):
433                                 break;
434                         case sizeof(short):
435                                 off = SHORTALIGN(off);
436                                 break;
437                         case sizeof(int32):
438                                 off = INTALIGN(off);
439                                 break;
440                         default:
441                                 if (att[attnum]->attlen < sizeof(int32))
442                                         elog(ERROR, "nocache_index_getattr: attribute %d has len %d",
443                                                  attnum, att[attnum]->attlen);
444                                 if (att[attnum]->attalign == 'd')
445                                         off = DOUBLEALIGN(off);
446                                 else
447                                         off = LONGALIGN(off);
448                                 break;
449                 }
450
451                 return (Datum) fetchatt(&att[attnum], tp + off);
452         }
453 }
454
455 RetrieveIndexResult
456 FormRetrieveIndexResult(ItemPointer indexItemPointer,
457                                                 ItemPointer heapItemPointer)
458 {
459         RetrieveIndexResult result;
460
461         Assert(ItemPointerIsValid(indexItemPointer));
462         Assert(ItemPointerIsValid(heapItemPointer));
463
464         result = (RetrieveIndexResult) palloc(sizeof *result);
465
466         result->index_iptr = *indexItemPointer;
467         result->heap_iptr = *heapItemPointer;
468
469         return (result);
470 }
471
472 /*
473  * Copies source into target.  If *target == NULL, we palloc space; otherwise
474  * we assume we have space that is already palloc'ed.
475  */
476 void
477 CopyIndexTuple(IndexTuple source, IndexTuple *target)
478 {
479         Size            size;
480         IndexTuple      ret;
481
482         size = IndexTupleSize(source);
483         if (*target == NULL)
484         {
485                 *target = (IndexTuple) palloc(size);
486         }
487
488         ret = *target;
489         memmove((char *) ret, (char *) source, size);
490 }