Inspect PostgreSQL procedural-language handlers and trust flags, run safe SQL/PL/pgSQL examples, prove a non-superuser cannot create C/untrusted routines, and govern optional PL runtimes as server-code dependencies.

Procedural Languages, Trusted vs Untrusted Code, and Operational Risk

Inspect PostgreSQL procedural-language handlers and trust flags, run safe SQL/PL/pgSQL examples, prove a non-superuser cannot create C/untrusted routines, and govern optional PL runtimes as server-code dependencies.

Intermediate → Advanced180–240 minutesPostgreSQL extensibility and federationPostgreSQL 18.6 baselineCore + PostgreSQL supplied extensions only in mandatory labsServiceHub disposable objects: ch19_*Admin/superuser required where postgres_fdw or untrusted-code boundaries are demonstratedFree local tooling; no managed service requiredLast reviewed: August 2026

Learning outcomes

Server-side functions are not all equal. SQL and PL/pgSQL operate inside PostgreSQL's normal SQL privilege environment. Optional languages can embed another runtime, and untrusted languages or C can potentially access operating-system resources or crash a backend. The language choice therefore changes the security and operational blast radius, not just syntax preference.

01

Inspect pg_language trust flags, handlers, validators, and USAGE privileges.

02

Differentiate built-in SQL, installed PL/pgSQL, optional procedural-language extensions, and native C/internal languages.

03

Prove a non-superuser can use a trusted PL only when schema/language privileges allow it.

04

Prove the same role cannot create an untrusted C-language function.

05

Create a governance checklist for optional runtimes, OS packages, shared libraries, patching, and target-major compatibility.

1. A procedural language is a handler registered in each database

sql · inspect language trust and handlers
SELECT l.lanname,       l.lanispl,       l.lanpltrusted,       pg_get_userbyid(l.lanowner) AS owner,       l.lanplcallfoid::regprocedure AS call_handler,       l.laninline::regprocedure AS inline_handler,       l.lanvalidator::regprocedure AS validator,       l.lanaclFROM pg_language AS lORDER BY l.lanname;

sql, internal, and c are language mechanisms known by the server. PL/pgSQL is normally installed in every new PostgreSQL database and appears as a trusted procedural language. A procedural language delegates execution to handler functions; those handlers are compiled server code even when the function body itself is Python, Perl, Tcl, or PL/pgSQL text.

2. Trusted means constrained to normal database privileges

A language marked trusted promises that code written in that language cannot bypass the database security environment to reach arbitrary external resources. PostgreSQL grants USAGE on trusted languages to PUBLIC by default, though administrators can revoke it. Untrusted languages can only be used by superusers to create functions.

sql · language privilege evidence
SELECT lanname,       lanpltrusted,       has_language_privilege(current_user, oid, 'USAGE') AS current_user_can_use,       lanaclFROM pg_languageWHERE lanname IN ('plpgsql','c','sql')ORDER BY lanname;

3. Safe mandatory lab: a trusted PL/pgSQL function

sql · create a sandbox writable by the application role
DROP SCHEMA IF EXISTS ch19_language_sandbox CASCADE;CREATE SCHEMA ch19_language_sandbox AUTHORIZATION servicehub_owner;GRANT USAGE, CREATE ON SCHEMA ch19_language_sandbox TO servicehub_app;
sql · application role creates a PL/pgSQL routine in its sandbox
SET ROLE servicehub_app;CREATE FUNCTION ch19_language_sandbox.normalize_region(p_region text)RETURNS textLANGUAGE plpgsqlIMMUTABLESTRICTAS $$BEGIN  RETURN lower(btrim(p_region));END$$;SELECT ch19_language_sandbox.normalize_region('  NORTH  ');RESET ROLE;

The function is allowed because the role has schema CREATE and language USAGE. Trusted does not mean “automatically safe logic”; SQL injection, search-path mistakes, expensive loops, and incorrect volatility labels still need review.

4. Same role, untrusted native C: rejected

sql · controlled privilege failure
SET ROLE servicehub_app;CREATE FUNCTION ch19_language_sandbox.native_demo()RETURNS integerAS '$libdir/not_a_real_library', 'demo'LANGUAGE c;-- Expected: permission denied for language cRESET ROLE;

