You are on page 1of 33

2/15/2017 HANAbasedBWTransformation|SAPBlogs

GetStarted

Solutions Support Training Community Developer Partner

About

Community / Blogs

HANAbasedBWTransformation
May24,2016 | 5,746Views |
TorstenKessler
morebythisauthor

BW(SAPBusinessWarehouse)

SAPHANA | abap | BusinessIntelligence | bw | calcscenario | calculationscenario | data


warehouse | datawarehousing | hana | pushdown | saphana | transformation

share
0 share
3 tweet share
405

Follow

1HANAbasedBWTransformation
WhatisaSAPHANApushdowninthecontextofBWtransformations?
Whendoesapushdownoccur?Whataretheprerequisitesforforcinga
SAPHANApushdown?

BeforeIstarttoexplainhowaSAPHANAbasedBWtransformation
couldbecreatedandwhatprerequisitesarenecessarytoforceapush
downIwillprovidesomebackgroundinformationonthedifferences
betweenanABAPandSAPHANAexecutedBWtransformation.

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 1/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

AHANAbasedBWtransformationexecutesthedatatransformation
logicinsidetheSAPHANAdatabase.Figure1.1showsonthelefthand
sidetheprocessingstepsforanABAPbasedtransformationandonthe
righthandsideforaSAPHANAbasedtransformation.

This blog provides information on the pushdown feature for


transformationsinSAPBWpoweredbySAPHANA.Thecontenthereis
based on experiences with real customer issues. The material used is
partly taken from the upcoming version of the SAP education course
PDEBWPBWBackendundProgramming.

Thisblogisplannedaspartofablogserieswhichsharesexperiences
collectedwhileworkingoncustomerissues.Thelistedexplanationsare
primarilybasedonreleasesbetweenBW7.40SP09andBW7.5SP00.

Thefollowingadditionalblogsareplanned/available:

HANAbasedTransformation(deepdive)(Addedon06/17/2016)
HANA based BW Transformation Analyzing and
debugging(Addedon06/23/2016)
SAPHANAAnalysisProcess
Generalrecommendation
HANA based BW Transformation New features delivered by
7.50SP04(Addedon08/29/2016)
Routines
ErrorHandling
HANAbasedBWTransformationSAPNotes(Addedon06/09/2016)

AHANAbasedBWtransformationisanormalBWtransformation.Thenew
featureisthatthetransformationlogicisexecutedinsidetheSAPHANA
database.Fromadesigntimeperspective,intheAdministratorWorkbench,
thereisnodifferencebetweenaHANAbasedBWtransformationandaBW
transformationthatisexecutedintheABAPstack.BydefaulttheBWruntime
triestopushdownalltransformationstoSAPHANA.Beawarethatthereare
somerestrictionswhichpreventapushdown.Forexampleapushdowntothe
database(SAPHANA)isnotpossibleifaBWtransformationcontainsoneor
moreABAProutines(Start,End,ExpertorFieldRoutine).Formore
informationseeTransformationsinSAPHANADatabase.

RestrictionsforHANAPushDown

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 2/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

FurtherrestrictionsarelistedintheHelpPortal.However,thedocumentation
isnotallinclusive.Somerestrictionsrelatedtocomplexandhiddenfeatures
in a BW transformation are not listed in the documentation. In this context
hidden means that the real reason is not directly visible inside the BW
transformation.

The BAdI RSAR_CONNECTOR is a good example for such a hidden


feature. A transformation using a customer specific formula implementation
based on this BAdI cannot be pushed down. In this case the processing
modeisswitchedtoABAPautomatically.

The BW workbench offers a check button in the BW transformation UI to


checkiftheBWtransformationisSAPHANAexecutableornot.Thecheck
willprovidealistofthefeaturesusedintheBWtransformationwhichprevent
apushdown.

SAPisconstantlyimprovingthepushdowncapabilitybyeliminatingmoreand
morerestrictionsInordertoimplementcomplexcustomerspecificlogicinside
aBWtransformationitispossibletocreateSAPHANAExpertScriptbased
BWtransformations.ThisfeatureissimilartotheABAPbasedExpertRoutine
andallowscustomerstoimplementtheirowntransformationlogicinSQL
Script.Adetaileddescriptionofthisfeatureisincludedlateron.

SAPNote2057542Recommendation:UsageofHANAbased
Transformationsprovidessomebasicinformationandrecommendations
regardingtheusageofSQLScriptinsideBWtransformations.

1.1HANAPushDown
WhatisaSAPHANApushdowninthecontextofBWtransformations?
Whendoesapushdownoccur?Whataretheprerequisitesforforcinga
SAPHANApushdown?

Before I start to explain how a SAP HANA based BW transformation


couldbecreatedandwhatprerequisitesarenecessarytoforceapush
down I will provide some background information on the differences
betweenanABAPandSAPHANAexecutedBWtransformation.

A HANA based BW transformation executes the data transformation


logicinsidetheSAPHANAdatabase.Figure1.1showsonthelefthand
sidetheprocessingstepsforanABAPbasedtransformationandonthe
righthandsideforaSAPHANAbasedtransformation.

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 3/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

Figure1.1:ExecutionofSAPBWTransformations

An ABAP based BW transformation loads the data package by


package from the source database objects into the memory of the
Application Server (ABAP) for further processing. The BW
transformation logic is executed inside the Application Server (ABAP)
and the transformed data packages are shipped back to the Database
Server.TheDatabaseServerwritestheresultingdatapackagesintothe
targetdatabaseobject.Therefore,thedataistransmittedtwicebetween
databaseandapplicationserver.

During processing of an ABAP based BW transformation, the source


data package is processed row by row (rowbased). The ABAP based
processing allows to define fieldbased rules, which are processed as
sequentialprocessingsteps.

For the HANA based BW transformation the entire transformation


