Skip to content

Instantly share code, notes, and snippets.

@gmcharlt
Last active December 17, 2015 06:49
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gmcharlt/5568683 to your computer and use it in GitHub Desktop.
Save gmcharlt/5568683 to your computer and use it in GitHub Desktop.
PL/Perl replacement for PostgresSQL 9.2+ xpath() function that preserves namespace declarations
CREATE OR REPLACE FUNCTION perl_xpath (xpath TEXT, doc XML, ns TEXT[][]) RETURNS TEXT[] AS $func$
use strict;
use XML::LibXML;
my $xpath = shift;
my $doc = shift;
my $ns_string = shift || '';
my %ns_list = $ns_string =~ m/\{([^{,]+),([^}]+)\}/g;
# The following approach uses the older XML::LibXML 1.69 / XML::LibXSLT 1.68
# methods of parsing XML documents and stylesheets, in the hopes of broader
# compatibility with distributions
my $parser = eval { $_SHARED{'_xslt_process'}{parsers}{xml} || XML::LibXML->new() };
return undef if ($@);
# Cache the XML parser, if we do not already have one
$_SHARED{'_xslt_process'}{parsers}{xml} = $parser
unless ($_SHARED{'_xslt_process'}{parsers}{xml});
# Look for a cached version of the doc, or parse it if none
my $dom = eval { $_SHARED{'_xslt_process'}{docs}{$doc} || $parser->parse_string($doc) };
return undef if ($@);
# Cache the parsed XML doc, if already there
$_SHARED{'_xslt_process'}{docs}{$doc} = $dom
unless ($_SHARED{'_xslt_process'}{docs}{$doc});
# Register the requested namespaces
$dom->documentElement->setNamespace( $ns_list{$_} => $_ ) for ( keys %ns_list );
my $xpc = XML::LibXML::XPathContext->new;
$xpc->registerNs( $ns_list{$_} => $_ ) for ( keys %ns_list );
# Gather and return nodes
my @nodes = $dom->findnodes($xpath);
return [ map { $_->toStringC14N } @nodes ];
$func$ LANGUAGE PLPERLU;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment