RFC Protocol Deep Dive
This comprehensive guide covers the RFC (Remote Function Call) protocol in ERPL. Learn how to read SAP tables, call function modules, and handle metadata like a pro.
RFC (Remote Function Call) is SAP's protocol for calling functions across system boundaries. It's the foundation of SAP integration and allows you to access SAP tables and execute function modules remotely.
RFC Architecture
Core RFC Functions
sap_read_table()
The primary function for reading SAP tables.
-- Basic table reading
SELECT * FROM sap_read_table('KNA1', MAX_ROWS => 100);
-- With field selection
SELECT KUNNR, NAME1, LAND1
FROM sap_read_table('KNA1', MAX_ROWS => 50);
-- With WHERE conditions
SELECT * FROM sap_read_table('VBAK', MAX_ROWS => 100)
WHERE ERDAT >= '2024-01-01';
Parameters
| Parameter | Type | Description | Example |
|---|---|---|---|
table_name | VARCHAR (positional) | SAP table name | 'KNA1' |
COLUMNS | LIST(VARCHAR) | Restrict to a subset of columns | COLUMNS => ['KUNNR', 'NAME1'] |
FILTER | VARCHAR | OpenSQL-style WHERE-fragment passed straight to RFC. Use for filters too complex to push down. | FILTER => 'LAND1 = ''DE''' |
MAX_ROWS | UINTEGER | Maximum rows to return | MAX_ROWS => 1000 |
THREADS | UINTEGER | Number of parallel RFC threads (default: 5) | THREADS => 4 |
READ_TABLE_FUNCTION | VARCHAR | Override the underlying RFC function module (e.g. /SAPDS/RFC_READ_TABLE2 for wide rows) | READ_TABLE_FUNCTION => '/SAPDS/RFC_READ_TABLE2' |
READ_TABLE_DELIMITER | VARCHAR | Delimiter RFC uses when packing rows. Override for tables whose values may contain whitespace. | `READ_TABLE_DELIMITER => ' |
SECRET | VARCHAR | Name of the DuckDB secret to authenticate with | SECRET => 'erp_prod' |
A regular SQL WHERE clause is pushed down to RFC automatically — you don't need the FILTER named parameter for simple predicates. COLUMNS is inferred from the SELECT list:
SELECT KUNNR, NAME1 FROM sap_read_table('KNA1') WHERE LAND1 = 'DE';
Reach for the named parameters when you need precise control over what RFC receives — for example, complex multi-table filters that DuckDB's optimizer can't safely lower.
RFC's underlying RFC_READ_TABLE has no server-side sort. Apply ORDER BY in DuckDB after the read; it executes client-side.
sap_rfc_invoke()
Call SAP function modules and BAPIs. Input parameters are passed as positional struct arguments after the function name — one struct per import / changing parameter the BAPI expects. DuckDB struct syntax ({'KEY': value, ...}) lets you express nested structures and tables naturally.
-- Simple function module with one import struct
SELECT trim(ECHOTEXT)
FROM sap_rfc_invoke('STFC_CONNECTION', {'REQUTEXT': 'Hello'});
-- BAPI returning a sub-table — pick it via path
SELECT *
FROM sap_rfc_invoke(
'BAPI_FLIGHT_GETLIST',
{'AIRLINE': 'LH', 'DESTINATION_FROM': {'AIRPORTID': 'FRA'}},
path => '/FLIGHT_LIST'
);
-- BAPI with nested import + path selection
SELECT *
FROM sap_rfc_invoke(
'BAPI_FLIGHT_GETDETAIL',
{'AIRLINEID': 'LH', 'CONNECTIONID': '0400', 'FLIGHTDATE': '2016-11-18'::DATE}
);
Parameters
| Parameter | Type | Description | Example |
|---|---|---|---|
function_name | VARCHAR (positional) | SAP function module / BAPI name | 'BAPI_FLIGHT_GETLIST' |
parameters... | STRUCT (positional varargs) | One struct per RFC import/changing parameter | {'AIRLINE': 'LH'} |
path | VARCHAR (named) | Optional path into the response structure — picks a sub-table of the result | path => '/FLIGHT_LIST' |
secret | VARCHAR (named) | Name of the DuckDB secret to authenticate with | secret => 'erp_prod' |
sap_rfc_describe_function()
Get detailed information about RFC function parameters.
-- Describe function parameters
SELECT * FROM sap_rfc_describe_function('BAPI_FLIGHT_GETLIST');
-- Get parameter details
SELECT
name,
import[1].name AS import_params,
export[1].name AS export_params
FROM sap_rfc_describe_function('STFC_CONNECTION');
sap_describe_fields()
Get table structure and field information. The result columns are pos, is_key, field,
text, sap_type, length, decimals, check_table, ref_table, ref_field, and
language.
-- Get table structure
SELECT * FROM sap_describe_fields('KNA1');
-- Get field details for a specific table
SELECT
field,
text,
sap_type,
length,
decimals
FROM sap_describe_fields('VBAK');
sap_rfc_authorizations()
Document which RFC function module each ERPL function calls — useful when requesting RFC
authorizations from SAP Basis. Returns extension, duckdb_function, rfc_function_module,
invocation, and purpose.
-- Every RFC module ERPL may invoke
SELECT * FROM sap_rfc_authorizations();
-- Just the distinct module names used by the RFC extension
SELECT DISTINCT rfc_function_module
FROM sap_rfc_authorizations()
WHERE extension = 'erpl_rfc';
sap_rfc_show_function()
List all available RFC functions.
-- List all RFC functions
SELECT * FROM sap_rfc_show_function();
sap_rfc_show_groups()
List all RFC function groups.
-- List all function groups
SELECT * FROM sap_rfc_show_groups();
sap_show_tables()
List all available SAP tables.
-- List all tables
SELECT * FROM sap_show_tables();
Common SAP Tables
Master Data Tables
-- Customer Master Data
SELECT
KUNNR AS customer_number,
NAME1 AS customer_name,
LAND1 AS country,
REGIO AS region,
ORT01 AS city
FROM sap_read_table('KNA1', MAX_ROWS => 1000)
WHERE LAND1 = 'DE';
-- Material Master Data
SELECT
MATNR AS material_number,
MTART AS material_type,
MEINS AS base_unit,
MATKL AS material_group
FROM sap_read_table('MARA', MAX_ROWS => 1000)
WHERE MTART = 'FERT'; -- Finished goods
-- Vendor Master Data
SELECT
LIFNR AS vendor_number,
NAME1 AS vendor_name,
LAND1 AS country,
REGIO AS region
FROM sap_read_table('LFA1', MAX_ROWS => 1000);
Transaction Data Tables
-- Sales Document Header
SELECT
VBELN AS sales_document,
ERDAT AS document_date,
KUNNR AS customer,
NETWR AS net_value,
WAERK AS currency
FROM sap_read_table('VBAK', MAX_ROWS => 1000)
WHERE ERDAT >= '2024-01-01';
-- Sales Document Items
SELECT
VBELN AS sales_document,
POSNR AS item_number,
MATNR AS material,
KWMENG AS quantity,
NETWR AS net_value
FROM sap_read_table('VBAP', MAX_ROWS => 1000)
WHERE VBELN IN ('0000000001', '0000000002');
Financial Tables
-- Accounting Document Header
SELECT
BUKRS AS company_code,
BELNR AS document_number,
GJAHR AS fiscal_year,
BLART AS document_type,
BUDAT AS posting_date
FROM sap_read_table('BKPF', MAX_ROWS => 1000)
WHERE GJAHR = '2024';
-- Accounting Document Items
SELECT
BUKRS AS company_code,
BELNR AS document_number,
GJAHR AS fiscal_year,
BUZEI AS line_item,
HKONT AS gl_account,
DMBTR AS amount_in_local_currency
FROM sap_read_table('BSEG', MAX_ROWS => 1000)
WHERE HKONT LIKE '1%'; -- Asset accounts
Advanced RFC Techniques
Metadata Handling
-- Get table structure
SELECT * FROM sap_describe_fields('KNA1');
-- Get function module parameters
SELECT * FROM sap_rfc_describe_function('BAPI_CUSTOMER_GETDETAIL');
-- List all available functions
SELECT * FROM sap_rfc_show_function();
-- List function groups
SELECT * FROM sap_rfc_show_groups();
-- List all tables
SELECT * FROM sap_show_tables();
Batch Processing
-- Process multiple tables in one call
SELECT
'KNA1' AS table_name,
COUNT(*) AS record_count
FROM sap_read_table('KNA1', MAX_ROWS => 10000)
UNION ALL
SELECT
'VBAK' AS table_name,
COUNT(*) AS record_count
FROM sap_read_table('VBAK', MAX_ROWS => 10000);
Reading function output and errors
sap_rfc_invoke returns the function module's actual export/table parameters as columns —
there are no synthetic return_code/error_message columns. For RFC_SYSTEM_INFO, the export
parameter is the RFCSI_EXPORT struct, so you read fields out of it:
-- System info is returned as the RFCSI_EXPORT struct
SELECT
RFCSI_EXPORT.rfcsysid AS system_id,
RFCSI_EXPORT.rfchost AS host,
RFCSI_EXPORT.rfcsaprl AS release
FROM sap_rfc_invoke('RFC_SYSTEM_INFO');
For BAPIs, error status lives in the BAPI's own RETURN table (BAPIRET2). Select it with the
path parameter and filter on its TYPE column ('E' = error, 'A' = abort):
SELECT *
FROM sap_rfc_invoke('BAPI_SOME_METHOD', {'KEY': 'VALUE'}, path => '/RETURN')
WHERE TYPE IN ('E', 'A');
Performance Optimization
Mounting SAP tables as a catalog
ERPL caches and reuses RFC connections per secret automatically — there is no separate
connection-pool object to configure. If you want SAP tables to appear as a browsable schema
(so you can write sap.KNA1 instead of sap_read_table('KNA1')), ATTACH a sap_rfc
catalog. Use the TABLES option to limit which tables are exposed:
-- Mount selected SAP tables as the "sap" catalog
ATTACH '' AS sap (TYPE sap_rfc, SECRET 'my_sap', TABLES 'KNA1,VBAK,MARA');
-- Query them like local tables (still read over RFC, with push-down)
SELECT KUNNR, NAME1 FROM sap.KNA1 WHERE LAND1 = 'DE';
SELECT * FROM sap.VBAK LIMIT 100;
Parallel Processing
-- Read multiple tables in parallel
SELECT 'KNA1' AS table_name, COUNT(*) AS count
FROM sap_read_table('KNA1', MAX_ROWS => 10000)
UNION ALL
SELECT 'VBAK' AS table_name, COUNT(*) AS count
FROM sap_read_table('VBAK', MAX_ROWS => 10000)
UNION ALL
SELECT 'MARA' AS table_name, COUNT(*) AS count
FROM sap_read_table('MARA', MAX_ROWS => 10000);
Real-World Examples
Daily Sales Report
-- Extract daily sales data
WITH daily_sales AS (
SELECT
ERDAT AS sales_date,
COUNT(*) AS order_count,
SUM(NETWR) AS total_value,
AVG(NETWR) AS avg_order_value
FROM sap_read_table('VBAK', MAX_ROWS => 100000)
WHERE ERDAT >= CURRENT_DATE - INTERVAL '30' DAY
GROUP BY ERDAT
)
SELECT
sales_date,
order_count,
total_value,
avg_order_value,
ROUND(avg_order_value, 2) AS avg_order_value_rounded
FROM daily_sales
ORDER BY sales_date DESC;
Customer Analysis
-- Analyze customer distribution by country
SELECT
LAND1 AS country,
COUNT(*) AS customer_count,
COUNT(*) * 100.0 / SUM(COUNT(*)) OVER() AS percentage
FROM sap_read_table('KNA1', MAX_ROWS => 100000)
GROUP BY LAND1
ORDER BY customer_count DESC
LIMIT 10;
Material Master Analysis
-- Analyze material types and groups
SELECT
MTART AS material_type,
MATKL AS material_group,
COUNT(*) AS material_count
FROM sap_read_table('MARA', MAX_ROWS => 100000)
GROUP BY MTART, MATKL
ORDER BY material_count DESC
LIMIT 20;
Troubleshooting
Common Issues
Table Not Found
-- Check table name (case-sensitive)
-- Verify table exists in your SAP system
SELECT * FROM sap_read_table('KNA1', MAX_ROWS => 1);
Permission Denied
-- Verify RFC access rights by calling a low-privilege function
SELECT * FROM sap_rfc_invoke('RFC_SYSTEM_INFO');
Connection Timeout
Configure timeouts via the sap_rfc secret options when creating the secret:
CREATE SECRET my_sap (
TYPE sap_rfc,
ASHOST 'your-host',
SYSNR '00',
USER 'username',
PASSWD 'password',
CLIENT '100',
LANG 'EN'
);
Debugging Tips
-- Ping the SAP system (pragma — no result rows; raises on failure)
PRAGMA sap_rfc_ping;
-- Monitor query performance
EXPLAIN SELECT * FROM sap_read_table('KNA1', MAX_ROWS => 100);
-- Smoke-test the RFC pipeline
SELECT * FROM sap_rfc_invoke('RFC_SYSTEM_INFO');
For SAP Experts
For SAP Basis administrators and developers: the binary RFC transport, performance characteristics, and security model. Expand the internals below.
RFC protocol internals — transport, performance, security
RFC Protocol Details
ERPL uses the proprietary SAP NetWeaver RFC SDK, which speaks SAP's binary RFC protocol (CPIC over TCP/IP) — the same library SAP GUI and other SAP tools use. Characteristics:
- Connection: direct application-server logon (
ASHOST+SYSNR) via the SAP Gateway (port33NN, whereNNis the instance number), or message-server/load-balanced logon (MSHOST+GROUP) - Protocol: binary RFC over TCP/IP (not HTTP)
- Authentication: user/password, or SNC (Secure Network Communication) for certificate-based, encrypted logon
- Transport security: SNC, or an SSH tunnel on untrusted networks
- Unicode: full Unicode support
All of this is configured through the DuckDB sap_rfc secret — the same secret used by the
BICS and ODP extensions, which ride on the same RFC
transport.
Performance Considerations
- Connection reuse: ERPL caches RFC connections per secret — keep work in one session
- Row cap: bound large reads with
MAX_ROWS, and tuneTHREADSfor your network - Field selection: use
COLUMNS => [...](or rely on SELECT-list projection push-down) to reduce data transfer - Filter push-down: a plain
WHEREclause is lowered to RFC; useFILTERfor predicates the optimizer can't push
Security Best Practices
- Network Security: Use VPN or secure network connections
- Authentication: Use strong passwords or certificates
- Authorization: Implement proper RFC user roles
- Audit Logging: Enable RFC audit logs in SAP
Next Steps
🚀 Ready for More?
- BICS Protocol Guide - Execute SAP BW queries
- ODP Protocol Guide - Delta replication
- Function Reference - Complete API docs
🔧 Advanced Topics
- RFC Metadata Guide - Discover SAP functions and tables
- Performance Tuning - Optimize RFC calls
- Real-World Use Cases - Complete scenarios
💡 Examples
- ERPL Examples - More real-world RFC examples
- Integration with Python - Use RFC data with Pandas
Need help? Check our troubleshooting guide or browse more examples.