logic is transformed into a CalculationScenario (CalcScenario). From a
technicalperspectivetheMetadatafortheCalcScenarioarestoredasa
SAPHANATransformationinBW(seetransactionRSDHATR).

ThisCalcScenarioisembeddedintoaColumnView.Toselectdatafrom
thesourceobject,theDTPcreatesaSQLSELECTstatementbasedon
this ColumnView (see blog HANA based BW Transformation
Analyzinganddebugging)andtheprocessinglogicoftheCalcScenario

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 4/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

appliesalltransformationrules(definedintheBWtransformation)tothe
selected source data. By shifting the transformation logic into the
CalcScenario, the data can be transferred directly from the source
object to the target object within a single processing step. Technically
this is implemented as an INSERT AS SELECT statement that reads
fromtheColumnViewandinsertsintothetargetdatabaseobjectofthe
BWtransformation.ThiseliminatesthedatatransferbetweenDatabase
Server and Application Server (ABAP). The complete processing takes
placeinSAPHANA.

1.2CreateaHANAbasedBWTransformation
ThefollowingstepsarenecessarytopushdownaBWtransformation:

CreateaSAPHANAexecutableBWtransformation
CreateaDataTransferProcess(DTP)toexecutetheBW
transformationinSAPHANA

1.2.1CreateastandardSAPHANAexecutableBW
transformation
A standard SAP HANA executable BW transformation is a BW
transformationwithoutSAPHANAspecificimplementation,whichforces
aSAPHANAexecution.

The BW Workbench tries to push down new BW transformations by


default.

The activation process checks a BW transformation for unsupported


push down features such as ABAP routines. For a detailed list of
restrictionsseeSAPHelpTransformationsinSAPHANADatabase.If
none of these features are used in a BW transformation, the activation
process will mark the BW transformation as SAP HANA Execution
Possiblesee(1)inFigure1.2.

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 5/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

Figure1.2:FirstsimpleSAPHANAbasedTransformation

WhenaBWtransformationcanbepusheddown,theactivationprocess
generates all necessary SAP HANA runtime objects. The required
metadata is also assembled in a SAP HANA Transformation (see
TransactionRSDHATR).TherelatedSAP HANA Transformation for a
BWtransformationcanbefoundinmenuExtras=> Display Generated
HANATransformation,see(2)inFigure1.2.

From a technical perspective a SAP HANA Transformation is a SAP


HANA Analysis Process (see Transaction RSDHAAP) with a strict
naming convention. The naming convention for a SAP HANA
Transformation is TR_<< Program ID for Transformation
(Generated)>>,see(3)inFigure1.2.ASAPHANATransformationis
onlyaruntimeobjectwhichcannotnotbeenexplicitcreatedormodified.

ThetabCalculationScenarioisonlyvisibleiftheExportMode(Extras=>
Export Mode On/Off) is switched on. The tab shows the technical
definition of the corresponding CalculationScenario which includes the
transformation logic and the SQLScript procedure (if the BW
transformationisbasedonaSAPHANAExpertScript).

If the transformation is marked as SAP HANA Execution Possible, see


(1)inFigure1.2thefirstpreconditionisgiventopushdownandexecute
theBWtransformationinsidethedatabase(SAPHANA).Thatmeansif
the flag SAP HANA Execution Possible is set the BW transformation is

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 6/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

able to execute in both modes (ABAP and HANA) and the real used
processing mode is set inside the DTP. To be prepared for both
processing modes the BW transformation framework generates the
runtimeobjectsforbothmodes.ThereforetheGeneratedProgram(see
Extras=>DisplayGeneratedProgram)fortheABAPprocessingwillalso
bevisible.

ThenextstepistocreatethecorrespondingDTP,seeparagraph1.2.4
Create a Data Transfer Process (DTP) to execute the BW
transformationinSAPHANA.

1.2.2CreateaSAPHANAtransformationwithSAP
HANAExpertScript
If the business requirement is more complex and it is not possible to
implement these requirements with the standard BW transformation
feature, it is possible to create a SQLScript procedure (SAP HANA
ExpertScript).WhenusingaSAPHANAExpertScripttoimplementthe
business requirements the BW framework pushes the transformation
logicdowntothedatabase.Beawarethatthereisnooptiontoexecute
a BW transformation with a SAP HANA Expert Script in the processing
modeABAP,onlyprocessingmodeHANAapplies.

From the BW modelling perspective a SAP HANA Expert Script is very


similar to an ABAP Expert Routine. The SAP HANA Expert Script
replaces the entire BW transformation logic. The SAP HANA Expert
Script has two parameters, one importing (inTab) and one exporting
(outTab)parameter.Theimportingparameterprovidesthesourcedata
package and the exporting parameter is used to return the result data
package.

However, there are differences from the perspective of implementation


betweenABAPandSQLScript.AnABAPprocessedtransformationloops
over the source data and processes them row by row. A SAP HANA
Expert Script based transformation tries to processes the data in one
block(INSERTASSELECT).Togetthebestperformancebenefitofthe
push down it is recommended to use declarative SQLScript Logic to
implement your business logic within the SAP HANA Expert Script, see
blogGeneralrecommendations.

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 7/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

Thefollowingpointsshouldbeconsideredbeforethebusiness
requirementsareimplementedwithSAPHANAExpertScript:

ABAPisfromtodaysperspective,themorepowerfullanguage
thanSQLScript
Developmentsupportfeaturessuchassyntaxhighlighting,
forwardnavigationbasedonerrormessages,debuggingsupport,
etc.isbetterintheABAPdevelopmentenvironment.
SQLscriptdevelopmentexperienceiscurrentlynotas
widespreadasABAPdevelopmentexperience
AHANAexecutedtransformationisnotalwaysfaster

From the technical perspective the SAP HANA Expert Script is a SAP
HANA database procedure. From the BW developer perspective the
SAP HANA Expert Script is a SAP HANA database procedure
implemented as a method in an AMDP (ABAP Managed Database
Procedure)class.

