33
33
* ENHANCEMENTS, OR MODIFICATIONS.
34
34
*
35
35
* IDENTIFICATION
36
- * $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.93 2005/10/15 02:49:49 momjian Exp $
36
+ * $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.94 2005/10/18 17:13:14 tgl Exp $
37
37
*
38
38
**********************************************************************/
39
39
@@ -119,9 +119,6 @@ Datum plperl_call_handler(PG_FUNCTION_ARGS);
119
119
Datum plperl_validator (PG_FUNCTION_ARGS );
120
120
void plperl_init (void );
121
121
122
- HV * plperl_spi_exec (char * query , int limit );
123
- SV * plperl_spi_query (char * );
124
-
125
122
static Datum plperl_func_handler (PG_FUNCTION_ARGS );
126
123
127
124
static Datum plperl_trigger_handler (PG_FUNCTION_ARGS );
@@ -131,8 +128,6 @@ static SV *plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc);
131
128
static void plperl_init_shared_libs (pTHX );
132
129
static HV * plperl_spi_execute_fetch_result (SPITupleTable * , int , int );
133
130
134
- void plperl_return_next (SV * );
135
-
136
131
/*
137
132
* This routine is a crock, and so is everyplace that calls it. The problem
138
133
* is that the cached form of plperl functions/queries is allocated permanently
@@ -1552,8 +1547,16 @@ plperl_spi_execute_fetch_result(SPITupleTable *tuptable, int processed,
1552
1547
}
1553
1548
1554
1549
1550
+ /*
1551
+ * Note: plperl_return_next is called both in Postgres and Perl contexts.
1552
+ * We report any errors in Postgres fashion (via ereport). If called in
1553
+ * Perl context, it is SPI.xs's responsibility to catch the error and
1554
+ * convert to a Perl error. We assume (perhaps without adequate justification)
1555
+ * that we need not abort the current transaction if the Perl code traps the
1556
+ * error.
1557
+ */
1555
1558
void
1556
- plperl_return_next (SV * sv )
1559
+ plperl_return_next (SV * sv )
1557
1560
{
1558
1561
plperl_proc_desc * prodesc = plperl_current_prodesc ;
1559
1562
FunctionCallInfo fcinfo = plperl_current_caller_info ;
@@ -1566,20 +1569,16 @@ plperl_return_next(SV * sv)
1566
1569
return ;
1567
1570
1568
1571
if (!prodesc -> fn_retisset )
1569
- {
1570
1572
ereport (ERROR ,
1571
1573
(errcode (ERRCODE_SYNTAX_ERROR ),
1572
1574
errmsg ("cannot use return_next in a non-SETOF function" )));
1573
- }
1574
1575
1575
1576
if (prodesc -> fn_retistuple &&
1576
1577
!(SvOK (sv ) && SvTYPE (sv ) == SVt_RV && SvTYPE (SvRV (sv )) == SVt_PVHV ))
1577
- {
1578
1578
ereport (ERROR ,
1579
1579
(errcode (ERRCODE_DATATYPE_MISMATCH ),
1580
1580
errmsg ("setof-composite-returning Perl function "
1581
1581
"must call return_next with reference to hash" )));
1582
- }
1583
1582
1584
1583
cxt = MemoryContextSwitchTo (rsi -> econtext -> ecxt_per_query_memory );
1585
1584
@@ -1637,17 +1636,23 @@ plperl_spi_query(char *query)
1637
1636
{
1638
1637
SV * cursor ;
1639
1638
1639
+ /*
1640
+ * Execute the query inside a sub-transaction, so we can cope with errors
1641
+ * sanely
1642
+ */
1640
1643
MemoryContext oldcontext = CurrentMemoryContext ;
1641
1644
ResourceOwner oldowner = CurrentResourceOwner ;
1642
1645
1643
1646
BeginInternalSubTransaction (NULL );
1647
+ /* Want to run inside function's memory context */
1644
1648
MemoryContextSwitchTo (oldcontext );
1645
1649
1646
1650
PG_TRY ();
1647
1651
{
1648
1652
void * plan ;
1649
1653
Portal portal = NULL ;
1650
1654
1655
+ /* Create a cursor for the query */
1651
1656
plan = SPI_prepare (query , 0 , NULL );
1652
1657
if (plan )
1653
1658
portal = SPI_cursor_open (NULL , plan , NULL , NULL , false);
@@ -1656,25 +1661,42 @@ plperl_spi_query(char *query)
1656
1661
else
1657
1662
cursor = newSV (0 );
1658
1663
1664
+ /* Commit the inner transaction, return to outer xact context */
1659
1665
ReleaseCurrentSubTransaction ();
1660
1666
MemoryContextSwitchTo (oldcontext );
1661
1667
CurrentResourceOwner = oldowner ;
1668
+
1669
+ /*
1670
+ * AtEOSubXact_SPI() should not have popped any SPI context, but just
1671
+ * in case it did, make sure we remain connected.
1672
+ */
1662
1673
SPI_restore_connection ();
1663
1674
}
1664
1675
PG_CATCH ();
1665
1676
{
1666
1677
ErrorData * edata ;
1667
1678
1679
+ /* Save error info */
1668
1680
MemoryContextSwitchTo (oldcontext );
1669
1681
edata = CopyErrorData ();
1670
1682
FlushErrorState ();
1671
1683
1684
+ /* Abort the inner transaction */
1672
1685
RollbackAndReleaseCurrentSubTransaction ();
1673
1686
MemoryContextSwitchTo (oldcontext );
1674
1687
CurrentResourceOwner = oldowner ;
1675
1688
1689
+ /*
1690
+ * If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
1691
+ * have left us in a disconnected state. We need this hack to return
1692
+ * to connected state.
1693
+ */
1676
1694
SPI_restore_connection ();
1695
+
1696
+ /* Punt the error to Perl */
1677
1697
croak ("%s" , edata -> message );
1698
+
1699
+ /* Can't get here, but keep compiler quiet */
1678
1700
return NULL ;
1679
1701
}
1680
1702
PG_END_TRY ();
@@ -1686,22 +1708,80 @@ plperl_spi_query(char *query)
1686
1708
SV *
1687
1709
plperl_spi_fetchrow (char * cursor )
1688
1710
{
1689
- SV * row = newSV (0 );
1690
- Portal p = SPI_cursor_find (cursor );
1711
+ SV * row ;
1712
+
1713
+ /*
1714
+ * Execute the FETCH inside a sub-transaction, so we can cope with errors
1715
+ * sanely
1716
+ */
1717
+ MemoryContext oldcontext = CurrentMemoryContext ;
1718
+ ResourceOwner oldowner = CurrentResourceOwner ;
1691
1719
1692
- if (!p )
1693
- return row ;
1720
+ BeginInternalSubTransaction (NULL );
1721
+ /* Want to run inside function's memory context */
1722
+ MemoryContextSwitchTo (oldcontext );
1694
1723
1695
- SPI_cursor_fetch (p , true, 1 );
1696
- if (SPI_processed == 0 )
1724
+ PG_TRY ();
1697
1725
{
1698
- SPI_cursor_close (p );
1699
- return row ;
1726
+ Portal p = SPI_cursor_find (cursor );
1727
+
1728
+ if (!p )
1729
+ row = newSV (0 );
1730
+ else
1731
+ {
1732
+ SPI_cursor_fetch (p , true, 1 );
1733
+ if (SPI_processed == 0 )
1734
+ {
1735
+ SPI_cursor_close (p );
1736
+ row = newSV (0 );
1737
+ }
1738
+ else
1739
+ {
1740
+ row = plperl_hash_from_tuple (SPI_tuptable -> vals [0 ],
1741
+ SPI_tuptable -> tupdesc );
1742
+ }
1743
+ SPI_freetuptable (SPI_tuptable );
1744
+ }
1745
+
1746
+ /* Commit the inner transaction, return to outer xact context */
1747
+ ReleaseCurrentSubTransaction ();
1748
+ MemoryContextSwitchTo (oldcontext );
1749
+ CurrentResourceOwner = oldowner ;
1750
+
1751
+ /*
1752
+ * AtEOSubXact_SPI() should not have popped any SPI context, but just
1753
+ * in case it did, make sure we remain connected.
1754
+ */
1755
+ SPI_restore_connection ();
1700
1756
}
1757
+ PG_CATCH ();
1758
+ {
1759
+ ErrorData * edata ;
1701
1760
1702
- row = plperl_hash_from_tuple (SPI_tuptable -> vals [0 ],
1703
- SPI_tuptable -> tupdesc );
1704
- SPI_freetuptable (SPI_tuptable );
1761
+ /* Save error info */
1762
+ MemoryContextSwitchTo (oldcontext );
1763
+ edata = CopyErrorData ();
1764
+ FlushErrorState ();
1765
+
1766
+ /* Abort the inner transaction */
1767
+ RollbackAndReleaseCurrentSubTransaction ();
1768
+ MemoryContextSwitchTo (oldcontext );
1769
+ CurrentResourceOwner = oldowner ;
1770
+
1771
+ /*
1772
+ * If AtEOSubXact_SPI() popped any SPI context of the subxact, it will
1773
+ * have left us in a disconnected state. We need this hack to return
1774
+ * to connected state.
1775
+ */
1776
+ SPI_restore_connection ();
1777
+
1778
+ /* Punt the error to Perl */
1779
+ croak ("%s" , edata -> message );
1780
+
1781
+ /* Can't get here, but keep compiler quiet */
1782
+ return NULL ;
1783
+ }
1784
+ PG_END_TRY ();
1705
1785
1706
1786
return row ;
1707
1787
}
0 commit comments