|
10 | 10 | * Copyright (c) 2002-2007, PostgreSQL Global Development Group
|
11 | 11 | *
|
12 | 12 | * IDENTIFICATION
|
13 |
| - * $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.73 2007/04/16 18:21:07 tgl Exp $ |
| 13 | + * $PostgreSQL: pgsql/src/backend/commands/prepare.c,v 1.74 2007/04/26 23:24:44 tgl Exp $ |
14 | 14 | *
|
15 | 15 | *-------------------------------------------------------------------------
|
16 | 16 | */
|
|
21 | 21 | #include "catalog/pg_type.h"
|
22 | 22 | #include "commands/explain.h"
|
23 | 23 | #include "commands/prepare.h"
|
24 |
| -#include "funcapi.h" |
| 24 | +#include "miscadmin.h" |
25 | 25 | #include "parser/analyze.h"
|
26 | 26 | #include "parser/parse_coerce.h"
|
27 | 27 | #include "parser/parse_expr.h"
|
@@ -743,92 +743,99 @@ ExplainExecuteQuery(ExecuteStmt *execstmt, ExplainStmt *stmt,
|
743 | 743 | Datum
|
744 | 744 | pg_prepared_statement(PG_FUNCTION_ARGS)
|
745 | 745 | {
|
746 |
| - FuncCallContext *funcctx; |
747 |
| - HASH_SEQ_STATUS *hash_seq; |
748 |
| - PreparedStatement *prep_stmt; |
749 |
| - |
750 |
| - /* stuff done only on the first call of the function */ |
751 |
| - if (SRF_IS_FIRSTCALL()) |
752 |
| - { |
753 |
| - TupleDesc tupdesc; |
754 |
| - MemoryContext oldcontext; |
755 |
| - |
756 |
| - /* create a function context for cross-call persistence */ |
757 |
| - funcctx = SRF_FIRSTCALL_INIT(); |
| 746 | + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; |
| 747 | + TupleDesc tupdesc; |
| 748 | + Tuplestorestate *tupstore; |
| 749 | + MemoryContext per_query_ctx; |
| 750 | + MemoryContext oldcontext; |
| 751 | + |
| 752 | + /* check to see if caller supports us returning a tuplestore */ |
| 753 | + if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo)) |
| 754 | + ereport(ERROR, |
| 755 | + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 756 | + errmsg("set-valued function called in context that cannot accept a set"))); |
| 757 | + if (!(rsinfo->allowedModes & SFRM_Materialize)) |
| 758 | + ereport(ERROR, |
| 759 | + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
| 760 | + errmsg("materialize mode required, but it is not " \ |
| 761 | + "allowed in this context"))); |
758 | 762 |
|
759 |
| - /* |
760 |
| - * switch to memory context appropriate for multiple function calls |
761 |
| - */ |
762 |
| - oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); |
| 763 | + /* need to build tuplestore in query context */ |
| 764 | + per_query_ctx = rsinfo->econtext->ecxt_per_query_memory; |
| 765 | + oldcontext = MemoryContextSwitchTo(per_query_ctx); |
763 | 766 |
|
764 |
| - /* allocate memory for user context */ |
765 |
| - if (prepared_queries) |
766 |
| - { |
767 |
| - hash_seq = (HASH_SEQ_STATUS *) palloc(sizeof(HASH_SEQ_STATUS)); |
768 |
| - hash_seq_init(hash_seq, prepared_queries); |
769 |
| - funcctx->user_fctx = (void *) hash_seq; |
770 |
| - } |
771 |
| - else |
772 |
| - funcctx->user_fctx = NULL; |
| 767 | + /* |
| 768 | + * build tupdesc for result tuples. This must match the definition of |
| 769 | + * the pg_prepared_statements view in system_views.sql |
| 770 | + */ |
| 771 | + tupdesc = CreateTemplateTupleDesc(5, false); |
| 772 | + TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", |
| 773 | + TEXTOID, -1, 0); |
| 774 | + TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement", |
| 775 | + TEXTOID, -1, 0); |
| 776 | + TupleDescInitEntry(tupdesc, (AttrNumber) 3, "prepare_time", |
| 777 | + TIMESTAMPTZOID, -1, 0); |
| 778 | + TupleDescInitEntry(tupdesc, (AttrNumber) 4, "parameter_types", |
| 779 | + REGTYPEARRAYOID, -1, 0); |
| 780 | + TupleDescInitEntry(tupdesc, (AttrNumber) 5, "from_sql", |
| 781 | + BOOLOID, -1, 0); |
773 | 782 |
|
774 |
| - /* |
775 |
| - * build tupdesc for result tuples. This must match the definition of |
776 |
| - * the pg_prepared_statements view in system_views.sql |
777 |
| - */ |
778 |
| - tupdesc = CreateTemplateTupleDesc(5, false); |
779 |
| - TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name", |
780 |
| - TEXTOID, -1, 0); |
781 |
| - TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement", |
782 |
| - TEXTOID, -1, 0); |
783 |
| - TupleDescInitEntry(tupdesc, (AttrNumber) 3, "prepare_time", |
784 |
| - TIMESTAMPTZOID, -1, 0); |
785 |
| - TupleDescInitEntry(tupdesc, (AttrNumber) 4, "parameter_types", |
786 |
| - REGTYPEARRAYOID, -1, 0); |
787 |
| - TupleDescInitEntry(tupdesc, (AttrNumber) 5, "from_sql", |
788 |
| - BOOLOID, -1, 0); |
789 |
| - |
790 |
| - funcctx->tuple_desc = BlessTupleDesc(tupdesc); |
791 |
| - MemoryContextSwitchTo(oldcontext); |
792 |
| - } |
| 783 | + /* |
| 784 | + * We put all the tuples into a tuplestore in one scan of the hashtable. |
| 785 | + * This avoids any issue of the hashtable possibly changing between calls. |
| 786 | + */ |
| 787 | + tupstore = tuplestore_begin_heap(true, false, work_mem); |
793 | 788 |
|
794 |
| - /* stuff done on every call of the function */ |
795 |
| - funcctx = SRF_PERCALL_SETUP(); |
796 |
| - hash_seq = (HASH_SEQ_STATUS *) funcctx->user_fctx; |
| 789 | + /* hash table might be uninitialized */ |
| 790 | + if (prepared_queries) |
| 791 | + { |
| 792 | + HASH_SEQ_STATUS hash_seq; |
| 793 | + PreparedStatement *prep_stmt; |
797 | 794 |
|
798 |
| - /* if the hash table is uninitialized, we're done */ |
799 |
| - if (hash_seq == NULL) |
800 |
| - SRF_RETURN_DONE(funcctx); |
| 795 | + hash_seq_init(&hash_seq, prepared_queries); |
| 796 | + while ((prep_stmt = hash_seq_search(&hash_seq)) != NULL) |
| 797 | + { |
| 798 | + HeapTuple tuple; |
| 799 | + Datum values[5]; |
| 800 | + bool nulls[5]; |
801 | 801 |
|
802 |
| - prep_stmt = hash_seq_search(hash_seq); |
803 |
| - if (prep_stmt) |
804 |
| - { |
805 |
| - Datum result; |
806 |
| - HeapTuple tuple; |
807 |
| - Datum values[5]; |
808 |
| - bool nulls[5]; |
| 802 | + /* generate junk in short-term context */ |
| 803 | + MemoryContextSwitchTo(oldcontext); |
809 | 804 |
|
810 |
| - MemSet(nulls, 0, sizeof(nulls)); |
| 805 | + MemSet(nulls, 0, sizeof(nulls)); |
811 | 806 |
|
812 |
| - values[0] = DirectFunctionCall1(textin, |
| 807 | + values[0] = DirectFunctionCall1(textin, |
813 | 808 | CStringGetDatum(prep_stmt->stmt_name));
|
814 | 809 |
|
815 |
| - if (prep_stmt->plansource->query_string == NULL) |
816 |
| - nulls[1] = true; |
817 |
| - else |
818 |
| - values[1] = DirectFunctionCall1(textin, |
| 810 | + if (prep_stmt->plansource->query_string == NULL) |
| 811 | + nulls[1] = true; |
| 812 | + else |
| 813 | + values[1] = DirectFunctionCall1(textin, |
819 | 814 | CStringGetDatum(prep_stmt->plansource->query_string));
|
820 | 815 |
|
821 |
| - values[2] = TimestampTzGetDatum(prep_stmt->prepare_time); |
822 |
| - values[3] = build_regtype_array(prep_stmt->plansource->param_types, |
823 |
| - prep_stmt->plansource->num_params); |
824 |
| - values[4] = BoolGetDatum(prep_stmt->from_sql); |
| 816 | + values[2] = TimestampTzGetDatum(prep_stmt->prepare_time); |
| 817 | + values[3] = build_regtype_array(prep_stmt->plansource->param_types, |
| 818 | + prep_stmt->plansource->num_params); |
| 819 | + values[4] = BoolGetDatum(prep_stmt->from_sql); |
| 820 | + |
| 821 | + tuple = heap_form_tuple(tupdesc, values, nulls); |
825 | 822 |
|
826 |
| - tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls); |
827 |
| - result = HeapTupleGetDatum(tuple); |
828 |
| - SRF_RETURN_NEXT(funcctx, result); |
| 823 | + /* switch to appropriate context while storing the tuple */ |
| 824 | + MemoryContextSwitchTo(per_query_ctx); |
| 825 | + tuplestore_puttuple(tupstore, tuple); |
| 826 | + } |
829 | 827 | }
|
830 | 828 |
|
831 |
| - SRF_RETURN_DONE(funcctx); |
| 829 | + /* clean up and return the tuplestore */ |
| 830 | + tuplestore_donestoring(tupstore); |
| 831 | + |
| 832 | + MemoryContextSwitchTo(oldcontext); |
| 833 | + |
| 834 | + rsinfo->returnMode = SFRM_Materialize; |
| 835 | + rsinfo->setResult = tupstore; |
| 836 | + rsinfo->setDesc = tupdesc; |
| 837 | + |
| 838 | + return (Datum) 0; |
832 | 839 | }
|
833 | 840 |
|
834 | 841 | /*
|
|
0 commit comments