TheAMDPclassisbegeneratedbytheBWframeworkandcanonlybe
modified within the ABAP Development Tools for SAP NetWeaver
(ADT), see https://tools.hana.ondemand.com/#abap. The generated
AMDP class cannot not be modified in the SAP GUI like Class editor
(SE24)ortheABAPWorkbench(SE80).Thereforeitisrecommendedto
implement the entire dataflow in the Modeling Tools for SAP BW
powered by SAP HANA, see https://tools.hana.ondemand.com/#bw.
The BW transformation itself must still be implemented in the Data
WarehousingWorkbench(RSA1).

Next Ill give a step by step introduction to create a BW transformation


withaSAPHANAExpertScript.

Step1:StartaSAPHANAStudiowithbothinstalledtools:

ABAPDevelopmentToolsforSAPNetWeaver(ADT)and
ModelingToolsforSAPBWpoweredbySAPHANA

NowwemustswitchintotheBWModelingPerspective.Toopenthe
BWModelingPerspectivegotoWindow=>Other..andselectinthe
upcomingdialogtheBWModelingPerspective,seeFigure1.3.

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 8/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

Figure1.3:OpentheBWModelingPerspective

To open the embedded SAP GUI a BW Project is needed. It is


necessary to create the BW Project before calling the SAP GUI. To
createanewBWProjectopenFile=>New=>BWProject.Tocreatea
BWProjectaSAPLogonConnectionisrequired,choosetheSAPLogon
connectionandusetheNextbuttontoenteryouruserlogondata.

Recommendations: After entering your logon data it is possible to


finalize the wizard and create the BW Project. I recommend to use the
Nextwizardpagetochangetheprojectname.Thedefaultprojectname
is:

<SystemID>_<Client>_<Username>_<Language>

Inormallyaddattheendapostfixfortheprojecttypesuchas_BWfor
the BW Project. For an ABAP project later on I will use the postfix
_ABAP.ThereasonIdothatisbothprojectsareusingthesamesymbol
intheprojectviewerandtheusedpostfixmakesiteasiertoidentifythe
rightproject.

OncetheBWProjectiscreatedwecanopentheembeddedSAPGUI.
The BW Modeling perspective toolbar provides a button to open the

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 9/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

embeddedSAPGUI,seeFigure1.4.

Figure1.4:OpentheembeddedSAPGUIinEclipse

Choose the created BW Project in the upcoming dialog. Next start the
BWWorkbench(RSA1)withintheembeddedSAPGUIandcreatethe
BWtransformationorswitchintotheeditmodeforanexistingone.

TocreateaSAPHANAExpertScriptopenEdit=>Routines=>SAPHANA
Expert Script Create in the menu of the BW transformation. Confirm the
request to delete the existing transformation logic. Keep in mind that all
implemented stuff like Start End or FieldRoutines and formulas will be
deletedifyouconfirmtocreateaSAPHANAExpertScript.

In the next step the BW framework opens the AMDP class by calling the
ABAP Development Tools for SAP NetWeaver (ADT). For this an ABAP
projectisneeded.SelectanexistingABAPProjectorcreateanewoneinthe
dialog.

AnewwindowwiththeAMDclasswillappear.Sometimesitisnecessaryto
reloadtheAMDPclassbypressingF5.Enteryourcredentialsifprompted.

The newly generated AMDP class, see Figure 1.5, cannot not directly be
activated.

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 10/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

Figure1.5:NewgeneratedAMDPClass

BeforeIexplaintheelementsoftheAMDPclassandthemethodIwillfinalize
the transformation with a simple valid SQL statement. The used SQL
statement,asshowninFigure1.6, is a simple 1:1 transformation and is only
usedasanexampletoexplainthetechnicalbehavior.

Figure1.6:SimplevalidAMDPMethod

Now we can activate the AMDP class and go back to the BW


transformationbyclosingtheAMDPclasswindow.Nowitisnecessaryto
activate the BW transformation also. For a BW transformation with a

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 11/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

SAP HANA Expert Script the flag SAP HANA Execution possible is set,
seeFigure1.7.

Figure1.7:BWTransformationwithSAPHANAScriptProcessing

As explained before, if you use a SAP HANA Expert Script the BW


transformation can only been processed in SAP HANA. It is not possible to
executethetransformationontheABAPstack.ThereforethegeneratedABAP
program (Extras => Display Generated Program) is not available for a BW
transformationwiththeprocessingtypeSAPHANAExpertScript.

1.2.2.1Sortingaftercallofexpertscript

Within the BW transformation the flag Sorting after call of expert script, see
Figure1.8,(Edit=> Sorting after call of expert script) can be used to ensure
thatthedataiswritteninthecorrectordertothetarget.

Figure1.8:Sortingaftercallofexpertscript

If the data is extracted by delta processing the sort order of the data
couldbeimportant(dependingonthetypeoftheuseddeltaprocess).

By default, the flag is always set for all new transformations and its
recommendedtoleaveitunchanged.

Foroldertransformations,createdwithareleasebefore7.40SP12,the
flagisnotsetbydefault.Sothecustomercansettheflagiftheyneed
thedatainaspecificsortorder.

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 12/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

Keepinmindthattheflaghasimpactattwopoints:

Theinput/outputstructureoftheSAPHANAExpertScriptis
enhanced/reducedbythefieldRECORD
TheresultdatafromtheSAPHANAExpertScriptwillbesortedby
thenewfieldRECORD,iftheflagisset,aftercallingtheSAPHANA
ExpertScript

TheinTabandtheoutTabstructureofaSAPHANAExpertScriptwillbe
enhancedbythefieldRECORDiftheflagisset.TheaddedfieldRECORDisa
combination of the fields REQUESTSID, DATAPAKID and RECORD from the
sourceobjectofthetransformation,seeFigure1.9.

