|
8 | 8 | *
|
9 | 9 | *
|
10 | 10 | * IDENTIFICATION
|
11 |
| - * $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.149 2008/11/12 13:09:27 petere Exp $ |
| 11 | + * $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.150 2008/11/14 00:51:46 tgl Exp $ |
12 | 12 | *
|
13 | 13 | *-------------------------------------------------------------------------
|
14 | 14 | */
|
@@ -4635,3 +4635,107 @@ array_fill_internal(ArrayType *dims, ArrayType *lbs,
|
4635 | 4635 |
|
4636 | 4636 | return result;
|
4637 | 4637 | }
|
| 4638 | + |
| 4639 | + |
| 4640 | +/* |
| 4641 | + * UNNEST |
| 4642 | + */ |
| 4643 | +Datum |
| 4644 | +array_unnest(PG_FUNCTION_ARGS) |
| 4645 | +{ |
| 4646 | + typedef struct |
| 4647 | + { |
| 4648 | + ArrayType *arr; |
| 4649 | + int nextelem; |
| 4650 | + int numelems; |
| 4651 | + char *elemdataptr; /* this moves with nextelem */ |
| 4652 | + bits8 *arraynullsptr; /* this does not */ |
| 4653 | + int16 elmlen; |
| 4654 | + bool elmbyval; |
| 4655 | + char elmalign; |
| 4656 | + } array_unnest_fctx; |
| 4657 | + |
| 4658 | + FuncCallContext *funcctx; |
| 4659 | + array_unnest_fctx *fctx; |
| 4660 | + MemoryContext oldcontext; |
| 4661 | + |
| 4662 | + /* stuff done only on the first call of the function */ |
| 4663 | + if (SRF_IS_FIRSTCALL()) |
| 4664 | + { |
| 4665 | + ArrayType *arr = PG_GETARG_ARRAYTYPE_P(0); |
| 4666 | + |
| 4667 | + /* create a function context for cross-call persistence */ |
| 4668 | + funcctx = SRF_FIRSTCALL_INIT(); |
| 4669 | + |
| 4670 | + /* |
| 4671 | + * switch to memory context appropriate for multiple function calls |
| 4672 | + */ |
| 4673 | + oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); |
| 4674 | + |
| 4675 | + /* allocate memory for user context */ |
| 4676 | + fctx = (array_unnest_fctx *) palloc(sizeof(array_unnest_fctx)); |
| 4677 | + |
| 4678 | + /* |
| 4679 | + * Initialize state. Note we assume that the originally passed |
| 4680 | + * array will stick around for the whole call series. |
| 4681 | + */ |
| 4682 | + fctx->arr = arr; |
| 4683 | + fctx->nextelem = 0; |
| 4684 | + fctx->numelems = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr)); |
| 4685 | + |
| 4686 | + fctx->elemdataptr = ARR_DATA_PTR(arr); |
| 4687 | + fctx->arraynullsptr = ARR_NULLBITMAP(arr); |
| 4688 | + |
| 4689 | + get_typlenbyvalalign(ARR_ELEMTYPE(arr), |
| 4690 | + &fctx->elmlen, |
| 4691 | + &fctx->elmbyval, |
| 4692 | + &fctx->elmalign); |
| 4693 | + |
| 4694 | + funcctx->user_fctx = fctx; |
| 4695 | + MemoryContextSwitchTo(oldcontext); |
| 4696 | + } |
| 4697 | + |
| 4698 | + /* stuff done on every call of the function */ |
| 4699 | + funcctx = SRF_PERCALL_SETUP(); |
| 4700 | + fctx = funcctx->user_fctx; |
| 4701 | + |
| 4702 | + if (fctx->nextelem < fctx->numelems) |
| 4703 | + { |
| 4704 | + int offset = fctx->nextelem++; |
| 4705 | + Datum elem; |
| 4706 | + |
| 4707 | + /* |
| 4708 | + * Check for NULL array element |
| 4709 | + */ |
| 4710 | + if (array_get_isnull(fctx->arraynullsptr, offset)) |
| 4711 | + { |
| 4712 | + fcinfo->isnull = true; |
| 4713 | + elem = (Datum) 0; |
| 4714 | + /* elemdataptr does not move */ |
| 4715 | + } |
| 4716 | + else |
| 4717 | + { |
| 4718 | + /* |
| 4719 | + * OK, get the element |
| 4720 | + */ |
| 4721 | + char *ptr = fctx->elemdataptr; |
| 4722 | + |
| 4723 | + fcinfo->isnull = false; |
| 4724 | + elem = ArrayCast(ptr, fctx->elmbyval, fctx->elmlen); |
| 4725 | + |
| 4726 | + /* |
| 4727 | + * Advance elemdataptr over it |
| 4728 | + */ |
| 4729 | + ptr = att_addlength_pointer(ptr, fctx->elmlen, ptr); |
| 4730 | + ptr = (char *) att_align_nominal(ptr, fctx->elmalign); |
| 4731 | + fctx->elemdataptr = ptr; |
| 4732 | + } |
| 4733 | + |
| 4734 | + SRF_RETURN_NEXT(funcctx, elem); |
| 4735 | + } |
| 4736 | + else |
| 4737 | + { |
| 4738 | + /* do when there is no more left */ |
| 4739 | + SRF_RETURN_DONE(funcctx); |
| 4740 | + } |
| 4741 | +} |
0 commit comments