2.6
Release date: 2026-06-23
Enhancements
- Add
tokenoption to USER MAPPING: A newtokenoption allows Bearer token authentication (RFC 6750) for SPARQL endpoints that use token-based access control instead of HTTP Basic Authentication. When set,rdf_fdwsends anAuthorization: Bearer <token>HTTP header with every request.
CREATE USER MAPPING FOR postgres
SERVER myserver OPTIONS (token 'mysecrettoken');
- Add
max_response_sizeoption to FOREIGN SERVERS: A newmax_response_sizeserver option caps the HTTP response body size in bytes. If the endpoint sends more data than the configured limit, the query is aborted with an error before PostgreSQL allocates further memory. The default is0(unlimited). This is particularly useful when connecting to public or untrusted SPARQL endpoints to prevent runaway memory consumption from unexpectedly large result sets.
-- Reject responses larger than 100 MB
CREATE SERVER dbpedia
FOREIGN DATA WRAPPER rdf_fdw
OPTIONS (endpoint 'https://dbpedia.org/sparql', max_response_size '104857600');
Pushdown now handles
NOTboolean expressions: this allowsNOT BOUND(),NOT isIRI(),NOT isLiteral(), andNOT isNumeric()to be translated to SPARQLFILTER (!...)and executed at the remote endpoint instead of being evaluated locally after fetching. This reduces the number of rows transferred from the endpoint when these conditions are selective.Add support for BCE dates and timestamps in rdfnode casts: PostgreSQL represents BCE years using BC notation and has no year 0, while XML Schema uses astronomical year numbering (
0000 = 1 BC,-0001 = 2 BC, etc.). Add conversion logic for date,timestamp, andtimestamptzcasts to ensure correct round-tripping of BCE values between PostgreSQL and RDF literals.
Deprecations
- Native PostgreSQL column types in
FOREIGN TABLEdefinitions: Declaring foreign table columns with standard PostgreSQL types (e.g.text,int,date,timestamp) is deprecated. Therdfnodetype must be used instead, as it correctly represents the full RDF term — including IRIs, language tags, and XSD datatypes — and is required by all SPARQL functions. Existing tables continue to work but will emit aWARNINGon every query listing the affected columns. The column optionsexpression,language,literal_type, andnodetypeare also deprecated, as they are only meaningful for native-typed columns. Support for native column types will be removed in a future release.
Bug Fixes
Fixed
CURLE_WRITE_ERROR(23)on SPARQL UPDATE and unrecognisedContent-Typeresponses:HeaderCallbackFunctionwas returning0for anyContent-Typenot recognised as an RDF or SPARQL XML type. Returning0from a libcurl write callback signals an abort and triggersCURLE_WRITE_ERROR(23), causing endpoints that respond withapplication/json— such as QLever on successful UPDATEs or Fuseki on HTTP 400 errors — to produce a spurious “unable to connect” error instead of the real outcome. Fixed by returningrealsizefor unrecognisedContent-Typeheaders.SPARQL UPDATE response body is now discarded: For
INSERT,DELETE, andUPDATEoperations only the HTTP status code matters; the response body is irrelevant. Previously the full body was accumulated in memory, which wasted resources for endpoints that return large JSON documents on success or failure. It now setschunk.max_size = 0to bypass themax_response_sizelimit.HTTP error response bodies are now truncated in log and error messages: Error bodies included in
errdetail()and server-logelog()calls were previously unbounded. A misconfigured proxy returning a large HTML error page would be written verbatim into the PostgreSQL server log. Error bodies are now truncated to 512 bytes (RDF_FDW_MAX_ERROR_BODY) before being included in any message.Encoded credentials are no longer written to server logs via libcurl verbose output: When
client_min_messagesis set toDEBUG3, libcurl’s verbose output is now routed through PostgreSQL’selog(DEBUG3)via a customCURLOPT_DEBUGFUNCTIONcallback (CurlDebugCallback) instead of being written directly tostderr. Crucially, any outgoing or incoming HTTP header whose name matchesAuthorization:orProxy-Authorization:has its value replaced with[REDACTED]before being logged, so Bearer tokens, Basic auth credentials (base64-encoded), and proxy passwords never appear in plaintext in the PostgreSQL server log.rdfnodes with invalid trailing content now triggers an error: rdfnodes with trailing content, which can contain malicious SPARQL instructions, were being silently truncated. While this avoided any attempt at SPARQL injection, it could lead to confusion, since the user was never aware of this truncation. The function
rdfnode_innow raises an error if such content is detected.Fixes trailing whitespace truncation in rdfnode:
rdfnodegenerated from strings containing trailing whitespaces were being truncated. This is now fixed.Blank nodes in FILTER expressions: Blank nodes in FILTER expressions are now passed as blank nodes; previously, they were cast as literals. This allows triplestores that deviate from the SPARQL specification to handle blank nodes according to their own implementation.
Fixed wrong ordering of string and language-tagged literals in
sparql.min()/sparql.max(): The aggregate comparator (rdfnode_cmp_for_aggregate) was usingvarstr_cmpwithDEFAULT_COLLATION_OIDfor lexical comparisons of plain literals,xsd:stringliterals, and language-tagged literals. On databases whose locale is notC, this produces locale-dependent ordering (e.g., accented characters may be folded or reordered), which violates SPARQL 1.1 §17.3 — string ordering must follow Unicode codepoint order. Both calls have been replaced withstrcmp.Fixed negative
xsd:durationround-trip: Casting a negative PostgreSQLintervaltordfnodeproduces a valid XSD duration with a leading-(e.g.,-P1Y2M), but casting that value back tointervalfailed withinvalid input syntax for type interval: "-P1Y2M"because PostgreSQL’sinterval_indoes not accept the XSD negative-duration notation. A helperxsd_duration_to_pg_intervalnow strips the leading-, parses the positive form, and negates the result.Fixed trailing zeros in fractional seconds of
xsd:durationoutput:interval_to_rdfnodewas always formatting sub-second values with six decimal places (e.g.,PT1M0.500000S). It now strips trailing zeros, producing the canonical lexical form (e.g.,PT1M0.5S).Fixed
LANGMATCHESto correctly returnfalsewhen the language tag is empty: Previously,LANGMATCHES("", "*")returnedtrue, violating SPARQL 1.1 and RFC 4647 basic filtering semantics, which require"*"to match only non-empty language tags. This affected queries filtering untagged literals viaFILTER LANGMATCHES(LANG(?x), "*"), which would incorrectly include untagged literals in results.Fixed
SECONDS()return type and value: thetextoverload was incorrectly declared as returningintinstead ofnumeric, causing fractional seconds to be truncated. Additionally, timezone-awarexsd:dateTimevalues were returning the wrong seconds value due to implicit session timezone adjustment during timestamp casting; the lexical form is now cast totimestamptzto preserve the correct component.Fixed
TZ()to reject invalid timezone offsets: when given anxsd:dateTimeliteral with an out-of-range timezone offset (e.g.,"2020-12-01T08:00:00+25:00"^^xsd:dateTime), the function would previously extract and return the offset as-is without validation. It now raises an error for offsets outside the valid XSD range of±14:00.Fixed session-timezone-dependent
xsd:dateTimecomparisons: Allrdfnodecomparison operators (=,<>,<,<=,>,>=) and the aggregate comparator were calling PostgreSQL’stimestamptz_in()for everyxsd:dateTimeliteral, including timezone-naive ones. This caused two bugs: (1) comparing a timezone-naive literal with a timezone-aware one could returntrueinstead of the correct SPARQL result of incomparable (false), depending on the session timezone; (2) the old timezone-detection heuristic usedstrpbrk(lex, "+-"), which matched the-separators in the date portion (e.g.,2025-04-25) and incorrectly classified every well-formed dateTime as timezone-aware. The fix introduces adatetime_has_tz()helper that restricts the search forZ,+, and-to the time portion of the lexical form (after theTor space separator). Timezone-aware pairs are compared viatimestamptz_in()/timestamptz_cmp_internal(); timezone-naive pairs viatimestamp_in()without any session-timezone influence; and mixed pairs returnfalse(incomparable) per XSD §3.2.7.4 and SPARQL 1.1 §17.3 (Operator Mapping).Fixed incomplete
xsd:timesupport inrdfnodecomparison operators:rdfnode_eq(=,<>) was missingxsd:timevalue-space comparison entirely, falling back to lexical comparison. The ordering operators (<,<=,>,>=) hadxsd:timesupport but lacked timezone handling, unconditionally usingtime_in()for all literals. The fix addsxsd:timeequality tordfnode_eqand introduces atime_has_tz()helper that detects timezone designators (Z,+,-) in the time lexical form; timezone-aware pairs are now compared viatimetz_in()/timetz_eq()(with UTC normalisation); timezone-naive pairs viatime_in()without session-timezone influence; and mixed pairs returnfalse(incomparable).Fixed NaN‑NaN comparison bug: Previously the expression
?a = ?breturned true when both operands were the literal"NaN"^^xsd:double(or"NaN"^^xsd:float). According to SPARQL 1.1 (which follows the XSD 1.1 definition of xsd:double and the IEEE‑754 rules), aNaNvalue is never equal to any value, including anotherNaN; all numeric comparison operators must therefore evaluate tofalseforNaNoperands. See XPath and XQuery Functions and Operators 3.1 at 4.3.1 op:numeric-equal, 4.3.2 op:numeric-less-than, and 4.3.3 op:numeric-greater-than.Add missing boolean-boolean comparison: This adds
xsd:booleansupport tordfnodecomparison operators (=,<>,<,<=,>,>=), using PostgreSQL’sboolin/booleq/boollt/boolle/boolgt/boolgefunctions.Fixed invalid XSD lexical forms for infinity in
rdfnodecast functions:float4_to_rdfnode,float8_to_rdfnode, andnumeric_to_rdfnodewere producing"Infinity"and"-Infinity"as lexical forms, which are not valid XSD representations. XSD 1.1 Part 2 §3.3.4/§3.3.5 definesINFand-INFas the only valid lexical forms for positive and negative infinity inxsd:floatandxsd:double.Reject blank nodes in
IRI,STRDT, andSTRLANG: These functions require a simple literal as their first argument. Previously, passing a blank node (e.g._:b1or the result ofsparql.bnode()) silently produced an invalid RDF term such as_:b1@enor a blank node with an attached datatype. Now these functions raise an error with a descriptive message when given a blank node.Fixed
cstring_to_rdfliteral()incorrectly treating literal content that resembles a blank node label (_:...) or IRI (<...>) as an actual blank node/IRI term, causingrdfnode_in()to strip quotes from literals such as"_:b1"and"<http://example.org>", makingsparql.isblank()andsparql.isiri()return incorrect results for these literals.Fixed SPARQL pushdown for IRI-valued constants: rdfnodes containing IRIs, when placed in the left side in an operation, were being incorrectly rendered as quoted literals, e.g.
WHERE '<http://example.org/property>'::rdfnode = sparql.iri(p)was being pushed down asRemote Filter: (("http://example.org/property" = IRI(?p)))instead ofRemote Filter: ((<http://example.org/property> = IRI(?p))). TheT_OpExprpath inDeparseExprnow skips the rdfnode normalisation when dealing with IRIs and blank nodes.Fixes isBlank result for NULL inputs: the SQL declaration of isBlank was defined as
STRICTwhich led function calls withNULLinputs to directly returnNULL, but isBlank should returnfalseinstead, as its companionsisIRI,isLiteral, andisNumericalready do.Fixed
CONTAINS()result for incompatible arguments:CONTAINS()now correctly returnsNULLinstead offalsewhen argument types are incompatible, e.g. a plain literal paired with a language-tagged literal such as@en). It now also now correctly returnsNULLinstead of true when either argument carries a non-string datatype (i.e. anything other thanxsd:stringorrdf:langString). The function was previously operating on the raw lexical form regardless of the datatype, which produced incorrect results for custom-typed literals such as"123"^^<http://example.com/int>.Fixed incorrect XSD data type for numeric arguments in
ROUND(): The functionROUND()was incorrectly returningxsd:doublefor PGnumericarguments, and it now returnsxsd:decimalas also defined in other numeric functions, such asCEIL(),FLOOR(), orABS().Fixed TIMEZONE() consistently for invalid XSD types:
TIMEZONE()now consistently raises an error when the argument carries a non-xsd:dateTime datatype (e.g.xsd:string) instead of returningNULL. A wrong datatype is a type error, not an unknown value, and should be surfaced explicitly.Fixed
DATATYPE()handling of malformed literals:DATATYPE()now correctly raises an error for syntactically malformed literals passed astext(e.g."foo"^<xsd:string>with a single caret, or"foo"^^xsd:string>with an unbalanced angle bracket) by delegating validation to the rdfnode type cast before processing.Fixed
rdfnodeinput parser:rdfnode_innow correctly rejects typed literals with an unterminated IRI in the datatype annotation (e.g."foo"^^<xsd:stringmissing the closing>). Previously the parser silently dropped the malformed datatype annotation and returned a plain literal ("foo"), which was incorrect and could mask data quality issues. Inputs tordfnode_inthat are not a (syntactically) valid RDF literal, IRI, or blank node now raiseERRCODE_INVALID_TEXT_REPRESENTATIONinstead of being silently coerced. IRIs and blank nodes are returned as-is without unnecessary literal parsing.Accept ill-typed literals: Ill-typed literals are no longer rejected by
rdfnode_in– a literal is ill-typed if its lexical form falls outside the lexical space of its datatype (e.g.,"foo"^^xsd:int). While semantically inconsistent under RDF 1.1, these literals are syntactically valid RDF. Validation is now deferred to the underlying triplestore rather than being enforced at the database level.Fixed
REPLACE()to use regex semantics:REPLACE()now usesregexp_replacefor the 3-argument form, consistent with the 4-argument form and the SPARQL 1.1 spec, which defines REPLACE() in terms of XPath regex in all forms. It now accepts empty string patterns, which are valid per XPath regex semantics and match at every position in the input string.Fixed
timetztordfnodecast: the functiontimetz_to_rdfnoderelied entirely on PostgreSQL’stimetz_outto convert the strings, which led to a minute truncation when the timestamp’s minutes was:00. It now produces well-formedxsd:timetimezone offsets (+02:00instead of+02).Fixed small memory leaks in
ExecuteSPARQL: the buffer returned bycurl_easy_escapewas not being released withcurl_free, and curl handles (curl_easy_cleanup,curl_slist_free_all,curl_global_cleanup) were not called on HTTP and network error paths.
2.5
Release date: 2026-04-20
Enhancements
Add ‘request_timeout’ to FOREIGN SERVERS: This option sets the maximum time in seconds allowed for a complete HTTP request (connect + transfer).
0disables the limit (default). Unlikeconnect_timeout, this applies to the entire duration of the request, including data transfer.Add ‘readonly’ option to FOREIGN SERVERS and FOREIGN TABLES: A new boolean
readonlyoption can be set on bothSERVERandFOREIGN TABLEto preventINSERT,UPDATE, andDELETEoperations before they reach the SPARQL endpoint. When set at the server level it applies to all foreign tables backed by that server; when set at the table level it overrides the server setting, allowing a read-only server to have individual writable tables (or a read-write server to have individual protected tables). PostgreSQL will report the correct updatability viaIsForeignRelUpdatable, so client tools that inspectpg_catalogcan also discover whether a table allows DML.
-- Mark the entire server as read-only
ALTER SERVER fuseki OPTIONS (ADD readonly 'true');
-- Override at the table level: this table is still writable despite the server setting
ALTER FOREIGN TABLE ft OPTIONS (ADD readonly 'false');
Minor Changes
- Fixed
rdfReScanForeignScanto reset the row index, making it correct for any future plan shape where PostgreSQL omits the Materialize node above a foreign scan.
2.4
Release date: 2026-02-14
Breaking Changes
Proxy authentication credentials moved to USER MAPPING: For improved security, proxy authentication credentials (
proxy_userandproxy_password) must now be specified inUSER MAPPINGinstead ofSERVERoptions. This change prevents proxy passwords from being visible to all users withUSAGEprivilege on the foreign server, as PostgreSQL automatically hidesUSER MAPPINGpasswords from non-owners.Before (v2.3):
sql CREATE SERVER myserver FOREIGN DATA WRAPPER rdf_fdw OPTIONS ( endpoint 'http://fuseki:3030/sparql', http_proxy 'http://proxy:3128', proxy_user 'proxyuser', proxy_user_password 'proxypass' );After (v2.4): ```sql CREATE SERVER myserver FOREIGN DATA WRAPPER rdf_fdw OPTIONS ( endpoint ‘http://fuseki:3030/sparql’, http_proxy ‘http://proxy:3128’ – Proxy URL stays in SERVER );
CREATE USER MAPPING FOR myuser SERVER myserver OPTIONS ( user ‘admin’, password ‘secret’, proxy_user ‘proxyuser’, – Moved from SERVER proxy_password ‘proxypass’ – Moved from SERVER (previously ‘proxy_user_password’ ) ); ```
Migration: Existing servers using
proxy_userorproxy_user_passwordin SERVER options will need to be updated. Remove these options from the server and add them to user mappings instead. The validator will reject the old options with a clear error message.
2.3
Release date: 2026-01-28
Enhancements
Removed librdf dependency: The extension no longer depends on the Redland RDF Library (
librdf). RDF/XML parsing forDESCRIBEqueries is now performed using onlylibxml2, reducing external dependencies and improving maintainability.Support to data modification queries: Introduced per-row SPARQL
INSERT DATA,DELETE DATA, andUPDATEoperations via thesparql_update_patternoption on foreign tables. The addition of thebatch_sizeparameter enables efficient batching of these operations, significantly improving performance for bulk modifications.Enhanced error handling in
ExecuteSPARQL: Improved the handling of HTTP errors by capturing and displaying detailed error messages from the SPARQL endpoint. This includes disablingCURLOPT_FAILONERRORto capture response bodies for HTTP errors, adding specific error messages for common HTTP status codes (e.g., 400, 401, 404, 500).
Breaking Changes
The
sparql.describe()function no longer accepts theraw_literalparameter. Users who need to extract plain text from literals can use thesparql.lex()function instead. This simplifies the function signature and encourages a more consistent approach to handling RDF literals.The
sparql.regexfunction is no longer available for local evaluation in PostgreSQL, as it turned out that its semantics cannot be reliably reproduced locally. Queries relying on local evaluation ofsparql.regexwill now fail with an error.
Minor Changes
- The
log_sparqloption for foreign tables now defaults tofalse. SinceINSERT,UPDATE, andDELETEoperations can generate large SPARQL queries, enabling this option by default could result in unnecessarily large log entries.
Bug Fixes
Fixed URIs and blank nodes being incorrectly handled as plain literals in
InsertRetrievedData()(used byrdf_fdw_clone_table()). When cloning foreign tables withrdfnodecolumns, URIs were being treated as plain text instead of being wrapped in angle brackets (e.g.,<http://example.com>), and blank nodes were missing the_:prefix. The fix now checks the target column type: forrdfnodecolumns, it properly formats URIs with<>and blank nodes with_:, while for standard PostgreSQL types it extracts only the raw content. This ensures correct round-trip behavior when materializing RDF data into ordinary tables.Fixed failure when extracting content from empty RDF literals in
InsertRetrievedData(). The code was incorrectly usingxmlNodeDump()to serialize RDF term nodes, which included XML tags in the output (e.g.,<literal datatype="...">value</literal>). This causedrdf_fdw_clone_table()calls on columns containing empty literals (e.g.,"",""@en, or""^^xsd:string) to fail. Now usesxmlNodeGetContent()to extract only the text content without XML tags, properly handling empty and non-empty RDF term nodes alike.Fixed critical bug in all date comparison operators (>, >=, <, <=, =, !=) between
rdfnodeand PostgreSQLdatetypes. Previously, the code used the wrong macro (PG_GETARG_INT16) to retrieve date arguments, causing all local date comparisons to fail or behave unpredictably. Now uses the correctPG_GETARG_DATEADTmacro and adds robust error handling for non-date values. This ensures correct filtering and pushdown of date conditions, especially for queries likeWHERE col > '1900-01-30'::date.Fixed a bug in
sparql.hours(rdfnode),sparql.minutes(rdfnode), andsparql.seconds(rdfnode)where RDF nodes containing onlyxsd:timewere not handled correctly. These functions now properly extract the hour, minute, and second from bothxsd:dateTimeandxsd:timetyped RDF nodes, instead of assuming all values arexsd:dateTime.Updated
sparql.concat()function to comply with SPARQL 1.1: now returns a simple literal (no language tag or datatype) when concatenating literals with conflicting language tags or incompatible datatypes, instead of throwing an error.Empty RDF literals incorrectly returned as
NULL: Fixed a bug where empty RDF literals (e.g.,"",""@en, or""^^xsd:string) were being incorrectly returned as SQL NULL values instead of empty strings. The issue occurred inCreateTuple()wherexmlNodeGetContent()returns NULL for empty XML elements. The fix now properly distinguishes between empty RDF terms (valid empty strings) and unbound SPARQL variables (SQL NULL) by checking the XML element type (<literal>,<uri>, or<bnode>).Literals with escaped quotes corrupted during round-trip: Fixed a critical bug where literals containing escaped quotes (e.g.,
"\"WWU\""@en) were being corrupted when retrieved from SPARQL results. TheCreateTuple()function was incorrectly reparsing raw XML text content as RDF syntax, causing quote characters to be interpreted as literal delimiters rather than data. This has been fixed by constructingrdfnodevalues directly from raw lexical content and manually appending language tags or datatypes.Control characters not properly escaped in SPARQL statements: Control characters (newlines, tabs, carriage returns) in literals are now properly escaped in SPARQL INSERT and DELETE statements, ensuring correct round-trip behavior.
DESCRIBEqueries with large result sets caused severe performance degradation: Fixed a critical performance issue wheresparql.describe()queries returning large result sets took too long to complete. The root cause was inDescribeIRI(), which usedlibrdf_parser_parse_string_into_model()to build a complete in-memory RDF graph model before extracting triples. This has been replaced withlibrdf_parser_parse_string_as_stream(), which processes RDF/XML on-the-fly without constructing an intermediate graph database. This dramatically reduces memory footprint and bringsDESCRIBEquery performance in line with SELECT queries handling similar-sized result sets.UCASE and LCASE functions failed to convert multibyte UTF-8 characters: Fixed a bug where
sparql.ucase()andsparql.lcase()were only converting ASCII characters (a-z, A-Z) and leaving multibyte UTF-8 characters unchanged. For example,sparql.ucase('"Westfälische Wilhelms-Universität Münster"@de')would incorrectly return"WESTFäLISCHE WILHELMS-UNIVERSITäT MüNSTER"@deinstead of properly uppercasing ä, ö, ü to Ä, Ö, Ü. The functions now use PostgreSQL’s built-inupper()andlower()functions with proper collation support, correctly handling all Unicode characters according to the database’s locale settings.SUBSTR function failed for multibyte UTF-8 characters and empty inputs: Fixed a bug in
sparql.substr()where it incorrectly counted bytes instead of characters for multibyte UTF-8 strings, leading to truncated results. Additionally, the function now correctly returns an empty string when the start position is beyond the string length, instead of throwing an error.Malformed SPARQL with
FILTER(NULL)in older PostgreSQL versions: Fixed a bug in PostgreSQL 9.5 where NULL constants in expressions were being deparsed as the literal string “NULL” instead of returning aNULLpointer. This caused malformed SPARQL queries likeFILTER(NULL)to be generated. The fix ensures that NULL constants are properly handled by preventing pushdown of such expressions.Fixed unexpected behavior in
sparql.bnode()where passing an already-formatted blank node (e.g.,_:bnode1) would return SQLNULLinstead of handling it gracefully. The function now implements idempotent behavior: if the input is already a blank node, it returns it as-is.Fixed a bug where local filters (
WHEREclauses) that were pushed down were also being evaluated locally, which was just redundant. Now, only conditions that cannot be pushed down are evaluated locally by PostgreSQL, ensuring correct results for non-pushable foreign tables.
2.2.0
Release date: 2025-12-07
Enhancements
SPARQL aggregate functions:
Added support for SPARQL-style aggregate functions
sparql.sum,sparql.avg,sparql.min,sparql.max,sparql.group_concat, andsparql.samplein SQL queries. These functions are now fully implemented and can be used for local aggregation in PostgreSQL, improving compatibility with SPARQL semantics and enabling more expressive analytics on RDF data. Aggregate pushdown to the SPARQL endpoint is not yet supported; all aggregation is performed locally by PostgreSQL.Enhanced version information:
The
rdf_fdw_version()function now returns a comprehensive version string that includes PostgreSQL version, compiler information, and all dependency versions (libxml, librdf, libcurl) in a single formatted output. A newrdf_fdw_settings()function provides extended dependency information including optional components like SSL, zlib, libSSH, and nghttp2. Therdf_fdw_settingsview parses this extended information into a table format for convenient programmatic access to individual component versions.Improved EXPLAIN diagnostics for Foreign Scan nodes:
EXPLAIN output now include rdf_fdw-specific details for each Foreign Scan node, showing which SQL clauses are pushed down to the remote SPARQL endpoint. The plan displays lines such as
Pushdown: enabled/disabled,Remote Filter,Remote Sort Key,Remote Limit, andRemote Select, making it easier to understand query translation and pushdown behavior.
Bug Fixes
Fix
lex()to correctly handle doubled-quote escapes in literals.RDF literals containing double-quotes were being truncated, leading to invalid results of
sparql.lex()or any function that depends on it. This has now being fixed.
2.1.0
Release date: 2025-09-25
Enhancements
SPARQL Prefix Management:
rdf_fdwnow includes built-in support for SPARQL prefix management via a structured catalog and helper functions. This feature introduces:prefix_contexts: Named groups of reusable SPARQL prefixes.prefixes: Individual prefix → URI mappings associated with a context.A suite of SQL functions to add, update, delete, and override contexts and prefixes. This enhancement simplifies query generation, reduces redundancy, and makes SPARQL integration more maintainable — especially when dealing with multiple endpoints or vocabularies.
Add
enable_xml_hugeserver option to support large XML result setsThe new
enable_xml_hugeoption allows users to enable libxml2’sXML_PARSE_HUGEflag when parsing SPARQL result sets. This is useful for consuming large XML responses that exceed libxml2’s default safety limits. By default, this option is disabled for security reasons.
Bug Fixes
NULL RDFNodes:
This fixes a bug that could potentially lead the system to crash if the triple store returns a
NULLvalue for an specific node (edge case).xmlParseMemory errors
An issue has been resolved where the system could potentially crash if libxml2 failed to parse a given XML string (for example, due to an out-of-memory error). A check has been added to detect and prevent such crashes.
xmlDocGetRootElement failing to get the root element
A safeguard has been introduced to handle cases where xmlDocGetRootElement fails to parse the root node of an XML document. Instead of proceeding with an empty set, an error message is now displayed to inform the user of the issue.
2.0.0
Release date: 2025-05-22
This is a major release of rdf_fdw featuring substantial new features, improved standards compliance, and important infrastructure enhancements. Backward compatibility is preserved, but users are encouraged to review the new features and updated behavior.
Enhancements
- PostgreSQL 9.5 and 18 (in beta1 as of this release) support.
- SPARQL
DESCRIBEquery support via the newsparql.describe()support function. - New
rdfnodedata type, enabling:- Representation of RDF literals and IRIs with full lexical fidelity.
- Precise round-tripping of SPARQL values within SQL.
- Equality and order comparisons with native PostgreSQL types (e.g.,
int,float,text,date).
- SPARQL 1.1 Built-in Function Support via SQL queries.
- Functional Forms:
bound,COALESCEandsameTerm.
- Functions on RDF Terms:
isIRI,isBlank,isLiteral,isNumeric,str,lang,datatype,IRI,BNODE,STRDT,STRLANG,UUID, andSTRUUID.
- Functions on Strings:
STRLEN,SUBSTR,UCASE,LCASE,STRSTARTS,STRENDS,CONTAINS,STRBEFORE,STRAFTER,ENCODE_FOR_URI,CONCAT,langMatches,REGEX, andREPLACE.
- Functions on Numerics:
abs,round,ceil,floor, andRAND.
- Functions on Dates and Times:
year,month,day,hours,minutes,seconds,timezone, andtz.
- Hash Functions:
md5.
These functions are translated to their SPARQL equivalents when pushed down to the foreign data wrapper.
- Functional Forms:
Minor Changes
- The
FOREIGN TABLEoptionlog_sparqlis now set totrue, if omitted. If you don’t want to log the SPARQL query, consider usingALTER FOREIGN TABLEto disable this option manually, e.g.
ALTER FOREIGN TABLE myrdftable OPTIONS (ADD log_sparql 'false');
Bug Fixes
Query cancellation support:
Added
CHECK_FOR_INTERRUPTS()in key execution points to allow PostgreSQL backends to detect user-initiated query cancellations (e.g., Ctrl+C), improving long-running query handling.
External Libraries
- Added a new dependency: Redland RDF Library (
librdf) — used for parsing and serializing RDF data, and supportingDESCRIBEqueries.
1.3.0
Release date: 2024-09-30
Enhancements
- Support for PostgreSQL 9.6, 10, and 17: This adds support for the long EOL’d PostgreSQL versions 9.6 and 10. It is definitely discouraged to use these unsupported versions, but in case you’re for whatever reason unable to perform an upgrade you can now use the
rdf_fdw.
1.2.0
Release date: 2024-05-22
Enhancements
- Pushdown support for Math, String and Date/Time functions:
abs,ceil,floor,roundlength,upper,lower,starts_with,substring,md5extract(year from x),extract(month from x),extract(year from x),extract(hour from x),extract(minute from x),extract(second from x)date_part(year, x),date_part('month',x),date_part('year', x),date_part('hour', x),date_part('minute', x),date_part('second', x)
When used in the
WHEREclause these functions will be translated to their correspondent SPARQLFILTERexpressions.
Bug Fixes
- Bug fix for WHERE conditions with “inverted” arguments - that is, value in the left side (T_Const) and column in the right side (T_Var): This fixes a bug that led the pushdown of
WHEREcondiditions containing “inverted” arguments to fail, e.g"foo" = column,42 > column. Now the order of T_Const and T_Var in the arguments is irrelevant.
1.1.0
Release date: 2024-04-10
Enhancements
USER MAPPINGsupport: This feature defines a mapping of a PostgreSQL user to an user in the target triplestore -userandpassword, so that the user can be authenticated. Requested by Matt Goldberg.Pushdown suuport for
pattern matching operatorsLIKEandILIKE: these operators are now translated into SPARQLFILTERexpressions asREGEXand pushed down.Enables usage of non-pushable data types in
FOREIGN TABLEcolumns: This disables a check that raised an excepetion when data types that cannot be pushed down were used. This includes non-standard data types, such asgeometryorgeographyfrom PostGIS.
Bug Fixes
Empty SPARQL
SELECTclause: This fixes a bug that led some SPARQL queries to be created without any variable in theSELECTclause. We now useSELECT *in case the planner cannot identify which nodes should be retrieved, which can be a bit inefficent if we’re dealing with many columns, but it is much better than an error message.Missing schema from foreign tables in
rdf_fdw_clone_tablecalls: This fixes a bug that led therdf_fdw_clone_tableprocedure to always look for the givenFOREIGN TABLEin thepublicschema.xmlDoc* memory leak: The xml document containing the resulst sets from the SPARQL queries wasn’t beeing freed after the query was complete. This led to a memory leak that could potentially cause a system crash once all available memory was consumed by the orphan documents - which is an issue for rather modest server settings that execute mutliple queries in the same session. Reported by Filipe Pessoa.
1.0.0
Release date: 2024-03-15
Initial release.
Support for PostgreSQL 11, 12, 13, 14, 15 and 16.
Main Features
- Pushdown:
LIMIT,ORDER BY,DISTINCT,WHEREwith several data types and operators - Table copy: This introduces the procedure
rdf_fdw_clone_table(), that is designed to copy data from aFOREIGN TABLEinto an ordinaryTABLE. It provides the possibility to retrieve the data set in batches. - Proxy Support for
SERVER: quite handy feature in case the PostgreSQL and SPARQL endpoint servers are in different networks and can only communicate through a proxy.