Figure1.9:ConcatenatedfieldRECORD

TheRECORDfieldfromtheoutTabstructureismappedtotheinternalfield
#SOURCE#.1.RECORD.Lateroninarownumnodeofthe
CalculationScenariotheresultdatawillbesortedbythenewinternalfield
#SOURCE#.1.RECORD,seeFigure1.10.

Figure1.10:CalculationScenarionoterownum

1.2.2.2TheAMDPClass
The BW transformation framework generates an ABAP class with a method
called PROCEDURE. The class implements the ABAP Managed Database
Procedure(AMDP)markerinterfaceIF_AMDP_MARKER_HDB.Theinterface
markstheABAPclassasanAMDPclass.AmethodofanAMDPclasscan
be written as a database procedure. Therefore the BW transformation
framework creates a HANA specific database procedure declaration for the
methodPROCEDURE,seeFigure1.11:

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 13/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

Figure1.11:MethodPROCEDUREdeclaration

This declaration specifies the method to the HANA database (HDB), the
languagetoSQLSCRIPTandfurtherondefinesthatthedatabaseprocedureis
READONLY. The read only option means that the method / procedure must
be sideeffect free. Sideeffect free means that only SQL elements (DML)
couldbeusedtoreaddata.ElementslikeDELETE,UPDATE,INSERTused
on persistent database objects are not allowed. These data modification
statementscanalsonotbeencapsulatedinafurtherprocedure.

YoucannotdirectlyreaddatafromadatabaseobjectmanagedbyABAPlikea
table,vieworprocedureinsideanAMDPprocedure,see(1)inFigure1.12.A
database object managed by ABAP has to be declared before they can used
inside an AMDP procedure, see (2). For more information about the USING
optionseeAMDPMethodsintheABAPdocumentation.

Modificationofthemethoddeclaration

IncaseofreadingfromfurthertablesinsidetheSQLScriptitcouldbe
necessarytochange(enhance)themethoddeclarationbyaddingtheUSING
option.Itisimportanttoensurethatthefirstpartofthemethoddeclarationis
stableandwillnotbechanged.Donotchangethefollowingpartofthe
declaration:

METHODPROCEDUREBYDATABASEPROCEDUREFORHDBLANGUAGE
SQLSCRIPTOPTIONSREADONLY

TheUSINGoptionmustbeaddedattheendofthedeclarationpart,see
Figure1.12

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 14/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

Figure1.12:DeclarationofDDICobjects

The AMDP framework generates wrapper objects for the declared database
object managed by ABAP . The view
/BIC/5MDEH7I6TAI98T0GHIE3P69D1=>/BIC/ATK_RAWMAT2#covw in (3) was
generatedforthedeclaredtable/BIC/ATK_RAWMAT2in(2).TheblogUnderthe
HANAhoodofanABAPManagedDatabaseProcedureprovidessomefurther
background information about AMDP processing and which objects are
generated.

AMDPClassmodification

OnlythemethodimplementationbelongstotheBWtransformationMetadataand
onlythispartoftheAMDPclasswouldbeenstored,seetableRSTRANSCRIPT.

CurrentlytheABAPDevelopmentToolsforSAPNetWeaver(ADT)doesnotprotect
the source code which should not been modified, like in an ABAP routine. That
meansallmodificationsintheAMDPclassoutsidethemethodimplementationwill
notbetransportedtothenextsystemandwillbeoverwrittenbythenextactivation
process.TheBWtransformationframeworkregeneratestheAMDPclassduringthe
activationprocess.

LateronIllprovidesomegeneralrecommendationsinaseparateblog
whicharebasedonexperienceswecollectedincustomer
implementationsandcustomerincidents.Thegeneralrecommendation
willcoverthefollowingtopics:

Avoidpreventingfilterpushdown
Keepinternaltablesmall
Initialvalues
https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 15/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

Columntypedefinition
Avoidimplicitcasting
UseofDISTINCT
PotentialpitfallatUNION/UNIONALL
InputParameterinsideunderlyingHANAobjects
Internalvs.externalformat

AMDPClassname

UntilversionBW7.50SP04theAMDPclassnameisgeneratedbasedonthefield
RSTRANTRANPROG.FromBW7.50SP04thefieldRSTRANEXPERTisusedto
generatetheAMDPclassname.ThemetadatastoragefortheAMDProutineswas
adjustedtoaligntheAMDPmetadatastorageanalogoustotheABAPmetadata.

AMDPdatabaseprocedure

Sometime, in BW 7.40, it could be happen that the corresponding database


procedureisnotgenerated.ThereportRSDBGEN_AMDPcanbeusedtogenerate
thedatabaseproceduresforagivenAMDPclass.

1.2.3DataflowwithmorethanoneBW
transformation
The push down option is not restricted on data flows with one BW
transformation. It is also possible to push down a complete data flow with
several included BW transformations (called stacked data flow). To get the
best performance benefits from the push down it is recommended to stack a
data flow by a maximum of three BW transformations. More are possible but
notrecommended.

The used InfoSources (see SAP Help: InfoSource and


RecommendationsforUsingInfoSources)inastackeddataflowcanbe
usedtoaggregatedatawithinthedataflowiftheprocessingmodeisset
toABAP.IftheprocessingmodesettoSAPHANAthedatawillnot be
aggregated as set in the InfoSource settings. The transformation itself
does not know the processing mode, therefore you will not get a
message about the InfoSource aggregation behavior. The used
processingmodeissetintheusedDTP.

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 16/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

That means, the BW transformation framework prepares the BW


transformationforbothprocessingmodes(ABAPandHANA).Duringthe
preparationtheframeworkwillnotthrowawarningregardingthelackof
aggregationintheprocessingmodeHANA.

