1"use strict";(self.webpackChunkerpl_docusaurus=self.webpackChunkerpl_docusaurus||[]).push([[3183],{25748:(e,s,n)=>{n.d(s,{A:()=>r});const r=n.p+"assets/images/bics_tui_analysis-649b4f7be73a8b532535305a1a1600ac.png"},26724:(e,s,n)=>{n.d(s,{A:()=>r});const r=n.p+"assets/images/bics_tui_props_kf-576ed44f529ef702855f8f8dcb9b3b27.png"},28453:(e,s,n)=>{n.d(s,{R:()=>t,x:()=>a});var r=n(96540);const i={},c=r.createContext(i);function t(e){const s=r.useContext(c);return r.useMemo(function(){return"function"==typeof e?e(s):{...s,...e}},[s,e])}function a(e){let s;return s=e.disableParentContext?"function"==typeof e.components?e.components(i):e.components||i:t(e.components),r.createElement(c.Provider,{value:s},e.children)}},75951:(e,s,n)=>{n.r(s),n.d(s,{assets:()=>d,contentTitle:()=>a,default:()=>h,frontMatter:()=>t,metadata:()=>r,toc:()=>l});const r=JSON.parse('{"id":"erpl/bics","title":"BICS Protocol Deep Dive","description":"Complete guide to BICS (BI Consumer Services) in ERPL - execute SAP BW queries over RFC, build cross-tabs, extract result sets, and track lineage","source":"@site/docs/erpl/bics.md","sourceDirName":"erpl","slug":"/erpl/bics","permalink":"/docs/erpl/bics","draft":false,"unlisted":false,"editUrl":"https://github.com/datazoode/erpl-landingpage/tree/main/docs/erpl/bics.md","tags":[],"version":"current","sidebarPosition":3,"frontMatter":{"sidebar_position":3,"title":"BICS Protocol Deep Dive","description":"Complete guide to BICS (BI Consumer Services) in ERPL - execute SAP BW queries over RFC, build cross-tabs, extract result sets, and track lineage","keywords":["erpl","bics","bi consumer services","sap bw","queries","cubes","lineage","metadata","duckdb","rfc"]},"sidebar":"docs","previous":{"title":"RFC Protocol Deep Dive","permalink":"/docs/erpl/rfc"},"next":{"title":"ODP Protocol Deep Dive","permalink":"/docs/erpl/odp"}}');var i=n(74848),c=n(28453);const t={sidebar_position:3,title:"BICS Protocol Deep Dive",description:"Complete guide to BICS (BI Consumer Services) in ERPL - execute SAP BW queries over RFC, build cross-tabs, extract result sets, and track lineage",keywords:["erpl","bics","bi consumer services","sap bw","queries","cubes","lineage","metadata","duckdb","rfc"]},a="BICS Protocol Deep Dive",d={},l=[{value:"How it works",id:"how-it-works",level:2},{value:"Connecting",id:"connecting",level:2},{value:"Discovering objects",id:"discovering-objects",level:2},{value:"Querying a cube (OLAP cross-tabs)",id:"querying-a-cube-olap-cross-tabs",level:2},{value:"Axes, not column projection",id:"axes-not-column-projection",level:3},{value:"Filtering members",id:"filtering-members",level:3},{value:"Hierarchies",id:"hierarchies",level:2},{value:"Describing structures",id:"describing-structures",level:2},{value:"Presentation properties (AO-style)",id:"presentation-properties-ao-style",level:2},{value:"Interactive: <code>bics-tui</code>",id:"interactive-bics-tui",level:3},{value:"Logon",id:"logon",level:4},{value:"Analysis screen",id:"analysis-screen",level:4},{value:"Properties \u2014 Characteristic on axis",id:"properties--characteristic-on-axis",level:4},{value:"Properties \u2014 Key figure",id:"properties--key-figure",level:4},{value:"Run it",id:"run-it",level:4},{value:"Lineage Tracking (for SAP BW experts)",id:"lineage-tracking-for-sap-bw-experts",level:2},{value:"Complete lineage as edges",id:"complete-lineage-as-edges",level:3},{value:"Forward trace from a source",id:"forward-trace-from-a-source",level:3},{value:"Lineage of a single query",id:"lineage-of-a-single-query",level:3},{value:"Lineage as a JSON graph",id:"lineage-as-a-json-graph",level:3},{value:"Metadata mining",id:"metadata-mining",level:3},{value:"Field-level & cross-reference metadata",id:"field-level--cross-reference-metadata",level:3},{value:"Real-world examples",id:"real-world-examples",level:2},{value:"Net value by product",id:"net-value-by-product",level:3},{value:"Combine BW data with ERP master data",id:"combine-bw-data-with-erp-master-data",level:3},{value:"Performance & good practice",id:"performance--good-practice",level:2},{value:"Troubleshooting",id:"troubleshooting",level:2},{value:"Enable tracing",id:"enable-tracing",level:3},{value:"For SAP BW experts: the BICS interface in detail",id:"for-sap-bw-experts-the-bics-interface-in-detail",level:2},{value:"The handle lifecycle",id:"the-handle-lifecycle",level:4},{value:"The state model and <code>state_version</code>
1",id:"the-state-model-and-state_version",level:4},{value:"Decoding the result set",id:"decoding-the-result-set",level:4},{value:"Variables",id:"variables",level:4},{value:"Metadata and lineage",id:"metadata-and-lineage",level:4},{value:"Security",id:"security",level:4},{value:"Next steps",id:"next-steps",level:2},{value:"\ud83d\ude80 Ready for More?",id:"-ready-for-more",level:3},{value:"\ud83d\udd27 Advanced Topics",id:"-advanced-topics",level:3},{value:"\ud83d\udca1 Examples",id:"-examples",level:3}];function o(e){const s={a:"a",admonition:"admonition",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",h4:"h4",header:"header",hr:"hr",img:"img",li:"li",mermaid:"mermaid",ol:"ol",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,c.R)(),...e.components},{Details:r}=s;return r||function(e,s){throw new Error("Expected "+(s?"component":"object")+" `"+e+"` to be defined: you likely forgot to import, pass, or provide it.")}("Details",!0),(0,i.jsxs)(i.Fragment,{children:[(0,i.jsx)(s.header,{children:(0,i.jsx)(s.h1,{id:"bics-protocol-deep-dive",children:"BICS Protocol Deep Dive"})}),"\n",(0,i.jsx)(s.p,{children:"This guide covers the BICS (BI Consumer Services) interface in ERPL: how to discover\nInfoProviders and BEx queries, build OLAP cross-tabs, fetch result sets, and trace\nend-to-end lineage from source tables to BEx queries \u2014 all from SQL in DuckDB."}),"\n",(0,i.jsx)(s.admonition,{title:"What is BICS?",type:"info",children:(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"BICS (BI Consumer Services)"})," is the SAP interface that tools like SAP Analysis for Office\nuse to consume SAP Business Warehouse (BW) data. ERPL talks to BICS through the ",(0,i.jsx)(s.strong,{children:"SAP\nNetWeaver RFC SDK"})," \u2014 it calls the ",(0,i.jsx)(s.code,{children:"BICS_PROV_*"})," RFC function modules to open a query,\nmanipulate its OLAP state, and read result sets. It is ",(0,i.jsx)(s.strong,{children:"not"})," a separate HTTP/web protocol:\nthere is no BICS port, no XML payload, and no SAML. Connectivity and authentication are\nexactly the same as for the ",(0,i.jsx)(s.a,{href:"/docs/erpl/rfc",children:"RFC protocol"})," \u2014 a DuckDB secret of type\n",(0,i.jsx)(s.code,{children:"sap_rfc"}),"."]})}),"\n",(0,i.jsx)(s.h2,{id:"how-it-works",children:"How it works"}),"\n",(0,i.jsx)(s.mermaid,{value:'graph TB\n subgraph "Your Environment"\n A[DuckDB]\n B["ERPL (erpl_bics + erpl_rfc)"]\n end\n\n subgraph "SAP BW System"\n direction TB\n C["RFC SDK<br/>(sap_rfc secret)"]\n D["BICS_PROV_* function modules<br/>OPEN \xb7 SET_STATE \xb7 GET_RESULT_SET"]\n E["RFC_READ_TABLE<br/>RSRREPDIR \xb7 RSDCUBE \xb7 RSTRAN \xb7 ROOSFIELD \u2026"]\n F[InfoProviders / Cubes / BEx Queries]\n G[DataSources / Transformations]\n end\n\n A --\x3e B\n B --\x3e|RFC| C\n C --\x3e|query session| D\n C --\x3e|metadata & lineage| E\n D --\x3e F\n E --\x3e F\n E --\x3e G\n\n style B fill:#fff100,stroke:#000,stroke-width:2px\n style C fill:#e1f5fe,stroke:#01579b,stroke-width:2px'}),"\n",(0,i.jsxs)(s.p,{children:["ERPL uses ",(0,i.jsx)(s.strong,{children:"two RFC paths"}),":"]}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Query execution"})," drives an OLAP query through the BICS provider modules\n(",(0,i.jsx)(s.code,{children:"BICS_CONS_CREATE_DATA_AREA"}),", ",(0,i.jsx)(s.code,{children:"BICS_PROV_OPEN"}),", ",(0,i.jsx)(s.code,{children:"BICS_PROV_GET_INITIAL_STATE"}),",\n",(0,i.jsx)(s.code,{children:"BICS_PROV_SET_STATE"}),", ",(0,i.jsx)(s.code,{children:"BICS_PROV_GET_RESULT_SET"}),", ",(0,i.jsx)(s.code,{children:"BICS_PROV_CLOSE"}),"). These power\n",(0,i.jsx)(s.code,{children:"sap_bics_begin"})," / ",(0,i.jsx)(s.code,{children:"rows"})," / ",(0,i.jsx)(s.code,{children:"columns"})," / ",(0,i.jsx)(s.code,{children:"filter"})," / ",(0,i.jsx)(s.code,{children:"result"}),"."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Metadata & lineage"})," read BW dictionary tables via ",(0,i.jsx)(s.code,{children:"RFC_READ_TABLE"})," (e.g. ",(0,i.jsx)(s.code,{children:"RSRREPDIR"}),",\n",(0,i.jsx)(s.code,{children:"RSZCOMPDIR"}),", ",(0,i.jsx)(s.code,{children:"RSDCUBE"}),", ",(0,i.jsx)(s.code,{children:"RSTRAN"}),", ",(0,i.jsx)(s.code,{children:"ROOSFIELD"}),", ",(0,i.jsx)(s.code,{children:"RSHIEDIR"}),", ",(0,i.jsx)(s.code,{children:"RSOOBJXREF"}),"). These power the\n",(0,i.jsx)(s.code,{children:"sap_bics_show*"}),", ",(0,i.jsx)(s.code,{children:"sap_bics_meta_*"}),", and ",(0,i.jsx)(s.code,{children:"sap_bics_lineage_*"})," functions."]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"connecting",children:"Connecting"}),"\n",(0,i.jsxs)(s.p,{children:["Every BICS function uses a DuckDB secret of type ",(0,i.jsx)(s.code,{children:"sap_rfc"})," \u2014 the same secret used by the RFC\nextension. Create one once per session (the example below uses the ABAP Platform Trial\ndefaults):"]}),"\n",(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-sql",children:"CREATE SECRET abap_trial (\n TYPE sap_rfc,\n ASHOST 'localhost',\n SYSNR '00',\n CLIENT '001',\n USER 'DEVELOPER',\n PASSWD 'ABAPtr2023#00',\n LANG 'EN'\n);\n"})}),"\n",(0,i.jsxs)(s.p,{children:["If a single unnamed/default secret exists, ERPL uses it automatically. When you keep several\nsecrets, pass the one you want with the ",(0,i.jsx)(s.code,{children:"secret"})," named parameter that ",(0,i.jsx)(s.strong,{children:"every"})," BICS function\naccepts, e.g. ",(0,i.jsx)(s.code,{children:"sap_bics_show_cubes(secret => 'abap_trial')"}),". For encrypted connections, add\nthe SNC parameters (",(0,i.jsx)(s.code,{children:"SNC_QOP"}),", ",(0,i.jsx)(s.code,{children:"SNC_MYNAME"}),", ",(0,i.jsx)(s.code,{children:"SNC_PARTNERNAME"}),", ",(0,i.jsx)(s.code,{children:"SNC_LIB"}),") to the secret \u2014\nsee the ",(0,i.jsx)(s.a,{href:"/docs/security",children:"security guide"}),". All examples below assume a usable secret\nexists and omit the parameter for brevity."]}),"\n",(0,i.jsx)(s.h2,{id:"discovering-objects",children:"Discovering objects"}),"\n",(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-sql",children:"-- InfoProviders (cubes, ADSOs, CompositeProviders, \u2026)\nSELECT * FROM sap_bics_show(obj_type => 'INFOPROVIDER');\n\n-- Just cubes, or just queries (dedicated helpers with search)\nSELECT * FROM sap_bics_show_cubes();\nSELECT * FROM sap_bics_show_queries();\n\n-- Search by technical name or text\nSELECT technical_name, text, type\nFROM sap_bics_show_queries(search => '0D_FC_NW_C01_Q0011', search_in_key => true);\n"})}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.code,{children:"sap_bics_show(obj_type => ...)"})," accepts ",(0,i.jsx)(s.code,{children:"INFOPROVIDER"}),", ",(0,i.jsx)(s.code,{children:"CUBE"}),", ",(0,i.jsx)(s.code,{children:"QUERY"}),", or ",(0,i.jsx)(s.code,{children:"INFOAREA"}),", and\nreturns ",(0,i.jsx)(s.code,{children:"technical_name"}),", ",(0,i.jsx)(s.code,{children:"text"}),", ",(0,i.jsx)(s.code,{children:"type"}),", ",(0,i.jsx)(s.code,{children:"cube_name"}),", ",(0,i.jsx)(s.code,{children:"is_folder"}),", ",(0,i.jsx)(s.code,{children:"last_changed"}),",\n",(0,i.jsx)(s.code,{children:"last_changed_by"}),", and ",(0,i.jsx)(s.code,{children:"level"}),"."]}),"\n",(0,i.jsx)(s.h2,{id:"querying-a-cube-olap-cross-tabs",children:"Querying a cube (OLAP cross-tabs)"}),"\n",(0,i.jsxs)(s.p,{children:["BICS queries are ",(0,i.jsx)(s.strong,{children:"stateful"}),". You open a query state, identify it with an ",(0,i.jsx)(s.code,{children:"id"}),", mutate that\nstate with separate calls, and finally read the result set by the same ",(0,i.jsx)(s.code,{children:"id"}),". Unlike ordinary\ntable functions, the BICS query functions are ",(0,i.jsx)(s.strong,{children:"not nested inside one another"})," \u2014 they are\nchained by passing the state ",(0,i.jsx)(s.code,{children:"id"})," string from one call to the next."]}),"\n",(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-sql",children:"-- 1. Open a query state on the cube and give it an id\nSELECT state_id, state_version\nFROM sap_bics_begin('0D_NW_C01', id => 'q1');\n\n-- 2. Place characteristics on the ROWS axis (the cross-tab \"drilldown\")\nSELECT state_id, state_version\nFROM sap_bics_rows('q1', '0D_NW_PROD', op => 'SET');\n\n-- 3. Place a characteristic on the COLUMNS axis\nSELECT state_id, state_version\nFROM sap_bics_columns('q1', '0CALMONTH', op => 'SET');\n\n-- 4. Read the result set for this state\nSELECT * FROM sap_bics_result('q1');\n"})}),"\n",(0,i.jsx)(s.h3,{id:"axes-not-column-projection",children:"Axes, not column projection"}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.code,{children:"sap_bics_rows"})," and ",(0,i.jsx)(s.code,{children:"sap_bics_columns"})," are ",(0,i.jsx)(s.strong,{children:"OLAP axes"}),", not a SQL column projection. They\ndecide which characteristics are drilled into the rows vs. the columns of the cross-tab \u2014 the\nsame as dragging a characteristic onto Rows or Columns in Analysis for Office. The key\nfigures of the cube are aggregated for whatever drilldown you choose. The ",(0,i.jsx)(s.code,{children:"op"}
1)," parameter\ncontrols how each call changes the axis:"]}),"\n",(0,i.jsxs)(s.table,{children:[(0,i.jsx)(s.thead,{children:(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.th,{children:(0,i.jsx)(s.code,{children:"op"})}),(0,i.jsx)(s.th,{children:"Effect"})]})}),(0,i.jsxs)(s.tbody,{children:[(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:(0,i.jsx)(s.code,{children:"SET"})}),(0,i.jsx)(s.td,{children:"Replace the axis with exactly these characteristics"})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:(0,i.jsx)(s.code,{children:"ADD"})}),(0,i.jsx)(s.td,{children:"Append characteristics to the existing axis"})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:(0,i.jsx)(s.code,{children:"REMOVE"})}),(0,i.jsx)(s.td,{children:"Remove the named characteristics from the axis"})]})]})]}),"\n",(0,i.jsxs)(s.p,{children:["Each mutating call returns the updated ",(0,i.jsx)(s.code,{children:"state_id"})," and an incrementing ",(0,i.jsx)(s.code,{children:"state_version"}),", so you\ncan confirm the state advanced. The ",(0,i.jsxs)(s.strong,{children:["columns of ",(0,i.jsx)(s.code,{children:"sap_bics_result"})]})," are the cube's own\ncharacteristic and key-figure technical names \u2014 for ",(0,i.jsx)(s.code,{children:"0D_NW_C01"})," that includes ",(0,i.jsx)(s.code,{children:'"0D_NW_PROD"'}),",\n",(0,i.jsx)(s.code,{children:'"0D_NW_NETV"'})," (net value), ",(0,i.jsx)(s.code,{children:'"0D_NW_QUANT"'})," (quantity), and so on. Because these names start\nwith a digit, quote them with double quotes in SQL."]}),"\n",(0,i.jsx)(s.admonition,{title:"Fetch results in one call",type:"tip",children:(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.code,{children:"begin"}),", ",(0,i.jsx)(s.code,{children:"rows"}),", ",(0,i.jsx)(s.code,{children:"columns"}),", and ",(0,i.jsx)(s.code,{children:"filter"})," accept ",(0,i.jsx)(s.code,{children:"return => 'RESULT'"})," to return the result set\ndirectly instead of the default state description (",(0,i.jsx)(s.code,{children:"return => 'DESCRIBE'"}),"). For example,\n",(0,i.jsx)(s.code,{children:"SELECT * FROM sap_bics_rows('q1', '0D_NW_PROD', op => 'SET', return => 'RESULT')"})," applies the\naxis change and hands back data in a single statement."]})}),"\n",(0,i.jsx)(s.h3,{id:"filtering-members",children:"Filtering members"}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.code,{children:"sap_bics_filter"}),' restricts a characteristic to one or more member values (a background\nfilter / "slice"). Pass the characteristic, then one or more member keys as positional\narguments:']}),"\n",(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-sql",children:"SELECT state_id FROM sap_bics_begin('0D_NW_C01', id => 'f1');\nSELECT state_id FROM sap_bics_rows('f1', '0D_NW_PROD', op => 'SET');\n\n-- Single-member filter: Division = 7 (\"High Tech\")\nSELECT state_id FROM sap_bics_filter('f1', '0D_NW_DIV', '7', op => 'SET');\n\nSELECT * FROM sap_bics_result('f1');\n"})}),"\n",(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-sql",children:"-- Multi-member filter: pass several values to one filter() call\nSELECT state_id FROM sap_bics_filter('f1', '0CALMONTH', '202401', '202402', '202403',\n op => 'SET');\n\n-- op => 'ADD' / 'REMOVE' adjust an existing selection instead of replacing it\nSELECT state_id FROM sap_bics_filter('f1', '0D_NW_DIV', '15', op => 'ADD');\n"})}),"\n",(0,i.jsxs)(s.p,{children:["You can also seed a filter at ",(0,i.jsx)(s.code,{children:"begin"})," time with the ",(0,i.jsx)(s.code,{children:"filters"})," named parameter; ad-hoc\n",(0,i.jsx)(s.code,{children:"sap_bics_filter"})," calls remain the flexible way to slice an open state."]}),"\n",(0,i.jsx)(s.h2,{id:"hierarchies",children:"Hierarchies"}),"\n",(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-sql",children:"-- List hierarchies available for an InfoObject\nSELECT * FROM sap_bics_show_hierarchies(info_object => '0D_NW_PROD');\n\n-- Read a hierarchy as a flat node list\nSELECT * FROM sap_bics_hierarchy('0D_NW_PROD_HIER');
1\n\n-- Pin a version / key date and return a recursive tree\nSELECT * FROM sap_bics_hierarchy('0D_NW_PROD_HIER', date_to => '20151231', as_tree => true);\n"})}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.code,{children:"sap_bics_hierarchy"})," returns ",(0,i.jsx)(s.code,{children:"node_id"}),", ",(0,i.jsx)(s.code,{children:"parent_id"}),", ",(0,i.jsx)(s.code,{children:"child_id"}),", ",(0,i.jsx)(s.code,{children:"next_id"}),", ",(0,i.jsx)(s.code,{children:"info_object"}),",\n",(0,i.jsx)(s.code,{children:"node_name"}),", ",(0,i.jsx)(s.code,{children:"node_value"}),", ",(0,i.jsx)(s.code,{children:"date_from"}),", ",(0,i.jsx)(s.code,{children:"date_to"}),", ",(0,i.jsx)(s.code,{children:"level"}),", and ",(0,i.jsx)(s.code,{children:"path"})," \u2014 enough to rebuild the\ntree yourself or filter by ",(0,i.jsx)(s.code,{children:"level"}),"."]}),"\n",(0,i.jsx)(s.h2,{id:"describing-structures",children:"Describing structures"}),"\n",(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-sql",children:"-- Describe a cube: characteristics + key figures\nSELECT technical_name, text FROM sap_bics_describe('0D_NW_C01');\n\n-- Describe a BEx query on a cube (adds its variables)\nSELECT technical_name, text, variables\nFROM sap_bics_describe('0D_NW_C01', '0D_FC_NW_C01_Q0011');\n\n-- Describe an open query state by its id\nSELECT * FROM sap_bics_describe(id => 'q1');\n"})}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.code,{children:"sap_bics_describe"})," returns ",(0,i.jsx)(s.code,{children:"technical_name"}),", ",(0,i.jsx)(s.code,{children:"text"}),", and the ",(0,i.jsx)(s.code,{children:"characteristics"})," / ",(0,i.jsx)(s.code,{children:"keyfigures"}),"\nstructs (plus ",(0,i.jsx)(s.code,{children:"variables"})," for the query overload). For attributes of a single InfoObject:"]}),"\n",(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-sql",children:"SELECT info_object, data_type, length, decimals\nFROM sap_bics_describe_infoobject('0D_NW_PROD');\n"})}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.code,{children:"sap_bics_describe_infoobject"})," returns ",(0,i.jsx)(s.code,{children:"info_object"}),", ",(0,i.jsx)(s.code,{children:"data_type"}),", ",(0,i.jsx)(s.code,{children:"conv_exit"}),",\n",(0,i.jsx)(s.code,{children:"output_length"}),", ",(0,i.jsx)(s.code,{children:"length"}),", and ",(0,i.jsx)(s.code,{children:"decimals"}),"."]}),"\n",(0,i.jsx)(s.h2,{id:"presentation-properties-ao-style",children:"Presentation properties (AO-style)"}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.code,{children:"sap_bics_set_char_prop"})," toggles per-characteristic display properties that match the radio\ncontrols in SAP Analysis for Office's ",(0,i.jsx)(s.em,{children:"Properties"})," panel. The mutation persists in the BICS\nstate and is honoured by the next ",(0,i.jsx)(s.code,{children:"sap_bics_result"})," call."]}),"\n",(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-sql",children:"-- Build a 1D cross-tab on country\nSELECT * FROM sap_bics_begin('0D_NW_C01', id => 'q1');\nSELECT * FROM sap_bics_rows('q1', '0D_NW_CNTRY', op => 'SET');\n\n-- Display member texts instead of keys (\"Germany\" vs \"DE\")\nSELECT * FROM sap_bics_set_char_prop('q1', '0D_NW_CNTRY', 'DISPLAY', 'TEXT');\n\n-- Show both key and text concatenated\nSELECT * FROM sap_bics_set_char_prop('q1', '0D_NW_CNTRY', 'DISPLAY', 'BOTH');\n\n-- Sort by member descending\nSELECT * FROM sap_bics_set_char_prop('q1', '0D_NW_CNTRY', 'SORT', 'DESC');\n\n-- Fetch with the new presentation\nSELECT * FROM sap_bics_result('q1');\n"})}),"\n",(0,i.jsxs)(s.table,{children:[(0,i.jsx)(s.thead,{children:(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.th,{children:(0,i.jsx)(s.code,{children:"prop"})}),(0,i.jsxs)(s.th,{children:["Allowed ",(0,i.jsx)(s.code,{children:"value"})]}),(0,i.jsx)(s.th,{children:"Maps to BICS state field"})]})}),(0,i.jsxs)(s.tbody,{children:[(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:(0,i.jsx)(s.code,{children:"DISPLAY"})}),(0,i.jsxs)(s.td,{children:[(0,i.jsx)(s.code,{children:"KEY"})," | ",(0,i.jsx)(s.code,{children:"TEXT"})," | ",(0,i.jsx)(s.code,{children:"BOTH"})]}),(0,i.jsxs)(s.td,{children:[(0,i.jsx)(s.code,{children:"RESULT_SET_PRESENTATION"})," bitflag (KEY=4, TEXT=32)"]})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:(0,i.jsx)(s.code,{children:"TOTALS"})}),(0,i.jsxs)(s.td,{children:[(0,i.jsx)(s.code,{children:"SHOW"})," | ",(0,i.jsx)(s.code,{children:"HIDE"})]}),(0,i.jsxs)(s.td,{children:[(0,i.jsx)(s.code,{children:"RESULT_VISIBILITY"})," ('A' / 'N')"]})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:(0,i.jsx)(s.code,{children:"SORT"})}),(0,i.jsxs)(s.td,{children:[(0,i.jsx)(s.code,{children:"ASC"})," | ",(0,i.jsx)(s.code,{children:"DESC"})," | ",(0,i.jsx)(s.code,{children:"NONE"})]}),(0,i.jsxs)(s.td,{children:[(0,i.jsx)(s.code,{children:"RESULT_SET_SORTING.DIRECTION"})," ('A' / 'D' / '')"]})]})]})]}),"\n",(0,i.jsxs)(s.p,{children:["The DESCRIBE payload of ",(0,i.jsx)(s.code,{children:"state_rows"}),", ",(0,i.jsx)(s.code,{children:"state_columns"}),", and ",(0,i.jsx)(s.code,{children:"state_free"})," carries\n",(0,i.jsx)(s.code,{children:"{display, totals, sort}"})," so clients can read the current values without an extra round-trip."]}),"\n",(0,i.jsx)(s.admonition,{title:"Grand-total row visibility",type:"note",children:(0,i.jsxs)(s.p,{children:['BICS does not expose a state field for the SUMME / "Overall Result" grand-total row.\n',(0,i.jsx)(s.code,{children:"TOTALS='HIDE'"})," is persisted to per-characteristic ",(0,i.jsx)(s.code,{children:"RESULT_VISIBILITY"})," but the server still\nreturns the grand-total row in ",(0,i.jsx)(s.code,{children:"sap_bics_result"}),'. Clients that want AO\'s "Hide Result"\nbehaviour can filter the row whose row-characteristic value matches ',(0,i.jsx)(s.code,{children:"SUMME"})," /\n",(0,i.jsx)(s.code,{children:"Overall Result"})," / localised variants \u2014 that's what AO itself does."]})}),"\n",(0,i.jsxs)(s.h3,{id:"interactive-bics-tui",children:["Interactive: ",(0,i.jsx)(s.code,{children:"bics-tui"})]}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.code,{children:"bics-tui"})," (in ",(0,i.jsx)(s.a,{href:"https://github.com/DataZooDE/erpl-bics/tree/main/examples/tui",children:(0,i.jsx)(s.code,{children:"bics/examples/tui/"})}),")\nis a terminal UI that wraps every BICS function above into a Textual app modelled on SAP\n",(0,i.jsx)(s.em,{children:"Analysis for Office"}),": log on, browse cubes / queries, build a cross-tab by moving\ncharacteristics between ",(0,i.jsx)(s.strong,{children:"Rows / Columns / Backgroun
1d Filter"}),", filter members, and toggle\n",(0,i.jsx)(s.strong,{children:"Display / Sort / Totals"})," for a focused characteristic or ",(0,i.jsx)(s.strong,{children:"Scaling Factor / Decimal\nPlaces"})," for a focused key figure \u2014 all from a context-sensitive ",(0,i.jsx)(s.strong,{children:"Properties"})," panel on the\nright. Every server-side action is mirrored into an always-on SQL recorder, so the resulting\nscript replays cleanly in a vanilla DuckDB shell."]}),"\n",(0,i.jsx)(s.h4,{id:"logon",children:"Logon"}),"\n",(0,i.jsxs)(s.p,{children:["The logon screen pre-fills the standard ABAP Platform Trial credentials \u2014 overwrite for your\nown system. The fields are wired straight into a DuckDB ",(0,i.jsx)(s.code,{children:"CREATE SECRET"})," of type ",(0,i.jsx)(s.code,{children:"sap_rfc"}),"."]}),"\n",(0,i.jsx)(s.p,{children:(0,i.jsx)(s.img,{alt:"bics-tui Logon screen",src:n(85657).A+"",width:"1400",height:"739"})}),"\n",(0,i.jsx)(s.h4,{id:"analysis-screen",children:"Analysis screen"}),"\n",(0,i.jsx)(s.p,{children:"Three-column layout: cross-tab grid on the left, design panel in the middle (Data Source /\nColumns / Rows / Background Filter / Key Figures), context-sensitive Properties panel on the\nright. Hotkeys are shown next to each section header."}),"\n",(0,i.jsx)(s.p,{children:(0,i.jsx)(s.img,{alt:"bics-tui Analysis
1screen with 0D_NW_PROD on Rows and 0D_NW_CNTRY on Background Filter",src:n(25748).A+"",width:"1400",height:"618"})}),"\n",(0,i.jsx)(s.h4,{id:"properties--characteristic-on-axis",children:"Properties \u2014 Characteristic on axis"}),"\n",(0,i.jsxs)(s.p,{children:["Focus a characteristic in any axis list and the Properties panel switches to three radio\ngroups: Display (Key / Text / Both), Totals (Show / Hide / Conditional), Sort (None / Asc /\nDesc). Display and Sort round-trip via\n",(0,i.jsx)(s.a,{href:"#presentation-properties-ao-style",children:(0,i.jsx)(s.code,{children:"sap_bics_set_char_prop"})}),"; Totals Hide is applied\nclient-side (BICS does not expose a state field for the grand-total row)."]}),"\n",(0,i.jsx)(s.p,{children:(0,i.jsx)(s.img,{alt:"bics-tui Properties panel for a focused characteristic",src:n(96599).A+"",width:"1400",height:"618"})}),"\n",(0,i.jsx)(s.h4,{id:"properties--key-figure",children:"Properties \u2014 Key figure"}),"\n",(0,i.jsx)(s.p,{children:"Focus a key figure and the panel switches to Scaling Factor and Decimal Places. Both are\nclient-side numeric formatting \u2014 BICS state carries no field for them. The KF columns in the\ncross-tab re-format live as you change the controls."}),"\n",(0,i.jsx)(s.p,{children:(0,i.jsx)(s.img,{alt:"bics-tui Properties panel for a focused key figure",src:n(26724).A+"",width:"1400",height:"618"})}),"\n",(0,i.jsx)(s.h4,{id:"run-it",children:"Run it"}),"\n",(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-bash",children:"GEN=ninja make release # from erpl monorepo root\ncd bics/examples/tui\nuv sync\nLD_LIBRARY_PATH=/path/to/erpl/nwrfcsdk/linux/lib uv run python -m bics_tui\n"})}),"\n",(0,i.jsxs)(s.p,{children:["The screenshots above are regenerated headlessly via\n",(0,i.jsx)(s.a,{href:"https://github.com/DataZooDE/erpl-bics/blob/main/examples/tui/scripts/capture_screens.py",children:(0,i.jsx)(s.code,{children:"scripts/capture_screens.py"})}),"\n(Textual's ",(0,i.jsx)(s.code,{children:"export_screenshot"})," \u2192 SVG \u2192 PNG with a mock session for layout-only capture; no SAP\nconnection needed)."]}),"\n",(0,i.jsx)(s.h2,{id:"lineage-tracking-for-sap-bw-experts",children:"Lineage Tracking (for SAP BW experts)"}),"\n",(0,i.jsx)(s.admonition,{title:"For BI Administrators",type:"tip",children:(0,i.jsxs)(s.p,{children:["This section covers lineage tracking from ERP tables through DataSources, InfoProviders, and\nBEx queries. It reads the BW dictionary tables via ",(0,i.jsx)(s.code,{children:"RFC_READ_TABLE"}),", so it does not need an\nopen query state."]})}),"\n",(0,i.jsx)(s.h3,{id:"complete-lineage-as-edges",children:"Complete lineage as edges"}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.code,{children:"sap_bics_lineage_edges()"})," returns the BW data flow as a flat edge list spanning ERP source\ntables \u2192 DataSources \u2192 transformations \u2192 InfoProviders \u2192 BEx queries. Each row is one\nsource-to-target edge with these columns:"]}),"\n",(0,i.jsxs)(s.table,{children:[(0,i.jsx)(s.thead,{children:(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.th,{children:"Column"}),(0,i.jsx)(s.th,{children:"Description"})]})}),(0,i.jsxs)(s.tbody,{children:[(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:(0,i.jsx)(s.code,{children:"edge_type"})}),(0,i.jsx)(s.td,{children:"Edge category (e.g. transformation, query-element)"})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsxs)(s.td,{children:[(0,i.jsx)(s.code,{children:"src_kind"}),", ",(0,i.jsx)(s.code,{children:"src_name"}),", ",(0,i.jsx)(s.code,{children:"src_field"})]}),(0,i.jsx)(s.td,{children:"Source object kind, name, and optional field"})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsxs)(s.td,{children:[(0,i.jsx)(s.code,{children:"tgt_kind"}),", ",(0,i.jsx)(s.code,{children:"tgt_name"}),", ",(0,i.jsx)(s.code,{children:"tgt_field"})]}),(0,i.jsx)(s.td,{children:"Target object kind, name, and optional field"})]})]})]}),"\n",(0,i.jsx)(s.p,{children:"Use cases:"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Impact analysis"})," \u2014 what happens if a source table changes?"]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Data governance"})," \u2014 track data flow and transformations"]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Documentation"})," \u2014 automatic lineage documentation"]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Compliance"})," \u2014 audit data lineage for regulations"]}),"\n"]}),"\n",(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-sql",children:"-- All edges in the system\nSELECT * FROM sap_bics_lineage_edges();\n\n-- Edges related to a specific BEx query \u2014 filter in SQL\nSELECT *\nFROM sap_bics_lineage_edges()\nWHERE tgt_name = '0D_FC_NW_C01_Q0008' OR
1src_name = '0D_FC_NW_C01_Q0008'\nORDER BY src_kind, tgt_kind;\n\n-- Scope the underlying RFC reads to one object (faster on large landscapes)\nSELECT * FROM sap_bics_lineage_edges(scope => '0D_NW_C01');\n"})}),"\n",(0,i.jsx)(s.h3,{id:"forward-trace-from-a-source",children:"Forward trace from a source"}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.code,{children:"sap_bics_lineage_trace()"}),' walks the graph forward from a specific source object/field \u2014\nperfect for "if I change this object, what downstream BW objects break?". The result includes\na ',(0,i.jsx)(s.code,{children:"hop"})," column and a ",(0,i.jsx)(s.code,{children:"path"})," column (the chain of objects walked) so you can see the full\ndownstream blast radius."]}),"\n",(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-sql",children:"-- Everything downstream of an InfoProvider\nSELECT * FROM sap_bics_lineage_trace(source_object => '0D_NW_C01')\nORDER BY hop;\n\n-- Field-level trace\nSELECT * FROM sap_bics_lineage_trace(\n source_object => '0D_NW_C01',\n source_field => '0D_NW_NETV'\n);\n"})}),"\n",(0,i.jsx)(s.h3,{id:"lineage-of-a-single-query",children:"Lineage of a single query"}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.code,{children:"sap_bics_query_lineage()"})," resolves the lineage rooted at one BEx query:"]}),"\n",(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-sql",children:"SELECT * FROM sap_bics_query_lineage('0D_FC_NW_C01_Q0011');\n"})}),"\n",(0,i.jsx)(s.h3,{id:"lineage-as-a-json-graph",children:"Lineage as a JSON graph"}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.code,{children:"sap_bics_lineage_graph_json()"})," returns the full lineage as a JSON document, suitable for\nvisualization libraries (D3, Cytoscape, etc.) or export to external graph tools."]}),"\n",(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-sql",children:"SELECT * FROM sap_bics_lineage_graph_json();\n"})}),"\n",(0,i.jsx)(s.h3,{id:"metadata-mining",children:"Metadata mining"}),"\n",(0,i.jsxs)(s.p,{children:["The ",(0,i.jsx)(s.code,{children:"sap_bics_meta_*"})," functions expose the BW dictionary tables directly."]}),"\n",(0,i.jsx)(s.admonition,{title:"Several metadata functions require an argument",type:"warning",children:(0,i.jsxs)(s.p,{children:["To avoid scanning an entire BW landscape by accident, some functions refuse a bare no-argument\ncall and return a message telling you which parameter to supply. The functions that ",(0,i.jsx)(s.strong,{children:"require\nat least one argument"})," are ",(0,i.jsx)(s.code,{children:"sap_bics_meta_providers"})," (",(0,i.jsx)(s.code,{children:"type"}),"), ",(0,i.jsx)(s.code,{children:"sap_bics_meta_datasources"}),"\n(",(0,i.jsx)(s.code,{children:"appcomp"}),"), ",(0,i.jsx)(s.code,{children:"sap_bics_meta_transformations"})," (",(0,i.jsx)(s.code,{children:"active_only"}),"), ",(0,i.jsx)(s.code,{children:"sap_bics_meta_query_stats"}),"\n(one of ",(0,i.jsx)(s.code,{children:"query_name"})," / ",(0,i.jsx)(s.code,{children:"from_date"})," / ",(0,i.jsx)(s.code,{children:"to_date"}),"), ",(0,i.jsx)(s.code,{children:"sap_bics_meta_objxref"}),"\n(",(0,i.jsx)(s.code,{children:"tlogo"})," / ",(0,i.jsx)(s.code,{children:"objnm"})," / ",(0,i.jsx)(s.code,{children:"tlogo_dep"}),"), and ",(0,i.jsx)(s.code,{children:"sap_bics_meta_hcpr_mapping"})," (",(0,i.jsx)(s.code,{children:"composite_provider"}),").\nThe field-level helpers below take the object name positionally."]})}),"\n",(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-sql",children:"-- InfoProvider metadata (type is required: CUBE | ADSO | HCPR | ODSO | ODSVIEW)\nSELECT * FROM sap_bics_meta_providers(type => 'CUBE');\n\n-- DataSource metadata (application component is required)\nSELECT * FROM sap_bics_meta_datasources(appcomp => 'NODE0000');\n\n-- Transformation metadata\nSELECT * FROM sap_bics_meta_transformations(active_only => true);\n\n-- Query directory (no argument required)\nSELECT * FROM sap_bics_meta_queries();\n\n-- Query usage / runtime statistics\nSELECT * FROM sap_bics_meta_query_usage('0D_FC_NW_C01_Q0011');\nSELECT * FROM sap_bics_meta_query_stats(query_name => '0D_FC_NW_C01_Q0011');\n\n-- Query elements (structures, selections, formulas)\nSELECT * FROM sap_bics_meta_query_elements('0D_FC_NW_C01_Q0011');\n"})}),"\n",(0,i.jsx)(s.h3,{id:"field-level--cross-reference-metadata",children:"Field-level & cross-reference metadata"}),"\n",(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-sql",children:"-- DataSource fields (DataSource name is positional)\nSELECT * FROM sap_bics_meta_datasource_fields('0FI_GL_4');\n\n-- InfoProvider fields (provider name is positional; pass provider_type to disambiguate)\nSELECT * FROM sap_bics_meta_provider_fields('0D_
1NW_C01', provider_type => 'CUBE');\n\n-- Transformation field mappings (transformation id is positional)\nSELECT * FROM sap_bics_meta_transform_fields('<transformation-id>');\n\n-- CompositeProvider (HCPR) components and their part-provider mapping\nSELECT * FROM sap_bics_meta_hcpr_components('<composite-provider>');\nSELECT * FROM sap_bics_meta_hcpr_mapping(composite_provider => '<composite-provider>');\n\n-- InfoObject catalog (no argument required; filter with iobjnm/iobjtp)\nSELECT * FROM sap_bics_meta_infoobjects(iobjnm => '0D_NW_PROD');\n\n-- Object cross-reference (which objects reference which)\nSELECT * FROM sap_bics_meta_objxref(tlogo => 'CUBE', objnm => '0D_NW_C01');\n"})}),"\n",(0,i.jsx)(s.p,{children:"The exact rows these return depend on what is configured in your BW system; the demo objects\nabove are populated on the ABAP Platform Trial, while DataSources and transformations are\ntypically richer on a productive landscape."}),"\n",(0,i.jsx)(s.admonition,{title:"HCPR",type:"note",children:(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.code,{children:"HCPR"})," is the SAP TLOGO type for a ",(0,i.jsx)(s.strong,{children:"HANA CompositeProvider"}),', not a "hierarchy change\npointer". The ',(0,i.jsx)(s.code,{children:"*_hcpr_*"})," helpers describe CompositeProviders and the part-providers they\nunion/join."]})}),"\n",(0,i.jsx)(s.h2,{id:"real-world-examples",children:"Real-world examples"}),"\n",(0,i.jsx)(s.h3,{id:"net-value-by-product",children:"Net value by product"}),"\n",(0,i.jsxs)(s.p,{children:["Build the state with separate statements, then query the result. Each call returns the\n",(0,i.jsx)(s.code,{children:"state_id"}),", so you can ignore those rows and read the data from ",(0,i.jsx)(s.code,{children:"sap_bics_result"}),":"]}),"\n",(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-sql",children:"SELECT state_id FROM sap_bics_begin('0D_NW_C01', id => 'rep1');\nSELECT state_id FROM sap_bics_rows('rep1', '0D_NW_PROD', op => 'SET');\nSELECT state_id FROM sap_bics_filter('rep1', '0D_NW_DIV', '7', op => 'SET');\n\nSELECT\n \"0D_NW_PROD\" AS product,\n \"0D_NW_NETV\" AS net_value,\n \"0D_NW_QUANT\" AS quantity\nFROM sap_bics_result('rep1')\nORDER BY net_value DESC\nLIMIT 20;\n"})}),"\n",(0,i.jsx)(s.h3,{id:"combine-bw-data-with-erp-master-data",children:"Combine BW data with ERP master data"}),"\n",(0,i.jsxs)(s.p,{children:["Once the ",(0,i.jsx)(s.code,{children:"rep1"})," state above exists, its result set joins to ERP master data read over RFC:"]}),"\n",(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-sql",children:"-- BW net value per product, joined to ERP material master via RFC\nWITH bw_sales AS (\n SELECT \"0D_NW_PROD\" AS product, \"0D_NW_NETV\" AS net_value\n FROM sap_bics_result('rep1') -- the state built above\n),\nerp_materials AS (\n SELECT MATNR AS material, MTART AS material_type, MEINS AS base_unit\n FROM sap_read_table('MARA', MAX_ROWS => 10000)\n)\nSELECT\n b.product, m.material_type, m.base_unit, b.net_value,\n CASE\n WHEN b.net_value > 1e9 THEN 'High Value'\n WHEN b.net_value > 1e8 THEN 'Medium Value'\n ELSE 'Low Value'\n END AS value_category\nFROM bw_sales b\nLEFT JOIN erp_materials m ON b.product = m.material\nORDER BY b.net_value DESC;\n"})}),"\n",(0,i.jsx)(s.h2,{id:"performance--good-practice",children:"Performance & good practice"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Slice early with filters and axes."})," Restricting members with ",(0,i.jsx)(s.code,{children:"sap_bics_filter"})," and\nlimiting the drilldown with ",(0,i.jsx)(s.code,{children:"sap_bics_rows"})," / ",(0,i.jsx)(s.code,{children:"sap_bics_columns"})," is what reduces the result\nset \u2014 there is no SQL-style column projection at the BICS layer."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Reuse the query state."})," ",(0,i.jsx)(s.code,{children:"begin"})," opens a server-side state; keep mutating the same ",(0,i.jsx)(s.code,{children:"id"}),"\nrather than re-opening for each variation. ",(0,i.jsx)(s.code,{children:"state_version"})," confirms each change landed."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Scope metadata reads."})," Pass ",(0,i.jsx)(s.code,{children:"scope"}),"/object arguments to the ",(0,i.jsx)(s.code,{children:"lineage"}),"/",(0,i.jsx)(s.code,{children:"meta"})," functions so\nthe underlying ",(0,i.jsx)(s.code,{children:"RFC_READ_TABLE"})," calls stay bounded on large landscapes."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Reuse connections."})," ERPL caches RFC connections per secret; running several BICS\nstatements in one DuckDB session avoids repeated logons."]}),"\n"]}),"\n",(0,i.jsx)(s.h2,{id:"troubleshooting",children:"Troubleshooting"}),"\n",(0,i.jsx)(s.p,{children:(0,i.jsx)(s.strong,{children:"Query or cube not found"})}),"\n",(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-sql",children:"-- Names are case-sensitive; search the catalog\nSELECT * FROM sap_bics_show_queries(search => 'Q0011');\nSELECT * FROM sap_bics_show(obj_type => 'CUBE', search => '0D_NW_C01');\n"})}),"\n",(0,i.jsx)(s.p,{children:(0,i.jsx)(s.strong,{children:"Empty result set"})}),"\n",(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-sql",children:"-- Re-c
1heck filter member keys (use keys, not display texts)\nSELECT * FROM sap_bics_describe_infoobject('0D_NW_DIV');\n\n-- Drop filters to confirm the cube has data at all\nSELECT * FROM sap_bics_begin('0D_NW_C01', id => 'probe', return => 'RESULT');\n"})}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:'Metadata function errors with "requires named parameters"'}),"\nThis is the guardrail described above \u2014 supply the named argument it asks for (e.g.\n",(0,i.jsx)(s.code,{children:"sap_bics_meta_providers(type => 'CUBE')"}),")."]}),"\n",(0,i.jsx)(s.h3,{id:"enable-tracing",children:"Enable tracing"}),"\n",(0,i.jsx)(s.p,{children:"ERPL has a built-in trace facility. Enable it to see the exact RFC calls and payloads:"}),"\n",(0,i.jsx)(s.pre,{children:(0,i.jsx)(s.code,{className:"language-sql",children:"SET erpl_trace_enabled = TRUE;\nSET erpl_trace_level = 'DEBUG'; -- TRACE | DEBUG | INFO | WARN | ERROR\nSET erpl_trace_output = 'console'; -- console | file | both\n"})}),"\n",(0,i.jsx)(s.h2,{id:"for-sap-bw-experts-the-bics-interface-in-detail",children:"For SAP BW experts: the BICS interface in detail"}),"\n",(0,i.jsxs)(s.p,{children:["BICS in ERPL is an ",(0,i.jsx)(s.strong,{children:"RFC interface"}),", not a network protocol of its own: it drives the same\n",(0,i.jsx)(s.code,{children:"BICS_PROV_*"})," consumer modules that SAP Analysis for Office uses, over the NetWeaver RFC SDK\nwith your ",(0,i.jsx)(s.code,{children:"sap_rfc"})," secret. Expand the deep dive below for the wire-level mechanics."]}),"\n",(0,i.jsxs)(r,{children:[(0,i.jsxs)("summary",{children:[(0,i.jsx)("b",{children:"BICS protocol deep dive"})," \u2014 handle lifecycle, state model & versioning, result-set decoding, variables, metadata, security"]}),(0,i.jsx)(s.h4,{id:"the-handle-lifecycle",children:"The handle lifecycle"}),(0,i.jsx)(s.p,{children:"A BICS session juggles three server-side handles, each a 4-character token:"}),(0,i.jsxs)(s.ol,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Application handle"})," \u2014 ",(0,i.jsx)(s.code,{children:"BICS_CONS_CREATE_DATA_AREA"})," creates the consumer data area and\nreturns ",(0,i.jsx)(s.code,{children:"E_APPLICATION_HANDLE"}),". This is the umbrella context for the session."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Data-provider handle"})," \u2014 ",(0,i.jsx)(s.code,{children:"BICS_PROV_OPEN"})," opens the InfoProvider (or BEx query) and\nreturns ",(0,i.jsx)(s.code,{children:"E_DATA_PROVIDER_HANDLE"})," plus ",(0,i.jsx)(s.code,{children:"E_VARIABLE_CONTAINER_HANDLE"}),". ERPL opens with\n",(0,i.jsx)(s.code,{children:"I_STATE_VARIABLE_MODE = 'U'"})," and ",(0,i.jsx)(s.code,{children:"I_OPTIMIZE_INIT_VERSION = 5"}),". A variable-container handle\nof ",(0,i.jsx)(s.code,{children:"0000"})," is ",(0,i.jsx)(s.strong,{children:"normal"})," for queries that declare no BEx variables \u2014 it is not an error."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Initial state"})," \u2014 ",(0,i.jsx)(s.code,{children:"BICS_PROV_GET_INITIAL_STATE"})," (with ",(0,i.jsx)(s.code,{children:"I_RETRIEVE_SEL_SPACE_OPT = 'X'"}),")\nreturns the complete default OLAP state: the characteristic catalog, the rows/columns/free\naxis assignments, the selection (filter) state, and the per-characteristic presentation\nstate."]}),"\n"]}),(0,i.jsxs)(s.p,{children:["At session end ",(0,i.jsx)(s.code,{children:"BICS_PROV_CLOSE"})," is called best-effort for the variable-container, data-provider,\nand application handles in turn (failures are swallowed)."]}),(0,i.jsx)(s.p,{children:"The full mapping from SQL function to RFC module:"}),(0,i.jsxs)(s.table,{children:[(0,i.jsx)(s.thead,{children:(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.th,{children:"ERPL step"}),(0,i.jsx)(s.th,{children:"RFC function module(s)"}),(0,i.jsx)(s.th,{children:"Key parameters"})]})}),(0,i.jsxs)(s.tbody,{children:[(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:(0,i.jsx)(s.code,{children:"sap_bics_begin"})}),(0,i.jsxs)(s.td,{children:[(0,i.jsx)(s.code,{children:"BICS_CONS_CREATE_DATA_AREA"})," \u2192 ",(0,i.jsx)(s.code,{children:"BICS_PROV_OPEN"})," \u2192 ",(0,i.jsx)(s.code,{children:"BICS_PROV_GET_INITIAL_STATE"})]}),(0,i.jsxs)(s.td,{children:[(0,i.jsx)(s.code,{children:"I_DATA_PROVIDER_INFO_PROVIDER"}),", ",(0,i.jsx)(s.code,{children:"I_DATA_PROVIDER_NAME"})," (query)"]}
1)]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsxs)(s.td,{children:[(0,i.jsx)(s.code,{children:"sap_bics_rows"})," / ",(0,i.jsx)(s.code,{children:"columns"})," / ",(0,i.jsx)(s.code,{children:"filter"})," / ",(0,i.jsx)(s.code,{children:"set_char_prop"})]}),(0,i.jsx)(s.td,{children:(0,i.jsx)(s.code,{children:"BICS_PROV_SET_STATE"})}),(0,i.jsxs)(s.td,{children:[(0,i.jsx)(s.code,{children:"I_S_ROWS"}),", ",(0,i.jsx)(s.code,{children:"I_S_COLUMNS"}),", ",(0,i.jsx)(s.code,{children:"I_S_FREE"}),", ",(0,i.jsx)(s.code,{children:"I_TH_CHARACTERISTICS"}),", ",(0,i.jsx)(s.code,{children:"I_TSX_SELECTION_STATE"}),", ",(0,i.jsx)(s.code,{children:"I_VALIDATE = 'X'"})]})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:(0,i.jsx)(s.code,{children:"sap_bics_result"})}),(0,i.jsx)(s.td,{children:(0,i.jsx)(s.code,{children:"BICS_PROV_GET_RESULT_SET"})}),(0,i.jsx)(s.td,{children:(0,i.jsx)(s.code,{children:"I_MAX_DATA_CELLS = 1000000"})})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsxs)(s.td,{children:[(0,i.jsx)(s.code,{children:"sap_bics_describe"})," (query)"]}),(0,i.jsxs)(s.td,{children:[(0,i.jsx)(s.code,{children:"BICS_PROV_GET_DESIGN_TIME_INFO"}),", ",(0,i.jsx)(s.code,{children:"BICS_PROV_VAR_GET_VARIABLES"})]}),(0,i.jsx)(s.td,{children:(0,i.jsx)(s.code,{children:"I_VARIABLE_CONTAINER_HANDLE"})})]}),(0,i.jsxs)(s.tr,{children:[(0,i.jsx)(s.td,{children:"(session end)"}),(0,i.jsx)(s.td,{children:(0,i.jsx)(s.code,{children:"BICS_PROV_CLOSE"})}),(0,i.jsx)(s.td,{children:"one call per handle"})]})]})]}),(0,i.jsxs)(s.h4,{id:"the-state-model-and-state_version",children:["The state model and ",(0,i.jsx)(s.code,{children:"state_version"})]}),(0,i.jsxs)(s.p,{children:["Although the OLAP state physically lives on the BW server between ",(0,i.jsx)(s.code,{children:"SET_STATE"})," calls, ERPL also\n",(0,i.jsx)(s.strong,{children:"persists each state snapshot client-side"})," in a DuckDB table keyed by ",(0,i.jsx)(s.code,{children:"(id, version)"}),". Every\nmutation \u2014 placing a characteristic on an axis, adding a filter, or changing a display/sort/\ntotals property \u2014 saves a new snapshot and increments ",(0,i.jsx)(s.code,{children:"state_version"}),". That is why a typical\n",(0,i.jsx)(s.code,{children:"begin \u2192 rows \u2192 columns \u2192 filter"})," sequence returns versions ",(0,i.jsx)(s.code,{children:"1 \u2192 2 \u2192 3 \u2192 4"}),". Because snapshots\nare immutable and addressed by id, several independent query states can coexist in one DuckDB\nsession, and you can branch a state by re-using an earlier id."]}),(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.code,{children:"BICS_PROV_SET_STATE"})," carries the whole OLAP state, not a delta. The notable sub-structures:"]}),(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Axes"})," \u2014 ",(0,i.jsx)(s.code,{children:"I_S_ROWS"})," / ",(0,i.jsx)(s.code,{children:"I_S_COLUMNS"})," / ",(0,i.jsx)(s.code,{children:"I_S_FREE"})," and their characteristic-reference tables\ndescribe which characteristics sit on the rows axis, the columns axis, and the ",(0,i.jsx)(s.em,{children:"free"}),"\n(background-filter) axis. ",(0,i.jsx)(s.code,{children:"op => 'SET'"})," clears the target axis first (key figures are kept on\nthe column axis), ",(0,i.jsx)(s.code,{children:"'ADD'"})," appends, and ",(0,i.jsx)(s.code,{children:"'REMOVE'"})," moves a characteristic to the free axis."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Selection state"})," (",(0,i.jsx)(s.code,{children:"I_TSX_SELECTION_STATE"}),") \u2014 one entry per filtered characteristic, each\nholding a ",(0,i.jsx)(s.code,{children:"SELECTION"})," list of ",(0,i.jsx)(s.code,{children:"{SIGN ('I'/'E'), OPERATOR ('EQ'/'BT'/\u2026), LOW, HIGH}"})," rows.\nThis is the BICS equivalent of an ABAP range table."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Per-characteristic presentation"})," (",(0,i.jsx)(s.code,{children:"I_TH_CHARACTERISTICS"}),") \u2014 the fields ",(0,i.jsx)(s.code,{children:"sap_bics_set_char_prop"}),"\nmanipulates: ",(0,i.jsx)(s.code,{children:"RESULT_SET_PRESENTATION"})," (a bitflag, ",(0,i.jsx)(s.code,{children:"KEY=4"}),", ",(0,i.jsx)(s.code,{children:"TEXT=32"}),", ",(0,i.jsx)(s.code,{children:"BOTH=36"}),"),\n",(0,i.jsx)(s.code,{children:"RESULT_VISIBILITY"})," (",(0,i.jsx)(s.code,{children:"'A'"})," show / ",(0,i.jsx)(s.code,{children:"'N'"})," hide / ",(0,i.jsx)(s.code,{children:"'C'"})," conditional for totals), and\n",(0,i.jsx)(s.code,{children:"RESULT_SET_SORTING.DIRECTION"})," (",(0,i.jsx)(s.code,{children:"'A'"})," / ",(0,i.jsx)(s.code,{children:"'D'"})," / ",(0,i.jsx)(s.code,{children:"''"}),")."]}),"\n"]}),(0,i.jsx)(s.h4,{id:"decoding-the-result-set",children:"Decoding the result set"}),(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.code,{children:"BICS_PROV_GET_RESULT_SET"})," returns a multidimensional cross-tab, not a flat table, which ERPL\nflattens:"]}),(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Members & presentation"})," \u2014 ",(0,i.jsx)(s.code,{children:"E_T_MEMBER"})," lists the axis members; each points into a\n",(0,i.jsx)(s.code,{children:"E_T_MEMBER_PRESENTATION"})," table via a ",(0,i.jsx)(s.code,{children:"PRESENTATION_INDEX_FROM/TO"})," range. The display mode\ndecides which presentation entry is used: ",(0,i.jsx)(s.code,{children:"KEY"})," takes the key, ",(0,i.jsx)(s.code,{children:"TEXT"})," the text, ",(0,i.jsx)(s.code,{children:"BOTH"}),"\nconcatenates them."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Row / column tuples"})," \u2014 ",(0,i.jsx)(s.code,{children:"E_T_ROWS"})," and ",(0,i.jsx)(s.code,{children:"E_T_COLUMNS"})," give the member references per axis\nposition, including a ",(0,i.jsx)(s.code,{children:"LEVEL"})," field that becomes the synthetic ",(0,i.jsx)(s.code,{children:'"<char>_HIER_LEVEL"'})," integer\ncolumn you see for hierarchy drilldowns."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Data cells"})," \u2014 ",(0,i.jsx)(s.code,{children:"E_T_DATA_CELLS"})," is a ",(0,i.jsx)(s.strong,{children:"sparse"})," ",(0,i.jsx)(s.code,{children:"{ROW, COLUMN, VALUE}"})," list (cells equal to\nzero are omitted by the server). ERPL initialises every cell to ",(0,i.jsx)(s.code,{children:"NULL"})," and fills only the\nreturned ones, so an absent cell is ",(0,i.jsx)(s.code,{children:"NULL"}),", not ",(0,i.jsx)(s.code,{children:"0.0"}),". Cell ",(0,i.jsx)(s.code,{children:"VALUE"}),"s are always ",(0,i.jsx)(s.code,{children:"DOUBLE"}),";\naxis member columns are always ",(0,i.jsx)(s.code,{children:"VARCHAR"}),"."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Grand total"}),' \u2014 the "Overall Result" / ',(0,i.jsx)(s.code,{children:"SUMME"})," row is an ordinary row in ",(0,i.jsx)(s.code,{children:"E_T_ROWS"})," whose\nmember key is the literal ",(0,i.jsx)(s.code,{children:"SUMME"}),"; it is not a separate structure (see the note under\n",(0,i.jsx)(s.em,{children:"Presentation properties"})," above for hiding it)."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Cell budget"})," \u2014 ERPL requests up to ",(0,i.jsx)(s.code,{children:"I_MAX_DATA_CELLS = 1,000,000"})," cells per fetch. A\ndrilldown that would exceed that must be narrowed with filters or fewer axis characteristics."]}),"\n"]}),(0,i.jsx)(s.h4,{id:"variables",children:"Variables"}),(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.code,{children:"BICS_PROV_VAR_GET_VARIABLES"})," retrieves a query's variable definitions (used by\n",(0,i.jsx)(s.code,{children:"sap_bics_describe"}),"), but ",(0,i.jsxs)(s.strong,{children:["variable ",(0,i.jsx)(s.em,{children:"filling"})," is not implemented"]})," \u2014 there is no\n",(0,i.jsx)(s.code,{children:"BICS_PROV_VAR_SET_VARIABLES"}
1)," call. Queries that declare variables execute with their\ndefault/empty values; restrict the result with ",(0,i.jsx)(s.code,{children:"sap_bics_filter"})," instead."]}),(0,i.jsx)(s.h4,{id:"metadata-and-lineage",children:"Metadata and lineage"}),(0,i.jsxs)(s.p,{children:["Discovery, ",(0,i.jsx)(s.code,{children:"sap_bics_meta_*"}),", and ",(0,i.jsx)(s.code,{children:"sap_bics_lineage_*"})," do ",(0,i.jsx)(s.strong,{children:"not"})," open a query. They read BW\ndictionary tables through ",(0,i.jsx)(s.code,{children:"RFC_READ_TABLE"})," \u2014 among them ",(0,i.jsx)(s.code,{children:"RSRREPDIR"})," and ",(0,i.jsx)(s.code,{children:"RSZCOMPDIR"})," (queries),\n",(0,i.jsx)(s.code,{children:"RSDCUBE"})," (InfoCubes), ",(0,i.jsx)(s.code,{children:"RSTRAN"})," (transformations), ",(0,i.jsx)(s.code,{children:"ROOSFIELD"})," (DataSource fields), ",(0,i.jsx)(s.code,{children:"RSHIEDIR"}),"\n(hierarchies), and ",(0,i.jsx)(s.code,{children:"RSOOBJXREF"})," (cross-references). Hierarchy nodes come from\n",(0,i.jsx)(s.code,{children:"RSNDI_SHIE_STRUCTURE_GET3"}),"; single-InfoObject attributes from ",(0,i.jsx)(s.code,{children:"BAPI_IOBJ_GETDETAIL"}),"."]}),(0,i.jsx)(s.h4,{id:"security",children:"Security"}),(0,i.jsx)(s.p,{children:"Because BICS rides on RFC, its security model is the RFC model:"}),(0,i.jsxs)(s.ol,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Authentication"})," \u2014 the ",(0,i.jsx)(s.code,{children:"sap_rfc"})," secret (user/password) or SNC for certificate-based,\nencrypted logon. There is no Basic Auth or SAML at the BICS layer."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Authorization"})," \u2014 standard BW analysis authorizations (RSEC) govern which InfoProviders,\nqueries, and characteristic values a user may read."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Transport security"})," \u2014 use SNC (or an SSH tunnel) to encrypt the RFC connection on\nuntrusted networks."]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.strong,{children:"Auditing"})," \u2014 SAP-side security audit log (SM19/SM20) records the RFC logons."]}),"\n"]})]}),"\n",(0,i.jsx)(s.h2,{id:"next-steps",children:"Next steps"}),"\n",(0,i.jsx)(s.h3,{id:"-ready-for-more",children:"\ud83d\ude80 Ready for More?"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/docs/erpl/odp",children:"ODP Protocol Guide"})," - Delta replication from SAP"]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/docs/erpl/rfc",children:"RFC Protocol Guide"})," - Read SAP tables and call functions"]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/docs/reference/erpl-functions",children:"Function Reference"})," - Complete API docs"]}),"\n"]}),"\n",(0,i.jsx)(s.h3,{id:"-advanced-topics",children:"\ud83d\udd27 Advanced Topics"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/docs/guides/advanced/bics-lineage-tracking",children:"BICS Lineage Tracking"})," - Track data lineage"]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/docs/guides/advanced/performance-tuning",children:"Performance Tuning"})," - Optimize BW queries"]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/docs/examples/real-world-use-cases",children:"Real-World Use Cases"})," - Complete scenarios"]}),"\n"]}),"\n",(0,i.jsx)(s.h3,{id:"-examples",children:"\ud83d\udca1 Examples"}),"\n",(0,i.jsxs)(s.ul,{children:["\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/docs/examples/erpl-examples",children:"ERPL Examples"})," - More real-world BICS examples"]}),"\n",(0,i.jsxs)(s.li,{children:[(0,i.jsx)(s.a,{href:"/docs/guides/integration/python-pandas",children:"Integration with Python"})," - Use BICS data with Pandas"]}),"\n"]}),"\n",(0,i.jsx)(s.hr,{}),"\n",(0,i.jsxs)(s.p,{children:[(0,i.jsx)(s.strong,{children:"Need help?"})," Check our ",(0,i.jsx)(s.a,{href:"/docs/reference/troubleshooting",children:"troubleshooting guide"})," or browse ",(0,i.jsx)(s.a,{href:"/docs/examples/erpl-examples",children:"more examples"}),"."]})]})}function h(e={}){const{wrapper:s}={...(0,c.R)(),...e.components};return s?(0,i.jsx)(s,{...e,children:(0,i.jsx)(o,{...e})}):o(e)}},85657:(e,s,n)=>{n.d(s,{A:()=>r});const r=n.p+"assets/images/bics_tui_logon-b37cd93d84a8202f524dbc2898019c6c.png"},96599:(e,s,n)=>{n.d(s,{A:()=>r});const r=n.p+"assets/images/bics_tui_props_char-7a7e31437df602b6dd3ad0de6a12875a.png"}}]);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.