Take the 2-minute tour ×
Stack Overflow is a question and answer site for professional and enthusiast programmers. It's 100% free, no registration required.

I'm migrating mysql database to postgres and ran into a roadblock regarding some basic xml functionality. In MySql I had stored procedures which would replace nodes inside xml document but cannot find any way to do so in postgres.

Here's my stored proc from mysql:

CREATE DEFINER=`root`@`localhost` PROCEDURE `SP_UpdateExamFilesXmlNode`(examFileId int, xPathExpression varchar(128), xmlNode longtext)
BEGIN
    DECLARE xmlData longtext;
    DECLARE newXmlData longtext;
    DECLARE xmlNodeCount int;
    SET xmlData = NULL;
    SELECT xml_data INTO xmlData FROM sonixhub.exam_files WHERE id = examFileId;

    IF xmlData IS NOT NULL THEN
        -- check if the node already exists and if it does then simply update it
        SET xmlNodeCount = ExtractValue(xmlData, CONCAT('count(',xPathExpression,')'));
        IF xmlNodeCount > 0 THEN
            SET newXmlData = UpdateXML(xmlData, xPathExpression, xmlNode);
        -- if node doesn't exist then we have to add it manually
        ELSE
            SET newXmlData = REPLACE(xmlData, '</ImageXmlData>', CONCAT(xmlNode, '</ImageXmlData>'));
        END IF;
        UPDATE sonixhub.exam_files SET xml_data = newXmlData WHERE id = examFileId;
    ELSE
        -- there is no xml found so create xml from scratch and insert the node
        SET xmlData = CONCAT('<ImageXmlData>',xmlNode,'</ImageXmlData>');
        UPDATE sonixhub.exam_files SET xml_data = xmlData WHERE id = examFileId;
    END IF;
END

Is there any way to replicate this functionality in postgres function instead of moving the logic into the application itself?

EDIT - FOUND A SOLUTION TO MY PROBLEM

I found a solution using mix of postgres xml and string formatting functions.

examFileId is used to find the row to be updated with the xml, change the code with your table info is the hardcoded root node in my case, but you can change it to whatever you like.

Here's how you call the function:

-- this adds <DicomTags> node to your xml value in the table, if <DicomTags> already exists then it's replaced by the one passed in
select update_exam_files_xml_node(1, '/ImageXmlData/DicomTags', '<DicomTags><DicomTag>xxx</DicomTag></DicomTags>');

-- this adds <Settings> node to your xml value in the table, if <Settings> already exists then it's replaced by the one passed in
select update_exam_files_xml_node(1, '/ImageXmlData/Settings', '<Settings>asdf</Settings>');


CREATE OR REPLACE FUNCTION update_exam_files_xml_node(examFileId int, xPathExpression text, xmlNode text)
  RETURNS void AS
$BODY$
    DECLARE xmlData xml;
    DECLARE newXmlData xml;
    DECLARE xmlNodeCount int;
    DECLARE replaceTag text;
BEGIN
    SELECT xml_data INTO xmlData FROM exam_files WHERE id = examFileId;

    IF xml_is_well_formed(xmlNode) = false THEN
        PERFORM add_error_log('update_exam_files_xml_node', 'xmlNode is not well formed xml');
        RETURN;
    END IF;

    IF xmlData IS NOT NULL THEN
        -- check if the node already exists and if it does then simply update it
        IF xmlexists(xPathExpression PASSING BY REF xml(xmlData)) = true THEN
            -- get the node name
            replaceTag := regexp_replace(xPathExpression, '/.*/', '');

            -- replace the existing node with the newly passed in node
            newXmlData := xml(regexp_replace(xmlData::text, '<'||replaceTag||'>.*</'||replaceTag||'>', xmlNode));

        -- if node doesn't exist then we have to add it manually
        ELSE
            newXmlData := xml(REPLACE(xmlData::text, '</ImageXmlData>', xmlNode||'</ImageXmlData>'));
        END IF;
        UPDATE exam_files SET xml_data = newXmlData WHERE id = examFileId;
    ELSE
        -- there is no xml found so create xml from scratch and insert the node
        xmlData := '<ImageXmlData>'||xmlNode||'</ImageXmlData>';
        UPDATE exam_files SET xml_data = xmlData WHERE id = examFileId;
    END IF;
END;
$BODY$
  LANGUAGE plpgsql VOLATILE
  COST 100;
share|improve this question
add comment

1 Answer

Glad you got a solution. To be honest, string formatting functions tend to be a bit difficult to reliably use inside SGML due to issues relating to hierarchies of languages. I.e. regexps have hard limits as to what they can do.

A better solution is likely to be to go a very different direction and write your functions in PL/PerlU or PL/Python, and use existing XML processing capabilities for those languages. This is likely to give you a better and more robust solution.

share|improve this answer
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.