By using the check button for the HANA processing mode, within the BW
transformation,youwillgetthecorrespondingmessage(warning)regardingthe
InfoSourceaggregation,seeFigure1.13

Figure1.13:HANAprocessingandInfoSources

CalculationScenarioinastackeddataflow

The corresponding CalculationScenario for a BW transformation is not


available if the source object is an InfoSource. That means the tab
CalculationScenario is not available in the export mode of the SAP HANA
transformation,seeExtras=>DisplayGeneratedHANATransformation.The
sourceobjectforthisCalculationScenarioisanInfoSourceandanInfoSource
cannot be used as data source object in a CalculationScenario. The related
CalculationScenario can only be obtain by using the SAP HANA
TransformationfromthecorrespondingDTP.Illexplainthisbehaviorlateron
intheblogHANAbasedTransformation(deepdive).

1.2.4CreateaDataTransferProcess(DTP)to
executetheBWtransformationinSAPHANA
TheDataTransferProcess(DTP)toexecuteaBWtransformationprovidesa
flagtocontroltheHANApushdownofthetransformation.TheDTPflagSAP
HANAExecution,see(1)inFigure1.14,canbecheckedoruncheckedbythe
user.However,theflagintheDTPcanonlybecheckedifthetransformation
ismarkedasSAPHANAExecutionPossible,see(1)inFigure1.2.Bydefault
theflagSAPHANAExecutionwillbesetforeachnewDTPif

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 17/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

the BW transformation is marked as SAP HANA execution


possibleand
theDTPdoesnotuseanyoptionswhichpreventapushdown.

UptoBW7.50SP04thefollowingDTPoptionspreventapushdown:

SemanticGroups
ErrorHandlingTrackRecordsafterFailedRequest

The DTP UI provides a check button, like the BW transformation UI, to


validateaDTPforHANApushdown.IncaseaDTPisnotabletopushdown
the data flow (all involved BW transformations) logic, the check button will
providethereason.

Figure1.14:DTPforthefirstsimpleSAPHANAbasedTransformation

InthesimpletransformationsampleaboveImusingoneBWtransformationto
connect a persistent source object (DataSource (RSDS)) with a persistent
target object (Standard DataStore Object (ODSO)). We also call this type a
nonstacked dataflow Ill provide more information about nonstacked and
stackeddataflowslater.TherelatedSAPHANATransformation for a DTP
can be found in menu Extras => Display Generated HANA Transformation,
see(2)inFigure1.14. In case of a nonstacked data flow the DTP uses the
SAPHANATransformationoftheBWtransformation,see(3)inFigure1.14.

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 18/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

TheusageofafilterintheDTPdoesnotpreventtheHANApushdown.ABAP
RoutinesorBExVariablescanbeusedaswell.Thefiltervalue(s)iscalculated
inaprestepandaddedtotheSQLSELECTstatementwhichreadsthedata
fromthesourceobject.Wewilllookintothislaterinmoredetail.

1.2.5ExecuteaSAPHANAbasedtransformation
Fromtheexecutionperspective,regardingthehandling,aHANAbased
transformation behaves comparable to an ABAP based transformation,
simply press the Execute button or execute the DTP form a process
chain.

LateronIwillprovidemoreinformationaboutpackagingandparallel
processing.

1.2.6Limitations
ThereisnooptiontoexecuteatransformationwithaSAPHANAScript
on the ABAP application server. With BW 7.50 SP04 (the next feature
pack) it is planned to deliver further option to use SAP HANA Scripts
(Start, End and FieldRoutines are planned) within a BW
transformation.

1.2.7Featurelistthatpreventsapushdown(Addedon
07/20/2016)

TheofficialSAPHelpprovidesasmallfeaturelistwhichpreventsa
HANAexecution.Hereareamoredetailedlist:

QueriesasInfoProviderarenotsupportedasthesource
ABAPStart,EndandFieldRoutines
WithBW7.50SP04correspondingSQLScriptroutinesare
supported
Formulaelementsthatpreventsapushdown
DATECONV,
WEEK_TO_1ST_DAY,
FISCPER_CALMONTH,
CALMONTH_FISCPER,
CONDENSE,
ABORT_PACKAGE,
SKIP_RECORD,
SKIP_RECORD_AS_ERROR

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 19/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

LOG_MESSAGE
Customercreatedformulas(BAdIRSAR_CONNECTOR)
TransferroutineCharacteristic/InfoObjectRoutine)arenot
supported
0SOURSYSTEMand0LOGSYSaresupported
RuletypeTIME(TimeCharacteristic)withTimeDistributionare
notsupported
Rulegroupsarenotsupported
InfoObjectwithtimedependentAttributesarenotsupportedas
sourceandastarget
SupportedwithBW7.50SP04
CubelikeDataStoreObjects(advanced)arenotsupportedas
target
SupportedwithBW7.50SP04
DataStoreObjects(advanced)withnoncumulativekeyfigure(s)
arenotsupportedastarget
SupportedwithBW7.50SP04
ToreaddatafromDataStoreobjects,theentirekeymustbe
provided
Nearlineconnections
SupportedwithBW7.50SP04(Addedon09/21/2016)
DTPOptions
ErrorHandlingnotsupported
SupportedwithBW7.50SP04
SemanticGroups

FurtherinformationaboutsupportedSAPHANAexecutionfeatureare
providedintheSAPnote:2329819SAPHANAexecutioninDTPs
(DataTransferProcesses)Optimizations.

AlertModerator

22Comments
YoumustbeLoggedontocommentorreplytoapost.

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 20/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

NileshPathak

June1,2016at1:35pm

