Technical Articles
Connecting SAP BI Platform to Snowflake
Disclaimer
The connector presented here is a proof of concept. While we are hoping it can add value to your SAP BI deployment, it has not undergone full certification in the SAP BI Platform. As such, it is not supported by SAP.
Snowflake
Snowflake is a cloud datawarehouse. It promises simplicity of deployment (as always in the cloud) and best-of-breed performance.
In the past few years it has been gaining traction, and we have recently received requests from joint customers who would like to use the SAP BI Platform to report off data from Snowflake.
Configuring the SAP BI Platform to connect to Snowflake
Following these requests, the Snowflake and SAP teams have collaborated to produce a working POC (proof-of-concept) of an SAP BI connector to Snowflake. Our efforts concentrated on ODBC, because:
- in the WebIntelligence stack, relying on a native connector is the most effective option as it avoids switching back and forth between Java and native code
- The Snowflake team favored the ODBC driver for the customer request at hand
Our POC was done on the Windows platform. Snowflake also provides an ODBC driver for Linux, but attempting to consume it would require extra configuration steps that are out of the scope of this blog post.
Configuration steps
The connector needs to be configured on both SAP BI Platform client and server machines, so the steps below need to be performed twice, once on the client machine and once on the server machine.
Go to the dataAccess\connectionServer folder in your SAP BI Platform setup.
In the setup subfolder, create a file:
- named snowflake_odbc.setup on the server machine
- named snowflake_odbc.32.setup on the client machine
Put the following contents into the file using your favorite text editor (contents is identical for both files) :
<?xml version="1.0" encoding="UTF-8"?><Product Name="Snowflake Access Pack (ODBC)" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../../config/core/setup.xsd">
<Version Type="Component">3.4.0.0</Version>
<Version Type="Build">14.3.0.2963</Version>
<Driver>
<NetworkLayer Name="ODBC"></NetworkLayer>
<Directory>odbc</Directory>
<DataFileName>snowflake</DataFileName>
</Driver>
</Product>
Go back to the dataAccess\connectionServer folder, and into the odbc subfolder. Create a snowflake.sbo file, with the following contents:
<?xml version="1.0" encoding="UTF-8"?><DriverConfiguration xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../sbo.xsd">
<Defaults>
<Parameter Name="Family">Snowflake</Parameter>
<Parameter Name="SQL External File">sqlsrv</Parameter>
<Parameter Name="SQL Parameter File">snowflake</Parameter>
<Parameter Name="Description File">odbc</Parameter>
<Parameter Name="Strategies File">sqlsrv</Parameter>
<Parameter Name="Driver Capabilities">Query</Parameter>
<Parameter Name="Extensions">snowflake,odbc</Parameter>
<Parameter Name="Array Fetch Available">True</Parameter>
<Parameter Name="Array Bind Available">True</Parameter>
<Parameter Name="Empty String">EmptyString</Parameter>
<Parameter Name="CharSet Table">odbc</Parameter>
<Parameter Name="Query TimeOut Available">True</Parameter>
<Parameter Name="Quote Identifiers">False</Parameter>
<Parameter Name="Optimize Execute">False</Parameter>
<Parameter Name="Use DataDirect OEM Driver" Platform="Unix">No</Parameter>
<Parameter Name="Force Execute">Procedures</Parameter>
</Defaults>
<DataBases>
<DataBase Active="Yes" Name="Snowflake">
<Libraries>
<Library>dbd_wodbc3</Library>
<Library>dbd_odbc3</Library>
<!-- To use UnixODBC 2.3.0 for 'MS SQL SERVER ODBC datasource' comment out previous 2 line & uncomment next 4 lines and set value of "Use DataDirect OEM Driver" as No -->
<!-- Library Platform="Unix">dbd_uxodbc23</Library -->
<!-- Library Platform="Unix">dbd_wuxodbc23</Library -->
<!-- Library Platform="Unix64">dbd_wux32odbc23</Library -->
<!-- Library Platform="Unix64">dbd_ux32odbc23</Library -->
</Libraries>
<Parameter Name="Extensions">snowflake,odbc</Parameter>
<Parameter Name="CharSet Table" Platform="Unix">datadirect</Parameter>
<Parameter Name="Driver Name">SnowflakeDSIDriver</Parameter>
<Parameter Name="SSO Available" Platform="MSWindows">False</Parameter>
<Parameter Name="Driver Capabilities">Query,Cancel</Parameter>
</DataBase>
</DataBases>
</DriverConfiguration>
Also create a snowflake.prm file with the following contents:
<?xml version="1.0" encoding="UTF-8"?><DBParameters xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../dbparameters.xsd">
<Configuration>
<Parameter Name="DB_TYPE">GENERIC</Parameter>
<Parameter Name="ORDER_BY_REQUIRES_SELECT">YES</Parameter>
<Parameter Name="GROUP_BY_SUPPORTS_COLUMN_INDEX">NO</Parameter>
<Parameter Name="JOIN">YES</Parameter>
<Parameter Name="INNER_JOIN">INNER JOIN</Parameter>
<Parameter Name="EXT_JOIN">NO</Parameter>
<Parameter Name="UNION">UNION</Parameter>
<Parameter Name="UNION_IN_SUBQUERY">NO</Parameter>
<Parameter Name="INTERSECT"></Parameter>
<Parameter Name="INTERSECT_IN_SUBQUERY">NO</Parameter>
<Parameter Name="MINUS"></Parameter>
<Parameter Name="MINUS_IN_SUBQUERY">NO</Parameter>
<Parameter Name="DISTINCT">NO</Parameter>
<Parameter Name="CONSTANT_SAMPLING_SUPPORTED">NO</Parameter>
<Parameter Name="ANALYTIC_CLAUSE"></Parameter>
<Parameter Name="PERCENT_RANK_SUPPORTED">NO</Parameter>
<Parameter Name="RANK_SUPPORTED">NO</Parameter>
<Parameter Name="ANALYTIC_FUNCTIONS"></Parameter>
<Parameter Name="SEED_SAMPLING_SUPPORTED">NO</Parameter>
<Parameter Name="FULL_EXT_JOIN">NO</Parameter>
<Parameter Name="LEFT_EXT_JOIN">NO</Parameter>
<Parameter Name="LEFT_OUTER"></Parameter>
<Parameter Name="RIGHT_EXT_JOIN">NO</Parameter>
<Parameter Name="RIGHT_OUTER"></Parameter>
<Parameter Name="GROUP_BY_SUPPORTS_COMPLEX">YES</Parameter>
<Parameter Name="GROUP_BY_SUPPORTS_CONSTANT">YES</Parameter>
<Parameter Name="GROUP_BY">YES</Parameter>
<Parameter Name="HAVING">YES</Parameter>
<Parameter Name="ORDER_BY">YES</Parameter>
<Parameter Name="ORDER_BY_SUPPORTS_COLUMN_INDEX">YES</Parameter>
<Parameter Name="INTERSECT_ALL">NO</Parameter>
<Parameter Name="MINUS_ALL">NO</Parameter>
<Parameter Name="UNION_ALL">YES</Parameter>
<Parameter Name="LIKE_SUPPORTS_ESCAPE_CLAUSE">NO</Parameter>
<Parameter Name="SELECT_SUPPORTS_NULL">NO</Parameter>
<Parameter Name="SUBQUERY_IN_FROM">YES</Parameter>
<Parameter Name="SUBQUERY_IN_IN">YES</Parameter>
<Parameter Name="SUBQUERY_IN_WHERE">YES</Parameter>
<Parameter Name="CALCULATION_FUNCTION">YES</Parameter>
</Configuration>
<DateOperations>
<DateOperation Name="YEAR">{fn year($D)}</DateOperation>
<DateOperation Name="MONTH">{fn month($D)}</DateOperation>
</DateOperations>
<Operators>
<Operator Arity="1" ID="ADD" Type="Numeric">+</Operator>
<Operator Arity="1" ID="SUBSTRACT" Type="Numeric">-</Operator>
<Operator Arity="1" ID="MULTIPLY" Type="Numeric">*</Operator>
<Operator Arity="1" ID="DIVIDE" Type="Numeric">/</Operator>
<Operator Arity="0" ID="NOT_NULL" Type="Logical">IS NOT NULL</Operator>
<Operator Arity="0" ID="NULL" Type="Logical">IS NULL</Operator>
<Operator Arity="1" ID="SUP" Type="Logical">>=</Operator>
<Operator Arity="1" ID="INF" Type="Logical"><=</Operator>
<Operator Arity="1" ID="EQUAL" Type="Logical">=</Operator>
<Operator Arity="1" ID="DIFF" Type="Logical"><></Operator>
<Operator Arity="1" ID="STRICT_SUP" Type="Logical">></Operator>
<Operator Arity="1" ID="STRICT_INF" Type="Logical"><</Operator>
<Operator Arity="1" ID="IN_LIST" Type="Logical">IN</Operator>
<Operator Arity="1" ID="NOT_IN_LIST" Type="Logical">NOT IN</Operator>
<Operator Arity="1" ID="MATCH" Type="Logical">LIKE</Operator>
<Operator Arity="1" ID="NOT_MATCH" Type="Logical">NOT LIKE</Operator>
<Operator Arity="2" ID="BETWEEN" Type="Logical">BETWEEN AND</Operator>
<Operator Arity="2" ID="NOT_BETWEEN" Type="Logical">NOT BETWEEN AND</Operator>
</Operators>
<Functions>
<Function Distinct="False" Group="True" ID="Minimum" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>min($1)</SQL>
</Function>
<Function Distinct="False" Group="True" ID="Maximum" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>max($1)</SQL>
</Function>
<Function Distinct="False" Group="True" ID="Average" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>avg($1)</SQL>
</Function>
<Function Distinct="False" Group="True" ID="Sum" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>sum($1)</SQL>
</Function>
<Function Distinct="False" Group="True" ID="Count" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="All"></Argument>
</Arguments>
<SQL>count($1)</SQL>
</Function>
<Function Group="False" ID="ASCII_code" InMacro="False" Type="String">
<Arguments>
<Argument Type="Char"></Argument>
</Arguments>
<SQL>{fn ascii($1)}</SQL>
</Function>
<Function Group="False" ID="Character" InMacro="False" Type="String">
<Arguments>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>{fn char($1)}</SQL>
</Function>
<Function Group="False" ID="Concat" InMacro="True" Type="String">
<Arguments>
<Argument Type="String"></Argument>
<Argument Type="String"></Argument>
</Arguments>
<SQL>{fn concat($1,$2)}</SQL>
</Function>
<Function Group="False" ID="Left" InMacro="True" Type="String">
<Arguments>
<Argument Type="String"></Argument>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>{fn left($1,$2)}</SQL>
</Function>
<Function Group="False" ID="LeftRemove" InMacro="True" Type="String">
<Arguments>
<Argument Type="String"></Argument>
</Arguments>
<SQL>{fn ltrim($1)}</SQL>
</Function>
<Function Group="False" ID="Length" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="String"></Argument>
</Arguments>
<SQL>{fn length($1)}</SQL>
</Function>
<Function Group="False" ID="Locate" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="String"></Argument>
<Argument Type="String"></Argument>
</Arguments>
<SQL>{fn locate($1,$2)}</SQL>
</Function>
<Function Group="False" ID="Lowercase" InMacro="True" Type="String">
<Arguments>
<Argument Type="String"></Argument>
</Arguments>
<SQL>{fn lcase($1)}</SQL>
</Function>
<Function Group="False" ID="Repeat" InMacro="True" Type="String">
<Arguments>
<Argument Type="String"></Argument>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>{fn repeat($1,$2)}</SQL>
</Function>
<Function Group="False" ID="Rightpart" InMacro="True" Type="String">
<Arguments>
<Argument Type="String"></Argument>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>{fn right($1,$2)}</SQL>
</Function>
<Function Group="False" ID="Rtrim" InMacro="True" Type="String">
<Arguments>
<Argument Type="String"></Argument>
</Arguments>
<SQL>{fn rtrim($1)}</SQL>
</Function>
<Function Group="False" ID="Substring" InMacro="True" Type="String">
<Arguments>
<Argument Type="String"></Argument>
<Argument Type="Numeric"></Argument>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>{fn substring($1,$2,$3)}</SQL>
</Function>
<Function Group="False" ID="Uppercase" InMacro="True" Type="String">
<Arguments>
<Argument Type="String"></Argument>
</Arguments>
<SQL>{fn ucase($1)}</SQL>
</Function>
<Function Group="False" ID="Absolute" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>{fn abs($1)}</SQL>
</Function>
<Function Group="False" ID="Arc_cosine" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>{fn acos($1)}</SQL>
</Function>
<Function Group="False" ID="Arc_sine" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>{fn asin($1)}</SQL>
</Function>
<Function Group="False" ID="Arc_tangent" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>{fn atan($1)}</SQL>
</Function>
<Function Group="False" ID="Angle_Tangent_2" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="Numeric"></Argument>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>{fn atan2($1,$2)}</SQL>
</Function>
<Function Group="False" ID="Cosine" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>{fn cos($1)}</SQL>
</Function>
<Function Group="False" ID="Ceil" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>{fn ceiling($1)}</SQL>
</Function>
<Function Group="False" ID="Exp" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>{fn exp($1)}</SQL>
</Function>
<Function Group="False" ID="Floor" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>{fn floor($1)}</SQL>
</Function>
<Function Group="False" ID="Log" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>{fn log($1)}</SQL>
</Function>
<Function Group="False" ID="Mod" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="Numeric"></Argument>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>{fn mod($1,$2)}</SQL>
</Function>
<Function Group="False" ID="Pi" InMacro="False" Type="Numeric">
<SQL>{fn pi()}</SQL>
</Function>
<Function Group="False" ID="Random" InMacro="False" Type="Numeric">
<Arguments>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>{fn rand($1)}</SQL>
</Function>
<Function Group="False" ID="Sign" InMacro="False" Type="Numeric">
<Arguments>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>{fn sign($1)}</SQL>
</Function>
<Function Group="False" ID="Sine" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>{fn sin($1)}</SQL>
</Function>
<Function Group="False" ID="Sqrt" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>{fn sqrt($1)}</SQL>
</Function>
<Function Group="False" ID="Tangent" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="Numeric"></Argument>
</Arguments>
<SQL>{fn tan($1)}</SQL>
</Function>
<Function Group="False" ID="Character_prompt" InMacro="True" Type="String">
<Arguments>
<Argument Type="String"></Argument>
</Arguments>
<SQL>@prompt($1,'A',,,)</SQL>
</Function>
<Function Group="False" ID="Numeric_prompt" InMacro="True" Type="Numeric">
<Arguments>
<Argument Type="String"></Argument>
</Arguments>
<SQL>@prompt($1,'N',,,)</SQL>
</Function>
<Function Group="False" ID="Date_prompt" InMacro="True" Type="DateTime">
<Arguments>
<Argument Type="String"></Argument>
</Arguments>
<SQL>@prompt($1,'D',,,)</SQL>
</Function>
</Functions>
</DBParameters>
Finally, create another snowflake.prm into the extensions\qt subfolder, with the following contents:
<?xml version="1.0" encoding="UTF-8"?><DBParameters xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../dbparameters.xsd">
<Configuration>
<Parameter Name="GROUPING_SETS">No</Parameter>
<Parameter Name="AUTOMATIC_CAST">No</Parameter>
<Parameter Name="ACCEPT_COLUMNS_IN_VIEW_DECLARATION">No</Parameter>
<Parameter Name="WITH_CLAUSE_SUPPORTED">Yes</Parameter>
<Parameter Name="Fct_IfThenElse">CASE WHEN( #1 ) THEN #2 ELSE #3 END</Parameter>
<Parameter Name="OWNER">Y</Parameter>
<Parameter Name="QUALIFIER">Y</Parameter>
<Parameter Name="REFRESH_COLUMNS_TYPE">T</Parameter>
<Parameter Name="CHECK_OWNER_STATE">Y</Parameter>
<Parameter Name="CHECK_QUALIFIER_STATE">Y</Parameter>
<Parameter Name="KEY_INFO_SUPPORTED">N</Parameter>
<Parameter Name="OUTERJOINS_GENERATION">NO</Parameter>
<Parameter Name="EVAL_WITHOUT_PARENTHESIS">N</Parameter>
<Parameter Name="USER_INPUT_DATE_FORMAT">{\d 'yyyy-mm-dd'}</Parameter>
<Parameter Language="ja" Name="USER_INPUT_DATE_FORMAT">{!d 'yyyy-mm-dd'}</Parameter>
<Parameter Name="USER_INPUT_NUMERIC_SEPARATOR">.</Parameter>
<Parameter Name="CONCAT">||</Parameter>
<Parameter Name="COMMA">|| ' ' ||</Parameter>
<Parameter Name="TUPLE_SEPARATOR">_</Parameter>
</Configuration>
</DBParameters>
Restart your client tools and SAP BI Platform server, the connector should now be available.
Final words
This is only a POC, so it is a quick a dirty copy with minimal edits of an existing connector (SQL Server). It works, though, and I do hope it will work for your use case.
However, the quick and dirty copy means that SQL function names in Information Design Tool may not match exactly what Snowflake supports, and that there may be glitches in the SQL generation.
So… Feel free to share your feedback using the comments !
Do also feel free to tweak the configuration files. They are 100% independent of the other connectors, so you can experiment with improvements to this connector without jeopardizing your other connections. If you do identify improvements to these files, please share them in the comments.
Thanks David for sharing this article. I was able to setup the connection without any issues.
Do you have update on when will SAP officially support Snowflake?
Hi Swapnil, it is planned for 4.3 and probably for a future SP of 4.2
It has been communicated to us that Snowflake support is planned for BI 4.2 sp8. Currently planned for release on February 14, 2020.
According the documents just release for SP8 it was clearly stated that no new functionality.
I just searched the notes file delivered with SP8 and nothing is noted.
Now my customer view point: Since the acquisition SAP has been slow to the "altar" with changes to support the changing cloud databases and giving the on-premise system native connectivity (think how Tableau is eating SAP BOBJ for lunch!).
Now there are the big 3 Azure, Snowflake, and Amazon and now native connectivity of KBs that show some support for the SAP Business Objects users.
Thanks David for these instructions!!! They work GREAT!!!!
it is in 4.2 Sp8 PAM, semantic layer attachment.
Nothing shows in the SP8 documents.
Hey Romaric!
Indeed you're right, it was added in SAP BI 4.2 SP8: https://blogs.sap.com/2020/03/12/snowflake-for-sap-businessobjects-4.2-sp08/
Thanks David. For this post.
I was able to connect to snowflake using client tool.
But the issue i have right now is i am not see Data Source Name of snowflake on BO server on linux box.
Did you managed to connect to DSN of BO server?
Are you able to create report in BI launchpad using snowflake connection of BO server?
Any suggestion will be of great help.
Hi Abishek,
You need to create the DSN manually by entering the relevant values into the odbc.ini file. The Snowflake documentation should provide information about this.
Please also be aware that deploying on Linux requires configuring access to a compatible ODBC driver manager. Again, Snowflake documentation should provide information about which driver managers you can use. You should then ensure that the WebI server has the path to this driver manager in its LD_LIBRARY_PATH.
Regards,
David.
During your testing did you run into this issue?
When I follow all the above direction and load the Snowflake 2.20.02 ODBC drivers, establish the connection via the 62 and 32 bit admin data source consoles.
[Snowflake][Snowflake] (25)
Result download worker error: Worker error: [Snowflake][Snowflake] (4)
REST request for URL https://djftjlsfcb1stg.blob.core.windows.net/results/01928de2-00e3-2296-0000-4a4900682552_0/main/data_0_0_7?sig=%2FeyFimGj1ufdj4DUjYcyrHwQ4yNPO9xLz%2F2xvKh2I%2Fg%3D&se=2020-02-29T16%3A56%3A00Z&sv=2018-11-09&spr=https&rsce=gzip&sp=rl&sr=b failed: CURLerror (curl_easy_perform() failed) - code=42 msg='Operation was aborted by an application callback'.
If you did encounter it let me know what you did to connect it.
Hi,
We have configured the snowflake with UDI connection.
While extracting data from
Transaction code RSA1 --> UDI connect- Executing the Infopackage ->ZTEST_SNF is the Data Source ->ZPAK**********************W40->
Getting following error "S:RSSDK:300 Query execution failed: No active warehouse select ed in the current session. Select an active wareh ouse with the 'use warehouse' command."
Can you pls suggest us whether to add/execute the snowflake warehouse name: WREHOUSE Name as prefix or whether we need to do any other connection setting different way.
pls let us know without using warehouse name we can able to pull the data or not?
Regards
Vijay S
Hello,
I'm not familiar with UDI, transaction codes, etc...
What I do know, though, is that you need a warehouse to run Snowflake queries. This is generally set in the ODBC driver configuration.
There are other ways you could set it (for instance using WebI's BEGIN_SQL), but I would recommend starting by setting it in the ODBC connection configuration.
Regards,
David.
Hi David,
We want communicate my SAP BW system to Snowflake system , could you please let us know how to do this.
Hi Phanikumar,
Your question is outside of the scope of this article, and of my area of expertise as well. The discussion here is about connecting the SAP BI Platform to Snowflake.
I would advise getting in touch with BW experts for your question.
Regards,
David.