Обсуждение: difficulty extracting variable-sized field on triggered row
Hello, I have been trying to write a trigger in C operating on each row that would (as a first step) copy a field of type TEXT into a text* variable. I think that I've got the SPI_connect, SPI_fnumber, and SPI_getbinval calls correct, but this gives me a Datum. How do I convert a Datum to a text*? The following is the best I could do, and it segfaults ... Thank you, Glen Mabey if( CALLED_AS_TRIGGER( fcinfo ) ) { elog(INFO, "unlink_file was called as a trigger." ); trigdata = (TriggerData *) fcinfo->context; if( ( ret = SPI_connect() ) != SPI_OK_CONNECT ) { elog(INFO, "SPI_connect returned %d", ret); } file_name_colnumber = SPI_fnumber( trigdata->tg_relation->rd_att, "file_name" ); if( file_name_colnumber == SPI_ERROR_NOATTRIBUTE ) { elog(INFO, "SPI_fnumber returned SPI_ERROR_NOATTRIBUTE" ); } SPI_getbinval( trigdata->tg_trigtuple, trigdata->tg_relation->rd_att, file_name_colnumber, isnull ); relative_filename_t = PG_DETOAST_DATUM( relative_filename_datum ); if( SPI_result == SPI_ERROR_NOATTRIBUTE ) { elog(INFO, "SPI_fnumber returned SPI_ERROR_NOATTRIBUTE" ); } if( ( ret = SPI_finish() ) != SPI_OK_FINISH ) { elog(INFO, "SPI_finish returned %d", ret); } }
On 04/12/2007, Glen W. Mabey <Glen.Mabey@swri.org> wrote: > Hello, > > I have been trying to write a trigger in C operating on each row > that would (as a first step) copy a field of type TEXT into a text* > variable. > > I think that I've got the SPI_connect, SPI_fnumber, and SPI_getbinval > calls correct, but this gives me a Datum. > > How do I convert a Datum to a text*? > use macro DatumGetPointer(datum) Pavel
On Tue, Dec 04, 2007 at 09:10:21AM -0600, Pavel Stehule wrote: > use macro DatumGetPointer(datum) When I do that, I get the following compiler warning: warning: assignment from incompatible pointer type which is what originally motivated me to look for another means, which led me to use PG_DETOAST_DATUM. Using DatumGetPointer also seg faults ... Is there somewhere that I am not adequately checking for an error? Thanks, Glen TriggerData *trigdata; Datum relative_filename_datum; text *relative_filename_t; int file_name_colnumber, ret; bool *isnull; if( CALLED_AS_TRIGGER( fcinfo ) ) { elog(INFO, "unlink_file was called as a trigger." ); trigdata = (TriggerData *) fcinfo->context; if( ( ret = SPI_connect() ) != SPI_OK_CONNECT ) { elog(INFO, "SPI_connect returned %d", ret); } file_name_colnumber = SPI_fnumber( trigdata->tg_relation->rd_att, "file_name" ); if( file_name_colnumber == SPI_ERROR_NOATTRIBUTE ) { elog(INFO, "SPI_fnumber returned SPI_ERROR_NOATTRIBUTE" ); } SPI_getbinval( trigdata->tg_trigtuple, trigdata->tg_relation->rd_att, file_name_colnumber, isnull ); if( SPI_result == SPI_ERROR_NOATTRIBUTE ) { elog(INFO, "SPI_fnumber returned SPI_ERROR_NOATTRIBUTE" ); } relative_filename_t = PG_DETOAST_DATUM( relative_filename_datum ); if( ( ret = SPI_finish() ) != SPI_OK_FINISH ) { elog(INFO, "SPI_finish returned %d", ret); } }
Glen W. Mabey escribió: > TriggerData *trigdata; > Datum relative_filename_datum; > text *relative_filename_t; > int file_name_colnumber, ret; > bool *isnull; This is wrong. Try bool isnull; and later: SPI_getbinval( trigdata->tg_trigtuple, trigdata->tg_relation->rd_att, file_name_colnumber, &isnull ); -- Alvaro Herrera http://www.amazon.com/gp/registry/5ZYLFMCVHXC "No hay ausente sin culpa ni presente sin disculpa" (Prov. francés)
"Glen W. Mabey" <Glen.Mabey@swri.org> writes: > Is there somewhere that I am not adequately checking for an error? 1. You're passing SPI_getbinval an uninitialized bool pointer. 2. You're discarding its result, which you need. 3. You're not checking for a null, and the error check you do have is wrong/redundant. 4. Use DatumGetTextP(), not DatumGetPointer nor PG_DETOAST_DATUM. regards, tom lane
On Tue, Dec 04, 2007 at 09:26:46AM -0600, Glen W. Mabey wrote: > On Tue, Dec 04, 2007 at 09:10:21AM -0600, Pavel Stehule wrote: > > use macro DatumGetPointer(datum) > > When I do that, I get the following compiler warning: > > warning: assignment from incompatible pointer type Well yes, you're getting a (void*) which you need to cast to a (text*). It's the generic cast for indirect types. > Using DatumGetPointer also seg faults ... What actually segfault? Not DatumGetPointer() since that's just a cast. > Is there somewhere that I am not adequately checking for an error? Depends what you're doing. Remember, a (text*) is a varlena object so you need to use the VAR_DATA,VAR_LEN macros to access it. The string is not nul-terminated for example. If you want to get a plain C-string, use textout or DatumToCString or something similar. However, DETOAST is the right step, because if you don't do that you might be handed a toasted string. Have a nice day, -- Martijn van Oosterhout <kleptog@svana.org> http://svana.org/kleptog/ > Those who make peaceful revolution impossible will make violent revolution inevitable. > -- John F Kennedy
Вложения
On Tue, Dec 04, 2007 at 09:53:37AM -0600, Tom Lane wrote: > "Glen W. Mabey" <Glen.Mabey@swri.org> writes: > > Is there somewhere that I am not adequately checking for an error? > > 1. You're passing SPI_getbinval an uninitialized bool pointer. Doh! > 2. You're discarding its result, which you need. Ah yes, a change I made while trying to debug things. > 3. You're not checking for a null, and the error check you do have > is wrong/redundant. The field is constrained to be NOT NULL, so I wasn't worried about checking that, but I don't see how the error check is wrong, according to the description of SPI_getbinval at http://www.postgresql.org/docs/8.2/interactive/spi-spi-getbinval.html What is then the appropriate way to check for failure of SPI_getbinval? > 4. Use DatumGetTextP(), not DatumGetPointer nor PG_DETOAST_DATUM. Searching for DatumGetTextP from the search text box at the top of www.postgresql.org yields no hits, and a google search on DatumGetTextP does not seem to turnip [sic] any direct documentation on this function. Is there somewhere in the docs that I should have found this (and other useful) functions? Thank you very much, Glen Mabey
"Glen W. Mabey" <Glen.Mabey@swri.org> writes: > On Tue, Dec 04, 2007 at 09:53:37AM -0600, Tom Lane wrote: >> 3. You're not checking for a null, and the error check you do have >> is wrong/redundant. > The field is constrained to be NOT NULL, so I wasn't worried about > checking that, but I don't see how the error check is wrong, Well, a check on the isnull state is a good idea anyway IMHO. This code has no way of being sure that a NOT NULL constraint exists, and dumping core if it's not there isn't my idea of being robust. What I didn't like about the error test was that it assumed that SPI_ERROR_NOATTRIBUTE is and always will be the only possible error code from SPI_getbinval. I'd test for SPI_result != 0 instead. (The "redundant" comment came from the thought that it's probably pointless to be making this test at all, considering that you just got the column number from SPI_fnumber on the same tupdesc.) >> 4. Use DatumGetTextP(), not DatumGetPointer nor PG_DETOAST_DATUM. > Is there somewhere in the docs that I should have found this (and other > useful) functions? fmgr.h, perhaps, or by looking at existing code that does more or less what you want to do. Postgres does not follow the model that you are supposed to look only at the SGML docs to find out how to do server-side programming. We are open source and one of the biggest benefits of that is that you can (and should) look at the source code to learn. regards, tom lane
On Tue, Dec 04, 2007 at 10:11:04AM -0600, Glen W. Mabey wrote: > Searching for DatumGetTextP from the search text box at the top of > www.postgresql.org yields no hits, and a google search on DatumGetTextP > does not seem to turnip [sic] any direct documentation on this function. > > Is there somewhere in the docs that I should have found this (and other > useful) functions? I find the following tool helpful: http://doxygen.postgresql.org/ Sam