HiTorsten,thanksforblogwewereworkingonBW7.5SP2HANAproject&
foroneofrequirementweneedtodeletesomedatacominginfromsourceERP
toBW.likeinBWwewouldnormallyusestartroutinetodeleteunwanted
recordsbuthereasweknowprocessingatHANADBwillnotbesupported
whenweuseStartroutine.
HencewewerelookingtouseHANASQLscriptinordertoleverageHANA
hencepleasecanyouletusknowifthisispossibletodeletecertainrecords
basedonsomeconditionletssay
IwanttodeleteallrecordsfromsourcewherefieldXisblank.
IfthisispossiblethencanyouadvisesyntaxplsaswhenIwastryingtouse
DELETEinprocedureitwasgivingerror.
Henceappreciateyourquickresponse.
regards
Nilesh

TorstenKessler Postauthor

June1,2016at2:42pm

Hi,
SQLScriptStart,EndandFieldroutinesareplannedforSP04.
Buttodelete(donotfurtherprocess)specificrowsyoucanfilterthe
inTab.
Foryoursampleaboveyoucanwritethewholecontentfromthe
inTabintoatemporarytableandfilterouttherowswhichyoudonot
wanttoprocessfurtheron.Example:
tmpInTab=
SELECT*
FROM:inTab
WHEREfield_X=
Iftherenofurtherlogicrequiredyoucanalsowritethefiltered
resultintotheoutTab:

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 21/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

outTab=
SELECT<fieldlist>
FROM:inTab
WHEREfield_X=
HereyoumustexplicitmapthefieldsfromtheinTabtothefieldfromthe
outTab!
Keepinmindthatthelogicshouldbeimplementedbyusingdeclarative
SQLScriptlogic
toenabletheoptimizertogetthebestruntimeperformance.
Illprovidemoreinformationaboutrecommendationinoneofthenext
upcomingblogs(Generalrecommendation).
Regrads
Torsten

TorstenKessler Postauthor

June9,2016at11:30am

Shortupdate:Addedbox:Modificationofthemethoddeclaration

ErdemPekcan

June10,2016at2:42pm

Greatinformation,thankyou!
WhatcouldbethereasonthatIcantseethegeneratedprocedureinFigure1.12
?
Also,Itrytomapasamplefieldwithalookup.Howshouldbethewholesytax?

ImworkingonaBW7.4SP14andHANA1.00.112.01system.
Thanks!

TorstenKessler Postauthor

June10,2016at7:22pm

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 22/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

Hi,
1.==WhatcouldbethereasonthatIcantseethegeneratedprocedurein
Figure1.12?==
Youmean,youhadcreatedatransformationwithaSAPHANAExpert
ScriptandactivatedtheAMDPclassandthedatabaseprocedure
<<CLASSNAME>>=>PROCEDUREisnotinthefolderPROCEDURE
intheschemaSAP<SID>?
Inthatcaseyoucanfirsttrytoactivatethetransformation.Isthe
DBprocedurestillnotthere.OpenthereportRSDBGEN_AMDP
enteryourclassnameandchoosetheoptionCreatedatabaseobjects.
Butoptiontwoshouldnotbenecessary.

2.==Also,Itrytomapasamplefieldwithalookup.Howshouldbethe
wholesytax?==
First,ourrecommendationisusestandardtransformationfunctionifit
possible.Firstcheckifastandardrulelike(Masterdataread,readfrom
classicDSO,readfromadvancedDSO)usable.Wedenhancedthe
lookuprulesothatyoualsocanmapfieldincasetheoriginalInfoObjects
arenotinthesourcestructureavailable.SeeSAPHelp:
ReadfromDataStoreObject(advanced):
ToreadfromaDataStoreobject(advanced),thesystemfirstprovides
youwithaproposalforhowthefieldscanbeassigned.Thekeyfields
mustbeassignedtothesourcefieldsofthetransformation.When
assigningthefields,thenamesdonotneedtobeidentical.

Incasethestandardrulesdoesntfityoucanread(join)thedata
fromatablewiththesourcedata(inTab).
ThefollowingsampleexplainshowIreadthefieldTK_VENDOR
formthetable/BIC/ATK_RAWMAT1andaddittotheoutTab.Todothat
IJOINtheinTabwiththetable/BIC/ATK_RAWMAT1.
outTab=SELECTintab.MATERIAL,
intab.PLANT,
...
mat.TK_VENDOR,
...
RECORD
FROM:inTabasintab
JOIN/BIC/ATK_RAWMAT1asmat
ONintab.MATERIAL=mat.TK_MAT
https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 23/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

MoreinformationabouttheJOINoperationcouldbefoundhereAll
aboutJoinsusingSQLinHANA
Hopethisishelpful
Regrads
Torsten

ErdemPekcan

June11,2016at5:10pm

HiTorsten,
Thanksforthedetailledexplanation.
WhatImeantwas,inFigure1.12theimagemarkedwith3
includesthefollowingline:
/BIC/5MDEH7I6TAI98T0GHIE3P69D1=>/BIC/ATK_RAWMAT2#cov
w
Thepart#covwseemstobeageneratedsuffix,right?
IsitgeneratedonceUSINGZTABLEisaddedandacivatedwithin
thescript?
Ifso,thatssomethingIcouldntfind.
Iwilltrythestandardoptionaslongaspossible.
Thanksagain.

TorstenKessler Postauthor

June13,2016at8:15am

Hi,
thatsrightthegeneratedobject
/BIC/5MDEH7I6TAI98T0GHIE3P69D1=>/BIC/ATK_RAWMAT2#covw

isaview(notaprocedure)andcouldbefindinthefolderview(not
columnview).
Torsten

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 24/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

AbyJacob

June13,2016at6:31am

ExcellentInfo
Thanks!!

VladimirKolodyazhny

June13,2016at12:05pm

HelloTorsten!
Wearecurrentlyfacingtheissuewithfilteringincomingdatabydtpfilters.
Inoticed,youreplanningsomearticlesabout
InputParameterinsideunderlyingHANAobjects

Maybeyoucanprovidesomeadditionalsourcestolookuponthistopics?