The server rejects the non-superuser before arbitrary native code can be registered. C-language routines run inside PostgreSQL backend processes and are not sandboxed from the server process address space. A bug can crash a backend or corrupt memory; malicious code can be far worse.

5. Optional PLs are packaging + runtime + database dependencies

sql · discover optional procedural-language packages
SELECT name, default_version, installed_version, commentFROM pg_available_extensionsWHERE name LIKE 'pl%'ORDER BY name;SELECT name, version, superuser, trusted, requiresFROM pg_available_extension_versionsWHERE name LIKE 'pl%'ORDER BY name, version;

Standard PostgreSQL distributions can provide PL/Perl, PL/Tcl, and PL/Python components, but operating-system packages differ. PL/Python is untrusted (plpython3u): Python code is not sandboxed from the host, so only superusers can create such functions. Some language families provide both trusted and untrusted variants with different capabilities.

If an optional runtime is missing from pg_available_extensions, that is a packaging state—not a reason to create a fake language definition by hand.

6. Why CREATE LANGUAGE is not a routine application operation

sql · catalog-level language registration requires superuser
-- Conceptual/admin-only; do not execute in the mandatory lab:-- CREATE LANGUAGE custom_pl--   HANDLER custom_pl_call_handler--   INLINE custom_pl_inline_handler--   VALIDATOR custom_pl_validator;-- Current practice is normally:-- CREATE EXTENSION language_extension_name;

Registering or changing a procedural language itself requires superuser privilege because the handler is compiled server code. Contemporary language packages conventionally ship as extensions; installation adds the handler and language object together.

7. Untrusted code expands your patch and incident surface

Layer Examples What must be governed
Database language object plpgsql, plpython3u Trust flag, USAGE, owners, functions using it
PostgreSQL package plpython3 package/module Matches PG 18 package/ABI and patch level
Runtime Python/Perl/Tcl Runtime security fixes, modules, OS paths
Function code Application routine Owner, SECURITY DEFINER, inputs, side effects
Host/server process backend worker Crash blast radius, filesystem/network permissions

A language extension can be “successfully installed” while its runtime module imports fail at function execution. Upgrade and disaster-recovery testing must exercise representative routines, not only query catalog rows.

8. Wrong approach: treating the database as an unrestricted app server

Deliberately wrong approach

Moving HTTP calls, filesystem manipulation, shell execution, machine-learning runtimes, or long CPU jobs into untrusted database functions can tie database availability to external services and runtime crashes. Keep correctness-critical data logic close to transactions; keep broad integration/orchestration in application or worker services unless there is a carefully governed reason otherwise.

9. Governance query and cleanup

sql · find non-system routines by language
SELECT n.nspname,       p.proname,       l.lanname,       l.lanpltrusted,       pg_get_userbyid(p.proowner) AS owner,       p.prosecdefFROM pg_proc AS pJOIN pg_namespace AS n ON n.oid = p.pronamespaceJOIN pg_language AS l ON l.oid = p.prolangWHERE n.nspname NOT IN ('pg_catalog','information_schema')ORDER BY l.lanname, n.nspname, p.proname;DROP SCHEMA ch19_language_sandbox CASCADE;

Check your understanding

  1. What does pg_language.lanpltrusted mean?
  2. Why can servicehub_app create a PL/pgSQL function but not a C function in the same writable schema?
  3. Why does an optional PL create an OS/runtime dependency as well as a database dependency?
  4. Why is CREATE LANGUAGE superuser-only?
  5. When should integration-heavy logic remain outside PostgreSQL?
Review the answers

Trusted languages are believed not to escape ordinary SQL privilege boundaries. The role has schema CREATE and PL/pgSQL USAGE, but untrusted C can only be used by superusers. Optional PLs depend on matching PostgreSQL packages and external runtimes. Language registration wires compiled handlers into the server, so it is privileged. External I/O/orchestration usually belongs in application/worker services unless transaction-local database execution is intentionally required and governed.

Authoritative references

Extension, FDW, language, and server-code behavior is privilege-, package-, version-, and platform-sensitive. These PostgreSQL 18 primary sources define the mechanisms used here.

Keep knowledge open

Help the academy stay free and grow.

If these tutorials save you time, a small donation supports new lessons, technical review, diagrams, examples, and long-term maintenance.

ETHEthereum / ERC-20 only
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0

Send only assets compatible with the Ethereum/ERC-20 network. Do not send TRC-20/TRON assets.