TorstenKessler Postauthor

June16,2016at12:16pm

HiVladimir,
Inoneofmynextblogs(generalrecommendations)Iwillprovidea
samplewhereIuseaCaclViewwithaninputvariable.TheCalcViewis
partofaCompositeProviderandtheCompositeProviderisasourceofa
transformation.
IllshowincasetheinputparameterismandatorythattheDTPfilteris
alsomandatory.

SorrybutIhavenofurthersourcematerialtoprovide.

Maybeyoucanexplainyourissueinmoredetailsorincaseofanerror
createanincident.

Torsten

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 25/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

VladimirKolodyazhny

June16,2016at12:52pm

HiTorsten!
IveopendiscussionInputparametersinHANADTPsfrom
CompositeProvider,VirtualProvideronHANAViews.Erroroccurs
whenItrytocheckHANAExecutioninDTPparameters.

abilashn

June13,2016at3:32pm

ExcellentBlogTorsten.. withdetailexplanation,screenshots.Bookmarkedit
ascurrentlyImovedfromBWtoHANA.DefinitelyabigLikefrommyend..
(expectingwillbebacktobwonceHANAprojectiscompleted)

TorstenKessler Postauthor

July20,2016at12:34pm

ShortupdateAddedparagraph:
1.2.7Featurelistthatpreventsapushdown

JanEeckhaut

August25,2016at12:46pm

Hello,
Verygoodblog,thankyou!
Iseeinyourexamplethatyourefertotheactivetableifyouneedtoreaddata
fromanotherADSOinyourSQLscript.
TobeNLSsafe,wereadtheExternalSAPHANAview

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 26/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

(example:SELECT*FROMsystemlocal.bw.bw2hana::DADSO1).
WhatisyourideaaboutusingSQLscriptinBWandNLS?
Regards

TorstenKessler Postauthor

December12,2016at10:59am

Hi,
regardingyourNLSquestion,doyoumean
aNLSlocatedobjectassourceobjectofthetransformationor
doyouwanttoreaddatafromaNLSlocatedobjectinsidethe
SQLscript?
Torsten

JanEeckhaut

December15,2016at9:31am

HelloTorsten,
ImeantreadingdatafromaNLSlocatedobjectinsidetheSQL
script.
Regards,
Jan

TorstenKessler Postauthor

December16,2016at1:52pm

Okay,Illcheckthisandcomebacktoyou.
Becauseofyearsenditcouldtakesomedays.
Torsten

VasanthGutta
https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 27/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

December12,2016at9:00am

NiceblogTorsten
IamwonderingifyoucanprovideussomesamplecodeforreadingainTabinto
temporarytableandanotherdsotablefromBWintoanothertemporarytable
withlookupusingABAPtemporaryworkareastructureandupdatingusingloop
functionstogetthefinalresult.
Sorryforaskinghelpinthiskindofcoding.Anyhelponsampletypostofollowin
AMDPSQLscript?
suchashowweusedtowriteexpertABAProutinesasbelow.
LoopatSOURCE_PACKAGEintoworkarea_source_package.
readtable/BIC/Ablablabla
intoworkarea_looptable
withkeykey=workarea_source_package.
ifsysubrc=0.
wa_result_packagefield1=wokarea_sourcepackagefield1.
wa_result_packagefield2=worarea_looptablefield2.
wa_result_packagefield3=worarea_looptablefield4.



endif.
appendworkarea_result_packagetoRESULT_PACKAGE.
endloop.

TorstenKessler Postauthor

December12,2016at10:45am

Hi,
asIwroteabove:
However,therearedifferencesfromtheperspectiveof
implementationbetweenABAPandSQLScript.AnABAPprocessed
transformationloopsoverthesourcedataandprocessesthemrow
byrow.ASAPHANAExpertScriptbasedtransformationtriesto
processesthedatainoneblock(INSERTASSELECT).Togetthe
bestperformancebenefitofthepushdownitisrecommendedto
usedeclarativeSQLScriptLogictoimplementyourbusinesslogic

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 28/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

withintheSAPHANAExpertScript,seeblogGeneral
recommendations.
Thatmeansyouhavetoconsiderthatapushdowntransformation
isprocessedinoneSQL(INSERTASSELECT)statement.
Thereforeageneralrecommendationistoavoidstatementslike:
LOOP,
IFTHEN,
WHILE,

likewearetypicalusinginABAP.
InaSQLScriptroutine(orAMDProutine)theparameterinTabis
thesameastheSOURCE_PACKAGEintheABAProutine.
IfyouwanttoselectsomedatafromtheinTabyoucancreatea
selectontheinTab:
tmpTable=SELECTCOLUMN_A,COLUMN_B,from:inTab
ToreaddatafromanactiveDSOtableyoucanalsocreateaselect
statement:
tmpDSO=SELECTCOLUMN_X,COLUMN_Yfrom
/BIC/Ablablabla
NowyoucancreateaUNIONoraJOINbasedonbothtmptable.It
isalsopossibletojoinoruniontheinTabdirectlywithadatabase
table.
Butkeepinmind,ifyoucreatetocomplexstatementsitis
sometimehardtoreadandunderstandthecodingifyousearchfor
anerrororifyousearchingthereasonforerroneousdata.
Otherwise,compilingthewholelogicinonecomplexstatement
canincreasetheruntmie.
Sothegoalistofindawayinbetween,agoodruntimeandcoding
thatcanbemaintained.
Torsten

VasanthGutta

December13,2016at12:19am

ThanksTorsten,

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 29/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

ThereasonIamaskingthetraditionalABAPwaybecausewehavelittlebit
differentsituation.Ihavetriedtodostraightselectstatementsbutitdoesnthelp
transformingthedata.Letmeexplainoursituationwithourdevelopments
Wehaveseveralhierarchiescomingfromseveralsources.Ihaveplannedto
loadthemusingBODSintoBWPSAandthentoaDSOwithoutany
transformations.FromthereweareagainloadingintoanotheraDSOfrom
flatteningithoweverthedataisnotasstraightasothertransformationfor
lookups.
Wehave5fieldsupto8levelsofhierarchy
1.Childhierarchycode
2.Levelno
3.Parenthierarchycode
4.Childhierarchycodeattribute1
5.Childhierarchycodeattribute2
MytargetaDSOislikebelow:
Lvl8CodeKey
Lvl8codeattr1
Lvl8codeattr2
Lvl7Code
Lvl7codeattr1
Lvl7codeattr2
Lvl6Code
Lvl6codeattr1
Lvl6codeattr2
Lvl5Code
Lvl5codeattr1
Lvl5codeattr2
Lvl4Code
Lvl4codeattr1
Lvl4codeattr2
Lvl3Code
Lvl3codeattr1
Lvl3codeattr2
Lvl2Code
Lvl2codeattr1
Lvl2codeattr2
Lvl1Code
Lvl1codeattr1
Lvl1codeattr2
https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 30/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

NowIamtryingtotransformthedataandflattenitfrommakingthetailendlevel
8codebeingthekeyinaDSO.
Mylogicisasbelow:
source1=select*from:inTabwherelevel=8
source2=seelct*from:inTabwherelevel<>8
fromhereIamstuckhowtopreparetheresultdataflattenedwithoutloops.
SorryforaskingsuchadeepdivecauseIamstuckatthiswhereIhavewritten
mostoftheinABAPexpertroutinestomovetheproject.Anyhelp?

TorstenKessler Postauthor

December14,2016at9:04am

Hi,
youcanuseallSQLScriptfeature.Itisalsopossibletouseloops
andconditions.
IdidntgetyourrequirementcompletebutIthinkitwouldbe
possibledogettherequestedresultwithastatementlike:
SELECTCASE(level=8)THEN
CASE(level=7)THEN
otherwiseyoucanimplementitinasamewayasinABAP.
Asexplainedintheblog,ifyouareusingdeclarativeprogramming
logicitwillbetranslatedintoL(LPop).
AndthisCAN(notmust)blogtheoptimizer!
Thegeneraldeclarationformthedevelopmentis:Itisimplausible
thataLPopisfasterthanapureSQLstatement!.
Torsten

VasanthGutta

December15,2016at3:00am

HiTorsten,
IhavefigureditoutandwrittentheAMDPscriptinSQLusingunionsandjoins
butveryclumsy.IhavebeendoinglotofABAPandconvertingthatintoHANA
AMDPSQLscriptisnotthatstraightforwardbutmademethinkalot.

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 31/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

IhavefinallywrittenandtestedthetransformationanddatasuccessfullybutI
couldntfinddifferenceinperformancefromBWroutineworldtoHANAAMDP
world.Ormaybeduetolessvolumeofdata.
Howeveritsmoreinterestingandchallengingtolearnnewcodingtechniques.I
wentthroughHANAsqlguideandwrittentlinebylinefor3daystofinishit.
Thisishowihavewritten:
definelvlint:=7
temp_source=selectfrom:inTabbyHLVLdesc,childasc
l_out=select
targetstructurewithNullsmostandrelevantfieldsforlevel8ofparentchild
hierarchywithattributesfromtemp_source
while:lvl=0DO
CASE(:lvl=7)
then
l_out=targetstructurewithNullsmostandrelevantfieldsforlevel7and8of
parentchildhierarchywithattributesfroml_outasmaininnerjointemp_source
aspart(main.parent=part.child)wheretemp_sourcelevel=:lvl
CASE(:lvl=6)
then
l_out=targetstructurewithNullsmostandrelevantfieldsforlevel6,7and8
ofparentchildhierarchywithattributesfroml_outasmaininnerjoin
temp_sourceaspart(main.parent=part.child)wheretemp_sourcelevel=:lvl
CASE(:lvl=5)
then
l_out=targetstructurewithNullsmostandrelevantfieldsforlevel5,6,7and
8ofparentchildhierarchywithattributesfroml_outasmaininnerjoin
temp_sourceaspart(main.parent=part.child)wheretemp_sourcelevel=:lvl
CASE(:lvl=4)
then
l_out=targetstructurewithNullsmostandrelevantfieldsforlevel4,5,6,7
and8ofparentchildhierarchywithattributesfroml_outasmaininnerjoin
temp_sourceaspart(main.parent=part.child)wheretemp_sourcelevel=:lvl
CASE(:lvl=3)
then
l_out=targetstructurewithNullsmostandrelevantfieldsforlevel3,4,5,6,7
and8ofparentchildhierarchywithattributesfroml_outasmaininnerjoin
temp_sourceaspart(main.parent=part.child)wheretemp_sourcelevel=:lvl
CASE(:lvl=2)
then
https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 32/33
2/15/2017 HANAbasedBWTransformation|SAPBlogs

l_out=targetstructurewithNullsmostandrelevantfieldsforlevel2,3,4,5,6,
7and8ofparentchildhierarchywithattributesfroml_outasmaininnerjoin
temp_sourceaspart(main.parent=part.child)wheretemp_sourcelevel=:lvl
CASE(:lvl=1)
then
l_out=targetstructurewithNullsmostandrelevantfieldsforlevel1,2,3,4,5,
6,7and8ofparentchildhierarchywithattributesfroml_outasmaininnerjoin
temp_sourceaspart(main.parent=part.child)wheretemp_sourcelevel=:lvl
endcase
lvl:=:lvl1
endwhile
outTab=selectfroml_outstructure
erorTab=selecterrorstuff

Share & Follow


Privacy TermsofUse LegalDisclosure Copyright Trademark Sitemap Newsletter

https://blogs.sap.com/2016/05/24/hanabasedbwtransformation/ 33/33

You might also like