1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351 |
- /*##############################################################################
- HPCC SYSTEMS software Copyright (C) 2012 HPCC Systems®.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- ############################################################################## */
- #include "platform.h"
- #include "jlib.hpp"
- #include "jfile.hpp"
- #include "jexcept.hpp"
- #include "jmisc.hpp"
- #include "jdebug.hpp"
- #include "defvalue.hpp"
- #include "hql.hpp"
- #include "hqlattr.hpp"
- #include "hqlfold.ipp"
- #include "eclrtl.hpp"
- #include "eclrtl_imp.hpp"
- #include "hqlerror.hpp"
- #include "hqlerrors.hpp"
- #include "hqlutil.hpp"
- #include "hqlpmap.hpp"
- #include "hqlmeta.hpp"
- #include "hqlfold.hpp"
- #include "hqlthql.hpp"
- #include "eclhelper.hpp"
- #ifdef __APPLE__
- #include <dlfcn.h>
- #endif
- //#define LOG_ALL_FOLDING
- //---------------------------------------------------------------------------
- // The following functions do not attempt to reorder datasets, e.g., filter(project)->project(filter).
- // Those changes can inadvertently cause common code to be lost. Those optimizations are performed by
- // hqlopt which ensures it keeps track of the number of times a dataset expression is used.
- IHqlExpression * createNullValue(IHqlExpression * expr)
- {
- return createConstant(createNullValue(expr->queryType()));
- }
- static bool isDuplicateMapCondition(const HqlExprArray & values, IHqlExpression * cond)
- {
- ForEachItemIn(i, values)
- {
- if (values.item(i).queryChild(0) == cond)
- return true;
- }
- return false;
- }
- static bool areIndenticalMapResults(const HqlExprArray & values, IHqlExpression * defaultExpr)
- {
- unsigned max = values.ordinality();
- for (unsigned i=0; i < max; i++)
- {
- if (values.item(i).queryChild(1)->queryBody() != defaultExpr->queryBody())
- return false;
- }
- return true;
- }
- static bool isOrderedType(ITypeInfo * type)
- {
- switch (type->getTypeCode())
- {
- case type_boolean:
- return false;
- }
- return true;
- }
- static IHqlExpression * createCompareResult(node_operator op, int compare)
- {
- switch (op)
- {
- case no_eq:
- return createConstant(compare == 0);
- case no_ne:
- return createConstant(compare != 0);
- case no_lt:
- return createConstant(compare < 0);
- case no_le:
- return createConstant(compare <= 0);
- case no_gt:
- return createConstant(compare > 0);
- case no_ge:
- return createConstant(compare >= 0);
- case no_order:
- return createConstant(createIntValue(compare, 4, true));
- default:
- throwUnexpectedOp(op);
- }
- }
- /*In castExpr, constExpr: NOT linked. Out: linked */
- static IHqlExpression * optimizeCast(node_operator compareOp, IHqlExpression * castExpr, IHqlExpression * constExpr)
- {
- assertex(isCast(castExpr));
- bool createTrueConst = false;
- bool createFalseConst = false;
- node_operator newNode = no_none;
- //castXXX refers to types/values with the cast in place uncastXXX refer to types/values with it removed.
- ITypeInfo * castType = castExpr->queryType();
- IHqlExpression * uncastChild = castExpr->queryChild(0);
- ITypeInfo * uncastType = uncastChild->queryType();
- //If the cast loses information then we can't remove it....
- if (!preservesValue(castType, uncastType))
- return NULL;
- //If the comparison is non equality and the cast changes the collation sequence then you can't remove it.
- if ((compareOp != no_eq) && (compareOp != no_ne))
- {
- if (!preservesOrder(castType, uncastType))
- return NULL;
- //This seems an arbitrary exception, but if the comparison is ordered, and value being cast doesn't really
- //have a sensible ordering (i.e. boolean) then the cast shouldn't be removed.
- //i.e. make sure "(real)boolval < 0.5" does not become "boolval <= true".
- if (!isOrderedType(uncastType))
- return NULL;
- }
- IValue * castValue = constExpr->queryValue();
- OwnedIValue uncastValue(castValue->castTo(uncastType));
- if (uncastValue)
- {
- //Check whether casting the value to the new type can be represented. If not then
- int rc = castValue->rangeCompare(uncastType);
- if (rc != 0)
- {
- //This is effectively RHS compare min/max lhs, so invert the compare result
- return createCompareResult(compareOp, -rc);
- }
- else
- {
- OwnedIValue recast(uncastValue->castTo(castType));
- if (recast)
- {
- int test = recast->compare(castValue);
- //test = newValue <=> oldValue
- switch (compareOp)
- {
- case no_eq:
- if (test == 0)
- newNode = no_eq;
- else
- createFalseConst = true;
- break;
- case no_ne:
- if (test == 0)
- newNode = no_ne;
- else
- createTrueConst = true;
- break;
- case no_lt:
- //If new value less than old value, so < now becomes <=
- newNode = (test < 0) ? no_le : no_lt;
- break;
- case no_ge:
- //If new value less than old value, so >= now becomes >
- newNode = (test < 0) ? no_gt : no_ge;
- break;
- case no_le:
- //If new value is greater than old value, <= becomes <
- newNode = (test > 0) ? no_lt : no_le;
- break;
- case no_gt:
- //If new value is greater than old value, > becomes >=
- newNode = (test > 0) ? no_ge : no_gt;
- break;
- default:
- throwUnexpected();
- }
- }
- }
- }
- else
- {
- createTrueConst = (compareOp == no_ne);
- createFalseConst = (compareOp == no_eq);
- }
- if (createTrueConst)
- return createConstant(true);
- if (createFalseConst)
- return createConstant(false);
-
- if (newNode != no_none)
- return createBoolExpr(newNode, LINK(uncastChild), createConstant(uncastValue.getClear()));
- return NULL;
- }
- //In castExpr: not linked. Out: linked
- static IHqlExpression * optimizeCastList(IHqlExpression * castExpr, HqlExprArray & inList, node_operator op)
- {
- assertex(isCast(castExpr));
- IHqlExpression * castChild = castExpr->queryChild(0);
- ITypeInfo * targetType = castChild->queryType();
- ITypeInfo * currentType = castExpr->queryType();
- //If the cast loses information then we can't remove it....
- if (!preservesValue(currentType, targetType))
- return NULL;
- //(cast)search in <list>
- //Produce a new list of values which only includes values that could possibly match the search value
- HqlExprArray newInConstants;
- ForEachItemIn(i, inList)
- {
- bool skip = true;
- IValue * constValue = inList.item(i).queryValue();
- if (!constValue)
- return NULL;
- OwnedIValue cast(constValue->castTo(targetType));
- if (cast)
- {
- int rc = constValue->rangeCompare(targetType);
- if (rc == 0)
- {
- OwnedIValue recast(cast->castTo(constValue->queryType()));
- if (recast)
- {
- int test = recast->compare(constValue);
- if (test == 0)
- skip = false;
- }
- }
- }
- if (!skip)
- newInConstants.append(*createConstant(cast.getClear()));
- }
- if (newInConstants.ordinality())
- {
- IHqlExpression * newList = createValue(no_list, makeSetType(LINK(targetType)), newInConstants);
- return createBoolExpr(op, LINK(castChild), newList);
- }
- return createConstant(op == no_notin);
- }
- static bool isInList(IValue * v, IHqlExpression * list)
- {
- if (list->getOperator()==no_all)
- return true;
- unsigned num = list->numChildren();
- for (unsigned idx = 0; idx < num; idx++)
- {
- IHqlExpression * elem = list->queryChild(idx);
- IValue * constValue = elem->queryValue();
- if (constValue)
- {
- if (orderValues(v, constValue) == 0)
- return true;
- }
- }
- return false;
- }
- static IValue * compareValues(node_operator op, IValue * leftValue, IValue * rightValue)
- {
- IValue * newConst;
- switch (op)
- {
- case no_eq:
- newConst = equalValues(leftValue,rightValue);
- break;
- case no_ne:
- newConst = notEqualValues(leftValue,rightValue);
- break;
- case no_lt:
- newConst = lessValues(leftValue,rightValue);
- break;
- case no_le:
- newConst = lessEqualValues(leftValue,rightValue);
- break;
- case no_gt:
- newConst = greaterValues(leftValue,rightValue);
- break;
- case no_ge:
- newConst = greaterEqualValues(leftValue,rightValue);
- break;
- case no_order:
- newConst = createIntValue(leftValue->compare(rightValue), 4, true);
- break;
- default:
- throwUnexpectedOp(op);
- }
- return newConst;
- }
- static IHqlExpression * compareLists(node_operator op, IHqlExpression * leftList, IHqlExpression * rightList)
- {
- unsigned lnum = leftList->numChildren();
- unsigned rnum = rightList->numChildren();
- int order = 0;
- unsigned num = lnum > rnum ? rnum : lnum;
- for (unsigned i=0; i < num; i++)
- {
- IValue * leftValue = leftList->queryChild(i)->queryValue();
- IValue * rightValue = rightList->queryChild(i)->queryValue();
- if (!leftValue || !rightValue)
- return NULL;
- order = orderValues(leftValue, rightValue);
- if (order != 0)
- return createCompareResult(op, order);
- }
- if (lnum != rnum)
- order = lnum > rnum ? +1 : -1;
- return createCompareResult(op, order);
- }
- static IHqlExpression * optimizeListConstant(node_operator op, IHqlExpression * list, IValue * constVal)
- {
- if ((list->getOperator() != no_list) || !list->isConstant())
- return NULL;
- //I don't really know what this function is trying to do. I think it is trying to optimize the case where
- //comparing against any of the values in the list will give the same result.
- OwnedIValue nullVal = createNullValue(list->queryType()->queryChildType());
- OwnedIValue result = compareValues(op, nullVal, constVal);
- ForEachChild(i, list)
- {
- IValue * curValue = list->queryChild(i)->queryValue();
- if (!curValue)
- return NULL;
- Owned<IValue> curResult = compareValues(op, curValue, constVal);
- if (curResult->compare(result) != 0)
- return NULL;
- }
- return createConstant(result.getClear());
- }
- static bool flattenConstantCase(IHqlExpression * caseExpr, HqlExprArray & constants, bool out)
- {
- assertex(caseExpr->getOperator()==no_case);
- unsigned num = caseExpr->numChildren()-1;
- for (unsigned i=1; i<num; i++)
- {
- IHqlExpression * map = caseExpr->queryChild(i);
- IHqlExpression * val = map->queryChild(out);
- if (!val->queryValue())
- return false;
- constants.append(*LINK(val));
- }
- return true;
- }
- static IHqlExpression * optimizeCaseConstant(node_operator op, IHqlExpression * caseExpr, IValue * constVal, bool swap)
- {
- HqlExprArray caseResults;
- if (flattenConstantCase(caseExpr, caseResults, true))
- {
- IValue * defValue = caseExpr->queryChild(caseExpr->numChildren()-1)->queryValue();
- if (defValue)
- {
- switch (op)
- {
- case no_eq:
- case no_ne:
- {
- //CASE(x,a1=>v1,a2=>v2,a3=>v3,v0) [not]= y
- //If y ==a0 then transform to x [NOT] IN [a<n>] where v<n>!=y
- bool matchesDefault = (defValue->compare(constVal) == 0);
- HqlExprCopyArray matches;
- HqlExprArray exceptions;
- for (unsigned i=0; i<caseResults.ordinality(); i++)
- {
- IHqlExpression * key = caseExpr->queryChild(i+1)->queryChild(0);
- IHqlExpression * val = &caseResults.item(i);
- bool caseMatches = (val->queryValue()->compare(constVal) == 0);
- if (caseMatches == matchesDefault)
- matches.append(*key->queryBody());
- else if (!matches.contains(*key->queryBody()))
- exceptions.append(*LINK(key));
- }
- bool defaultsToTrue = (matchesDefault && (op == no_eq)) || (!matchesDefault && (op == no_ne));
- if (exceptions.ordinality() == 0)
- return createConstant(defaultsToTrue);
- node_operator inOp = defaultsToTrue ? no_notin : no_in;
- IHqlExpression * test = caseExpr->queryChild(0);
- return createBoolExpr(inOp,
- LINK(test),
- createValue(no_list, makeSetType(test->getType()), exceptions));
- }
- }
- }
- }
- return NULL;
- }
- static IHqlExpression * optimizeCompare(IHqlExpression * expr)
- {
- IHqlExpression * leftChild = expr->queryChild(0);
- IHqlExpression * rightChild = expr->queryChild(1);
- node_operator op = expr->getOperator();
- node_operator leftOp = leftChild->getOperator();
- node_operator rightOp = rightChild->getOperator();
- if ((leftChild->queryBody() == rightChild->queryBody()) ||
- (leftOp == no_all && rightOp == no_all))
- {
- return createCompareResult(op, 0);
- }
- if ((leftOp == no_all) && rightChild->isConstant())
- return createCompareResult(op, +1);
-
- if ((rightOp == no_all) && leftChild->isConstant())
- return createCompareResult(op, -1);
-
- if (((leftOp == no_sortlist) || (leftOp == no_list)) && ((rightOp == no_sortlist) || (rightOp == no_list)))
- return compareLists(op, leftChild, rightChild);
- IValue * leftValue = leftChild->queryValue();
- IValue * rightValue = rightChild->queryValue();
- if (leftValue && rightValue)
- {
- int order = orderValues(leftValue, rightValue);
- return createCompareResult(op, order);
- }
- if (op == no_order)
- return NULL;
-
- bool swap = false;
- IHqlExpression * castChild = NULL;
- IHqlExpression * constChild = NULL;
- if (leftValue)
- {
- ITypeInfo * rType = rightChild->queryType();
- if (rType->isUnsignedNumeric() && isZero(leftChild))
- {
- switch (op)
- {
- case no_le:
- return createConstant(true);
- case no_gt:
- return createConstant(false);
- }
- }
- if (rType->getTypeCode() == type_boolean)
- {
- bool val = leftValue->getBoolValue();
- switch (op)
- {
- case no_eq:
- if (val)
- return LINK(rightChild);
- return getInverse(rightChild);
- case no_ne:
- if (!val)
- return LINK(rightChild);
- return getInverse(rightChild);
- }
- }
- swap = true;
- switch(rightChild->getOperator())
- {
- case no_cast:
- case no_implicitcast:
- castChild = rightChild;
- constChild = leftChild;
- break;
- case no_index:
- return optimizeListConstant(getReverseOp(op), rightChild->queryChild(0), leftValue);
- }
- }
- else if (rightValue)
- {
- ITypeInfo * lType = leftChild->queryType();
- if (lType->isUnsignedNumeric() && isZero(rightChild))
- {
- switch (op)
- {
- case no_ge:
- return createConstant(true);
- case no_lt:
- return createConstant(false);
- }
- }
- if (lType->getTypeCode() == type_boolean)
- {
- bool val = rightValue->getBoolValue();
- switch (op)
- {
- case no_eq:
- if (val)
- return LINK(leftChild);
- return getInverse(leftChild);
- case no_ne:
- if (!val)
- return LINK(leftChild);
- return getInverse(leftChild);
- }
- }
- switch(leftChild->getOperator())
- {
- case no_cast:
- case no_implicitcast:
- castChild = leftChild;
- constChild = rightChild;
- break;
- case no_index:
- return optimizeListConstant(op, leftChild->queryChild(0), rightValue);
- case no_case:
- return optimizeCaseConstant(op, leftChild, rightValue, false);
- }
- }
- if (castChild)
- {
- OwnedHqlExpr value = optimizeCast(swap ? getReverseOp(op) : op, castChild, constChild);
- if (value)
- return value.getClear();
- }
- if (swap)
- {
- //Normalize simple comparisons so they are always (field op value)
- return createValue(getReverseOp(op), makeBoolType(), LINK(rightChild), LINK(leftChild));
- }
- return NULL;
- }
- static bool isSimpleComparisonArg(IHqlExpression * expr)
- {
- switch (expr->getOperator())
- {
- case no_constant:
- case no_getresult:
- return true;
- }
- return false;
- }
- //---------------------------------------------------------------------------
- /*********************************************************
- * Constant folding for an external function call
- * Supports the following external function parameter types:
- * - INTEGER (Tested)
- * - REAL
- * - STRINGN (Tested)
- * - STRING (Tested)
- * - VARSTRINGN
- * - VARSTRING
- * - BOOLEAN
- * Supports the following external function return types:
- * - INTEGER (TESTED)
- * - STRING (tested)
- * - STRINGN (Tested)
- * - VARSTRING
- * - VARSTRINGN
- * - REAL
- * - BOOLEAN
- * NOTE: Tested with the functions in default.StringLib. The
- * functions need to be declared with extern "C".
- *********************************************************/
- //MORE: This function never unloads the plugin dll - this may cause problems in the long run.
- bool checkExternFoldable(IHqlExpression* expr, unsigned foldOptions, StringBuffer &library, StringBuffer &entry)
- {
- IHqlExpression * funcdef = expr->queryExternalDefinition();
- if(!funcdef)
- return false;
- IHqlExpression *body = funcdef->queryChild(0);
- if(!body)
- return false;
- //Check all parameters are constant - saves dll load etc.
- unsigned numParam = expr->numChildren();
- for(unsigned iparam = 0; iparam < numParam; iparam++)
- {
- if (!expr->queryChild(iparam)->queryValue()) //NB: Already folded...
- return false;
- }
- IHqlExpression * formals = funcdef->queryChild(1);
- unsigned numArg = formals->numChildren();
- if(numParam > numArg)
- {
- if (foldOptions & HFOthrowerror)
- throw MakeStringException(ERR_PARAM_TOOMANY,"Too many parameters passed to function '%s': expected %d, given %d",
- str(expr->queryName()), numParam, numArg);
- return false;
- }
- else if(numParam < numArg)
- {
- if (foldOptions & HFOthrowerror)
- throw MakeStringException(ERR_PARAM_TOOFEW,"Not enough parameters passed to function '%s': expected %d, given %d",
- str(expr->queryName()), numParam, numArg);
- return false;
- }
- StringBuffer mangledEntry;
- getAttribute(body, entrypointAtom, entry);
- getAttribute(body, libraryAtom, library);
- if (!library.length())
- getAttribute(body, pluginAtom, library);
- if(entry.length() == 0)
- {
- if (foldOptions & HFOthrowerror)
- throw MakeStringException(ERR_SVC_NOENTRYPOINT,"Missing entrypoint for function folding");
- return false;
- }
- if (library.length() == 0)
- {
- if (foldOptions & HFOthrowerror)
- throw MakeStringException(ERR_SVC_NOLIBRARY,"Missing library for function folding");
- return false;
- }
- if (!pathExtension(library))
- {
- library.insert(0, SharedObjectPrefix);
- ensureFileExtension(library, SharedObjectExtension);
- }
- if (!body->hasAttribute(foldAtom) || body->hasAttribute(nofoldAtom))
- {
- if (foldOptions & HFOthrowerror)
- throw MakeStringException(ERR_TMPLT_NOFOLDFUNC, "%s does not have FOLD specified, can't constant fold it", str(expr->queryName()));
- return false;
- }
- if (body->hasAttribute(_disallowed_Atom))
- {
- if (foldOptions & HFOthrowerror)
- throw MakeStringException(ERR_TMPLT_NOFOLDFUNC, "You do not have permission to constant-fold %s", str(expr->queryName()));
- return false;
- }
- if(!body->hasAttribute(pureAtom) && !body->hasAttribute(templateAtom) && !(foldOptions & (HFOfoldimpure|HFOforcefold)))
- {
- if (foldOptions & HFOthrowerror)
- throw MakeStringException(ERR_TMPLT_NONPUREFUNC, "%s/%s is not a pure function, can't constant fold it", library.str(), entry.str());
- return false;
- }
- if(body->hasAttribute(contextAtom) || body->hasAttribute(globalContextAtom) ||
- body->hasAttribute(gctxmethodAtom) || body->hasAttribute(ctxmethodAtom) || body->hasAttribute(omethodAtom))
- {
- if (foldOptions & HFOthrowerror)
- throw MakeStringException(ERR_TMPLT_NONEXTERNCFUNC, "%s/%s requires a runtime context to be executed, can't constant fold it", library.str(), entry.str());
- return false;
- }
- if(!body->hasAttribute(cAtom))
- {
- if (!createMangledFunctionName(mangledEntry, funcdef))
- {
- if (foldOptions & HFOthrowerror)
- throw MakeStringException(ERR_TMPLT_NONEXTERNCFUNC, "%s/%s is not declared as extern C, can't constant fold it", library.str(), entry.str());
- return false;
- }
- entry.set(mangledEntry);
- }
- return true;
- }
- void *loadExternalEntryPoint(IHqlExpression* expr, unsigned foldOptions, ITemplateContext *templateContext, const char *library, const char *entrypoint, HINSTANCE &hDLL)
- {
- IHqlExpression * funcdef = expr->queryExternalDefinition();
- IHqlExpression *body = funcdef->queryChild(0);
- // Get the handle to the library and procedure.
- #ifdef __APPLE__
- StringBuffer fullLibraryPath;
- // OSX is not good at finding eclrtl. This hack is a workaround
- if (streq(library, "libeclrtl.dylib"))
- {
- Dl_info info;
- if (dladdr((const void *) rtlStrToUInt4, &info)) // Any function in eclrtl would do...
- {
- fullLibraryPath.set(info.dli_fname);
- library = fullLibraryPath.str();
- }
- }
- #ifdef _DEBUG
- if (streq(library, "libpy3embed.dylib") || streq(library, "libpyembed.dylib") || streq(library, "libv8embed.dylib") || streq(library, "libjavaembed.dylib"))
- {
- Dl_info info;
- if (dladdr((const void *) rtlStrToUInt4, &info)) // Any function in eclrtl would do...
- {
- fullLibraryPath.set(info.dli_fname);
- fullLibraryPath.replaceString("libeclrtl.dylib", library);
- library = fullLibraryPath.str();
- }
- }
- #endif
- #endif
- hDLL=LoadSharedObject(library, false, false);
- if (!LoadSucceeded(hDLL))
- {
- if (body->hasAttribute(templateAtom))
- throw MakeStringException(ERR_SVC_LOADLIBFAILED, "Error when trying to load library %s for template helper function", library);
- if (foldOptions & HFOthrowerror)
- throw MakeStringException(ERR_SVC_LOADLIBFAILED, "Error when trying to load library %s", library);
- return NULL;
- }
- void* fh = GetSharedProcedure(hDLL, entrypoint);
- if (!fh)
- {
- FreeSharedObject(hDLL);
- if (foldOptions & HFOthrowerror)
- throw MakeStringException(ERR_SVC_LOADFUNCFAILED, "Error when trying to load procedure %s from library %s", entrypoint, library);
- return NULL;
- }
- return fh;
- }
- IValue * doFoldExternalCall(IHqlExpression* expr, unsigned foldOptions, ITemplateContext *templateContext, const char *library, const char *entrypoint, void *fh)
- {
- // NOTE - on OSX there are compiler bugs that prevent exceptions thrown from within this function from properly unwinding.
- // Hence anything that can throw an exception should be pre-checked in one of the functions above.
- IHqlExpression * funcdef = expr->queryExternalDefinition();
- IHqlExpression *body = funcdef->queryChild(0);
- // create a FuncCallStack to generate a stack used to pass parameters to
- // the called function
- FuncCallStack fstack;
-
- if(body->hasAttribute(templateAtom))
- fstack.pushPtr(templateContext);
- //if these were allowed to be optional - then the following code would be needed
- if(body->hasAttribute(contextAtom) || body->hasAttribute(globalContextAtom))
- fstack.pushPtr(NULL);
- bool retCharStar = false;
- bool retUCharStar = false;
- bool charStarInParam = false;
- unsigned resultsize = 4; // the number of bytes of the result.
- int isRealvalue = 0;
- unsigned tlen = 0;
- char* tgt = NULL;
- // Process return value
- ITypeInfo * retType = funcdef->queryType()->queryChildType();
- type_t typecode = retType->getTypeCode();
- switch (typecode)
- {
- case type_varstring:
- case type_varunicode:
- if (retType->getSize() == UNKNOWN_LENGTH)
- {
- // variable length varstring, should return as char*
- retCharStar = true;
- if(typecode==type_varunicode) retUCharStar = true;
- resultsize = sizeof(char *);
- break;
- }
- //fallthrough
- case type_string:
- case type_data:
- case type_qstring:
- case type_unicode:
- case type_utf8:
- if (retType->getSize() == UNKNOWN_LENGTH)
- {
- // string, pass in the reference of length var and char* var. After function call,
- // values will be stored in them.
- fstack.pushRef(tlen);
- fstack.pushRef(tgt);
- }
- else
- {
- // stringn or varstringn, create a char array and pass it. Don't pass length var(as the
- // length is fixed).
- tlen = retType->getSize();
- tgt = (char*) malloc(tlen + 1); // To be safe, allocate one byte more.
- fstack.push(tgt);
- }
- charStarInParam = true;
- break;
- case type_real:
- // For real, get the result size
- resultsize = retType->getSize();
- isRealvalue = 1;
- break;
- case type_boolean:
- case type_int:
- case type_decimal:
- case type_date:
- case type_char:
- case type_enumerated:
- case type_swapint:
- case type_packedint:
- resultsize = retType->getSize();
- break;
- case type_void:
- if (!(foldOptions & (HFOfoldimpure|HFOforcefold)))
- {
- if (foldOptions & HFOthrowerror)
- throw MakeStringException(ERR_TMPLT_NONPUREFUNC, "%s/%s is not an action, can't constant fold it", library, entrypoint);
- return NULL;
- }
- break;
- default:
- //can't fold things that return sets/datasets etc.
- return NULL;
- }
- // process all the parameters passed in
- unsigned numParam = expr->numChildren();
- IHqlExpression * formals = funcdef->queryChild(1);
- for(unsigned i = 0; i < numParam; i++)
- {
- IHqlExpression * curParam = expr->queryChild(i); //NB: Already folded...
- IHqlExpression * curArg = formals->queryChild(i);
- if(!curArg) {
- free(tgt);
- return NULL;
- }
- ITypeInfo * argType = curArg->queryType();
- if(!argType) {
- free(tgt);
- return NULL;
- }
- IValue * paramValue = curParam->queryValue();
- if (fstack.push(argType, paramValue) == -1)
- {
- free(tgt);
- return NULL;
- }
- }
- // Get the length and address of the stack
- unsigned len = fstack.getSp();
- #ifdef REGPARAMS
- while (len < REGPARAMS*REGSIZE)
- len = fstack.pushPtr(NULL); // ensure enough to fill REGPARAMS registers
- #endif
- #ifdef ODD_STACK_ALIGNMENT
- // Some architectures (arm) require that the total amount pushed onto the stack for parameters is an odd number of words
- // (so that the stack alignment is always an even number of words once the return IP is pushed)
- if ((len & REGSIZE) == 0)
- len = fstack.pushPtr(NULL);
- #endif
- #ifdef EVEN_STACK_ALIGNMENT
- // Other architectures (x86) require that the total amount pushed onto the stack for parameters is an even number of words
- // (so that the stack alignment is always an even number of words after the callq, which pushes an even number of words)
- if ((len & REGSIZE) == REGSIZE)
- len = fstack.pushPtr(NULL);
- #endif
- char* strbuf = fstack.getMem();
- int intresult = 0;
- #ifdef __64BIT__
- __int64 int64result = 0;
- #else
- int intresulthigh = 0;
- #endif
- float floatresult = 0.0;
- double doubleresult = 0.0;
- #ifdef _ARCH_X86_64_
- // __asm__ ("\tint $0x3\n"); // for debugging
- #endif
- try{
- // Assembly code that does the dynamic function call. The calling convention is a combination of
- // Pascal and C, that is the parameters are pushed from left to right, the stack goes downward(i.e.,
- // the stack pointer decreases as you push), and the caller is responsible for restoring the
- // stack pointer.
- // **** Windows ****
- #ifdef _WIN32
- // Note - we assume X86/X86_64 Procedure Call Standard
- #if defined (_ARCH_X86_64_)
- UNIMPLEMENTED;
- #elif defined (_ARCH_X86_)
- _asm{
- ;save registers that will be used
- push ecx
- push esi
- push eax
- push edx
- push ebx
- ;copy parameters to the stack
- mov ecx, len
- sub esp, len
- mov esi, strbuf
- jcxz loop1tail
- loop1:
- mov al, [esi]
- mov [esp], al
- inc esp
- inc esi
- dec ecx
- jnz loop1
- loop1tail:
- ;call the procedure
- sub esp, len
- call fh
- add esp, len
- ;save result
- mov ebx, isRealvalue
- cmp ebx, 1
- je isreal
- mov intresult, eax
- mov intresulthigh, edx
- jmp finish
- isreal:
- mov ebx, resultsize
- cmp ebx, 4
- ja isdouble
- fstp DWORD PTR floatresult
- jmp finish
- isdouble:
- fstp QWORD PTR doubleresult
- finish:
- ;restore registers that were saved
- pop ebx
- pop edx
- pop eax
- pop esi
- pop ecx
- }
- #else
- UNIMPLEMENTED;
- #endif
- #else // WIN32
- // **** Linux/Mac ****
- #ifdef _ARCH_X86_64_
- assertex((len & 15) == 0); // We need to make sure we add an EVEN number of words to stack, so that it is 16-byte aligned before the callq
- __int64 dummy1, dummy2,dummy3,dummy4;
- void * floatstack = fstack.getFloatMem();
- if (floatstack) { // sets xmm0-7
- unsigned * floatSizes = fstack.getFloatSizes();
- __asm__ (
- ".doparm0: \n\t"
- "cmpl $4,(%%rdi) \n\t"
- "jl .floatdone \n\t"
- "je .dofloat0 \n\t"
- "movsd (%%rsi),%%xmm0 \n\t"
- "jmp .doparm1 \n\t"
- ".dofloat0: \n\t"
- "movss (%%rsi),%%xmm0 \n\t"
- ".doparm1: \n\t"
- "cmpl $4,4(%%rdi) \n\t"
- "jl .floatdone \n\t"
- "je .dofloat1 \n\t"
- "movsd 8(%%rsi),%%xmm1 \n\t"
- "jmp .doparm2 \n\t"
- ".dofloat1: \n\t"
- "movss 8(%%rsi),%%xmm1 \n\t"
- ".doparm2: \n\t"
- "cmpl $4,8(%%rdi) \n\t"
- "jl .floatdone \n\t"
- "je .dofloat2 \n\t"
- "movsd 16(%%rsi),%%xmm2 \n\t"
- "jmp .doparm3 \n\t"
- ".dofloat2: \n\t"
- "movss 16(%%rsi),%%xmm2 \n\t"
- ".doparm3: \n\t"
- "cmpl $4,12(%%rdi) \n\t"
- "jl .floatdone \n\t"
- "je .dofloat3 \n\t"
- "movsd 24(%%rsi),%%xmm3 \n\t"
- "jmp .doparm4 \n\t"
- ".dofloat3: \n\t"
- "movss 24(%%rsi),%%xmm3 \n\t"
- ".doparm4: \n\t"
- "cmpl $4,16(%%rdi) \n\t"
- "jl .floatdone \n\t"
- "je .dofloat4 \n\t"
- "movsd 32(%%rsi),%%xmm4 \n\t"
- "jmp .doparm5 \n\t"
- ".dofloat4: \n\t"
- "movss 32(%%rsi),%%xmm4 \n\t"
- ".doparm5: \n\t"
- "cmpl $4,20(%%rdi) \n\t"
- "jl .floatdone \n\t"
- "je .dofloat5 \n\t"
- "movsd 40(%%rsi),%%xmm5 \n\t"
- "jmp .doparm6 \n\t"
- ".dofloat5: \n\t"
- "movss 40(%%rsi),%%xmm5 \n\t"
- ".doparm6: \n\t"
- "cmpl $4,24(%%rdi) \n\t"
- "jl .floatdone \n\t"
- "je .dofloat6 \n\t"
- "movsd 48(%%rsi),%%xmm6 \n\t"
- "jmp .doparm7 \n\t"
- ".dofloat6: \n\t"
- "movss 48(%%rsi),%%xmm6 \n\t"
- ".doparm7: \n\t"
- "cmpl $4,28(%%rdi) \n\t"
- "jl .floatdone \n\t"
- "je .dofloat7 \n\t"
- "movsd 56(%%rsi),%%xmm7 \n\t"
- "jmp .floatdone \n\t"
- ".dofloat7: \n\t"
- "movss 56(%%rsi),%%xmm7 \n\t"
- ".floatdone: \n\t"
- :
- : "S"(floatstack),"D"(floatSizes)
- : "cc","xmm0","xmm1","xmm2","xmm3","xmm4","xmm5","xmm6","xmm7"
- );
- }
- __asm__ (
- "sub %%rcx, %%rsp \n\t"
- "mov %%rsp, %%rdi \n\t"
- "cld \n\t"
- "rep \n\t"
- "movsb \n\t"
- "pop %%rdi \n\t"
- "pop %%rsi \n\t"
- "pop %%rdx \n\t"
- "pop %%rcx \n\t"
- "pop %%r8 \n\t"
- "pop %%r9 \n\t"
- "call *%%rax \n\t"
- "add %%rbx, %%rsp \n\t" // Restore stack pointer (note have popped 6 registers)
- : "=a"(int64result),"=d"(dummy1),"=c"(dummy1),"=S"(dummy3),"=D"(dummy4)
- : "c"(len),"b"(len-REGPARAMS*REGSIZE),"S"(strbuf),"a"(fh)
- : "cc","r8","r9","xmm0"
- );
-
- // Get real (float/double) return values;
- if(isRealvalue)
- {
- if(resultsize <= 4)
- {
- __asm__ __volatile__(
- "movss %%xmm0,(%%rdi) \n\t"
- :
- : "D"(&(floatresult))
- );
- }
- else
- {
- __asm__ __volatile__(
- "movsd %%xmm0, (%%rdi) \n\t"
- :
- : "D"(&(doubleresult))
- );
- }
- }
- else {
- intresult = (int)int64result;
- }
- #elif defined(_ARCH_X86_)
- int dummy1, dummy2,dummy3;
- __asm__ __volatile__(
- "push %%ebx \n\t"
- "movl %%ecx, %%ebx \n\t"
- "subl %%ecx, %%esp \n\t"
- "movl %%esp, %%edi \n\t"
- "cld \n\t"
- "rep \n\t"
- "movsb \n\t"
- "call *%%edx \n\t"
- "addl %%ebx, %%esp \n\t"
- "pop %%ebx \n\t"
- : "=a"(intresult),"=d"(intresulthigh),"=c"(dummy1),"=S"(dummy2),"=D"(dummy3)
- : "c"(len),"S"(strbuf),"d"(fh)
- : "cc"
- );
- // Get real (float/double) return values;
- if(isRealvalue)
- {
- if(resultsize <= 4)
- {
- __asm__ __volatile__(
- "fstps (%%edi) \n\t"
- :
- : "D"(&(floatresult))
- );
- }
- else
- {
- __asm__ __volatile__(
- "fstpl (%%edi) \n\t"
- :
- : "D"(&(doubleresult))
- );
- }
- }
- #elif defined(_ARCH_ARM32_)
- // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042e/IHI0042E_aapcs.pdf
- #ifdef MAXFPREGS
- void * floatstack = fstack.getFloatMem();
- if (floatstack) {
- unsigned * floatSizes = fstack.getFloatSizes();
- __asm__ __volatile__ (
- ".doparm0: \n\t"
- "ldr r0,[%[sizes],#0] \n\t"
- "cmp r0, #4 \n\t"
- "blt .floatdone \n\t"
- "beq .dofloat0 \n\t"
- "fldd d0,[%[vals], #0] \n\t"
- "b .doparm1 \n\t"
- ".dofloat0: \n\t"
- "flds s0,[%[vals], #0] \n\t"
- ".doparm1: \n\t"
- "ldr r0,[%[sizes],#4] \n\t"
- "cmp r0, #4 \n\t"
- "blt .floatdone \n\t"
- "beq .dofloat1 \n\t"
- "fldd d1,[%[vals], #8] \n\t"
- "b .doparm2 \n\t"
- ".dofloat1: \n\t"
- "flds s2,[%[vals], #8] \n\t"
- ".doparm2: \n\t"
- "ldr r0,[%[sizes],#8] \n\t"
- "cmp r0, #4 \n\t"
- "blt .floatdone \n\t"
- "beq .dofloat2 \n\t"
- "fldd d2,[%[vals], #16] \n\t"
- "b .doparm3 \n\t"
- ".dofloat2: \n\t"
- "flds s4,[%[vals], #16] \n\t"
- ".doparm3: \n\t"
- "ldr r0,[%[sizes],#12] \n\t"
- "cmp r0, #4 \n\t"
- "blt .floatdone \n\t"
- "beq .dofloat3 \n\t"
- "fldd d3,[%[vals], #24] \n\t"
- "b .doparm4 \n\t"
- ".dofloat3: \n\t"
- "flds s6,[%[vals], #24] \n\t"
- ".doparm4: \n\t"
- "ldr r0,[%[sizes],#16] \n\t"
- "cmp r0, #4 \n\t"
- "blt .floatdone \n\t"
- "beq .dofloat4 \n\t"
- "fldd d4,[%[vals], #32] \n\t"
- "b .doparm5 \n\t"
- ".dofloat4: \n\t"
- "flds s8,[%[vals], #32] \n\t"
- ".doparm5: \n\t"
- "ldr r0,[%[sizes],#20] \n\t"
- "cmp r0, #4 \n\t"
- "blt .floatdone \n\t"
- "beq .dofloat4 \n\t"
- "fldd d5,[%[vals], #40] \n\t"
- "b .doparm6 \n\t"
- ".dofloat5: \n\t"
- "flds s10,[%[vals], #40] \n\t"
- ".doparm6: \n\t"
- "ldr r0,[%[sizes],#24] \n\t"
- "cmp r0, #4 \n\t"
- "blt .floatdone \n\t"
- "beq .dofloat6 \n\t"
- "fldd d6,[%[vals], #48] \n\t"
- "b .doparm7 \n\t"
- ".dofloat6: \n\t"
- "flds s12,[%[vals], #48] \n\t"
- ".doparm7: \n\t"
- "ldr r0,[%[sizes],#28] \n\t"
- "cmp r0, #4 \n\t"
- "blt .floatdone \n\t"
- "beq .dofloat7 \n\t"
- "fldd d7,[%[vals], #56] \n\t"
- "b .floatdone \n\t"
- ".dofloat7: \n\t"
- "flds s14,[%[vals], #56] \n\t"
- ".floatdone: \n\t"
- :
- : [vals] "r"(floatstack), [sizes] "r"(floatSizes)
- : "r0"
- );
- }
- #endif
- assertex((len & 7) == 4); // We need to make sure we add an ODD number of words to stack, so that it gets 8-byte aligned once pc is pushed by the call
- register unsigned _intresult asm("r0"); // Specific register for result
- register unsigned _intresulthigh asm("r1"); // Specific register for result
- register unsigned _poplen asm("r4") = len-REGPARAMS*REGSIZE; // Needs to survive the call
- register void *_fh asm("r5") = fh; // Needs to survive until the call
- __asm__ __volatile__ (
- "subs sp, sp, %[len] \n\t" // Make space on stack
- "mov r2, sp \n\t" // r2 = destination for loop
- ".repLoop: \n\t"
- "ldrb r3, [%[strbuf]], #1 \n\t" // copy a byte from src array to r3
- "strb r3, [r2], #1 \n\t" // and then from r3 onto stack
- "subs %[len], %[len], #1 \n\t" // decrement and repeat
- "bne .repLoop \n\t"
- "pop {r0,r1,r2,r3} \n\t" // first 4 parameters go in registers
- "blx %[fh] \n\t" // make the call
- "adds sp, sp, %[poplen] \n\t" // Restore stack pointer (note have popped 4 registers, so poplen is len - 16)
- : "=r"(_intresult), "=r"(_intresulthigh)
- : [len] "r"(len), [poplen] "r"(_poplen), [strbuf] "r"(strbuf), [fh] "r"(_fh)
- : "r2","r3","lr" // function we call may corrupt lr
- );
- intresult = _intresult;
- intresulthigh = _intresulthigh;
- if (isRealvalue)
- {
- #ifdef MAXFPREGS
- if(resultsize <= 4)
- {
- __asm__ __volatile__(
- "fsts s0,[%[fresult]] \n\t"
- :
- : [fresult] "r"(&(floatresult))
- );
- }
- else
- {
- __asm__ __volatile__(
- "fstd d0,[%[fresult]] \n\t"
- :
- : [fresult] "r"(&(doubleresult))
- );
- }
- #else
- if(resultsize <= 4)
- {
- floatresult = *(float*)&intresult;
- }
- else
- {
- union
- {
- struct { int lo, int hi } i;
- double d;
- } u;
- u.lo = intresult;
- u.hi = intresulthigh;
- doubleresult = u.d;
- }
- #endif
- }
- #elif defined(_ARCH_ARM64_)
- // http://infocenter.arm.com/help/topic/com.arm.doc.ihi0055c/IHI0055C_beta_aapcs64.pdf
- UNIMPLEMENTED;
- #else
- // Unknown architecture
- UNIMPLEMENTED;
- #endif
- #endif //win32
- }
- catch (...) {
- if(retCharStar || charStarInParam) { // Char* return type, need to free up tgt.
- free(tgt);
- }
- if (foldOptions & HFOthrowerror)
- throw MakeStringException(ERR_SVC_EXCPTIONEXEFUNC,"Exception thrown when try to execute function %s/%s, please check the function\n", library, entrypoint);
- return NULL;
- }
- IValue* result = NULL;
- if(retCharStar || charStarInParam) { // Char* return type
- if(retCharStar) {
- #ifdef __64BIT__
- tgt = (char *)int64result;
- #else
- tgt = (char *)intresult;
- #endif
- tlen = retUCharStar ? rtlUnicodeStrlen((UChar *)tgt) : strlen(tgt);
- }
-
- Linked<ITypeInfo> resultType = retType;
- if (resultType->getSize() == UNKNOWN_LENGTH)
- resultType.setown(getStretchedType(tlen, resultType));
- switch (typecode)
- {
- case type_varstring:
- result = createVarStringValue(tlen+1, tgt, resultType.getLink());
- break;
- case type_data:
- result = createDataValue(tgt, tlen);
- break;
- case type_qstring:
- result = createQStringValue(tlen, tgt, resultType.getLink());
- break;
- case type_unicode:
- result = createUnicodeValue(tlen, tgt, LINK(resultType));
- break;
- case type_varunicode:
- result = createVarUnicodeValue(tlen, tgt, LINK(resultType));
- break;
- case type_utf8:
- result = createUtf8Value(tlen, tgt, LINK(resultType));
- break;
- default:
- result = createStringValue(tgt, resultType.getLink());
- break;
- }
- rtlFree(tgt);
- }
- else if(isRealvalue) { // REAL return type
- if(resultsize == 4) {
- result = createRealValue(floatresult, resultsize);
- }
- else {
- result = createRealValue(doubleresult, resultsize);
- }
- }
- else if(typecode == type_boolean) { // BOOLEAN return type
- intresult = intresult & 0xff;
- result = createBoolValue(intresult != 0);
- }
- else if (typecode == type_void)
- {
- result = NULL;
- }
- else { // By default, we take the return type as INTEGER
- LINK(retType);
- #ifndef __64BIT__
- __int64 int64result = (__int64)(((unsigned __int64) intresulthigh) << 32) + (unsigned)intresult;
- #endif
- unsigned shift = (sizeof(int64result)-resultsize) * 8;
- if (retType->isSigned())
- int64result = (int64result << shift) >> shift;
- else
- int64result = (((__uint64)int64result) << shift) >> shift;
- result = createIntValue(int64result, retType);
- }
- return result;
- }
- IValue * foldExternalCall(IHqlExpression* expr, unsigned foldOptions, ITemplateContext *templateContext)
- {
- StringBuffer library;
- StringBuffer entry;
- if (!checkExternFoldable(expr, foldOptions, library, entry))
- return NULL;
- // NOTE - we do not call FreeSharedObject(hDLL) - the embedded language folding requires that the dll stay loaded, and it's also more efficient for other cases
- HINSTANCE hDll;
- void *funcptr = loadExternalEntryPoint(expr, foldOptions, templateContext, library.str(), entry.str(), hDll);
- if (!funcptr)
- return NULL;
- return doFoldExternalCall(expr, foldOptions, templateContext, library.str(), entry.str(), funcptr);
- }
- //------------------------------------------------------------------------------------------
- bool checkEmbeddedFoldable(IHqlExpression* expr, unsigned foldOptions)
- {
- IHqlExpression * funcdef = expr->queryBody()->queryFunctionDefinition();
- IHqlExpression * outofline = funcdef->queryChild(0);
- assertex(outofline->getOperator() == no_outofline);
- IHqlExpression * body = outofline->queryChild(0);
- assertex(body->getOperator()==no_embedbody);
- IHqlExpression * formals = funcdef->queryChild(1);
- ITypeInfo * returnType = funcdef->queryType()->queryChildType();
- if (body->hasAttribute(_disallowed_Atom) || !body->hasAttribute(foldAtom))
- return false; // Not allowed
- ForEachChild(idx, body)
- {
- IHqlExpression *child = body->queryChild(idx);
- if (child->isAttribute() && !isInternalEmbedAttr(child->queryName()) && !child->isConstant())
- // Note that the language attr is a function call and thus NOT considered constant, hence we can't just
- // test body->isConstant()
- return false;
- }
- IHqlExpression *languageAttr = body->queryAttribute(languageAtom);
- if (!languageAttr)
- return false; // Can't fold embedded C++
- switch (returnType->getTypeCode())
- {
- case type_row:
- case type_table:
- case type_groupedtable:
- // Can't do as yet without the type info
- return false;
- }
- // check all the parameters passed in
- unsigned numParam = formals->numChildren();
- for(unsigned i = 0; i < numParam; i++)
- {
- IHqlExpression * curArg = formals->queryChild(i);
- assertex(curArg != NULL);
- ITypeInfo * paramType = curArg->queryType();
- switch (paramType->getTypeCode())
- {
- case type_row:
- case type_table:
- case type_groupedtable:
- // Can't do as yet without the type info
- return false;
- }
- }
- return true;
- }
- class DummyContext: implements ICodeContext
- {
- // Perhaps this should go into eclrtl - address cleaner uses it too
- virtual const char *loadResource(unsigned id) { throwUnexpected(); }
- // Fetching interim results from workunit/query context
- virtual bool getResultBool(const char * name, unsigned sequence) { throwUnexpected(); }
- virtual void getResultData(unsigned & tlen, void * & tgt, const char * name, unsigned sequence) { throwUnexpected(); }
- virtual void getResultDecimal(unsigned tlen, int precision, bool isSigned, void * tgt, const char * stepname, unsigned sequence) { throwUnexpected(); }
- virtual void getResultDictionary(size32_t & tcount, byte * * & tgt, IEngineRowAllocator * _rowAllocator, const char * name, unsigned sequence, IXmlToRowTransformer * xmlTransformer, ICsvToRowTransformer * csvTransformer, IHThorHashLookupInfo * hasher) { throwUnexpected(); }
- virtual void getResultRaw(unsigned & tlen, void * & tgt, const char * name, unsigned sequence, IXmlToRowTransformer * xmlTransformer, ICsvToRowTransformer * csvTransformer) { throwUnexpected(); }
- virtual void getResultSet(bool & isAll, size32_t & tlen, void * & tgt, const char * name, unsigned sequence, IXmlToRowTransformer * xmlTransformer, ICsvToRowTransformer * csvTransformer) { throwUnexpected(); }
- virtual __int64 getResultInt(const char * name, unsigned sequence) { throwUnexpected(); }
- virtual double getResultReal(const char * name, unsigned sequence) { throwUnexpected(); }
- virtual void getResultRowset(size32_t & tcount, byte * * & tgt, const char * name, unsigned sequence, IEngineRowAllocator * _rowAllocator, bool isGrouped, IXmlToRowTransformer * xmlTransformer, ICsvToRowTransformer * csvTransformer) { throwUnexpected(); }
- virtual void getResultString(unsigned & tlen, char * & tgt, const char * name, unsigned sequence) { throwUnexpected(); }
- virtual void getResultStringF(unsigned tlen, char * tgt, const char * name, unsigned sequence) { throwUnexpected(); }
- virtual void getResultUnicode(unsigned & tlen, UChar * & tgt, const char * name, unsigned sequence) { throwUnexpected(); }
- virtual char *getResultVarString(const char * name, unsigned sequence) { throwUnexpected(); }
- virtual UChar *getResultVarUnicode(const char * name, unsigned sequence) { throwUnexpected(); }
- // Writing results to workunit/query context/output
- virtual void setResultBool(const char *name, unsigned sequence, bool value) { throwUnexpected(); }
- virtual void setResultData(const char *name, unsigned sequence, int len, const void * data) { throwUnexpected(); }
- virtual void setResultDecimal(const char * stepname, unsigned sequence, int len, int precision, bool isSigned, const void *val) { throwUnexpected(); }
- virtual void setResultInt(const char *name, unsigned sequence, __int64 value, unsigned size) { throwUnexpected(); }
- virtual void setResultRaw(const char *name, unsigned sequence, int len, const void * data) { throwUnexpected(); }
- virtual void setResultReal(const char * stepname, unsigned sequence, double value) { throwUnexpected(); }
- virtual void setResultSet(const char *name, unsigned sequence, bool isAll, size32_t len, const void * data, ISetToXmlTransformer * transformer) { throwUnexpected(); }
- virtual void setResultString(const char *name, unsigned sequence, int len, const char * str) { throwUnexpected(); }
- virtual void setResultUInt(const char *name, unsigned sequence, unsigned __int64 value, unsigned size) { throwUnexpected(); }
- virtual void setResultUnicode(const char *name, unsigned sequence, int len, UChar const * str) { throwUnexpected(); }
- virtual void setResultVarString(const char * name, unsigned sequence, const char * value) { throwUnexpected(); }
- virtual void setResultVarUnicode(const char * name, unsigned sequence, UChar const * value) { throwUnexpected(); }
- // Checking persists etc are up to date
- virtual unsigned getResultHash(const char * name, unsigned sequence) { throwUnexpected(); }
- virtual unsigned getExternalResultHash(const char * wuid, const char * name, unsigned sequence) { throwUnexpected(); }
- virtual unsigned __int64 getDatasetHash(const char * name, unsigned __int64 crc) { throwUnexpected(); }
- // Fetching various environment information, typically accessed via std.system
- virtual char *getClusterName() { throwUnexpected(); } // caller frees return string.
- virtual char *getEnv(const char *name, const char *defaultValue) const { throwUnexpected(); }
- virtual char *getGroupName() { throwUnexpected(); } // caller frees return string.
- virtual char *getJobName() { throwUnexpected(); } // caller frees return string.
- virtual char *getJobOwner() { throwUnexpected(); } // caller frees return string.
- virtual unsigned getNodeNum() { throwUnexpected(); }
- virtual unsigned getNodes() { throwUnexpected(); }
- virtual char *getOS() { throwUnexpected(); } // caller frees return string
- virtual char *getPlatform() { throwUnexpected(); } // caller frees return string.
- virtual unsigned getPriority() const { throwUnexpected(); }
- virtual char *getWuid() { throwUnexpected(); } // caller frees return string.
- // Exception handling
- virtual void addWuException(const char*, unsigned int, unsigned int, const char*) { throwUnexpected(); } //n.b. this might be better named: it should only be used for adding user-generated exceptions (via the logging plug-in) --- there's a call in IAgentContext which takes a source argument too
- virtual void addWuAssertFailure(unsigned code, const char * text, const char * filename, unsigned lineno, unsigned column, bool isAbort) { throwUnexpected(); }
- // File resolution etc
- virtual char * getExpandLogicalName(const char * logicalName) { throwUnexpected(); }
- virtual unsigned __int64 getFileOffset(const char *logicalPart) { throwUnexpected(); }
- virtual char *getFilePart(const char *logicalPart, bool create=false) { throwUnexpected(); } // caller frees return string.
- virtual IDistributedFileTransaction *querySuperFileTransaction() { throwUnexpected(); }
- virtual IUserDescriptor *queryUserDescriptor() { throwUnexpected(); }
- // Graphs, child queries etc
- virtual void executeGraph(const char * graphName, bool realThor, size32_t parentExtractSize, const void * parentExtract) { throwUnexpected(); }
- virtual unsigned getGraphLoopCounter() const { return 0; }
- virtual IThorChildGraph * resolveChildQuery(__int64 activityId, IHThorArg * colocal) { throwUnexpected(); }
- virtual IEclGraphResults * resolveLocalQuery(__int64 activityId) { return NULL; }
- // Logging etc
- virtual unsigned logString(const char *text) const { throwUnexpected(); }
- virtual IDebuggableContext *queryDebugContext() const { return NULL; }
- // Memory management
- virtual IEngineRowAllocator * getRowAllocator(IOutputMetaData * meta, unsigned activityId) const { throwUnexpected(); }
- virtual const char * cloneVString(const char *str) const { throwUnexpected(); }
- virtual const char * cloneVString(size32_t len, const char *str) const { throwUnexpected(); }
- // Called from generated code for FROMXML/TOXML
- virtual const void * fromXml(IEngineRowAllocator * _rowAllocator, size32_t len, const char * utf8, IXmlToRowTransformer * xmlTransformer, bool stripWhitespace) { throwUnexpected(); }
- virtual void getRowXML(size32_t & lenResult, char * & result, IOutputMetaData & info, const void * row, unsigned flags) { throwUnexpected(); }
- // Miscellaneous
- virtual void getExternalResultRaw(unsigned & tlen, void * & tgt, const char * wuid, const char * stepname, unsigned sequence, IXmlToRowTransformer * xmlTransformer, ICsvToRowTransformer * csvTransformer) { throwUnexpected(); } // shouldn't really be here, but it broke thor.
- virtual char * queryIndexMetaData(char const * lfn, char const * xpath) { throwUnexpected(); }
- // Called from generated code for FROMJSON
- virtual const void * fromJson(IEngineRowAllocator * _rowAllocator, size32_t len, const char * utf8, IXmlToRowTransformer * xmlTransformer, bool stripWhitespace) { throwUnexpected(); }
- virtual void getRowJSON(size32_t & lenResult, char * & result, IOutputMetaData & info, const void * row, unsigned flags) { throwUnexpected(); }
- virtual const IContextLogger &queryContextLogger() const
- {
- return queryDummyContextLogger();
- }
- virtual IEngineContext *queryEngineContext() { return NULL; }
- virtual char *getDaliServers() { throwUnexpected(); }
- virtual IWorkUnit* updateWorkUnit() const { throwUnexpected(); }
- virtual ISectionTimer * registerTimer(unsigned activityId, const char * name) { throwUnexpected(); }
- virtual IEngineRowAllocator * getRowAllocatorEx(IOutputMetaData * meta, unsigned activityId, unsigned flags) const { throwUnexpected(); }
- };
- IHqlExpression *deserializeConstantSet(ITypeInfo *type, bool isAll, size32_t len, const void *vdata)
- {
- ITypeInfo *childType = type->queryChildType();
- if (isAll)
- return createValue(no_all, LINK(childType));
- else if (!len)
- return createValue(no_null, LINK(childType));
- else
- {
- const char *data = (const char *) vdata;
- const char *end = data + len;
- HqlExprArray values;
- while (data < end)
- {
- size32_t size = childType->getSize();
- size32_t numChars = childType->getStringLen();
- switch (childType->getTypeCode())
- {
- case type_int:
- case type_real:
- case type_boolean:
- values.append(*createConstant(createValueFromMem(LINK(childType), data)));
- break;
- case type_varstring:
- values.append(*createConstant(data));
- if (size==UNKNOWN_LENGTH)
- size = strlen(data)+1;
- break;
- case type_string:
- if (size==UNKNOWN_LENGTH)
- {
- size = *(size32_t *) data;
- data += sizeof(size32_t);
- }
- values.append(*createConstant(createStringValue(data, size)));
- break;
- case type_data:
- if (size==UNKNOWN_LENGTH)
- {
- size = *(size32_t *) data;
- data += sizeof(size32_t);
- }
- values.append(*createConstant(createDataValue(data, size)));
- break;
- case type_unicode:
- if (size==UNKNOWN_LENGTH)
- {
- numChars = *(size32_t *) data; // in characters
- data += sizeof(size32_t);
- values.append(*createConstant(createUnicodeValue((const UChar *) data, numChars, LINK(childType))));
- size = numChars * sizeof(UChar);
- }
- else
- {
- values.append(*createConstant(createUnicodeValue((const UChar *) data, numChars, LINK(childType))));
- }
- break;
- case type_utf8:
- // size is always UNKNOWN_LENGTH for uft8
- assertex(size==UNKNOWN_LENGTH);
- numChars = *(size32_t *) data; // in characters
- data += sizeof(size32_t);
- values.append(*createConstant(createUtf8Value(numChars, data, LINK(childType))));
- size = rtlUtf8Size(numChars, data);
- break;
- default:
- return NULL;
- }
- if (size != UNKNOWN_LENGTH)
- data += size;
- }
- return createValue(no_list, LINK(type), values);
- }
- }
- IHqlExpression * foldEmbeddedCall(IHqlExpression* expr, unsigned foldOptions, ITemplateContext *templateContext)
- {
- if (!checkEmbeddedFoldable(expr, foldOptions))
- return NULL;
- IHqlExpression * funcdef = expr->queryBody()->queryFunctionDefinition();
- IHqlExpression * outofline = funcdef->queryChild(0);
- assertex(outofline->getOperator() == no_outofline);
- IHqlExpression * body = outofline->queryChild(0);
- assertex(body->getOperator()==no_embedbody);
- ITypeInfo * returnType = funcdef->queryType()->queryChildType();
- bool isImport = body->hasAttribute(importAtom);
- IHqlExpression * formals = funcdef->queryChild(1);
- assertex(formals->numChildren() == expr->numChildren()); // MORE - do default params change this?
- unsigned flags = isImport ? EFimport : EFembed;
- if (formals->numChildren()==0)
- flags |= EFnoparams;
- if (returnType->getTypeCode()==type_void)
- flags |= EFnoreturn;
- StringBuffer optionsStr;
- ForEachChild(idx, body)
- {
- IHqlExpression *child = body->queryChild(idx);
- if (child->isAttribute() && !isInternalEmbedAttr(child->queryName()))
- {
- if (optionsStr.length())
- optionsStr.append(",");
- optionsStr.append(child->queryName());
- IHqlExpression * value = child->queryChild(0);
- if (value)
- {
- optionsStr.append("=");
- value->queryValue()->getUTF8Value(optionsStr);
- }
- }
- }
- IHqlExpression *languageAttr = body->queryAttribute(languageAtom);
- HqlExprArray noParams;
- OwnedHqlExpr langLoadCall = createTranslatedExternalCall(NULL, languageAttr->queryChild(0), noParams);
- Owned<IValue> plugin = foldExternalCall(langLoadCall, foldOptions, templateContext);
- if (plugin == nullptr)
- return NULL;
- Owned<IEmbedContext> __plugin = (IEmbedContext *) plugin->getIntValue(); // We declared as int since ecl has no pointer type - not sure what the clean fix is here...
- DummyContext dummyContext;
- Owned<IEmbedFunctionContext> __ctx = __plugin->createFunctionContextEx(&dummyContext,flags,optionsStr.str());
- IValue *query = body->queryChild(0)->queryValue();
- assertex(query);
- StringBuffer queryText;
- query->getUTF8Value(queryText);
- if (!body->hasAttribute(prebindAtom))
- {
- if (isImport)
- __ctx->importFunction(queryText.lengthUtf8(), queryText.str());
- else
- __ctx->compileEmbeddedScript(queryText.lengthUtf8(), queryText.str());
- }
- // process all the parameters passed in
- unsigned numParam = expr->numChildren();
- for(unsigned i = 0; i < numParam; i++)
- {
- IHqlExpression * curParam = expr->queryChild(i); //NB: Already folded...
- IHqlExpression * curArg = formals->queryChild(i);
- assertex(curArg != NULL);
- ITypeInfo * paramType = curArg->queryType();
- assertex(paramType != NULL);
- IValue * paramValue = curParam->queryValue();
- assertex(curParam->isConstant());
- unsigned paramSize = paramType->getSize();
- IIdAtom * paramId = curArg->queryId();
- const char * name = str(paramId);
- switch (paramType->getTypeCode())
- {
- case type_int:
- {
- __int64 value = paramValue->getIntValue();
- if (paramSize<8)
- {
- if (paramType->isSigned())
- __ctx->bindSignedSizeParam(name, paramSize, value);
- else
- __ctx->bindUnsignedSizeParam(name, paramSize, value);
- }
- else
- {
- if (paramType->isSigned())
- __ctx->bindSignedParam(name, value);
- else
- __ctx->bindUnsignedParam(name, value);
- }
- break;
- }
- case type_varstring:
- {
- StringBuffer value;
- paramValue->getStringValue(value);
- __ctx->bindVStringParam(name, value.str());
- break;
- }
- case type_string:
- {
- StringBuffer value;
- paramValue->getStringValue(value);
- __ctx->bindStringParam(name, value.length(), value.str());
- break;
- }
- case type_real:
- {
- double value = paramValue->getRealValue();
- if (paramType->getSize()==4)
- __ctx->bindFloatParam(name, value);
- else
- __ctx->bindRealParam(name, value);
- break;
- }
- case type_boolean:
- __ctx->bindBooleanParam(name, paramValue->getBoolValue());
- break;
- case type_utf8:
- {
- StringBuffer value;
- paramValue->getUTF8Value(value);
- __ctx->bindUTF8Param(name, value.lengthUtf8(), value.str());
- break;
- }
- case type_unicode:
- {
- unsigned len = paramValue->queryType()->getStringLen();
- UChar * value = (UChar *)malloc(len*2);
- paramValue->getUCharStringValue(len, value);
- __ctx->bindUnicodeParam(name, len, value);
- free(value);
- break;
- }
- case type_data:
- {
- __ctx->bindDataParam(name, paramValue->getSize(), paramValue->queryValue());
- break;
- }
- case type_row:
- case type_table:
- case type_groupedtable:
- // Can't do as yet without the type info
- return NULL;
- case type_set:
- {
- MemoryBuffer setValue;
- if (!createConstantField(setValue, curArg, curParam))
- return NULL;
- bool isAll;
- size32_t totalSize;
- setValue.read(isAll);
- setValue.read(totalSize);
- ITypeInfo *childType = paramType->queryChildType();
- type_t typeCode = childType->getTypeCode();
- if (childType->isInteger() && !childType->isSigned())
- typeCode = type_unsigned;
- __ctx->bindSetParam(name, (unsigned) typeCode, paramType->queryChildType()->getSize(), isAll, totalSize, setValue.readDirect(totalSize));
- break;
- }
- default:
- return NULL;
- }
- }
- if (body->hasAttribute(prebindAtom))
- {
- if (isImport)
- __ctx->importFunction(queryText.lengthUtf8(), queryText.str());
- else
- __ctx->compileEmbeddedScript(queryText.lengthUtf8(), queryText.str());
- }
- __ctx->callFunction();
- switch (returnType->getTypeCode())
- {
- case type_int:
- return createConstant(returnType->isSigned() ? __ctx->getSignedResult() : __ctx->getUnsignedResult(), LINK(returnType));
- case type_varstring:
- case type_string:
- {
- size32_t lenResult;
- rtlDataAttr result;
- __ctx->getStringResult(lenResult, result.refstr());
- return createConstant(createStringValue(result.getstr(), lenResult));
- }
- case type_real:
- return createConstant(createRealValue(__ctx->getRealResult(), LINK(returnType)));
- case type_boolean:
- return createConstant(__ctx->getBooleanResult());
- case type_unicode:
- {
- size32_t lenResult;
- rtlDataAttr result;
- __ctx->getUnicodeResult(lenResult, result.refustr());
- return createConstant(createUnicodeValue(result.getustr(), lenResult, LINK(returnType)));
- }
- case type_utf8:
- {
- size32_t lenResult;
- rtlDataAttr result;
- __ctx->getUTF8Result(lenResult, result.refstr());
- return createConstant(createUtf8Value(lenResult, result.getstr(), LINK(returnType)));
- }
- case type_data:
- {
- size32_t lenResult;
- rtlDataAttr result;
- __ctx->getDataResult(lenResult, result.refdata());
- return createConstant(createDataValue(result.getstr(), lenResult));
- }
- case type_set:
- {
- ITypeInfo *childType = returnType->queryChildType();
- type_t typeCode = childType->getTypeCode();
- if (childType->isInteger() && !childType->isSigned())
- typeCode = type_unsigned;
- bool isAllResult;
- size32_t resultBytes;
- rtlDataAttr result;
- __ctx->getSetResult(isAllResult, resultBytes, result.refdata(), (unsigned) typeCode, returnType->queryChildType()->getSize());
- return deserializeConstantSet(returnType, isAllResult,resultBytes, result.getdata());
- }
- case type_row:
- case type_table:
- case type_transform:
- case type_void:
- // Can't do yet, maybe never - should probably check this earlier!'
- return NULL;
- }
- return NULL;
- }
- //------------------------------------------------------------------------------------------
- // optimize ((a BAND b) <> 0) OR ((a BAND c) <> 0) to ((a BAND (b BOR c)) <> 0)
- bool isFieldMask(IHqlExpression * expr)
- {
- if (expr->getOperator() != no_ne)
- return false;
- IHqlExpression * left = expr->queryChild(0);
- if (left->getOperator() != no_band)
- return false;
- IValue * rightValue = expr->queryChild(1)->queryValue();
- if (!rightValue || rightValue->getIntValue() != 0)
- return false;
- return true;
- }
- bool isFieldMask(IHqlExpression * field, IHqlExpression * expr)
- {
- return isFieldMask(expr) && (expr->queryChild(0)->queryChild(0) == field);
- }
- IHqlExpression * combineMask(IHqlExpression * left, IHqlExpression * right)
- {
- IHqlExpression * zero = left->queryChild(1);
- IHqlExpression * field = left->queryChild(0)->queryChild(0);
- IHqlExpression * mask1 = left->queryChild(0)->queryChild(1);
- IHqlExpression * mask2 = right->queryChild(0)->queryChild(1);
- ITypeInfo * borType = getBorType(mask1->queryType(), mask2->queryType());
- ITypeInfo * bandType = getBandType(field->queryType(), borType);
- OwnedHqlExpr newMask = createValue(no_bor, borType, ensureExprType(mask1, borType), ensureExprType(mask2, borType));
- IHqlExpression * newTest = createValue(no_band, bandType, ensureExprType(field, bandType), ensureExprType(newMask, bandType));
- return createBoolExpr(no_ne, newTest, ensureExprType(zero, bandType));
- }
- bool constantComparison(IHqlExpression * field, IHqlExpression * expr, HqlExprArray & values)
- {
- IHqlExpression * left = expr->queryChild(0);
- IHqlExpression * right = expr->queryChild(1);
- switch (expr->getOperator())
- {
- case no_eq:
- if (field && field!= left)
- return false;
- if (!right->queryValue())
- return false;
- if (values.find(*right) == NotFound)
- values.append(*LINK(right));
- return true;
- case no_in:
- {
- if (field && field != left)
- return false;
- if (right->getOperator() != no_list)
- return false;
- ForEachChild(i, right)
- {
- IHqlExpression * cur = right->queryChild(i);
- if (values.find(*cur) == NotFound)
- values.append(*LINK(cur));
- }
- return true;
- }
- }
- return false;
- }
- bool isFilteredWithin(IHqlExpression * expr, IHqlExpression * dataset, HqlExprArray & filters)
- {
- bool invert = false;
- if (expr->getOperator() == no_not)
- {
- invert = true;
- expr = expr->queryChild(0);
- }
- if (expr->getOperator() != no_within)
- return false;
- IHqlExpression * child0 = expr->queryChild(0);
- if (child0->getOperator() != no_filter)
- return false;
- if (dataset && dataset != child0->queryChild(0))
- return false;
- unsigned max = child0->numChildren();
- for (unsigned idx=1; idx < max; idx++)
- {
- IHqlExpression * cur = LINK(child0->queryChild(idx));
- if (invert)
- cur = createValue(no_not, makeBoolType(), cur);
- filters.append(*cur);
- }
- return true;
- }
- void mergeWithins(node_operator op, HqlExprArray & transformedArgs)
- {
- for (unsigned idxWithin = 0; idxWithin < transformedArgs.ordinality(); idxWithin++)
- {
- IHqlExpression & cur = transformedArgs.item(idxWithin);
- HqlExprArray filters;
- if (isFilteredWithin(&cur, NULL, filters))
- {
- IHqlExpression * dataset = cur.queryChild(0)->queryChild(0);
- bool changed = false;
- for (unsigned idxMatch = idxWithin+1; idxMatch < transformedArgs.ordinality();)
- {
- IHqlExpression & match = transformedArgs.item(idxMatch);
- if (isFilteredWithin(&match, dataset, filters))
- {
- changed = true;
- transformedArgs.remove(idxMatch);
- }
- else
- idxMatch++;
- }
- if (changed)
- {
- HqlExprArray filterArgs;
- filterArgs.append(*LINK(dataset));
- filterArgs.append(*createBinaryList(op, filters));
- IHqlExpression * filteredDataset = createDataset(no_filter, filterArgs);
- IHqlExpression * within = createValue(no_within, makeBoolType(), filteredDataset);
- transformedArgs.replace(*within, idxWithin);
- }
- }
- }
- }
- IHqlExpression * foldOrExpr(IHqlExpression * expr, bool fold_x_op_not_x)
- {
- HqlExprArray args, transformedArgs;
- expr->unwindList(args, expr->getOperator());
- //First transform all the arguments, removing and always false, and short circuit if always true
- //also remove duplicates a || a == a
- ForEachItemIn(idx, args)
- {
- IHqlExpression * transformed = &args.item(idx);
- IValue * value = transformed->queryValue();
- if (value)
- {
- if (value->getBoolValue())
- return LINK(transformed);
- }
- else
- {
- if (transformedArgs.find(*transformed) == NotFound)
- {
- if (fold_x_op_not_x)
- {
- //Check for x OR NOT x => always true...
- //Needs to be done this way because the no_not often gets folded...
- OwnedHqlExpr inverse = createValue(no_not, makeBoolType(), LINK(transformed));
- if (transformedArgs.contains(*inverse))
- return createConstant(true);
- }
- transformedArgs.append(*LINK(transformed));
- }
- }
- }
- if (transformedArgs.ordinality() == 0)
- return createConstant(false);
- // optimize ((a BAND b) <> 0) OR ((a BAND c) <> 0) to ((a BAND (b BOR c)) <> 0)
- for (unsigned idx2 = 0; idx2 < transformedArgs.ordinality()-1; idx2++)
- {
- IHqlExpression & cur = transformedArgs.item(idx2);
- if (isFieldMask(&cur))
- {
- IHqlExpression * masked = cur.queryChild(0)->queryChild(0);
- LinkedHqlExpr combined = &cur;
- for (unsigned idx3 = transformedArgs.ordinality()-1; idx3 != idx2; idx3--)
- {
- IHqlExpression & cur2 = transformedArgs.item(idx3);
- if (isFieldMask(masked, &cur2))
- {
- combined.setown(combineMask(combined, &cur2));
- transformedArgs.remove(idx3);
- }
- }
- if (combined != &cur)
- transformedArgs.replace(*combined.getClear(), idx2);
- }
- }
- //optimize x=a|x=b|x=c to x in (a,b,c)
- HqlExprArray constantValues;
- for (unsigned idx4 = 0; idx4 < transformedArgs.ordinality()-1; idx4++)
- {
- IHqlExpression & cur = transformedArgs.item(idx4);
- constantValues.kill();
- if (constantComparison(NULL, &cur, constantValues))
- {
- bool merged = false;
- IHqlExpression * compare = cur.queryChild(0);
- for (unsigned idx5 = idx4+1; idx5 < transformedArgs.ordinality(); )
- {
- IHqlExpression & cur2 = transformedArgs.item(idx5);
- if (constantComparison(compare, &cur2, constantValues))
- {
- merged = true;
- transformedArgs.remove(idx5);
- }
- else
- idx5++;
- }
- if (merged)
- {
- //MORE: Should promote all items in the list to the same type.
- IHqlExpression & first = constantValues.item(0);
- IHqlExpression * list = createValue(no_list, makeSetType(first.getType()), constantValues);
- OwnedHqlExpr combined = createBoolExpr(no_in, LINK(compare), list);
- transformedArgs.replace(*combined.getClear(), idx4);
- }
- }
- }
- #if 0
- else
- {
- // optimize (( BOOL)(a BAND b) ) OR ((bool)(a BAND c) ) to ((bool)(a BAND (b BOR c)) )
- // Lots of work for a very particular special case that happens a lot in DMS
- assertex (leftChild->queryType() == rightChild->queryType());
- assertex (leftChild->queryType()->getTypeCode() == type_boolean);
- #if 0
- dbglogExpr(leftChild->queryBody());
- dbglogExpr(rightChild->queryBody());
- #endif
- IHqlExpression *select = NULL;
- if (leftChild->getOperator()==no_select && rightChild->getOperator()==no_select && leftChild->queryChild(0)==rightChild->queryChild(0))
- {
- select = leftChild->queryChild(0);
- leftChild.set(leftChild->queryChild(1));
- rightChild.set(rightChild->queryChild(1));
- }
- while (leftChild && leftChild->getOperator()==no_field)
- leftChild.set(leftChild->queryChild(0));
- while (rightChild && rightChild->getOperator()==no_field)
- rightChild.set(rightChild->queryChild(0));
- if (leftChild && rightChild)
- {
- #if 0
- dbglogExpr(leftChild->queryBody());
- dbglogExpr(rightChild->queryBody());
- #endif
- if (isCast(leftChild) && isCast(rightChild))
- {
- IHqlExpression * lBand = leftChild->queryChild(0);
- IHqlExpression * rBand = rightChild->queryChild(0);
- if (lBand->getOperator() == no_band && rBand->getOperator() == no_band)
- {
- IHqlExpression * aLeft = lBand->queryChild(0);
- IHqlExpression * aRight = rBand->queryChild(0);
- if (aLeft == aRight)
- {
- IHqlExpression * bLeft = lBand->queryChild(1);
- IHqlExpression * cRight = rBand->queryChild(1);
- IHqlExpression * newBor = createValue(no_bor, getPromotedType(bLeft->queryType(), cRight->queryType()), LINK(bLeft), LINK(cRight));
- IHqlExpression * newBand = createValue(no_band, getPromotedType(aLeft->queryType(), newBor->queryType()), LINK(aLeft), newBor);
- OwnedHqlExpr newNode = createBoolExpr(no_cast, newBand);
- if (select)
- newNode.setown(createBoolExpr(no_select, LINK(select), newNode.getClear());
- return transform(newNode);
- }
- }
- }
- }
- }
- #endif
- //mergeWithins(no_or, transformedArgs);
- if (arraysSame(args, transformedArgs))
- return LINK(expr);
- return createBinaryList(no_or, transformedArgs);
- }
- IHqlExpression * foldAndExpr(IHqlExpression * expr, bool fold_x_op_not_x)
- {
- HqlExprArray args, transformedArgs;
- expr->unwindList(args, expr->getOperator());
- //First transform all the arguments, removing if always true, and short circuit if always false
- //also remove duplicates a && a == a
- ForEachItemIn(idx, args)
- {
- IHqlExpression * cur = &args.item(idx);
- IValue * value = cur->queryValue();
- if (value)
- {
- if (!value->getBoolValue())
- return LINK(cur);
- }
- else
- {
- if (transformedArgs.find(*cur) == NotFound)
- {
- if (fold_x_op_not_x)
- {
- //Check for x AND NOT x. => false
- OwnedHqlExpr inverse = getInverse(cur);
- if (transformedArgs.find(*inverse) != NotFound)
- return createConstant(false);
- }
- transformedArgs.append(*LINK(cur));
- }
- }
- }
- //mergeWithins(no_and, transformedArgs);
- if (transformedArgs.ordinality() == 0)
- return createConstant(true);
- if (arraysSame(args, transformedArgs))
- return LINK(expr);
- return createBinaryList(no_and, transformedArgs);
- }
- IHqlExpression * applyBinaryFold(IHqlExpression * expr, binaryFoldFunc folder)
- {
- IHqlExpression * leftChild = expr->queryChild(0);
- IHqlExpression * rightChild = expr->queryChild(1);
- IValue * leftValue = leftChild->queryValue();
- IValue * rightValue = rightChild->queryValue();
- if (leftValue && rightValue)
- {
- IValue * res = folder(leftValue, rightValue);
- assertex(res);
- #if 0
- //A useful consistency test, but not always true for no_concat, so commented out for the moment
- if (!isUnknownSize(expr->queryType()))
- assertex(res->queryType() == expr->queryType());
- #endif
- return createConstant(res);
- }
- return LINK(expr);
- }
- static bool isStringOrUnicode(ITypeInfo * type)
- {
- switch (type->getTypeCode())
- {
- case type_data:
- case type_string:
- case type_varstring:
- case type_qstring:
- case type_unicode:
- case type_varunicode:
- case type_utf8:
- return true;
- }
- return false;
- }
- static bool isNonAscii(ITypeInfo * type)
- {
- switch (type->getTypeCode())
- {
- case type_string:
- case type_varstring:
- return type->queryCharset()->queryName() != asciiAtom;
- }
- return false;
- }
- static bool castHidesConversion(ITypeInfo * t1, ITypeInfo * t2, ITypeInfo * t3)
- {
- return (t1->getTypeCode() == type_data) && (isNonAscii(t2) || isNonAscii(t3));
- }
- static IHqlExpression * optimizeConstInList(IValue * search, IHqlExpression * list)
- {
- if (!list) return NULL;
- switch (list->getOperator())
- {
- case no_null:
- case no_all:
- return LINK(list);
- case no_list:
- {
- HqlExprArray values;
- bool same = true;
- unsigned num = list->numChildren();
- for (unsigned idx = 0; idx < num; idx++)
- {
- IHqlExpression * elem = list->queryChild(idx);
- IValue * constValue = elem->queryValue();
- if (constValue)
- {
- if (orderValues(search, constValue) == 0)
- return createValue(no_all, list->getType());
- same = false;
- }
- else
- values.append(*LINK(elem));
- }
- if (same)
- return LINK(list);
- return list->clone(values);
- }
- case no_addsets:
- {
- IHqlExpression * lhs = list->queryChild(0);
- IHqlExpression * rhs = list->queryChild(1);
- OwnedHqlExpr newLhs = optimizeConstInList(search, lhs);
- OwnedHqlExpr newRhs = optimizeConstInList(search, rhs);
- if ((newLhs->getOperator() == no_all) || isNullList(newRhs))
- return LINK(newLhs);
- if ((newRhs->getOperator() == no_all) || isNullList(newLhs))
- return LINK(newRhs);
- if ((lhs == newLhs) && (rhs == newRhs))
- return LINK(list);
- if ((newLhs->getOperator() != no_list) || (newRhs->getOperator() != no_list))
- return createValue(no_addsets, list->getType(), newLhs.getClear(), newRhs.getClear());
- HqlExprArray args;
- unwindChildren(args, newLhs);
- unwindChildren(args, newRhs);
- return createValue(no_list, list->getType(), args);
- }
- case no_if:
- {
- IHqlExpression * lhs = list->queryChild(1);
- IHqlExpression * rhs = list->queryChild(2);
- OwnedHqlExpr newLhs = optimizeConstInList(search, lhs);
- OwnedHqlExpr newRhs = optimizeConstInList(search, rhs);
- //might both turn out to be all/empty
- if (newLhs->queryBody() == newRhs->queryBody())
- return LINK(newLhs);
- if ((lhs == newLhs) && (rhs == newRhs))
- return LINK(list);
- HqlExprArray args;
- unwindChildren(args, list);
- args.replace(*newLhs.getClear(), 1);
- args.replace(*newRhs.getClear(), 2);
- return list->clone(args);
- }
- }
- return LINK(list);
- }
- static bool hashElement(node_operator op, IHqlExpression * expr, unsigned __int64 & hashCode)
- {
- IValue * value = expr->queryValue();
- if (!value)
- return false;
- ITypeInfo * type = value->queryType();
- switch (type->getTypeCode())
- {
- case type_qstring:
- case type_string:
- {
- Owned<ITypeInfo> unknownLengthString = makeStringType(UNKNOWN_LENGTH);
- Owned<IValue> castValue = value->castTo(unknownLengthString);
- const char * cdata = static_cast<const char *>(castValue->queryValue());
- size32_t len = rtlTrimStrLen(castValue->queryType()->getStringLen(), cdata);
- hashCode = (op == no_hash32) ? rtlHash32Data(len, cdata, (unsigned)hashCode) : rtlHash64Data(len, cdata, hashCode);
- return true;
- }
- case type_data:
- {
- size32_t len = type->getSize();
- const char * cdata = static_cast<const char *>(value->queryValue());
- hashCode = (op == no_hash32) ? rtlHash32Data(len, cdata, (unsigned)hashCode) : rtlHash64Data(len, cdata, hashCode);
- return true;
- }
- case type_varstring:
- {
- const char * cdata = static_cast<const char *>(value->queryValue());
- hashCode = (op == no_hash32) ? rtlHash32VStr(cdata, (unsigned)hashCode) : rtlHash64VStr(cdata, hashCode);
- return true;
- }
- case type_unicode:
- {
- const UChar * udata = static_cast<const UChar *>(value->queryValue());
- size32_t len = type->getStringLen();
- hashCode = (op == no_hash32) ? rtlHash32Unicode(len, udata, (unsigned)hashCode) : rtlHash64Unicode(len, udata, hashCode);
- return true;
- }
- case type_varunicode:
- {
- const UChar * udata = static_cast<const UChar *>(value->queryValue());
- hashCode = (op == no_hash32) ? rtlHash32VUnicode(udata, (unsigned)hashCode) : rtlHash64VUnicode(udata, hashCode);
- return true;
- }
- case type_utf8:
- {
- const char * udata = static_cast<const char *>(value->queryValue());
- size32_t len = rtlTrimUtf8StrLen(type->getStringLen(), udata);
- hashCode = (op == no_hash32) ? rtlHash32Utf8(len, udata, (unsigned)hashCode) : rtlHash64Utf8(len, udata, hashCode);
- return true;
- }
- case type_int:
- case type_swapint:
- {
- unsigned __int64 intValue = value->getIntValue();
- hashCode = (op == no_hash32) ? rtlHash32Data(sizeof(intValue), &intValue, (unsigned)hashCode) : rtlHash64Data(sizeof(intValue), &intValue, hashCode);
- return true;
- }
- break;
- }
- return false;
- }
- static IHqlExpression * foldHashXX(IHqlExpression * expr)
- {
- IHqlExpression * child = expr->queryChild(0);
- node_operator op = expr->getOperator();
- unsigned __int64 hashCode = 0;
- switch (op)
- {
- case no_hash32:
- hashCode = HASH32_INIT;
- break;
- case no_hash64:
- hashCode = HASH64_INIT;
- break;
- }
- if (child->getOperator() == no_sortlist)
- {
- ForEachChild(i, child)
- {
- if (!hashElement(op, child->queryChild(i), hashCode))
- return NULL;
- }
- }
- else
- {
- if (!hashElement(op, child, hashCode))
- return NULL;
- }
- return createConstant(expr->queryType()->castFrom(true, (__int64)hashCode));
- }
- //---------------------------------------------------------------------------
- IHqlExpression * foldConstantOperator(IHqlExpression * expr, unsigned foldOptions, ITemplateContext * templateContext)
- {
- node_operator op = expr->getOperator();
- switch (op)
- {
- case no_assertkeyed:
- {
- assertex(expr->hasAttribute(_selectors_Atom));
- IHqlExpression * child = expr->queryChild(0);
- IValue * value = child->queryValue();
- if (value)
- {
- if (!value->getBoolValue())
- return LINK(child);
- IHqlExpression * opt = expr->queryAttribute(extendAtom);
- IHqlExpression * selectors = expr->queryAttribute(_selectors_Atom);
- return createValue(no_assertwild, makeBoolType(), createValue(no_all), LINK(selectors), LINK(opt));
- }
- break;
- }
- case no_or:
- case no_and:
- {
- IHqlExpression * left = expr->queryChild(0);
- IHqlExpression * right = expr->queryChild(1);
- IValue * leftValue = left->queryValue();
- if (leftValue)
- {
- bool leftBool = leftValue->getBoolValue();
- if ((op == no_and) ? leftBool : !leftBool)
- return LINK(right);
- return LINK(left);
- }
- IValue * rightValue = right->queryValue();
- if (rightValue)
- {
- bool rightBool = rightValue->getBoolValue();
- if ((op == no_and) ? rightBool : !rightBool)
- return LINK(left);
- return LINK(right);
- }
- break;
- }
- case no_assertconstant:
- case no_globalscope:
- {
- IHqlExpression * child = expr->queryChild(0);
- if (child->queryValue())
- return LINK(child);
- break;
- }
- case no_not:
- {
- node_operator inverseOp = no_none;
- IHqlExpression * child = expr->queryChild(0);
- switch (child->getOperator())
- {
- case no_not:
- return ensureExprType(child->queryChild(0), expr->queryType());
- case no_constant:
- return createConstant(!child->queryValue()->getBoolValue());
- case no_notnot:
- inverseOp = no_not;
- break;
- case no_eq:
- inverseOp = no_ne;
- break;
- case no_ne:
- inverseOp = no_eq;
- break;
- case no_lt:
- inverseOp = no_ge;
- break;
- case no_le:
- inverseOp = no_gt;
- break;
- case no_gt:
- inverseOp = no_le;
- break;
- case no_ge:
- inverseOp = no_lt;
- break;
- case no_in:
- inverseOp = no_notin;
- break;
- case no_notin:
- inverseOp = no_in;
- break;
- case no_between:
- inverseOp = no_notbetween;
- break;
- case no_notbetween:
- inverseOp = no_between;
- break;
- case no_if:
- if (child->queryChild(1)->isConstant() || child->queryChild(2)->isConstant())
- return getInverse(child);
- break;
- }
- if (inverseOp)
- {
- HqlExprArray children;
- unwindChildren(children, child);
- return createValue(inverseOp, child->getType(), children);
- }
- break;
- }
- case no_add:
- {
- IHqlExpression * left = expr->queryChild(0);
- IHqlExpression * right = expr->queryChild(1);
- if (isZero(left))
- return ensureExprType(right, expr->queryType());
- if (isZero(right))
- return ensureExprType(left, expr->queryType());
- return applyBinaryFold(expr, addValues);
- }
- case no_sub:
- {
- IHqlExpression * left = expr->queryChild(0);
- IHqlExpression * right = expr->queryChild(1);
- if (isZero(right))
- return ensureExprType(left, expr->queryType());
- return applyBinaryFold(expr, subtractValues);
- }
- case no_hash32:
- case no_hash64:
- {
- IHqlExpression * folded = foldHashXX(expr);
- if (folded)
- return folded;
- break;
- }
- case no_mul:
- {
- IHqlExpression * left = expr->queryChild(0);
- IHqlExpression * right = expr->queryChild(1);
- //Multiply by zero (from constant folding count(ds)) can reduce a non-constant dataset to constant
- if (isZero(left) || isZero(right))
- {
- OwnedHqlExpr zero = getSizetConstant(0);
- return ensureExprType(zero, expr->queryType());
- }
- if (matchesConstantValue(left, 1))
- return ensureExprType(right, expr->queryType());
- if (matchesConstantValue(right, 1))
- return ensureExprType(left, expr->queryType());
- return applyBinaryFold(expr, multiplyValues);
- }
- case no_div:
- if (matchesConstantValue(expr->queryChild(1), 1))
- {
- IHqlExpression * left = expr->queryChild(0);
- if (left->queryType()->isInteger())
- return ensureExprType(left, expr->queryType());
- }
- //fall through
- case no_modulus:
- {
- IValue * leftValue = expr->queryChild(0)->queryValue();
- IValue * rightValue = expr->queryChild(1)->queryValue();
- if (leftValue && rightValue)
- {
- DBZaction onZero = (foldOptions & HFOforcefold) ? DBZfail : DBZnone;
- IValue * res;
- if (op == no_div)
- res = divideValues(leftValue, rightValue, onZero);
- else
- res = modulusValues(leftValue, rightValue, onZero);
- if (res)
- return createConstant(res);
- }
- return LINK(expr);
- }
- case no_concat:
- return applyBinaryFold(expr, concatValues);
- case no_band:
- {
- if (isZero(expr->queryChild(0)) || isZero(expr->queryChild(1)))
- return createConstant(expr->queryType()->castFrom(true, I64C(0)));
- OwnedHqlExpr ret = applyBinaryFold(expr, binaryAndValues);
- if (ret->getOperator() == no_band)
- {
- // ((x BAND y) BAND z) == (x BAND (y BAND z)) - especially if y + z are constants.
- IHqlExpression * leftChild = ret->queryChild(0);
- if (leftChild->getOperator()==no_band)
- {
- IValue * rightValue = ret->queryChild(1)->queryValue();
- if (rightValue)
- {
- IValue * grandValue = leftChild->queryChild(1)->queryValue();
- if (grandValue)
- {
- IHqlExpression * mask = createConstant(binaryAndValues(grandValue, rightValue));
- IHqlExpression * newBand = createValue(no_band, expr->getType(), LINK(leftChild->queryChild(0)), mask);
- return newBand;
- }
- }
- }
- }
- return ret.getClear();
- }
- case no_bor:
- {
- IHqlExpression * lhs = expr->queryChild(0);
- IHqlExpression * rhs = expr->queryChild(1);
- if (isZero(lhs))
- return ensureExprType(rhs, expr->queryType());
- if (isZero(rhs))
- return ensureExprType(lhs, expr->queryType());
- return applyBinaryFold(expr, binaryOrValues);
- }
- case no_bxor:
- return applyBinaryFold(expr, binaryXorValues);
- case no_power:
- return applyBinaryFold(expr, powerValues);
- case no_atan2:
- return applyBinaryFold(expr, atan2Value);
- case no_lshift:
- return applyBinaryFold(expr, shiftLeftValues);
- case no_rshift:
- return applyBinaryFold(expr, shiftRightValues);
- case no_regex_find:
- {
- IValue * t0 = expr->queryChild(0)->queryValue();
- IValue * t1 = expr->queryChild(1)->queryValue();
- IHqlExpression * c2 = queryRealChild(expr, 2);
- IValue * t2 = c2 ? c2->queryValue() : NULL;
- if (t0 && t1 && (!c2 || t2))
- {
- IValue * result;
- if(isUnicodeType(t0->queryType()))
- {
- unsigned plen = t0->queryType()->getStringLen();
- unsigned slen = t1->queryType()->getStringLen();
- UChar * pattern = (UChar *)malloc((plen+1)*2);
- UChar * search = (UChar *)malloc((slen)*2);
- t0->getUCharStringValue(plen+1, pattern); //plen+1 so get null-terminated
- t1->getUCharStringValue(slen, search);
- ICompiledUStrRegExpr * compiled = rtlCreateCompiledUStrRegExpr(pattern, !expr->hasAttribute(noCaseAtom));
- IUStrRegExprFindInstance * match = compiled->find(search, 0, slen);
- ITypeInfo * type = expr->queryType();
- if(type->getTypeCode() == type_boolean)
- {
- result = createBoolValue(match->found());
- }
- else
- {
- assertex(c2 && t2);
- size32_t len;
- UChar * data;
- match->getMatchX(len, data, (unsigned)t2->getIntValue());
- result = createUnicodeValue(len, data, LINK(type));
- rtlFree(data);
- }
- rtlDestroyUStrRegExprFindInstance(match);
- rtlDestroyCompiledUStrRegExpr(compiled);
- free(pattern);
- free(search);
- }
- else
- {
- StringBuffer pattern, search;
- t0->getStringValue(pattern);
- t1->getStringValue(search);
- rtlCompiledStrRegex compiled;
- compiled.setPattern(pattern.str(), !expr->hasAttribute(noCaseAtom));
- IStrRegExprFindInstance * match = compiled->find(search.str(), 0, search.length(), false);
- ITypeInfo * type = expr->queryType();
- if(type->getTypeCode() == type_boolean)
- {
- result = createBoolValue(match->found());
- }
- else
- {
- assertex(c2 && t2);
- size32_t len;
- char * data;
- match->getMatchX(len, data, (unsigned)t2->getIntValue());
- result = type->castFrom(len, data);
- rtlFree(data);
- }
- rtlDestroyStrRegExprFindInstance(match);
- }
- return createConstant(result);
- }
- break;
- }
- case no_regex_findset:
- {
- IValue * v0 = expr->queryChild(0)->queryValue();
- IValue * v1 = expr->queryChild(1)->queryValue();
- if (v0 && v1)
- {
- bool isAllResult;
- size32_t resultBytes;
- rtlDataAttr matchResults;
- if(isUnicodeType(v0->queryType()))
- {
- size32_t plen = v0->queryType()->getStringLen();
- OwnedMalloc<UChar> pattern (plen+1);
- v0->getUCharStringValue(plen+1, pattern.get()); //plen+1 so get null-terminated
- size32_t slen = v1->queryType()->getStringLen();
- OwnedMalloc<UChar> search (slen);
- v1->getUCharStringValue(slen, search);
- ICompiledUStrRegExpr * compiled = rtlCreateCompiledUStrRegExpr(pattern, !expr->hasAttribute(noCaseAtom));
- compiled->getMatchSet(isAllResult, resultBytes, matchResults.refdata(), slen, search.get());
- rtlDestroyCompiledUStrRegExpr(compiled);
- }
- else
- {
- StringBuffer pattern, search;
- v0->getStringValue(pattern);
- v1->getStringValue(search);
- rtlCompiledStrRegex compiled;
- compiled.setPattern(pattern.str(), !expr->hasAttribute(noCaseAtom));
- compiled->getMatchSet(isAllResult, resultBytes, matchResults.refdata(), search.length(), search.str());
- }
- return convertSetToExpression(isAllResult, resultBytes, matchResults.getdata(), expr->queryType());
- }
- break;
- }
- case no_regex_replace:
- {
- IValue * t0 = expr->queryChild(0)->queryValue();
- IValue * t1 = expr->queryChild(1)->queryValue();
- IValue * t2 = expr->queryChild(2)->queryValue();
- if (t0 && t1 && t2)
- {
- IValue * result;
- if(isUnicodeType(t0->queryType()))
- {
- unsigned plen = t0->queryType()->getStringLen();
- unsigned slen = t1->queryType()->getStringLen();
- unsigned rlen = t2->queryType()->getStringLen();
- UChar * pattern = (UChar *)malloc((plen+1)*2);
- UChar * search = (UChar *)malloc(slen*2);
- UChar * replace = (UChar *)malloc(rlen*2);
- t0->getUCharStringValue(plen+1, pattern); //plen+1 so null-terminated
- t1->getUCharStringValue(slen, search);
- t2->getUCharStringValue(rlen, replace);
- size32_t outlen;
- UChar * out;
- ICompiledUStrRegExpr * compiled = rtlCreateCompiledUStrRegExpr(pattern, !expr->hasAttribute(noCaseAtom));
- compiled->replace(outlen, out, slen, search, rlen, replace);
- result = createUnicodeValue(outlen, out, expr->getType());
- rtlFree(out);
- rtlDestroyCompiledUStrRegExpr(compiled);
- free(pattern);
- free(search);
- free(replace);
- }
- else
- {
- StringBuffer pattern, search, replace;
- t0->getStringValue(pattern);
- t1->getStringValue(search);
- t2->getStringValue(replace);
- size32_t outlen;
- char * out;
- rtlCompiledStrRegex compiled;
- compiled.setPattern(pattern.str(), !expr->hasAttribute(noCaseAtom));
- compiled->replace(outlen, out, search.length(), search.str(), replace.length(), replace.str());
- result = createStringValue(out, outlen);
- rtlFree(out);
- }
- return createConstant(result);
- }
- break;
- }
- case no_intformat:
- {
- IValue * c0 = expr->queryChild(0)->queryValue();
- IValue * c1 = expr->queryChild(1)->queryValue();
- IValue * c2 = expr->queryChild(2)->queryValue();
- if (c0 && c1 && c2)
- {
- __int64 value = c0->getIntValue();
- unsigned width = (unsigned)c1->getIntValue();
- unsigned flags = (unsigned)c2->getIntValue();
- if ((int) width < 0)
- width = 0;
-
- MemoryAttr tempBuffer(width);
- holeIntFormat(width, (char *)tempBuffer.bufferBase(), value, width, flags);
- return createConstant(createStringValue((char *)tempBuffer.bufferBase(), width));
- }
- break;
- }
- case no_realformat:
- {
- IValue * c0 = expr->queryChild(0)->queryValue();
- IValue * c1 = expr->queryChild(1)->queryValue();
- IValue * c2 = expr->queryChild(2)->queryValue();
- if (c0 && c1 && c2)
- {
- double value = c0->getRealValue();
- unsigned width = (unsigned)c1->getIntValue();
- unsigned places = (unsigned)c2->getIntValue();
- unsigned len;
- char * ptr;
- rtlRealFormat(len, ptr, value, width, places);
- IHqlExpression * ret = createConstant(createStringValue(ptr, len));
- rtlFree(ptr);
- return ret;
- }
- break;
- }
- case no_indict:
- if (isNull(expr->queryChild(1)))
- return createConstant(false);
- break;
- case no_in:
- case no_notin:
- {
- IHqlExpression * child = expr->queryChild(0);
- IHqlExpression * originalList = expr->queryChild(1);
- IValue * constValue = child->queryValue();
- OwnedHqlExpr inList = normalizeListCasts(originalList);
- if (constValue)
- inList.setown(optimizeConstInList(constValue, inList));
- switch (inList->getOperator())
- {
- case no_all:
- return createConstant((op == no_in));
- case no_null:
- return createConstant((op != no_in));
- case no_if:
- {
- IHqlExpression * lhs = inList->queryChild(1);
- IHqlExpression * rhs = inList->queryChild(2);
- if ((foldOptions & (HFOcanbreakshared|HFOforcefold)) || (lhs->isConstant() && rhs->isConstant()))
- {
- IHqlExpression * ret = querySimplifyInExpr(expr);
- if (ret)
- return ret;
- }
- break;
- }
- case no_addsets:
- {
- if (foldOptions & (HFOcanbreakshared|HFOforcefold))
- {
- IHqlExpression * ret = querySimplifyInExpr(expr);
- if (ret)
- return ret;
- }
- break;
- }
- }
- if (inList->getOperator() == no_list)
- {
- if (inList->numChildren() == 0)
- return createConstant((op != no_in));
- bool allConst = inList->isConstant();
- if (inList->numChildren() == 1)
- {
- op = (op==no_in) ? no_eq : no_ne;
- IHqlExpression * item1 = inList->queryChild(0);
- Owned<ITypeInfo> type = getPromotedCompareType(child->queryType(), item1->queryType());
- return createBoolExpr(op, ensureExprType(child, type), ensureExprType(item1, type));
- }
- //MORE: Could still remove cases that were impossible to reduce the comparison time,
- // even if the default value is included in the list.
- if (allConst)
- {
- switch (child->getOperator())
- {
- case no_case:
- {
- // CASE(x,a1=>v1,a2=>v2,v3) IN [x1,x2,x3,x4]
- // becomes CASE(x,a1=>v1 IN X,a2=>v2 IN X, v3 IN X)
- // becomes x [NOT] IN [am] where vm is in x1..xn
- HqlExprArray caseResults;
- if (flattenConstantCase(child, caseResults, true))
- {
- IValue *defval = child->queryChild(child->numChildren()-1)->queryValue();
- if (defval)
- {
- bool defaultInList = isInList(defval, inList);
- HqlExprArray exceptions;
- ForEachItemIn(i, caseResults)
- {
- IHqlExpression * inConst = &caseResults.item(i);
- IValue * inValue = inConst->queryValue();
- bool thisInList = isInList(inValue, inList);
- if (thisInList != defaultInList)
- exceptions.append(*LINK(child->queryChild(i+1)->queryChild(0)));
- }
- bool defaultReturn = (defaultInList && op==no_in) || (!defaultInList && op==no_notin);
- if (exceptions.ordinality() == 0)
- return createConstant(defaultReturn);
- node_operator inOp = defaultReturn ? no_notin : no_in;
- IHqlExpression * test = child->queryChild(0);
- return createBoolExpr(inOp,
- LINK(test),
- createValue(no_list, makeSetType(test->getType()), exceptions));
- }
- }
- }
- break;
- case no_cast:
- case no_implicitcast:
- {
- HqlExprArray inConstants;
- unwindChildren(inConstants, inList);
- IHqlExpression * ret = optimizeCastList(child, inConstants, op);
- if (ret)
- return ret;
- break;
- }
- }
- }
- if (inList != originalList)
- return replaceChild(expr, 1, inList);
- }
- break;
- }
- case no_if:
- {
- IHqlExpression * child = expr->queryChild(0);
- IValue * constValue = child->queryValue();
- if (constValue)
- {
- unsigned idx = constValue->getBoolValue() ? 1 : 2;
- IHqlExpression * branch = expr->queryChild(idx);
- if (!branch)
- {
- assertex(expr->isAction());
- return createValue(no_null, makeVoidType());
- }
- return LINK(branch);
- }
- if (expr->queryChild(2))
- {
- IHqlExpression * trueValue = expr->queryChild(1);
- IHqlExpression * falseValue = expr->queryChild(2);
- if (trueValue == falseValue) // occurs in generated code...
- return LINK(trueValue);
- if (expr->queryType()->getTypeCode() == type_boolean)
- {
- HqlExprAttr ret;
- if (trueValue->queryValue())
- {
- //IF(cond1, true, cond2) == cond1 || cond2
- //if(cond1, false, cond2) == !cond1 && cond2
- if (trueValue->queryValue()->getBoolValue())
- ret.setown(createBoolExpr(no_or, LINK(child), LINK(falseValue)));
- else
- ret.setown(createBoolExpr(no_and, getInverse(child), LINK(falseValue)));
- }
- else if (falseValue->queryValue())
- {
- //IF(cond1, cond2, true) == !cond1 || cond2
- //if(cond1, cond2, false) == cond1 && cond2
- if (falseValue->queryValue()->getBoolValue())
- ret.setown(createBoolExpr(no_or, getInverse(child), LINK(trueValue)));
- else
- ret.setown(createBoolExpr(no_and, LINK(child), LINK(trueValue)));
- }
- if (ret)
- return ret.getClear();
- }
- }
- break;
- }
- case no_choose:
- case no_chooseds:
- {
- IHqlExpression * child = expr->queryChild(0);
- IValue * constValue = child->queryValue();
- unsigned last = numNonAttributes(expr)-1;
- if (constValue)
- {
- unsigned idx = (unsigned)constValue->getIntValue();
- if (idx > last || idx == 0)
- idx = last;
- return LINK(expr->queryChild(idx));
- }
- //Remove any trailing conditions which match the default condition
- IHqlExpression * defaultExpr = expr->queryChild(last);
- unsigned cur = last-1;
- while (cur != 0)
- {
- if (expr->queryChild(cur)->queryBody() != defaultExpr->queryBody())
- break;
- cur--;
- }
- if (cur != last-1)
- {
- //All match default => just return the default
- if (cur == 0)
- return LINK(defaultExpr);
- HqlExprArray args;
- for (unsigned i=0; i <= cur; i++)
- args.append(*LINK(expr->queryChild(i)));
- args.append(*LINK(defaultExpr));
- return expr->clone(args);
- }
- break;
- }
- case no_charlen:
- {
- IHqlExpression * child = expr->queryChild(0);
- ITypeInfo * type = child->queryType();
- size32_t len = type->getStringLen();
- if (len != UNKNOWN_LENGTH)
- return getSizetConstant(len);
- if (child->getOperator() == no_substring)
- {
- IHqlExpression * range = child->queryChild(1);
- switch (range->getOperator())
- {
- case no_range:
- {
- IValue * lowValue = range->queryChild(0)->queryValue();
- IValue * highValue = range->queryChild(1)->queryValue();
- if (lowValue && highValue)
- {
- __int64 low = lowValue->getIntValue();
- __int64 high = highValue->getIntValue()+1;
- if (low < 1)
- low = 1;
- if (high < low)
- high = low;
- return getSizetConstant((unsigned)(high - low));
- }
- break;
- }
- case no_rangeto:
- {
- IValue * highValue = range->queryChild(0)->queryValue();
- if (highValue)
- {
- __int64 high = highValue->getIntValue();
- if (high < 0)
- high = 0;
- return getSizetConstant((unsigned)high);
- }
- break;
- }
- case no_constant:
- return getSizetConstant(1);
- }
- }
- }
- break;
- case no_negate:
- case no_roundup:
- case no_truncate:
- case no_exp:
- case no_ln:
- case no_sin:
- case no_cos:
- case no_tan:
- case no_asin:
- case no_acos:
- case no_atan:
- case no_sinh:
- case no_cosh:
- case no_tanh:
- case no_log10:
- case no_sqrt:
- case no_abs:
- {
- //MORE: I'm sure this could be cleaned up.... e.g., have a function passed a pointer to function
- IHqlExpression * child = expr->queryChild(0);
- IValue * constValue = child->queryValue();
- if (constValue)
- {
- switch (op)
- {
- case no_negate:
- if (isNumericType(child->queryType()))
- return createConstant(negateValue(constValue));
- break;
- case no_roundup:
- return createConstant(roundUpValue(constValue));
- case no_truncate:
- return createConstant(truncateValue(constValue));
- case no_exp:
- return createConstant(expValue(constValue));
- case no_ln:
- return createConstant(lnValue(constValue));
- case no_sin:
- return createConstant(sinValue(constValue));
- case no_cos:
- return createConstant(cosValue(constValue));
- case no_tan:
- return createConstant(tanValue(constValue));
- case no_asin:
- return createConstant(asinValue(constValue));
- case no_acos:
- return createConstant(acosValue(constValue));
- case no_atan:
- return createConstant(atanValue(constValue));
- case no_sinh:
- return createConstant(sinhValue(constValue));
- case no_cosh:
- return createConstant(coshValue(constValue));
- case no_tanh:
- return createConstant(tanhValue(constValue));
- case no_log10:
- return createConstant(log10Value(constValue));
- case no_sqrt:
- return createConstant(sqrtValue(constValue));
- case no_abs:
- return createConstant(absValue(constValue));
- }
- }
- break;
- }
- case no_round:
- {
- //MORE: I'm sure this could be cleaned up.... e.g., have a function passed a pointer to function
- IHqlExpression * arg = expr->queryChild(0);
- IHqlExpression * places = expr->queryChild(1);
- IValue * constValue = arg->queryValue();
- if (constValue)
- {
- if (places)
- {
- if (places->queryValue())
- return createConstant(roundToValue(constValue, (int)getIntValue(places)));
- }
- else
- return createConstant(roundValue(constValue));
- }
- break;
- }
- case no_eq:
- case no_ne:
- case no_lt:
- case no_le:
- case no_gt:
- case no_ge:
- case no_order:
- {
- IHqlExpression * ret = optimizeCompare(expr);
- if (ret)
- return ret;
- //Note, don't optimize IF(a,b,c) op x to IF(a,b op x, c OP x) because it uncommons attributes increasing the size of the queries.
- break;
- }
- case no_unicodeorder:
- {
- IHqlExpression * left = expr->queryChild(0);
- IHqlExpression * right = expr->queryChild(1);
- IHqlExpression * locale = expr->queryChild(2);
- IHqlExpression * strength = expr->queryChild(3);
- IValue * leftv = left->queryValue();
- IValue * rightv = right->queryValue();
- IValue * locv = locale->queryValue();
- IValue * strv = strength->queryValue();
- if(leftv && rightv && locv && strv)
- {
- unsigned leftsz = leftv->queryType()->getStringLen()+1;
- unsigned rightsz = rightv->queryType()->getStringLen()+1;
- UChar * leftstr = new UChar[leftsz];
- UChar * rightstr = new UChar[rightsz];
- leftv->getUCharStringValue(leftsz, leftstr);
- rightv->getUCharStringValue(rightsz, rightstr);
- StringBuffer locstr;
- locv->getStringValue(locstr);
- int val = rtlCompareVUnicodeVUnicodeStrength(leftstr, rightstr, locstr.str(), (unsigned)strv->getIntValue());
- delete [] leftstr;
- delete [] rightstr;
- return createConstant(val);
- }
- break;
- }
- case no_notnot:
- {
- return ensureExprType(expr->queryChild(0), expr->queryType());
- }
- case no_cast:
- case no_implicitcast:
- {
- IHqlExpression * child = expr->queryChild(0);
- ITypeInfo * exprType = expr->queryType();
- if (exprType == child->queryType())
- return LINK(child);
- node_operator childOp = child->getOperator();
- switch (childOp)
- {
- case no_constant:
- return createConstant(child->queryValue()->castTo(exprType));
- case no_cast:
- case no_implicitcast:
- {
- //MORE: Not sure if this is a good idea because it loses commonality between attributes.
- // (T1)((T2)(X:T3))
- // Can remove the cast to T2 if T3->T2 doesn't lose any information,
- // and if the convertion from T2->T1 produces same results as converting T3->T1
- // (For the moment only assume this is true if target is numeric)
- // could possibly remove if T3-T2 and T2->T1 lose information, but they might
- // lose different information
- IHqlExpression * grand = child->queryChild(0);
- ITypeInfo * g_type = grand->queryType();
- ITypeInfo * c_type = child->queryType();
- ITypeInfo * e_type = exprType;
- bool preserveValueG2C = preservesValue(c_type, g_type);
- if (preserveValueG2C)
- {
- bool sameResults = false;
- if (isNumericType(e_type))
- sameResults = true;
- else if (isStringOrUnicode(e_type) && isStringOrUnicode(c_type) && isStringOrUnicode(g_type))
- sameResults = true;
-
- // Don't allow casts involving data and non-ascii datasets because it can cause ascii convertions to get lost
- if (castHidesConversion(e_type, c_type, g_type) ||
- castHidesConversion(c_type, e_type, g_type) ||
- castHidesConversion(g_type, c_type, e_type))
- sameResults = false;
- if (sameResults)
- {
- if (e_type == g_type)
- return LINK(grand);
- return createValue(op, LINK(e_type), LINK(grand));
- }
- }
- break;
- }
- case no_case:
- {
- //Don't allow variable size string returns to be lost...
- if (((child->queryType()->getTypeCode() == type_varstring) ||
- (child->queryType()->getTypeCode() == type_varunicode)) &&
- (exprType->getSize() == UNKNOWN_LENGTH))
- break;
- HqlExprArray caseResults;
- if (flattenConstantCase(child, caseResults, true))
- {
- IValue * defVal = child->queryChild(child->numChildren()-1)->queryValue();
- if (defVal)
- {
- HqlExprArray newCaseMaps;
- ITypeInfo * newType = exprType;
- for (unsigned i=0; i<caseResults.ordinality(); i++)
- {
- IHqlExpression * result = (IHqlExpression *)&caseResults.item(i);
- IValue * castRes = result->queryValue()->castTo(newType);
- IHqlExpression * newMapping = createValue(no_mapto, LINK(child->queryChild(i+1)->queryChild(0)), createConstant(castRes));
- newCaseMaps.append(*newMapping);
- }
- newCaseMaps.append(*createConstant(defVal->castTo(newType)));
- newCaseMaps.add(*LINK(child->queryChild(0)), 0);
- IHqlExpression * newCase = createValue(no_case, LINK(newType), newCaseMaps);
- return newCase;
- }
- }
- break;
- }
- #if 0
- case no_if:
- {
- if (isStringType(exprType) && (exprType->getSize() != UNKNOWN_LENGTH) && (child->queryType()->getSize() == UNKNOWN_LENGTH))
- {
- HqlExprArray args;
- unwindChildren(args, child);
- args.replace(*ensureExprType(&args.item(1), exprType, op), 1);
- if (queryRealChild(child, 2))
- args.replace(*ensureExprType(&args.item(2), exprType, op), 2);
- return child->clone(args);
- }
- break;
- }
- #endif
- case no_all:
- case no_list:
- return ensureExprType(child, exprType);
- case no_substring:
- {
- //(stringN)(X[1..m]) -> (stringN)X if m >= N
- unsigned castLen = exprType->getStringLen();
- type_t tc = exprType->getTypeCode();
- if ((castLen != UNKNOWN_LENGTH) && ((tc == type_string) || (tc == type_data) || (tc == type_qstring) || (tc == type_unicode) || (tc == type_utf8)))
- {
- IHqlExpression * range = child->queryChild(1);
- bool simplify = false;
- if (range->getOperator() == no_range)
- simplify = (getIntValue(range->queryChild(0), 0) == 1) && (getIntValue(range->queryChild(1), 0) >= castLen);
- else if (range->getOperator() == no_rangeto)
- simplify = (getIntValue(range->queryChild(0), 0) >= castLen);
- else if (range->getOperator() == no_constant)
- simplify = (castLen == 1) && (getIntValue(range, 0) == castLen);
- if (simplify)
- {
- HqlExprArray children;
- children.append(*LINK(child->queryChild(0)));
- return expr->clone(children);
- }
- }
- break;
- }
- }
- break;
- }
- case no_typetransfer:
- {
- IHqlExpression * child = expr->queryChild(0);
- IValue * childValue = child->queryValue();
- if (childValue)
- {
- Linked<ITypeInfo> exprType = expr->queryType();
- ITypeInfo * childType = child->queryType();
- size32_t childSize = childValue->getSize();
- const void * rawvalue = childValue->queryValue();
- unsigned newSize = exprType->getSize();
- if (newSize == UNKNOWN_LENGTH)
- {
- unsigned newLen = UNKNOWN_LENGTH;
- switch (exprType->getTypeCode())
- {
- case type_string:
- case type_varstring:
- newLen = childSize;
- break;
- case type_unicode:
- newLen = childSize / sizeof(UChar);
- break;
- case type_utf8:
- newLen = rtlUtf8Length(childSize, rawvalue);
- break;
- }
- if (newLen != UNKNOWN_LENGTH)
- {
- newSize = childSize;
- exprType.setown(getStretchedType(newLen, exprType));
- }
- }
- if (newSize <= childSize)
- {
- IValue * transferred = createValueFromMem(LINK(exprType), rawvalue);
- if (transferred && transferred->isValid())
- return createConstant(transferred);
- }
- }
- break;
- }
- case no_case:
- {
- IHqlExpression * leftExpr = expr->queryChild(0);
- unsigned max = expr->numChildren();
- unsigned numCases = max-2;
- IValue * leftValue = leftExpr->queryValue();
- if (leftValue)
- {
- HqlExprArray args;
- args.append(*LINK(leftExpr));
- for (unsigned idx = 1; idx <= numCases; idx++)
- {
- IHqlExpression * child = expr->queryChild(idx);
- IHqlExpression * grand = child->queryChild(0);
- IValue * grandValue = grand->queryValue();
- if (grandValue)
- {
- if (orderValues(leftValue, grandValue) == 0)
- {
- if (args.ordinality() == 1)
- return LINK(child->queryChild(1));
- args.append(*LINK(child->queryChild(1)));
- return expr->clone(args);
- }
- }
- else
- args.append(*LINK(child));
- }
- IHqlExpression * defaultValue = expr->queryChild(numCases+1);
- if (args.ordinality()==1)
- return LINK(defaultValue);
- if (args.ordinality() != numCases+1)
- {
- args.append(*LINK(defaultValue));
- return expr->clone(args);
- }
- }
- else if (leftExpr->getOperator() == no_case)
- {
- HqlExprArray caseResults1;
- HqlExprArray caseInput2;
- HqlExprArray newCaseMaps;
- if (flattenConstantCase(leftExpr, caseResults1, true) &&
- flattenConstantCase(expr, caseInput2, false))
- {
- IHqlExpression * defCase2 = leftExpr->queryChild(leftExpr->numChildren()-1);
- IValue * defVal2 = defCase2->queryValue();
- if (defVal2)
- {
- unsigned inRes = 0;
- unsigned numInput = caseInput2.ordinality();
- unsigned i;
- for (i=0; i<numInput; i++)
- {
- IHqlExpression * val = (IHqlExpression*)&caseInput2.item(i);
- if (val->queryValue()->compare(defVal2) == 0)
- {
- inRes = i+1;
- break;
- }
- }
- IHqlExpression * defCase1 = expr->queryChild(expr->numChildren()-1);
- for (i=0; i<caseResults1.ordinality(); i++)
- {
- bool found = false;
- IHqlExpression * val1 = (IHqlExpression*)&caseResults1.item(i);
- for (unsigned k=0; k<numInput; k++)
- {
- IHqlExpression * val2 = (IHqlExpression*)&caseInput2.item(k);
- if (val1->queryValue()->compare(val2->queryValue()) == 0)
- {
- IHqlExpression * newMapping = createValue(no_mapto, LINK(leftExpr->queryChild(i+1)->queryChild(0)), LINK(expr->queryChild(k+1)->queryChild(1)));
- newCaseMaps.append(*newMapping);
- found = true;
- break;
- }
- }
- if (inRes && !found)
- {
- IHqlExpression * newMapping = createValue(no_mapto, LINK(leftExpr->queryChild(i+1)->queryChild(0)), LINK(defCase1));
- newCaseMaps.append(*newMapping);
- }
- }
- if (inRes)
- newCaseMaps.append(*LINK(expr->queryChild(inRes)->queryChild(1)));
- else
- newCaseMaps.append(*LINK(defCase1));
- newCaseMaps.add(*LINK(leftExpr->queryChild(0)), 0);
- return createValue(no_case, expr->getType(), newCaseMaps);
- }
- }
- }
- IHqlExpression * defaultValue = expr->queryChild(max-1);
- bool allMatchDefault = true;
- for (unsigned i=1; i < max-1; i++)
- {
- if (expr->queryChild(i)->queryChild(1) != defaultValue)
- {
- allMatchDefault = false;
- break;
- }
- }
- if (allMatchDefault)
- return LINK(defaultValue);
- if (numCases == 1)
- {
- IHqlExpression * mapto = expr->queryChild(1);
- IHqlExpression * key = mapto->queryChild(0);
- OwnedITypeInfo type = getPromotedCompareType(leftExpr->queryType(), key->queryType());
- IHqlExpression * newEqual = createBoolExpr(no_eq, ensureExprType(leftExpr, type), ensureExprType(key, type));
- return createIf(newEqual, LINK(mapto->queryChild(1)), LINK(expr->queryChild(2)));
- }
- break;
- }
- case no_map:
- {
- assertex(expr->numChildren()>=1);
- unsigned num = expr->numChildren()-1;
-
- LinkedHqlExpr defaultResult = expr->queryChild(num);
- HqlExprArray args;
- bool allAreIn = true;
- bool changed = false;
- IHqlExpression * allTestField = NULL;
- for (unsigned idx = 0; idx < num; idx++)
- {
- IHqlExpression * child = expr->queryChild(idx);
- IHqlExpression * cond = child->queryChild(0);
- IValue * value = cond->queryValue();
- if (value)
- {
- changed = true;
- if (value->getBoolValue())
- {
- //New default condition - don't check any more arguments.
- defaultResult.set(child->queryChild(1));
- break;
- }
- //otherwise ignore that argument...
- }
- else if (isDuplicateMapCondition(args, cond))
- {
- //Can occur when other earlier conditions have been simplified by constant foldeding
- changed = true;
- }
- else
- {
- if (allAreIn && (cond->getOperator() == no_in))
- {
- IHqlExpression * condSearch = cond->queryChild(0);
- IHqlExpression * condSet = cond->queryChild(1);
- if ((allTestField && (allTestField != condSearch)) || !condSet->isConstant() || (condSet->getOperator() != no_list))
- allAreIn = false;
- else
- allTestField = condSearch;
- }
- else if (allAreIn && (cond->getOperator() == no_eq))
- {
- IHqlExpression * condSearch = cond->queryChild(0);
- IHqlExpression * condSet = cond->queryChild(1);
- if ((allTestField && (allTestField != condSearch)) || (condSet->getOperator() != no_constant))
- allAreIn = false;
- else
- allTestField = condSearch;
- }
- else
- allAreIn = false;
- args.append(*LINK(child));
- }
- }
- //If no conditions yet, then the true value is the result, otherwise it is the default...
- if (args.ordinality() == 0 || areIndenticalMapResults(args, defaultResult))
- return defaultResult.getLink();
- if (allAreIn)
- {
- //Transform this map to a case - it will be much more efficient.
- HqlExprArray args2;
- ICopyArray alreadyDone;
- args2.append(*LINK(allTestField));
- ForEachItemIn(i, args)
- {
- IHqlExpression & cur = args.item(i);
- IHqlExpression * cond = cur.queryChild(0);
- IHqlExpression * condValue = cond->queryChild(1);
- IHqlExpression * mapValue = cur.queryChild(1);
- if (cond->getOperator() == no_in)
- {
- ForEachChild(j, condValue)
- {
- IHqlExpression * value = condValue->queryChild(j);
- if (alreadyDone.find(*value) == NotFound)
- {
- alreadyDone.append(*value);
- args2.append(*createValue(no_mapto, LINK(value), LINK(mapValue)));
- }
- }
- }
- else
- {
- if (alreadyDone.find(*condValue) == NotFound)
- {
- alreadyDone.append(*condValue);
- args2.append(*createValue(no_mapto, mapValue->getType(), LINK(condValue), LINK(mapValue)));
- }
- }
- }
- args2.append(*defaultResult.getLink());
- return createWrapper(no_case, expr->queryType(), args2);
- }
- if (changed)
- {
- args.append(*defaultResult.getLink());
- return expr->clone(args);
- }
- #if 0
- //This is a sensible change - but it causes a bit too much code to be included in expressions at the moment
- if (num == 1)
- {
- IHqlExpression * child = expr->queryChild(0);
- return createIf(LINK(child->queryChild(0)), LINK(child->queryChild(1)), LINK(expr->queryChild(1)));
- }
- #endif
- return LINK(expr);
- }
- case no_between:
- case no_notbetween:
- {
- IHqlExpression * child = expr->queryChild(0);
- IHqlExpression * lowExpr = expr->queryChild(1);
- IHqlExpression * highExpr = expr->queryChild(2);
- IValue * constValue = child->queryValue();
- IValue * low = lowExpr->queryValue();
- IValue * high = highExpr->queryValue();
- if (constValue && low && high)
- {
- bool ret = false;
- if (orderValues(constValue, low) >= 0)
- {
- if (orderValues(constValue, high) <= 0)
- ret = true;
- }
- return createConstant(op == no_between ? ret : !ret);
- }
- if (lowExpr == highExpr)
- return createValue(op == no_between ? no_eq : no_ne, makeBoolType(), LINK(child), LINK(lowExpr));
- break;
- }
- case no_substring:
- {
- IHqlExpression * child = expr->queryChild(0);
- IValue * constValue = child->queryValue();
- if (constValue)
- {
- IHqlExpression * limit = expr->queryChild(1);
- IValue * subString = NULL;
- if (limit->isConstant())
- {
- switch (limit->getOperator())
- {
- case no_range:
- {
- IValue * lower = limit->queryChild(0)->queryValue();
- IValue * upper = limit->queryChild(1)->queryValue();
- if (lower && upper)
- subString = substringValue(constValue, lower, upper);
- break;
- }
- case no_rangeto:
- {
- IValue * upper = limit->queryChild(0)->queryValue();
- if (upper)
- subString = substringValue(constValue, NULL, upper);
- break;
- }
- case no_rangefrom:
- {
- IValue * lower = limit->queryChild(0)->queryValue();
- if (lower)
- subString = substringValue(constValue, lower, NULL);
- break;
- }
- case no_constant:
- {
- IValue * v = limit->queryValue();
- subString = substringValue(constValue, v, v);
- break;
- }
- }
- }
- if (subString)
- return createConstant(subString);
- }
- //((stringN)X)[1..m] -> (stringN)X if m == N
- if (isCast(child))
- {
- ITypeInfo * type = child->queryType();
- unsigned castLen = type->getStringLen();
- type_t tc = type->getTypeCode();
- if ((castLen != UNKNOWN_LENGTH) && ((tc == type_string) || (tc == type_data) || (tc == type_qstring) || (tc == type_unicode) || (tc == type_utf8)))
- {
- IHqlExpression * range = expr->queryChild(1);
- bool simplify = false;
- if (range->getOperator() == no_range)
- simplify = (getIntValue(range->queryChild(0), 0) == 1) && (getIntValue(range->queryChild(1), 0) == castLen);
- else if (range->getOperator() == no_rangeto)
- simplify = (getIntValue(range->queryChild(0), 0) == castLen);
- else if (range->getOperator() == no_constant)
- simplify = (castLen == 1) && (getIntValue(range, 0) == castLen);
- if (simplify)
- return LINK(child);
- }
- }
- //x[n..0], x[m..n] n<m == ''
- IHqlExpression * range = expr->queryChild(1);
- if (range->getOperator() == no_range)
- {
- IHqlExpression * rangeLow = range->queryChild(0);
- IHqlExpression * rangeHigh = range->queryChild(1);
- if (isZero(rangeHigh))
- return createNullExpr(expr);
- if (getIntValue(rangeLow, 1) > getIntValue(rangeHigh, I64C(0x7fffffffffffffff)))
- return createNullExpr(expr);
- }
- break;
- }
- case no_externalcall:
- { //external function folding.
- IValue * result = foldExternalCall(expr, foldOptions, templateContext);
- if (result)
- return createConstant(result);
- break;
- }
- case no_call:
- {
- ForEachChild(i, expr)
- {
- if (!expr->queryChild(i)->isConstant())
- return LINK(expr);
- }
- IHqlExpression * def = expr->queryBody()->queryFunctionDefinition();
- IHqlExpression * body = def->queryChild(0);
- if (body->getOperator() == no_outofline && body->queryChild(0)->getOperator()==no_embedbody)
- {
- IHqlExpression * result = foldEmbeddedCall(expr, foldOptions, templateContext);
- if (result)
- return result;
- break;
- }
- OwnedHqlExpr folded = expandOutOfLineFunctionCall(expr);
- if ((folded != expr) && folded->isConstant())
- return folded.getClear();
- break;
- }
- case no_trim:
- {
- IHqlExpression * child = expr->queryChild(0);
- // 'R' - trim right, 'L' - Left, 'B' - Left and Right, 'A' - All
- char typecode = 'R';
- if(expr->hasAttribute(allAtom))
- typecode = 'A';
- else if(expr->hasAttribute(leftAtom) && expr->hasAttribute(rightAtom))
- typecode = 'B';
- else if(expr->hasAttribute(leftAtom))
- typecode = 'L';
- IValue * constValue = child->queryValue();
- IValue* resultstr = NULL;
- if (constValue)
- resultstr = trimStringValue(constValue, typecode);
- if (resultstr)
- return createConstant(resultstr);
- //extendin a string won't change the alue of trim(x), unless not trimming the rhs
- //i.e., trim((string60)string12expression) => trim(string12expression);
- if ((typecode != 'L') && isCast(child))
- {
- IHqlExpression * uncast = child->queryChild(0);
- ITypeInfo * castType = child->queryType();
- ITypeInfo * uncastType = uncast->queryType();
- if ((castType->getSize() >= uncastType->getSize()) && (castType->getTypeCode() == uncastType->getTypeCode()))
- {
- OwnedITypeInfo stretched = getStretchedType(castType->getStringLen(), uncastType);
- if (stretched == castType)
- {
- HqlExprArray args;
- args.append(*LINK(uncast));
- unwindChildren(args, expr, 1);
- return expr->clone(args);
- }
- }
- }
- break;
- }
- case no_which:
- case no_rejected:
- {
- bool isWhich = (op == no_which);
- unsigned num = expr->numChildren();
- ITypeInfo * exprType = expr->queryType();
- switch (num)
- {
- case 1:
- {
- int trueValue = isWhich ? 1 : 0;
- return createValue(no_if, LINK(exprType), LINK(expr->queryChild(0)), createConstant(trueValue, LINK(exprType)), createConstant(1 - trueValue, LINK(exprType)));
- }
- }
- bool allConst = true;
- IHqlExpression * newWhich = createOpenValue(op, expr->getType());
- for (unsigned idx = 0; idx < num; idx++)
- {
- IHqlExpression * child = expr->queryChild(idx);
- IValue * constValue = child->queryValue();
- if (constValue)
- {
- bool bVal = constValue->getBoolValue();
- if (isWhich ? bVal : !bVal)
- {
- if (allConst)
- {
- newWhich->closeExpr()->Release();
- return createConstant((__int64)idx+1, LINK(exprType));
- }
- else
- {
- //Add a value which will always match
- newWhich->addOperand(createConstant(isWhich));
- return newWhich->closeExpr();
- }
- }
- else
- newWhich->addOperand(LINK(child));
- }
- else
- {
- allConst = false;
- newWhich->addOperand(LINK(child));
- }
- }
- newWhich->closeExpr()->Release();
- if (allConst)
- return createConstant(0, LINK(exprType));
- break;
- }
- case no_index:
- case no_rowsetindex:
- {
- IHqlExpression * leftChild = expr->queryChild(0);
- IHqlExpression * rightChild = expr->queryChild(1);
- node_operator leftOp = leftChild->getOperator();
- if (leftOp == no_null)
- return createNullValue(expr);
- if ((leftOp != no_list) && (leftOp != no_datasetlist))
- break;
- IValue * rightValue = rightChild->queryValue();
- if(rightValue)
- {
- unsigned idx = (unsigned)rightValue->getIntValue();
- if ((idx != 0) && (leftChild->numChildren()>=idx))
- return LINK(leftChild->queryChild(idx-1));
- else
- return createNullValue(expr);
- }
- else if (!leftChild->numChildren())
- return createNullValue(expr);
- }
- break;
- case no_addsets:
- {
- IHqlExpression * left = expr->queryChild(0);
- IHqlExpression * right = expr->queryChild(1);
- if (left->getOperator() == no_all)
- return LINK(left);
- if (right->getOperator() == no_all)
- return LINK(right);
- if ((left->getOperator() == no_list) && (right->getOperator() == no_list))
- {
- HqlExprArray args;
- unwindChildren(args, left);
- unwindChildren(args, right);
- return left->clone(args);
- }
- break;
- }
- case no_max:
- case no_min:
- case no_ave:
- case no_evaluate:
- {
- IHqlExpression * dataset = expr->queryChild(0);
- if (dataset->getOperator() == no_null)
- return createNullValue(expr);
- //MORE: Not so sure about this - what if the dataset turns out to have 0 elements???
- IHqlExpression * child = expr->queryChild(1);
- IValue * value = child->queryValue();
- if (value)
- return createConstant(value->castTo(expr->queryType()));
- if (dataset->getOperator() == no_datasetfromrow)
- return replaceSelector(child, dataset, dataset->queryChild(0));
- }
- break;
- case no_countdict:
- {
- IHqlExpression * child = expr->queryChild(0);
- node_operator childOp = child->getOperator();
- // Can't optimize count of a dictionary in general, since the input dataset may contain duplicates which will be removed.
- switch (child->getOperator())
- {
- case no_null:
- return createConstant(0);
- }
- break;
- }
- case no_existsdict:
- {
- IHqlExpression * child = expr->queryChild(0);
- node_operator childOp = child->getOperator();
- switch (child->getOperator())
- {
- case no_null:
- return createConstant(false);
- }
- break;
- }
- case no_countlist:
- {
- IHqlExpression * child = expr->queryChild(0);
- switch (child->getOperator())
- {
- case no_null:
- case no_list:
- return createConstant(createIntValue(child->numChildren(), LINK(expr->queryType())));
- }
- break;
- }
- case no_existslist:
- {
- IHqlExpression * child = expr->queryChild(0);
- switch (child->getOperator())
- {
- case no_null:
- return createConstant(false);
- case no_list:
- return createConstant(child->numChildren() != 0);
- }
- break;
- }
- case no_minlist:
- case no_maxlist:
- {
- IHqlExpression * child = expr->queryChild(0);
- bool isUnsigned = expr->queryType()->isUnsignedNumeric();
- switch (child->getOperator())
- {
- case no_null:
- return createNullExpr(expr);
- case no_list:
- {
- IValue * best = NULL;
- bool allConstant = true;
- HqlExprArray values;
- bool same = true;
- ForEachChild(i, child)
- {
- IHqlExpression * cur = child->queryChild(i);
- IValue * value = cur->queryValue();
- if (value)
- {
- if (isUnsigned && isZero(cur))
- {
- if (op==no_minlist)
- return LINK(cur); // Nothing can be lower than zero in an unsigned minlist...
- else
- {
- same = false;
- continue;
- }
- }
- if (best)
- {
- int c = value->compare(best);
- if (op == no_minlist ? c < 0 : c > 0)
- best = value;
- }
- else
- best = value;
- values.append(*LINK(cur));
- }
- else
- {
- if (!values.containsBody(*cur))
- values.append(*LINK(cur));
- else
- same = false;
- allConstant = false;
- }
- }
- if (allConstant)
- {
- if (!best)
- return createNullExpr(expr);
- return createConstant(LINK(best));
- }
-
- if (values.ordinality() == 1)
- return expr->cloneAllAnnotations(&values.item(0));
- if (!same)
- {
- OwnedHqlExpr newList = child->clone(values);
- return replaceChild(expr, 0, newList);
- }
- }
- break;
- }
- break;
- }
- case no_sumlist:
- {
- IHqlExpression * child = expr->queryChild(0);
- OwnedHqlExpr folded;
- switch (child->getOperator())
- {
- case no_null:
- return createNullExpr(expr);
- case no_list:
- if (child->isConstant())
- {
- ITypeInfo * exprType = expr->queryType();
- Owned<IValue> sum = createNullValue(exprType);
- bool ok = true;
- ForEachChild(i, child)
- {
- IHqlExpression * cur = child->queryChild(i);
- IValue * value = cur->queryValue();
- if (value)
- {
- Owned<IValue> castValue = value->castTo(exprType);
- sum.setown(addValues(sum, castValue));
- }
- else
- {
- ok = false;
- break;
- }
- }
- if (ok)
- folded.setown(createConstant(sum.getClear()));
- }
- if (child->numChildren() == 1)
- folded.set(child->queryChild(0));
- if (folded)
- {
- OwnedHqlExpr cast = ensureExprType(folded, expr->queryType());
- return expr->cloneAllAnnotations(cast);
- }
- break;
- }
- break;
- }
- break;
- case no_notwithin:
- {
- IHqlExpression * child = expr->queryChild(0);
- if (child->getOperator() == no_null)
- return createConstant(true);
- }
- break;
- case no_createset:
- {
- //If constant folding has caused the argument to be a constant then can convert this to a simple list
- IHqlExpression * ds = expr->queryChild(0);
- IHqlExpression * value = expr->queryChild(1);
- if (value->isConstant() && hasSingleRow(ds))
- return createValue(no_list, expr->getType(), LINK(value));
- break;
- }
- case no_list:
- break;
- case no_tounicode:
- {
- IHqlExpression * dataChild = expr->queryChild(0);
- IHqlExpression * codepageChild = expr->queryChild(1);
- IValue * dataValue = dataChild->queryValue();
- IValue * codepageValue = codepageChild->queryValue();
- if(dataValue && codepageValue)
- {
- unsigned unicodeLength;
- UChar * unicode;
- StringBuffer buff;
- rtlCodepageToUnicodeX(unicodeLength, unicode, dataValue->getSize(), (char const *)dataValue->queryValue(), codepageValue->getStringValue(buff));
- ITypeInfo * unicodeType = makeUnicodeType(unicodeLength, 0);
- IValue * unicodeValue = createUnicodeValue(unicodeLength, unicode, unicodeType);
- rtlFree(unicode);
- return createConstant(unicodeValue);
- }
- break;
- }
- case no_fromunicode:
- {
- IHqlExpression * unicodeChild = expr->queryChild(0);
- IHqlExpression * codepageChild = expr->queryChild(1);
- IValue * unicodeValue = unicodeChild->queryValue();
- IValue * codepageValue = codepageChild->queryValue();
- if(unicodeValue && codepageValue)
- {
- unsigned dataLength;
- char * data;
- StringBuffer buff;
- rtlUnicodeToCodepageX(dataLength, data, unicodeValue->queryType()->getStringLen(), (UChar const *)unicodeValue->queryValue(), codepageValue->getStringValue(buff));
- IValue * dataValue = createDataValue(data, dataLength);
- rtlFree(data);
- return createConstant(dataValue);
- }
- break;
- }
- case no_keyunicode:
- {
- IHqlExpression * val = expr->queryChild(0);
- IHqlExpression * locale = expr->queryChild(1);
- IHqlExpression * strength = expr->queryChild(2);
- IValue * valv = val->queryValue();
- IValue * locv = locale->queryValue();
- IValue * strv = strength->queryValue();
- if(valv && locv && strv)
- {
- unsigned outlen;
- void * out;
- unsigned vallen = valv->queryType()->getStringLen();
- UChar * valstr = new UChar[vallen];
- valv->getUCharStringValue(vallen, valstr);
- StringBuffer locstr;
- locv->getStringValue(locstr);
- rtlKeyUnicodeStrengthX(outlen, out, vallen, valstr, locstr.str(), (unsigned)strv->getIntValue());
- delete [] valstr;
- IValue * dataValue = createDataValue((char *)out, outlen);
- rtlFree(out);
- return createConstant(dataValue);
- }
- break;
- }
- case no_random:
- if (foldOptions & (HFOfoldimpure|HFOforcefold))
- return createConstant(expr->queryType()->castFrom(true, (__int64)rtlRandom()));
- break;
- case no_catch:
- if (expr->isConstant())
- {
- try
- {
- return LINK(expr->queryChild(0));
- }
- catch (IException * e)
- {
- e->Release();
- return LINK(expr->queryChild(1));
- }
- }
- //maybe we should stop folding of the children.
- break;
- case no_section:
- if (expr->queryChild(0)->isConstant())
- return LINK(expr->queryChild(0));
- break;
- case no_sizeof:
- {
- IHqlExpression * child = expr->queryChild(0);
- if (child->isRecord())
- {
- //Need to be careful to use the serialized record - otherwise record size can be inconsistent
- OwnedHqlExpr record = getSerializedForm(child, diskAtom);
- if (expr->hasAttribute(maxAtom))
- {
- if (maxRecordSizeCanBeDerived(record))
- return getSizetConstant(getMaxRecordSize(record, 0));
- }
- else if (expr->hasAttribute(minAtom))
- {
- return getSizetConstant(getMinRecordSize(record));
- }
- else
- {
- if (!isVariableSizeRecord(record))
- return getSizetConstant(getMaxRecordSize(record, 0));
- }
- }
- //MORE: Handle types - but be very careful about maxlength attributes... (no_typeof doesn't exist yet either)
- //else if (child->getOperator() == no_typeof)
- break;
- }
- case no_actionlist:
- case no_orderedactionlist:
- {
- bool same = true;
- HqlExprArray args;
- ForEachChild(i, expr)
- {
- IHqlExpression * cur = expr->queryChild(i);
- if (isNull(cur))
- {
- if (same)
- unwindChildren(args, expr, 0, i);
- same = false;
- }
- else
- {
- if (!same)
- args.append(*LINK(cur));
- }
- }
- if (!same)
- return createActionList(op, args);
- break;
- }
- case no_exists:
- if (isNull(expr->queryChild(0)))
- return createConstant(false);
- break;
- case no_alias:
- {
- IHqlExpression * arg = expr->queryChild(0);
- if (arg->getOperator() == no_constant)
- return LINK(arg);
- break;
- }
- }
- return LINK(expr);
- }
- //---------------------------------------------------------------------------
- bool isNullRowDs(IHqlExpression * expr)
- {
- return ((expr->getOperator() == no_datasetfromrow) && isNull(expr->queryChild(0)));
- }
- IHqlExpression * preserveGrouping(IHqlExpression * child, IHqlExpression * expr)
- {
- if (!isGrouped(expr))
- {
- if (isGrouped(child))
- return createDataset(no_group, LINK(child));
- }
- else
- {
- //weird, but just about possible if grouped keyed join was replaced with rhs, check just in case
- assertex(isGrouped(child));
- }
- return LINK(child);
- }
- static bool matchesAtmost1(IHqlExpression * expr)
- {
- IHqlExpression * atmost = expr->queryAttribute(atmostAtom);
- if (!atmost)
- return false;
- if (!matchesConstantValue(atmost->queryChild(0), 1))
- return false;
- return true;
- }
- static bool hasRowLimit(IHqlExpression * expr)
- {
- IHqlExpression * limit = expr->queryAttribute(rowLimitAtom);
- return limit && !matchesConstantValue(limit->queryChild(0), 0);
- }
- IHqlExpression * NullFolderMixin::foldNullDataset(IHqlExpression * expr)
- {
- IHqlExpression * child = expr->queryChild(0);
- //These items remove the current node - so don't need to check if the children are shared.
- node_operator op = expr->getOperator();
- switch (op)
- {
- case no_distribute:
- case no_distributed:
- {
- if (isNull(child))
- return replaceWithNull(expr);
- if (isFail(child))
- return removeParentNode(expr);
- if (expr->hasAttribute(skewAtom))
- break;
- //Careful - distribute also destroys grouping, so don't remove if input is grouped.
- if ((queryDistribution(expr) == queryDistribution(child)) && !isGrouped(child))
- return removeParentNode(expr);
- break;
- }
- case no_sort:
- case no_subsort:
- case no_sorted:
- {
- //Subsort is unusual because the order applied to an unsorted dataset will also be unsorted
- if ((op != no_subsort) || hasKnownSortGroupDistribution(child, expr->hasAttribute(localAtom)))
- {
- //If action does not change the type information, then it can't have done anything...
- if (hasSameSortGroupDistribution(expr, child))
- return removeParentNode(expr);
- }
- if (isNull(child) || hasNoMoreRowsThan(child, 1))
- return removeParentNode(expr);
- //If all arguments to sort are constant then remove it, otherwise the activities will not like it.
- //NOTE: MERGE has its sort order preserved, so it won't cause issues there.
- bool allConst = true;
- ForEachChildFrom(i, expr, 1)
- {
- IHqlExpression * cur = expr->queryChild(i);
- if (!cur->isAttribute() && !cur->isConstant())
- {
- allConst = false;
- break;
- }
- }
- if (allConst && (op == no_sort))
- return removeParentNode(expr);
- break;
- }
- case no_if:
- {
- //Processed hereThis won't split shared nodes, but one of the children may be shared - so proce
- if (isNull(expr->queryChild(1)))
- {
- //A no_null action is treated the same as a non existant action.
- IHqlExpression * falseBranch = expr->queryChild(2);
- if (!falseBranch || isNull(falseBranch))
- return replaceWithNull(expr);
- }
- break;
- }
- case no_group:
- case no_grouped:
- // case no_preservemeta:
- {
- //If action does not change the type information, then it can't have done anything...
- if (hasSameSortGroupDistribution(expr, child))
- return removeParentNode(expr);
- if (isNull(child))
- return replaceWithNull(expr);
- break;
- }
- case no_denormalize:
- case no_denormalizegroup:
- case no_join:
- {
- IHqlExpression * rhs = expr->queryChild(1);
- bool leftIsNull = isNull(child);
- bool rightIsNull = isNull(rhs);
- bool cvtLeftProject = false;
- bool cvtRightProject = false;
- const char * reason = NULL;
- if (leftIsNull || rightIsNull)
- {
- bool createNull = false;
- if (isFullJoin(expr))
- createNull = leftIsNull && rightIsNull;
- else if (isLeftJoin(expr))
- createNull = leftIsNull;
- else if (isRightJoin(expr))
- createNull = rightIsNull;
- else
- createNull = leftIsNull || rightIsNull;
- if (createNull)
- return replaceWithNull(expr);
- if (leftIsNull)
- cvtRightProject = true;
- else if (rightIsNull)
- {
- //JOIN(ds,<null>) becomes a project
- //DENORMALIZE(ds, <null>) becomes a nop (since the transform will not be called)
- //DENORMALIZE(ds, <null>, GROUP) becomes a project
- if (op == no_denormalize)
- return removeParentNode(expr); // ok because this returns queryChild(0)
- cvtLeftProject = true;
- reason = "(ds,<empty>)";
- }
- }
- //JOIN with false condition - can occur once constants are folded.
- IValue * condValue = expr->queryChild(2)->queryValue();
- if (condValue && !condValue->getBoolValue())
- {
- //Never matches, so either LHS is modified by the transform - like a project, or it never returns anything.
- if (isLeftJoin(expr))
- {
- if (op == no_denormalize)
- return removeParentNode(expr); // ok because this returns queryChild(0)
- cvtLeftProject = true;
- reason = "(false)";
- }
- else if (isInnerJoin(expr))
- return replaceWithNull(expr);
- }
- //JOIN, left outer, keep(1) with no reference to RIGHT in the transform => convert to a project!
- //again can occur once the implicit project has started getting to work.
- if (!cvtLeftProject)
- {
- const char * potentialLeftProjectReason = NULL;
- if (isSpecificJoin(expr, leftouterAtom))
- {
- if (matchesConstantValue(queryAttributeChild(expr, keepAtom, 0), 1) && !hasRowLimit(expr))
- potentialLeftProjectReason = "(,LEFT OUTER,KEEP(1))";
- else if (matchesAtmost1(expr) && !hasRowLimit(expr))
- potentialLeftProjectReason = "(,LEFT OUTER,ATMOST(1))";
- else if (expr->hasAttribute(lookupAtom) && !expr->hasAttribute(manyAtom))
- potentialLeftProjectReason = "(,LEFT OUTER,SINGLE LOOKUP)";
- else if (hasNoMoreRowsThan(expr, 1))
- potentialLeftProjectReason = "(<single-row>,LEFT OUTER)";
- }
- if (potentialLeftProjectReason)
- {
- //This cannot match if the transform contains a skip - since that would
- IHqlExpression * selSeq = querySelSeq(expr);
- OwnedHqlExpr right = createSelector(no_right, rhs, selSeq);
- IHqlExpression * transform = expr->queryChild(3);
- if (!exprReferencesDataset(transform, right))
- {
- cvtLeftProject = true;
- reason = potentialLeftProjectReason;
- }
- if (cvtLeftProject && (expr->getOperator() == no_denormalize))
- {
- OwnedHqlExpr left = createSelector(no_left, child, selSeq);
- //Denormalize with no match will not call the transform, so we can't convert that to a project
- //unless the transform is a nop
- if (!transformReturnsSide(expr, no_left, 0))
- cvtLeftProject = false;
- }
- }
- }
- if (cvtLeftProject)
- {
- IHqlExpression * selSeq = querySelSeq(expr);
- OwnedHqlExpr right = createSelector(no_right, rhs, selSeq);
- OwnedHqlExpr null = createRow(no_newrow, createNullExpr(right));
- OwnedHqlExpr newTransform = replaceSelector(expr->queryChild(3), right, null);
- if (op == no_denormalizegroup)
- {
- IHqlExpression * rowsid = expr->queryAttribute(_rowsid_Atom);
- OwnedHqlExpr rowsExpr = createDataset(no_rows, LINK(right), LINK(rowsid));
- OwnedHqlExpr nullExpr = createDataset(no_null, LINK(rhs->queryRecord()));
- newTransform.setown(replaceExpression(newTransform, rowsExpr, nullExpr));
- }
- if (op == no_denormalize)
- {
- IHqlExpression * counter = queryAttributeChild(expr, _countProject_Atom, 0);
- if (counter)
- {
- OwnedHqlExpr one = createConstant(counter->queryType()->castFrom(false, I64C(1)));
- //Remove the annotations from the transform, otherwise it may say t(LEFT,COUNTER) which is confusing.
- newTransform.setown(replaceExpression(newTransform->queryBody(), counter, one));
- }
- }
- HqlExprArray args;
- args.append(*preserveGrouping(child, expr));
- args.append(*newTransform.getClear());
- args.append(*LINK(selSeq));
- OwnedHqlExpr ret = createDataset(no_hqlproject, args);
- DBGLOG("Folder: Replace %s%s with PROJECT", getOpString(op), reason);
- return ret.getClear();
- }
- #if 0
- //This is pretty unlikely, and may introduce an ambiguity in LEFT (if selector sequences aren't unique)
- if (cvtRightProject && !isGrouped(expr))
- {
- IHqlExpression * selSeq = querySelSeq(expr);
- OwnedHqlExpr left = createSelector(no_left, child, selSeq);
- OwnedHqlExpr null = createRow(no_newrow, createNullExpr(left));
- OwnedHqlExpr transformNoLeft = replaceSelector(expr->queryChild(3), left, null);
- OwnedHqlExpr right = createSelector(no_right, rhs, selSeq);
- OwnedHqlExpr newLeft = createSelector(no_left, child, selSeq);
- OwnedHqlExpr newTransform = replaceSelector(transformNoLeft, right, newLeft);
- HqlExprArray args;
- args.append(*preserveGrouping(rhs, expr));
- args.append(*newTransform.getClear());
- args.append(*LINK(selSeq));
- OwnedHqlExpr ret = createDataset(no_hqlproject, args);
- DBGLOG("Folder: Replace JOIN(<empty>, ds) with PROJECT");
- return ret.getClear();
- }
- #endif
- break;
- }
- case no_merge:
- case no_addfiles:
- case no_regroup:
- case no_nonempty:
- case no_cogroup:
- {
- HqlExprArray args;
- bool changed = false;
- IHqlExpression * lastInput = NULL;
- unsigned inputCount = 0;
- //Careful - the node may have attributes, which we want to preserve if the node is preserved.
- ForEachChild(i, expr)
- {
- IHqlExpression * cur = expr->queryChild(i);
- if ((cur->getOperator() != no_null) && ((op != no_nonempty) || !args.contains(*cur)))
- {
- if (!cur->isAttribute())
- {
- lastInput = cur;
- inputCount++;
- }
- args.append(*LINK(cur));
- }
- else
- changed = true;
- }
- if (changed || (inputCount == 1))
- {
- //NOTE: The only branches removed are no_null, so don't need to worry about decrementing their link counts.
- switch (inputCount)
- {
- case 0:
- return replaceWithNull(expr);
- case 1:
- if (op == no_cogroup)
- {
- DBGLOG("Folder: Replace %s with group", getOpString(op));
- IHqlExpression * grouping = queryAttributeChild(expr, groupAtom, 0);
- IHqlExpression * mappedGrouping = replaceSelector(grouping, queryActiveTableSelector(), lastInput);
- OwnedHqlExpr group = createDataset(no_group, LINK(lastInput), mappedGrouping);
- return expr->cloneAllAnnotations(group);
- }
- else
- {
- DBGLOG("Folder: Replace %s with child", getOpString(op));
- return LINK(lastInput);
- }
- default:
- DBGLOG("Folder: Remove %d inputs from %s", expr->numChildren()-args.ordinality(), getOpString(op));
- return expr->clone(args);
- }
- }
- break;
- }
- case no_fetch:
- if (isNull(expr->queryChild(1)))
- return replaceWithNull(expr);
- break;
- case no_aggregate:
- case no_newaggregate:
- if (isNull(child))
- {
- if (isGrouped(child) || queryRealChild(expr, 3))
- return replaceWithNull(expr);
- return replaceWithNullRowDs(expr);
- }
- break;
- case no_inlinetable:
- if (expr->queryChild(0)->numChildren() == 0)
- return replaceWithNull(expr);
- break;
- case no_dataset_from_transform:
- if (isZero(expr->queryChild(0)))
- return replaceWithNull(expr);
- break;
- case no_temptable:
- {
- IHqlExpression * values = expr->queryChild(0);
- if (isNull(values) || ((values->getOperator() == no_list) && (values->numChildren() == 0)))
- return replaceWithNull(expr);
- break;
- }
- case no_newusertable:
- if (isNullProject(expr, false, false))
- return removeParentNode(expr);
- if (isNull(child))
- {
- //a grouped aggregate is ok - will generate no rows, as will a non-aggregate
- if (datasetHasGroupBy(expr) || !isAggregateDataset(expr))
- return replaceWithNull(expr);
- }
- break;
- case no_compound_diskread:
- case no_compound_disknormalize:
- case no_compound_diskgroupaggregate:
- case no_compound_diskcount:
- case no_compound_indexread:
- case no_compound_indexnormalize:
- case no_compound_indexgroupaggregate:
- case no_compound_indexcount:
- case no_compound_childread:
- case no_compound_childnormalize:
- case no_compound_childgroupaggregate:
- case no_compound_childcount:
- case no_compound_inline:
- case no_compound_selectnew:
- {
- node_operator childOp = child->getOperator();
- if ((op == childOp) || (childOp == no_null))
- return removeParentNode(expr);
- break;
- }
- case no_compound_diskaggregate:
- case no_compound_indexaggregate:
- case no_compound_childaggregate:
- {
- if (isNullRowDs(child))
- return removeParentNode(expr);
- node_operator childOp = child->getOperator();
- if ((op == childOp) || (childOp == no_null))
- return removeParentNode(expr);
- break;
- }
- case no_assert_ds:
- {
- if (isNull(child))
- return removeParentNode(expr);
- bool hasAssert = false;
- ForEachChildFrom(i, expr, 1)
- {
- IHqlExpression * cur = queryRealChild(expr, i);
- if (cur && (cur->getOperator() != no_null))
- {
- hasAssert = true;
- break;
- }
- }
- //All asserts have constant folded away...
- if (!hasAssert)
- return removeParentNode(expr);
- break;
- }
- case no_choosen:
- {
- if (isNull(child) || isFail(child))
- return removeParentNode(expr);
- IHqlExpression * choosenLimit = expr->queryChild(1);
- IValue * choosenValue = choosenLimit->queryValue();
- if (choosenValue)
- {
- __int64 v = choosenValue->getIntValue();
- if (v == 0)
- return replaceWithNull(expr);
- ITypeInfo * type = choosenLimit->queryType();
- if (type->isSigned() && (v < 0))
- return replaceWithNull(expr);
- if (!queryRealChild(expr, 2))
- {
- //choosen(x, n) n>0 on a single row is same as the single row
- if (hasNoMoreRowsThan(child, 1)) // could use v
- return removeParentNode(expr);
- if (!isGrouped(expr))
- {
- if (v == CHOOSEN_ALL_LIMIT)
- return removeParentNode(expr);
- if (!isLocalActivity(expr) && hasNoMoreRowsThan(child, v))
- return removeParentNode(expr);
- }
- }
- }
- break;
- }
- case no_dedup:
- case no_rollup: // rollup on a single row does not call the transform => can be removed.
- if (isNull(child) || hasNoMoreRowsThan(child, 1) || isFail(child))
- return removeParentNode(expr);
- break;
- case no_limit:
- {
- if (isNull(child) || isFail(child))
- return removeParentNode(expr);
- __int64 limit = getIntValue(expr->queryChild(1), 0);
- if (limit >= 1)
- {
- if (hasNoMoreRowsThan(child, 1))
- return removeParentNode(expr);
- if (!isGrouped(expr) && !isLocalActivity(expr) && hasNoMoreRowsThan(child, limit))
- return removeParentNode(expr);
- }
- break;
- }
- case no_catchds:
- {
- if (isNull(child))
- return removeParentNode(expr);
- break;
- }
- case no_filter:
- case no_keyeddistribute:
- case no_choosesets:
- case no_enth:
- case no_sample:
- case no_keyedlimit:
- case no_cosort:
- case no_topn:
- case no_iterate:
- case no_preload:
- case no_alias:
- case no_forcelocal:
- case no_nothor:
- case no_cluster:
- case no_forcenolocal:
- case no_assertsorted:
- case no_assertgrouped:
- case no_assertdistributed:
- case no_stepped:
- case no_filtergroup:
- case no_section:
- case no_related:
- case no_unordered:
- case no_preservemeta:
- if (isNull(child) || isFail(child))
- return removeParentNode(expr);
- break;
- case no_transformebcdic:
- case no_transformascii:
- case no_rollupgroup:
- case no_normalizegroup:
- case no_parse:
- case no_newparse:
- case no_xmlparse:
- case no_newxmlparse:
- case no_selfjoin:
- case no_process:
- if (isNull(child))
- return replaceWithNull(expr);
- break;
- case no_normalize:
- if (isNull(child) || matchesConstantValue(expr->queryChild(1), 0))
- return replaceWithNull(expr);
- break;
- case no_allnodes:
- case no_thisnode:
- if (isNull(child) && expr->isDataset())
- return replaceWithNull(expr);
- break;
- case no_combine:
- case no_combinegroup:
- if (isNull(child) && isNull(expr->queryChild(1)))
- return replaceWithNull(expr);
- break;
- case no_createdictionary:
- if (isNull(child))
- return replaceWithNull(expr);
- break;
- case no_selectmap:
- if (isNull(child))
- return replaceWithNullRow(child);
- break;
- case no_selectnth:
- // if (isNull(child) || isZero(expr->queryChild(1)))
- if (isNull(child))
- return replaceWithNullRow(child);
- break;
- case no_select:
- if (isNull(child) && expr->hasAttribute(newAtom))
- return replaceWithNull(expr);
- break;
- case no_createset:
- if (isNull(child))
- return replaceWithNull(expr);
- break;
- case no_hqlproject:
- case no_projectrow:
- {
- if (isNullProject(expr, false, false))
- return removeParentNode(expr);
- if (isNull(child))
- return replaceWithNull(expr);
- break;
- }
- case no_output:
- {
- //Appending a null dataset to an output does nothing (sometimes occurs as a kind of nop)
- if (!queryRealChild(expr, 1) && expr->hasAttribute(extendAtom))
- {
- if (isNull(child) && child->isDataset())
- return replaceWithNull(expr);
- }
- break;
- }
- case no_compound:
- if (isNull(child) && child->isAction())
- return LINK(expr->queryChild(1)); // Could cause overlinking of child when called from HqlOpt
- break;
- case no_executewhen:
- {
- IHqlExpression * action = expr->queryChild(1);
- if (isNull(action) && action->isAction())
- return removeParentNode(expr);
- break;
- }
- case no_globalscope:
- if (isRedundantGlobalScope(expr))
- return removeParentNode(expr);
- break;
- case no_evaluate_stmt:
- {
- IHqlExpression * arg = expr->queryChild(0);
- if (arg->isConstant() || arg->getOperator() == no_table)
- return createNullExpr(expr);
- break;
- }
- }
- return NULL;
- }
- IHqlExpression * NullFolderMixin::queryOptimizeAggregateInline(IHqlExpression * expr, __int64 numRows)
- {
- node_operator specialOp = querySimpleAggregate(expr, false, true);
- IHqlExpression * transform = expr->queryChild(2);
- IHqlExpression * assign = transform->queryChild(0); // guaranteed to be in simple form
- LinkedHqlExpr value;
- switch (specialOp)
- {
- case no_existsgroup:
- value.setown(createConstant(numRows != 0));
- break;
- case no_countgroup:
- {
- ITypeInfo * type = assign->queryChild(0)->queryType();
- value.setown(createConstant(type->castFrom(true, numRows)));
- break;
- }
- //could do max/min/sum if really wanted to
- }
- if (!value)
- return NULL;
- HqlExprArray args;
- args.append(*createAssign(LINK(assign->queryChild(0)), LINK(value)));
- OwnedHqlExpr newTransform = createValue(no_transform, transform->getType(), args);
- OwnedHqlExpr values = createValue(no_transformlist, newTransform.getClear());
- return createDataset(no_inlinetable, values.getClear(), LINK(expr->queryRecord()));
- }
- //---------------------------------------------------------------------------
- IHqlExpression * getLowerCaseConstant(IHqlExpression * expr)
- {
- ITypeInfo * type = expr->queryType();
- switch (type->getTypeCode())
- {
- case type_unicode:
- case type_varunicode:
- case type_string:
- case type_data:
- case type_varstring:
- case type_utf8:
- break;
- default:
- return LINK(expr);
- }
- IValue * value = expr->queryValue();
- assertex(value);
- const void * data = value->queryValue();
- unsigned size = type->getSize();
- unsigned stringLen = type->getStringLen();
- MemoryAttr lower(size);
- memcpy(lower.bufferBase(), data, size);
- if (type->getTypeCode() == type_utf8)
- rtlUtf8ToLower(stringLen, (char *)lower.get(), str(type->queryLocale()));
- else if (isUnicodeType(type))
- rtlUnicodeToLower(stringLen, (UChar *)lower.get(), str(type->queryLocale()));
- else
- {
- if (type->queryCharset()->queryName() == ebcdicAtom)
- rtlEStrToStr(stringLen, (char*)lower.get(), stringLen, (char*)lower.get()); // Yes it does work in place.
- rtlStringToLower(stringLen, (char *)lower.get());
- if (type->queryCharset()->queryName() == ebcdicAtom)
- rtlStrToEStr(stringLen, (char*)lower.get(), stringLen, (char*)lower.get()); // Yes it does work in place.
- }
- if (memcmp(lower.get(), data, size) == 0)
- return LINK(expr);
- return createConstant(createValueFromMem(LINK(type), lower.get()));
- }
- //---------------------------------------------------------------------------
- static HqlTransformerInfo constantReplacingTransformerInfo("ConstantReplacingTransformer");
- class HQL_API ConstantReplacingTransformer : public NewHqlTransformer
- {
- public:
- ConstantReplacingTransformer(IHqlExpression * _selector) : NewHqlTransformer(constantReplacingTransformerInfo) { selector = _selector; }
- virtual IHqlExpression * createTransformed(IHqlExpression * expr)
- {
- node_operator op = expr->getOperator();
- //Special case for things that really shouldn't have substitutions
- switch (op)
- {
- case no_attr:
- case no_attr_link:
- case no_attr_expr:
- {
- IAtom * name = expr->queryName();
- if (name == _selectors_Atom)
- {
- HqlExprArray args;
- ForEachChild(i, expr)
- args.append(*transformSelector(expr->queryChild(i)));
- return expr->clone(args);
- }
- break;
- }
- #if 0
- //MORE: These should be constant folded , otherwise it can mess up the graph commoning. (see outmod.xhql)
- case no_crc:
- case no_hash:
- case no_hash32:
- case no_hash64:
- return LINK(expr);
- #endif
- }
- if (expr->isConstant())
- return LINK(expr);
- unsigned numNonHidden = activityHidesSelectorGetNumNonHidden(expr, selector);
- if (numNonHidden == 0)
- return NewHqlTransformer::createTransformed(expr);
- bool same = true;
- HqlExprArray children;
- for (unsigned i=0; i < numNonHidden; i++)
- {
- IHqlExpression * cur = expr->queryChild(i);
- IHqlExpression * mapped = transform(cur);
- children.append(*mapped);
- if (cur != mapped)
- same = false;
- }
- if (same)
- return LINK(expr);
- unwindChildren(children, expr, numNonHidden);
- return expr->clone(children);
- }
- void setMapping(IHqlExpression * oldValue, IHqlExpression * newValue)
- {
- NewHqlTransformer::setMapping(oldValue, newValue);
- //Nasty... I'm not sure if this should really happen, but...
- //if we are replacing a row, then the old active selector needs to become an inline row (e.g., prwo.xhql)
- if (oldValue->isDatarow())
- {
- OwnedHqlExpr newRow = createRow(no_newrow, LINK(newValue));
- setSelectorMapping(oldValue, newRow);
- }
- }
- protected:
- IHqlExpression * selector;
- };
- static bool isWorthPercolating(IHqlExpression * expr)
- {
- switch (expr->getOperator())
- {
- case no_attr:
- case no_attr_link:
- case no_constant:
- case no_getresult:
- case no_record:
- case no_all:
- return false;
- }
- return !expr->isConstant();
- }
- static bool isWorthPercolating(const HqlExprArray & exprs)
- {
- ForEachItemIn(i, exprs)
- if (isWorthPercolating(&exprs.item(i)))
- return true;
- return false;
- }
- bool expressionsEquivalent(IHqlExpression * left, IHqlExpression * right)
- {
- if (left->queryBody() == right->queryBody())
- return true;
- if ((left->getOperator() == no_null) && (right->getOperator() == no_null))
- return recordTypesMatch(left, right);
- return false;
- }
- bool exprsReferencesDataset(const HqlExprArray & source, IHqlExpression * exprSelector)
- {
- ForEachItemIn(i, source)
- {
- if (exprReferencesDataset(&source.item(i), exprSelector))
- return true;
- }
- return false;
- }
- class HqlConstantPercolator : public CInterface
- {
- public:
- HqlConstantPercolator(IHqlExpression *ds = NULL)
- {
- if (ds)
- self.setown(getSelf(ds));
- }
- void addEquality(IHqlExpression * target, IHqlExpression * source)
- {
- addTransformMapping(target, source);
- }
- bool empty() { return targets.empty(); }
- IHqlExpression * expandFields(IHqlExpression * expr, IHqlExpression * exprSelector)
- {
- if (!isWorthPercolating(expr))
- return LINK(expr);
- if (!exprReferencesDataset(expr, exprSelector))
- return LINK(expr);
- ConstantReplacingTransformer transformer(exprSelector);
- initTransformer(exprSelector, transformer);
- return transformer.transformRoot(expr);
- }
- IHqlExpression * expandField(IHqlExpression * field)
- {
- ForEachItemIn(i, targets)
- {
- if (targets.item(i).queryChild(1) == field)
- return LINK(&sources.item(i));
- }
- return NULL;
- }
- bool expandFields(HqlExprArray & target, const HqlExprArray & source, IHqlExpression * exprSelector)
- {
- if (!isWorthPercolating(source))
- return false;
- if (!exprsReferencesDataset(source, exprSelector))
- return false;
- ConstantReplacingTransformer transformer(exprSelector);
- initTransformer(exprSelector, transformer);
- ForEachItemIn(i, source)
- {
- IHqlExpression & cur = source.item(i);
- if (exprReferencesDataset(&cur, exprSelector))
- target.append(*transformer.transformRoot(&cur));
- else
- target.append(*LINK(&cur));
- }
- return true;
- }
- void inheritMapping(const HqlConstantPercolator * other)
- {
- assertex(other);
- if (other->self)
- {
- assertex(!self || self == other->self);
- self.set(other->self);
- }
- ForEachItemIn(i, other->targets)
- {
- targets.append(OLINK(other->targets.item(i)));
- sources.append(OLINK(other->sources.item(i)));
- }
- }
- void intersectMapping(const HqlConstantPercolator * other)
- {
- assertex(other);
- ForEachItemInRev(i, targets)
- {
- unsigned match = other->targets.find(targets.item(i));
- if ((match == NotFound) || !expressionsEquivalent(&sources.item(i), &other->sources.item(match)))
- {
- sources.remove(i);
- targets.remove(i);
- }
- }
- }
- IHqlExpression * querySelf() { return self; }
- IHqlExpression * resolveField(IHqlExpression * search)
- {
- ForEachItemIn(i, targets)
- {
- IHqlExpression & cur = targets.item(i);
- if ((cur.queryChild(1) == search) && (cur.queryChild(0) == self))
- return &sources.item(i);
- }
- return NULL;
- }
- static HqlConstantPercolator * extractConstantMapping(IHqlExpression * transform)
- {
- if (!isKnownTransform(transform))
- return NULL;
- Owned<HqlConstantPercolator> mapping = new HqlConstantPercolator;
- mapping->extractConstantTransform(transform);
- if (mapping->empty())
- return NULL;
- return mapping.getClear();
- }
- static HqlConstantPercolator * extractNullMapping(IHqlExpression * record)
- {
- Owned<HqlConstantPercolator> mapping = new HqlConstantPercolator;
- mapping->extractNullTransform(record);
- if (mapping->empty())
- return NULL;
- return mapping.getClear();
- }
- protected:
- void addMapping(IHqlExpression * select, IHqlExpression * expr)
- {
- assertex(select->getOperator() == no_select);
- targets.append(*LINK(select));
- sources.append(*LINK(expr));
- // if (select->isDatarow() && !expr->isDatarow())
- }
- void addTransformMapping(IHqlExpression * tgt, IHqlExpression * src)
- {
- #ifdef _DEBUG
- assertex(tgt->getOperator() == no_select);
- IHqlExpression * sel = queryDatasetCursor(tgt->queryChild(0));
- assertex(sel == self);
- #endif
- OwnedHqlExpr castRhs = ensureExprType(src, tgt->queryType());
- addMapping(tgt, castRhs);
- }
- void doExtractConstantTransform(IHqlExpression * transform, IHqlExpression * selector);
- void expandNullRowMapping(IHqlExpression * selector, IHqlExpression * record);
- void extractConstantTransform(IHqlExpression * transform);
- void initTransformer(IHqlExpression * selector, ConstantReplacingTransformer & transformer) const;
- void extractNullTransform(IHqlExpression * record)
- {
- self.setown(getSelf(record));
- expandNullRowMapping(self, record);
- }
- protected:
- OwnedHqlExpr self;
- HqlExprArray targets;
- HqlExprArray sources;
- };
- void HqlConstantPercolator::extractConstantTransform(IHqlExpression * transform)
- {
- self.setown(getSelf(transform));
- doExtractConstantTransform(transform, NULL);
- }
- void HqlConstantPercolator::expandNullRowMapping(IHqlExpression * selector, IHqlExpression * record)
- {
- ForEachChild(i, record)
- {
- IHqlExpression * cur = record->queryChild(i);
- switch (cur->getOperator())
- {
- case no_record:
- expandNullRowMapping(selector, cur);
- break;
- case no_ifblock:
- //valid - since if protecting fields are false, the fields will also be null
- expandNullRowMapping(selector, cur->queryChild(1));
- break;
- case no_field:
- {
- OwnedHqlExpr selected = createSelectExpr(LINK(selector), LINK(cur));
- try
- {
- OwnedHqlExpr null = createNullExpr(selected);
- addMapping(selected, null);
- }
- catch (IException * e)
- {
- e->Release();
- }
- if (selected->isDatarow())
- expandNullRowMapping(selected, selected->queryRecord());
- break;
- }
- }
- }
- }
- void HqlConstantPercolator::doExtractConstantTransform(IHqlExpression * transform, IHqlExpression * selector)
- {
- unsigned max = transform->numChildren();
- for (unsigned idx = 0; idx < max; idx++)
- {
- IHqlExpression * cur = transform->queryChild(idx);
- switch (cur->getOperator())
- {
- case no_assign:
- {
- IHqlExpression * lhs = cur->queryChild(0);
- IHqlExpression * rhs = cur->queryChild(1);
- IHqlExpression * lf = lhs->queryChild(1);
- IHqlExpression * self = lhs->queryChild(0);
- assertex(self->getOperator() == no_self);
- OwnedHqlExpr selected = selector ? createSelectExpr(LINK(selector), LINK(lf)) : LINK(lhs);
- if (rhs->isConstant())
- addTransformMapping(selected, rhs);
- if (lhs->isDatarow())
- {
- if (rhs->getOperator() == no_null)
- expandNullRowMapping(selected, selected->queryRecord());
- else if (rhs->getOperator() == no_createrow)
- doExtractConstantTransform(rhs->queryChild(0), selected);
- }
- }
- break;
- case no_assignall:
- doExtractConstantTransform(cur, selector);
- break;
- case no_attr:
- case no_attr_link:
- case no_attr_expr:
- case no_alias_scope:
- case no_skip:
- case no_assert:
- break;
- default:
- assertex(!"Transforms should only contain assignments");
- break;
- }
- }
- }
- void HqlConstantPercolator::initTransformer(IHqlExpression * selector, ConstantReplacingTransformer & transformer) const
- {
- ForEachItemIn(i, sources)
- {
- OwnedHqlExpr value = replaceSelector(&targets.item(i), self, selector);
- transformer.setMapping(value, &sources.item(i));
- }
- }
- IHqlExpression * CExprFolderTransformer::doFoldTransformed(IHqlExpression * unfolded, IHqlExpression * original)
- {
- IHqlExpression * nullFolded = foldNullDataset(unfolded);
- if (nullFolded)
- return nullFolded;
- #if 0
- IHqlExpression * body = unfolded->queryBody();
- OwnedHqlExpr expr = foldConstantOperator(body, foldOptions, templateContext);
- if ((unfolded != body) && !expr->isAnnotation() && !expr->queryValue())
- expr.setown(unfolded->cloneAllAnnotations(expr));
- #else
- OwnedHqlExpr expr = foldConstantOperator(unfolded, foldOptions, templateContext);
- #endif
- node_operator op = expr->getOperator();
- switch (op)
- {
- //Scalar operators that are not handled in foldConstantOperator()
- case no_or:
- return foldOrExpr(expr, (foldOptions & HFOx_op_not_x) != 0);
- case no_and:
- return foldAndExpr(expr, (foldOptions & HFOx_op_not_x) != 0);
- //Operations that involve constant folding on datasets.
- case no_normalize:
- {
- // Identify expressions
- IHqlExpression * ds = expr->queryChild(0);
- IHqlExpression * count = expr->queryChild(1);
- IHqlExpression * transform = expr->queryChild(2);
- OwnedHqlExpr left = createSelector(no_left, ds, querySelSeq(expr));
- if (!hasSingleRow(ds) || exprReferencesDataset(count, left)) // Complicate things more
- break;
- // Replace LEFT from normalize transform (if used) by ROW's contents
- OwnedHqlExpr newTransform;
- if (exprReferencesDataset(transform, left)) {
- OwnedHqlExpr newRow;
- // Make sure it's one of the recognised formats
- switch (ds->getOperator())
- {
- case no_datasetfromrow: // DATASET(ROW(transform))
- {
- IHqlExpression * row = ds->queryChild(0);
- if (row->getOperator() == no_createrow)
- newRow.set(row);
- break;
- }
- case no_inlinetable: // DATASET([transform()]) or DATASET([value],{ myfield })
- {
- IHqlExpression * transformList = ds->queryChild(0);
- assertex(transformList->getOperator() == no_transformlist);
- newRow.setown(createRow(no_createrow, LINK(transformList->queryChild(0))));
- break;
- }
- }
- if (!newRow)
- break;
- //Instead of evaluating once newRow will be evaluated multiple times. Is that ok (e.g., volatile)
- if (!canDuplicateActivity(newRow))
- break;
- OwnedHqlExpr replacementRow = createRow(no_newrow, LINK(newRow));
- newTransform.setown(replaceSelector(transform, left, replacementRow));
- }
- HqlExprArray args;
- unwindChildren(args, expr, 1); // (count, trans)
- if (newTransform)
- args.replace(*newTransform.getClear(), 1);
- removeAttribute(args, _selectorSequence_Atom);
- return createDataset(no_dataset_from_transform, args);
- }
- case no_filter:
- {
- IHqlExpression * child = expr->queryChild(0);
- HqlExprArray args;
- args.append(*LINK(child));
- unsigned num = expr->numChildren();
- for (unsigned idx = 1; idx < num; idx++)
- {
- IHqlExpression * cur = expr->queryChild(idx);
- IValue * value = cur->queryValue();
- if (value)
- {
- if (!value->getBoolValue())
- return createNullDataset(child);
- }
- else
- args.append(*LINK(cur));
- }
- if (args.ordinality() == 1)
- return removeParentNode(expr);
- //Fold filter conditions with previous projects to see if they are always true, or always false.
- //Similar code also appears in the optimizer...
- switch (child->getOperator())
- {
- case no_newusertable:
- case no_hqlproject:
- if ((foldOptions & HFOfoldfilterproject) && !(foldOptions & HFOpercolateconstants))
- {
- // Following are possibilities, but aren't worth the extra cycles....
- //case no_join:
- //case no_iterate:
- //case no_denormalize:
- //case no_normalize:
- //case no_selfjoin:
- if (!isAggregateDataset(child) && (args.ordinality() > 1))
- {
- NewProjectMapper2 mapper;
- mapper.setMapping(queryNewColumnProvider(child));
- //Iterate all but last
- for (unsigned i = args.ordinality(); --i != 0; )
- {
- IHqlExpression * cur = &args.item(i);
- OwnedHqlExpr expandedFilter = mapper.expandFields(cur, child, NULL, NULL);
- if (expandedFilter->isConstant())
- {
- //Following would be sensible, but can't call transform at this point, so replace arg, and wait for it to re-iterate
- IIdAtom * nameF = expr->queryId();
- IIdAtom * nameP = child->queryId();
- DBGLOG("Folder: Combining FILTER %s with %s %s produces constant filter", nameF ? str(nameF) : "", getOpString(child->getOperator()), nameP ? str(nameP) : "");
- expandedFilter.setown(transformExpanded(expandedFilter));
- IValue * value = expandedFilter->queryValue();
- if (value)
- {
- if (!value->getBoolValue())
- return replaceWithNull(expr);
- args.remove(i);
- }
- else
- args.replace(*LINK(expandedFilter), i);
- }
- }
- }
- }
- break;
- case no_inlinetable:
- if (foldOptions & HFOconstantdatasets)
- {
- OwnedITypeInfo boolType = makeBoolType();
- OwnedHqlExpr filterCondition = createBalanced(no_and, boolType, args, 1, args.ordinality());
- HqlExprArray filtered;
- bool allFilteredOk = true;
- IHqlExpression * values = child->queryChild(0);
- ForEachChild(i, values)
- {
- IHqlExpression * curTransform = values->queryChild(i);
- NewProjectMapper2 mapper;
- mapper.setMapping(curTransform);
- OwnedHqlExpr expandedFilter = mapper.expandFields(filterCondition, child, NULL, NULL);
- OwnedHqlExpr folded = transformExpanded(expandedFilter);
- IValue * value = folded->queryValue();
- if (value)
- {
- if (value->getBoolValue())
- filtered.append(*LINK(curTransform));
- }
- else
- {
- allFilteredOk = false;
- break;
- }
- }
- if (allFilteredOk)
- {
- if (filtered.ordinality() == 0)
- return replaceWithNull(expr);
- if (filtered.ordinality() == values->numChildren())
- return removeParentNode(expr);
- StringBuffer s1, s2;
- DBGLOG("Folder: Node %s reduce values in child: %s from %d to %d", queryChildNodeTraceText(s1, expr), queryChildNodeTraceText(s2, child), values->numChildren(), filtered.ordinality());
- HqlExprArray args;
- args.append(*values->clone(filtered));
- unwindChildren(args, child, 1);
- return child->clone(args);
- }
- }
- break;
- }
- if (args.ordinality() == 1)
- return removeParentNode(expr);
- return cloneOrLink(expr, args);
- }
- case no_newaggregate:
- {
- //Duplicated in constant folder and optimizer
- IHqlExpression * child = expr->queryChild(0);
- node_operator childOp = child->getOperator();
- IHqlExpression * ret = NULL;
- switch (childOp)
- {
- case no_inlinetable:
- if ((foldOptions & HFOconstantdatasets) && isNoSkipInlineDataset(child))
- ret = queryOptimizeAggregateInline(expr, child->queryChild(0)->numChildren());
- break;
- default:
- if ((foldOptions & HFOconstantdatasets) && hasSingleRow(child))
- ret = queryOptimizeAggregateInline(expr, 1);
- break;
- }
- if (ret)
- return ret;
- break;
- }
- case no_count:
- {
- IHqlExpression * child = expr->queryChild(0);
- node_operator childOp = child->getOperator();
- switch (childOp)
- {
- case no_inlinetable:
- if (isNoSkipInlineDataset(child))
- return createConstant(expr->queryType()->castFrom(false, (__int64)child->queryChild(0)->numChildren()));
- break;
- case no_temptable:
- {
- IHqlExpression * values = child->queryChild(0);
- if (values->isList())
- return createValue(no_countlist, expr->getType(), LINK(values));
- break;
- }
- case no_null:
- return createNullValue(expr);
- #if 0
- // Enabling this generally makes code worse because of count(file), count(x) > n, and extra hoisting.
- case no_addfiles:
- {
- OwnedHqlExpr lhs = replaceChild(expr, 0, child->queryChild(0));
- OwnedHqlExpr rhs = replaceChild(expr, 0, child->queryChild(1));
- return createValue(no_add, expr->getType(), LINK(lhs), LINK(rhs));
- }
- case no_if:
- {
- OwnedHqlExpr lhs = replaceChild(expr, 0, child->queryChild(1));
- OwnedHqlExpr rhs = replaceChild(expr, 0, child->queryChild(2));
- return createValue(no_if, expr->getType(), LINK(child->queryChild(0)), LINK(lhs), LINK(rhs));
- }
- #endif
- default:
- if (hasSingleRow(child))
- return createConstant(expr->queryType()->castFrom(false, I64C(1)));
- break;
- }
- break;
- }
- case no_exists:
- {
- IHqlExpression * child = expr->queryChild(0);
- node_operator childOp = child->getOperator();
- switch (childOp)
- {
- case no_inlinetable:
- if (isNoSkipInlineDataset(child))
- {
- bool hasChildren = (child->queryChild(0)->numChildren() != 0);
- return createConstant(hasChildren);
- }
- break;
- #if 0
- case no_addfiles:
- {
- OwnedHqlExpr lhs = replaceChild(expr, 0, child->queryChild(0));
- OwnedHqlExpr rhs = replaceChild(expr, 0, child->queryChild(1));
- return createValue(no_or, expr->getType(), LINK(lhs), LINK(rhs));
- }
- case no_if:
- {
- OwnedHqlExpr lhs = replaceChild(expr, 0, child->queryChild(1));
- OwnedHqlExpr rhs = replaceChild(expr, 0, child->queryChild(2));
- return createValue(no_if, expr->getType(), LINK(child->queryChild(0)), LINK(lhs), LINK(rhs));
- }
- #endif
- case no_null:
- return createConstant(op != no_exists);
- default:
- if (hasSingleRow(child))
- return createConstant(op == no_exists);
- break;
- }
- break;
- }
- case no_within:
- case no_sum:
- {
- IHqlExpression * dataset = expr->queryChild(0);
- if (dataset->getOperator() == no_null)
- return createNullValue(expr);
- if (op == no_sum && dataset->getOperator() == no_addfiles)
- {
- IHqlExpression * arg = expr->queryChild(1);
- IHqlExpression * addLeft = dataset->queryChild(0);
- IHqlExpression * addRight = dataset->queryChild(1);
- OwnedHqlExpr sumLeft = createValue(op, expr->getType(), LINK(addLeft), replaceSelector(arg, dataset, addLeft));
- OwnedHqlExpr sumRight = createValue(op, expr->getType(), LINK(addRight), replaceSelector(arg, dataset, addRight));
- return createValue(no_add, expr->getType(), LINK(sumLeft), LINK(sumRight));
- }
- }
- break;
- #if 0
- //Following are not enabled because they have a strange side-effect of stopping the dataset being hoisted
- //since an inline dataset (which is hoisted) is converted to a no_list, which isn't.
- //If the key segment monitors for IN were improved then it may be worth
- case no_createset:
- {
- IHqlExpression * child = expr->queryChild(0);
- switch (child->getOperator())
- {
- case no_inlinetable:
- {
- IHqlExpression * select = expr->queryChild(1);
- //check a simple select from the dataset
- if ((select->getOperator() == no_select) && (select->queryChild(0) == child->queryNormalizedSelector()))
- {
- HqlExprArray args;
- bool ok = true;
- IHqlExpression * transforms = child->queryChild(0);
- IHqlExpression * field = select->queryChild(1);
- ForEachChild(i, transforms)
- {
- IHqlExpression * cur = transforms->queryChild(i);
- if (!cur->isPure() || containsSkip(cur))
- {
- ok = false;
- break;
- }
- IHqlExpression * match = getExtractSelect(cur, field, false);
- if (!match)
- {
- ok = false;
- break;
- }
- args.append(*match);
- }
- if (ok)
- return createValue(no_list, expr->getType(), args);
- }
- break;
- }
- case no_temptable:
- {
- IHqlExpression * list = child->queryChild(0);
- if (list->getOperator() == no_list)
- return ensureExprType(list, expr->queryType());
- break;
- }
- }
- break;
- }
- #endif
- case no_select:
- {
- //Don't fold dataset references that are in scope,
- //otherwise the dataset will fail to match...
- if (expr->hasAttribute(newAtom))
- {
- IHqlExpression * left = expr->queryChild(0);
- switch (left->getOperator())
- {
- case no_null:
- return createNullExpr(expr);
- case no_datasetfromrow:
- if (left->queryChild(0)->getOperator() == no_null)
- return createNullExpr(expr);
- break;
- case no_createrow:
- #if 1
- if (!expr->isDataset() && !expr->isDatarow())
- {
- OwnedHqlExpr match = getExtractSelect(left->queryChild(0), expr->queryChild(1), false);
- if (match && match->isConstant())
- return match.getClear();
- }
- #else
- //This generates better code most of the time, but causes worse code for a few examples
- //e.g., bug74112 cmorton47 ncferr
- //Should enable once I've had time to investigate
- if (!expr->isDataset())// && !expr->isDatarow())
- {
- OwnedHqlExpr match = getExtractSelect(left->queryChild(0), expr->queryChild(1), false);
- if (match)// && match->isConstant())
- return match.getClear();
- }
- #endif
- break;
- case no_selectnth:
- if (foldOptions & HFOpercolateconstants)
- {
- IHqlExpression * ds = left->queryChild(0);
- IHqlExpression * elem = left->queryChild(1);
- if ((ds->getOperator() == no_inlinetable) && elem->queryValue())
- {
- __int64 idx = elem->queryValue()->getIntValue() - 1;
- IHqlExpression * transforms = ds->queryChild(0);
- if (idx >= 0 && idx < transforms->numChildren())
- {
- IHqlExpression * transform = transforms->queryChild((unsigned)idx);
- HqlConstantPercolator * mapping = gatherConstants(transform);
- if (mapping)
- {
- IHqlExpression * resolved = mapping->resolveField(expr->queryChild(1));
- if (resolved)
- return LINK(resolved);
- }
- }
- else
- return createNullExpr(expr);
- }
- }
- }
- }
- // default is to call transformSelector() if not new
- break;
- }
- case no_sample:
- {
- IHqlExpression * limit = expr->queryChild(1);
- IValue * value = limit->queryValue();
- if (value && (value->getIntValue() == 1))
- return removeParentNode(expr);
- break;
- }
- /*
- case no_alias_scope:
- {
- IHqlExpression * child = expr->queryChild(0);
- if (child->queryValue())
- return LINK(child);
- break;
- }
- */
- /*
- no_table,
- no_temptable,
- no_pipe:
- no_fetch,
- no_join,
- no_joined,
- */
- case no_usertable:
- //These should have been removed by the time we get called, but can very occasionally occur
- //if it is called in the parser (e.g., when folding a filename)
- break;
- case no_selectfields:
- {
- //These should have been removed by the time we get called.
- //This is sometimes added by the SQL generator, but it shouldn't perform a project
- assertex(expr->queryChild(1)->getOperator() == no_null);
- IHqlExpression * dataset = expr->queryChild(0);
- if (isNull(dataset))
- return LINK(dataset);
- break;
- }
- case no_compound:
- if (foldOptions & HFOforcefold)
- return LINK(expr->queryChild(1));
- break;
- case no_hqlproject:
- if (expr != original)
- {
- //Could have removed whether or not somethin needs to be a count project
- IHqlExpression * counter = queryAttributeChild(expr, _countProject_Atom, 0);
- if (counter && !transformContainsCounter(expr->queryChild(1), counter))
- return removeAttribute(expr, _countProject_Atom);
- }
- break;
- case no_temptable:
- {
- if (expr->queryChild(0)->getOperator() == no_list)
- {
- ECLlocation dummyLocation(0, 0, 0, NULL);
- OwnedHqlExpr inlineTable = convertTempTableToInlineTable(errorProcessor, dummyLocation, expr);
- if (expr != inlineTable)
- return inlineTable.getClear();
- }
- break;
- }
- case no_assert:
- if (getBoolValue(expr->queryChild(0), false))
- return createValue(no_null, makeVoidType());
- break;
- case no_sequential:
- case no_parallel:
- case no_orderedactionlist:
- if (expr->numChildren() == 1)
- {
- if (expr->queryChild(0)->isAttribute())
- return createValue(no_null, makeVoidType());
- return removeParentNode(expr);
- }
- break;
- case no_likely:
- case no_unlikely:
- {
- IHqlExpression * child = expr->queryChild(0);
- switch(child->getOperator())
- {
- case no_likely:
- case no_unlikely:
- return replaceChild(expr, 0, child->queryChild(0));
- case no_constant:
- return LINK(child);
- }
- break;
- }
- }
- return LINK(expr);
- }
- //---------------------------------------------------------------------------
- FolderTransformInfo::~FolderTransformInfo()
- {
- ::Release(mapping);
- }
- static HqlTransformerInfo cExprFolderTransformerInfo("CExprFolderTransformer");
- CExprFolderTransformer::CExprFolderTransformer(IErrorReceiver & _errorProcessor, ITemplateContext * _templateContext, unsigned _options)
- : NewHqlTransformer(cExprFolderTransformerInfo), templateContext(_templateContext), errorProcessor(_errorProcessor)
- {
- foldOptions = _options;
- }
- IHqlExpression * CExprFolderTransformer::createTransformedAnnotation(IHqlExpression * expr)
- {
- return CExprFolderTransformer::createTransformed(expr);
- }
- ANewTransformInfo * CExprFolderTransformer::createTransformInfo(IHqlExpression * expr)
- {
- return CREATE_NEWTRANSFORMINFO(FolderTransformInfo, expr);
- }
- IHqlExpression * createListMatchStructure(node_operator op, IHqlExpression * expr, const HqlExprArray & args, unsigned & idx)
- {
- if (expr->getOperator() != op)
- return &OLINK(args.item(idx++));
- IHqlExpression * lhs = expr->queryChild(0);
- IHqlExpression * rhs = expr->queryChild(1);
- OwnedHqlExpr newLhs = createListMatchStructure(op, lhs, args, idx);
- OwnedHqlExpr newRhs = createListMatchStructure(op, rhs, args, idx);
- if ((lhs == newLhs) && (rhs == newRhs))
- return LINK(expr);
- if (op == no_and)
- {
- if (matchesBoolean(newLhs, true))
- return newRhs.getClear();
- if (matchesBoolean(newRhs, true))
- return newLhs.getClear();
- }
- else
- {
- if (matchesBoolean(newLhs, false))
- return newRhs.getClear();
- if (matchesBoolean(newRhs, false))
- return newLhs.getClear();
- }
- OwnedHqlExpr value = createValue(op, expr->getType(), newLhs.getClear(), newRhs.getClear());
- return expr->cloneAllAnnotations(value);
- }
- IHqlExpression * createListMatchStructure(node_operator op, IHqlExpression * expr, const HqlExprArray & args)
- {
- unsigned idx = 0;
- OwnedHqlExpr ret = createListMatchStructure(op, expr, args, idx);
- assertex(idx == args.ordinality());
- return ret.getClear();
- }
- //dedup and rollup need to be very careful substituting constants for the rollup conditions.
- //If the original condition is something like right.combine, which is then replaced with false
- //it would then be processed as an equality false=false condition, causing extra dedups.
- //If it is substitued with true it will behave the same, so don't do anything.
- static IHqlExpression * preserveRollupConditions(IHqlExpression * update, IHqlExpression * original, unsigned from, unsigned to)
- {
- HqlExprArray args;
- unwindChildren(args, update);
- bool same = true;
- for (unsigned i=from; i < to; i++)
- {
- IHqlExpression * cur = &args.item(i);
- IHqlExpression * curOriginal = original->queryChild(i);
- LinkedHqlExpr mapped;
- if (cur->queryValue() && cur->queryType()->getTypeCode() == type_boolean && (cur->queryValue()->getBoolValue() == false))
- {
- if (!curOriginal->queryValue())
- mapped.set(curOriginal);
- }
- if (mapped && (cur != mapped))
- {
- args.replace(*mapped.getClear(), i);
- same = false;
- }
- }
- if (same)
- return LINK(update);
- return update->clone(args);
- }
- static IHqlExpression * stripDedupConditions(IHqlExpression * update, IHqlExpression * original)
- {
- HqlExprArray args;
- unwindChildren(args, update);
- unsigned max = args.ordinality();
- bool same = true;
- bool hadCriteria = false;
- for (unsigned i=max-1; i != 0; i--)
- {
- IHqlExpression * cur = &args.item(i);
- LinkedHqlExpr mapped;
- switch (cur->getOperator())
- {
- case no_left:
- case no_right:
- case no_attr:
- case no_attr_expr:
- case no_attr_link:
- case no_record:
- break;
- default:
- if (cur->isConstant())
- {
- args.remove(i);
- same = false;
- }
- else
- hadCriteria = true;
- break;
- }
- }
- if (same)
- return LINK(update);
- if (!hadCriteria)
- args.add(*createConstant(true), 1);
- return update->clone(args);
- }
- IHqlExpression * CExprFolderTransformer::percolateConstants(IHqlExpression * expr)
- {
- IHqlExpression * child = expr->queryChild(0);
- LinkedHqlExpr updated = expr;
- node_operator op = expr->getOperator();
- switch (op)
- {
- case no_iterate:
- //only sustitute for right, left contains rolled up record.
- updated.setown(percolateConstants(updated, child, no_right));
- //could call gatherconstants() on updated, and then substitude for no_left
- //But unlikely to matching anything that was constant, and if it was, the subsitution may well be wrong.
- break;
- case no_rollup:
- {
- //only sustitute for right, left contains rolled up record.
- updated.setown(percolateConstants(updated, child, no_right));
- //If any assignments of the form
- //self.x := left.x then the constant can be percolated from the input, but
- //self.x := left.x + 1 would cause invalid results.
- //updated.setown(percolateRollupInvariantConstants(updated, child, no_left));
- //changing the grouping conditions may confuse the code generator exactly what kind of dedup is going on,
- //so preserve any that would cause complications..
- OwnedHqlExpr fixed = preserveRollupConditions(updated, expr, 1, updated->numChildren());
- if (fixed != updated)
- {
- //If these are false then we should be able to turn the rollup into a project
- updated.set(fixed);
- }
- }
- break;
- case no_dedup:
- {
- //Need to be very careful about dedup criteria
- updated.setown(percolateConstants(updated, child, no_left));
- updated.setown(percolateConstants(updated, child, no_right));
- //Check if any conditions are now always false - that weren't before
- //if so it means the condition is always false, => will never dedup, => can just use the input
- OwnedHqlExpr fixed = preserveRollupConditions(updated, expr, 1, updated->numChildren());
- if (fixed != updated)
- return removeParentNode(expr);
- updated.setown(percolateConstants(updated, child, no_none));
- //any conditions just made constant, can be removed, if there are no conditions left then we'll add,true
- updated.setown(stripDedupConditions(updated, fixed));
- //updated.setown(preserveRollupConditions(updated, expr, 1, updated->numChildren()));
- }
- break;
- case no_selfjoin:
- case no_join:
- case no_denormalize:
- case no_denormalizegroup:
- {
- //The constants can only be percolated into the transform if certain conditions are met,
- //However they can always be percolated into the join condition... (test separately)
- IAtom * joinKind = queryJoinKind(expr);
- IHqlExpression * rhs = expr->queryChild(1);
- IHqlExpression * oldCond = updated->queryChild(2);
- IHqlExpression * atmost = updated->queryAttribute(atmostAtom);
- switch (op)
- {
- case no_denormalize:
- {
- //Nasty: left is repeated, and likely to be left outer => only replace join condition
- updated.setown(percolateConstants(updated, child, no_left, 2));
- updated.setown(percolateConstants(updated, rhs, no_right, 2));
- break;
- }
- case no_denormalizegroup:
- {
- if ((joinKind == innerAtom) || (joinKind == leftonlyAtom) || (joinKind == leftouterAtom))
- updated.setown(percolateConstants(updated, child, no_left));
- else
- updated.setown(percolateConstants(updated, child, no_left, 2));
- updated.setown(percolateConstants(updated, rhs, no_right, 2));
- break;
- }
- case no_selfjoin:
- rhs = child;
- // fallthrough
- case no_join:
- {
- if (joinKind == innerAtom)
- {
- updated.setown(percolateConstants(updated, child, no_left));
- updated.setown(percolateConstants(updated, rhs, no_right));
- }
- else if ((joinKind == leftonlyAtom) || (joinKind == leftouterAtom))
- {
- updated.setown(percolateConstants(updated, child, no_left));
- updated.setown(percolateConstants(updated, rhs, no_right, 2));
- //MORE: Could also replace intersection of rhs constants with a NULL row
- }
- else if ((joinKind == rightonlyAtom) || (joinKind == rightouterAtom))
- {
- updated.setown(percolateConstants(updated, child, no_left, 2));
- updated.setown(percolateConstants(updated, rhs, no_right));
- }
- else
- {
- updated.setown(percolateConstants(updated, child, no_left, 2));
- updated.setown(percolateConstants(updated, rhs, no_right, 2));
- }
- break;
- }
- }
- //If we've turned a fake all join into a join(true), then add an all attribute
- IHqlExpression * updatedCond = updated->queryChild(2);
- if (updatedCond != oldCond)
- {
- //At most is too complicated - either remove the atmost, or restore the join condition, and old atmost
- if (atmost)
- {
- if (matchesBoolean(updatedCond, false))
- updated.setown(removeAttribute(updated, atmostAtom));
- else
- {
- //KEYED joins support ATMOST and RIGHT.xxx = value
- if (!isKeyedJoin(updated) && joinHasRightOnlyHardMatch(updated, false))
- {
- HqlExprArray args;
- unwindChildren(args, updated);
- args.replace(*LINK(oldCond), 2);
- removeAttribute(args, atmostAtom);
- args.append(*LINK(atmost));
- updated.setown(updated->clone(args));
- }
- else
- updated.setown(appendOwnedOperand(updated, createAttribute(_conditionFolded_Atom)));
- }
- }
- //otherwise this might convert to an all join, accept variants that are supported by all joins
- //We don't currently have a self-join all. Would possibly be a good idea...
- else if ((joinKind == innerAtom || joinKind == leftouterAtom || joinKind == leftonlyAtom) && (op != no_selfjoin))
- updated.setown(appendOwnedOperand(updated, createAttribute(_conditionFolded_Atom)));
- else
- {
- //check there is still some kind of join condition left
- IHqlExpression * selSeq = querySelSeq(updated);
- IHqlExpression * updatedLhs = updated->queryChild(0);
- IHqlExpression * updatedRhs = (op == no_selfjoin) ? updatedLhs : updated->queryChild(1);
- JoinSortInfo joinInfo(updatedCond, updatedLhs, updatedRhs, selSeq, atmost);
- joinInfo.findJoinSortOrders(false);
- //if will convert to an all join, then restore the old condition,
- if (!joinInfo.hasRequiredEqualities())
- updated.setown(replaceChild(updated, 2, oldCond));
- else
- updated.setown(appendOwnedOperand(updated, createAttribute(_conditionFolded_Atom)));
- }
- }
- break;
- }
- case no_process:
- {
- //only substitute left; right contains iterated values
- updated.setown(percolateConstants(updated, child, no_left));
- break;
- }
- case no_merge:
- {
- HqlConstantPercolator * mapping = gatherConstants(expr);
- if (mapping)
- {
- IHqlExpression * sorted = expr->queryAttribute(sortedAtom);
- assertex(sorted);
- OwnedHqlExpr newSorted = percolateConstants(mapping, sorted, child, no_activetable);
- updated.setown(replaceOwnedAttribute(updated, newSorted.getClear()));
- }
- break;
- }
- case no_loop:
- case no_graphloop:
- case no_keyeddistribute:
- //Safer to do nothing...
- break;
- case no_select:
- {
- //E.g., Substitute ds[1].x if x is fixed. Useful in addition to the inline[n].x below
- //Useful in its own right, but latestcompreport.xhql required it because it was using it as a highly
- //unusual guard condition.
- //MORE: This needs more work to allow ds[1].x.y to be substituted, but that is very unusual
- //simplest would be to recurse, build up for a list of fields, and then pass to resolveFields()
- if (expr->hasAttribute(newAtom))
- {
- IHqlExpression * field = expr->queryChild(1);
- OwnedHqlExpr transformedDs = transform(expr->queryChild(0));
- HqlConstantPercolator * mapping = gatherConstants(transformedDs);
- if (mapping)
- {
- IHqlExpression * resolved = mapping->resolveField(field);
- if (resolved)
- updated.set(resolved);
- }
- }
- break;
- }
- break;
- default:
- {
- childDatasetType type = getChildDatasetType(expr);
- switch (type)
- {
- case childdataset_none:
- case childdataset_nway_left_right:
- case childdataset_many_noscope:
- case childdataset_many:
- case childdataset_if:
- case childdataset_case:
- case childdataset_map:
- case childdataset_evaluate:
- break;
- case childdataset_dataset_noscope:
- case childdataset_dataset:
- updated.setown(percolateConstants(updated, child, no_none));
- break;
- case childdataset_datasetleft:
- updated.setown(percolateConstants(updated, child, no_none));
- updated.setown(percolateConstants(updated, child, no_left));
- break;
- case childdataset_left:
- updated.setown(percolateConstants(updated, child, no_left));
- break;
- case childdataset_same_left_right:
- updated.setown(percolateConstants(updated, child, no_left));
- updated.setown(percolateConstants(updated, child, no_right));
- break;
- case childdataset_top_left_right:
- updated.setown(percolateConstants(updated, child, no_none));
- updated.setown(percolateConstants(updated, child, no_left));
- updated.setown(percolateConstants(updated, child, no_right));
- break;
- case childdataset_leftright:
- updated.setown(percolateConstants(updated, child, no_left));
- updated.setown(percolateConstants(updated, expr->queryChild(1), no_right));
- break;
- default:
- UNIMPLEMENTED;
- }
- break;
- }
- }
- return updated.getClear();
- }
- IHqlExpression * CExprFolderTransformer::createTransformed(IHqlExpression * expr)
- {
- if (foldOptions & HFOloseannotations)
- expr = expr->queryBody();
- //Special cases which don't want children to be transformed first...
- OwnedHqlExpr dft;
- node_operator op = expr->getOperator();
- switch (op)
- {
- case no_alias:
- {
- OwnedHqlExpr folded = transform(expr->queryChild(0));
- if (folded->getOperator() == no_alias || folded->queryValue())
- return folded.getClear();
- break;
- }
- case no_or:
- case no_and:
- {
- //Transform all children that do not match the operator - otherwise we get an n^2 (or n^3) algorithm
- HqlExprArray args, transformedArgs;
- expr->unwindList(args, op);
- bool same = true;
- ForEachItemIn(i, args)
- {
- IHqlExpression * cur = &args.item(i);
- IHqlExpression * t = transform(cur);
- transformedArgs.append(*t);
- if (t != cur)
- same = false;
- //Sort circuit always-true or always-false early to avoid subsequent transforms..
- IValue * value = t->queryValue();
- if (value)
- {
- if (value->getBoolValue())
- {
- if (op == no_or)
- return LINK(t);
- }
- else
- {
- if (op == no_and)
- return LINK(t);
- }
- }
- }
- if (same)
- dft.set(expr);
- else
- {
- //Need to preserve the no_and/no_or structure so that cse/alias opportunities aren't lost.
- //e.g. x := a and b; if (d and x and c, f(x), ...)
- dft.setown(createListMatchStructure(op, expr, transformedArgs));
- }
- break;
- }
- case no_if:
- {
- //transform this early - to short circuit lots of other work...
- OwnedHqlExpr child = transform(expr->queryChild(0));
- IValue * constValue = child->queryValue();
- if (constValue)
- {
- unsigned idx = constValue->getBoolValue() ? 1 : 2;
- IHqlExpression * branch = expr->queryChild(idx);
- OwnedHqlExpr ret;
- if (!branch)
- {
- assertex(expr->isAction());
- ret.setown(createValue(no_null, makeVoidType()));
- }
- else
- ret.setown(transform(branch));
- if (hasNamedSymbol(ret))
- return ret.getClear();
- return expr->cloneAllAnnotations(ret);
- }
- break;
- }
- case no_case:
- {
- OwnedHqlExpr leftExpr = transform(expr->queryChild(0));
- IValue * leftValue = leftExpr->queryValue();
- if (leftValue)
- {
- unsigned numCases = expr->numChildren()-2;
- IHqlExpression * result = expr->queryChild(numCases+1);
- for (unsigned idx = 1; idx <= numCases; idx++)
- {
- IHqlExpression * map = expr->queryChild(idx);
- OwnedHqlExpr grand = transform(map->queryChild(0));
- IValue * grandValue = grand->queryValue();
- if (grandValue)
- {
- if (orderValues(leftValue, grandValue) == 0)
- {
- result = map->queryChild(1);
- break;
- }
- }
- else
- {
- result = NULL;
- break;
- }
- }
- if (result)
- return cloneAnnotationAndTransform(expr, result);
- }
- break;
- }
- case no_map:
- {
- unsigned num = expr->numChildren()-1;
- IHqlExpression * result = expr->queryChild(num);
- for (unsigned idx = 0; idx < num; idx++)
- {
- IHqlExpression * map = expr->queryChild(idx);
- OwnedHqlExpr cond = transform(map->queryChild(0));
- IValue * value = cond->queryValue();
- if (value)
- {
- if (value->getBoolValue())
- {
- result = map->queryChild(1);
- break;
- }
- }
- else
- {
- result = NULL;
- break;
- }
- }
- if (result)
- return cloneAnnotationAndTransform(expr, result);
- break;
- }
- case no_call:
- {
- //Ensure the bodies of out of line function calls are also folded.
- IHqlExpression * funcDef = expr->queryDefinition();
- Owned<IHqlExpression> newFuncDef = transform(funcDef);
- if (funcDef != newFuncDef)
- {
- HqlExprArray children;
- transformChildren(expr, children);
- dft.setown(createReboundFunction(newFuncDef, children));
- }
- break;
- }
- }
- if (!dft)
- dft.setown(PARENT::createTransformed(expr));
- //If the parent has changed to no_null, then the active selector may have changed out of step with the parent dataset
- //so need to explcitly remap the dataset
- updateOrphanedSelectors(dft, expr);
- OwnedHqlExpr updated = (foldOptions & HFOpercolateconstants) ? percolateConstants(dft) : LINK(dft);
- OwnedHqlExpr transformed = doFoldTransformed(updated, expr);
- #ifdef LOG_ALL_FOLDING
- if ((op != transformed->getOperator()) || (expr->numChildren() != transformed->numChildren()))
- DBGLOG("Folding %s to %s", getOpString(updated->getOperator()), getOpString(transformed->getOperator()));
- #endif
- #ifdef _DEBUG
- if (expr->isConstant() && !transformed->queryValue())
- {
- ITypeInfo * type = expr->queryType();
- if (type && type->isScalar())
- {
- switch (op)
- {
- case no_none:
- case no_mapto:
- case no_negate:
- case no_comma:
- case no_assign:
- case no_assignall:
- case no_transform:
- case no_newtransform:
- break;
- /*
- case no_count:
- case no_max:
- case no_min:
- case no_sum:
- case no_exists:
- case no_ave:
- //Could implement this on a temp table, or at least count...
- //not sufficient to just fix these, because functions of these also fail.
- break;
- */
- #if 0
- default:
- {
- DBGLOG("Error - expression is marked as constant but did not fold");
- OwnedHqlExpr again = doFoldTransformed(dft, expr);
- StringBuffer s;
- expr->toString(s);
- DBGLOG("%s", s.str());
- throw MakeStringException(0, "Internal error - expression is marked as constant but did not fold");
- }
- #endif
- }
- }
- }
- #endif
- //No folding operation=>return
- if (transformed == dft)
- return transformed.getClear();
- //Just lost named symbol for some reason
- if (transformed->queryBody() == dft->queryBody())
- return LINK(dft);
- //Folded to a constant, or stripped parent node=>return
- if ((transformed->getOperator() == no_constant) || (transformed == dft->queryChild(0)))
- return transformed.getClear();
- return cloneAnnotationAndTransform(expr, transformed);
- }
- //Could handle NOT and OR, but generally makes it worse (it just messes up code generated for a few transforms)
- //Could also handle skip attributes on transforms.
- static void gatherConstantFilterMappings(HqlConstantPercolator & mappings, IHqlExpression * selector, IHqlExpression * expr)
- {
- switch (expr->getOperator())
- {
- case no_and:
- gatherConstantFilterMappings(mappings, selector, expr->queryChild(0));
- gatherConstantFilterMappings(mappings, selector, expr->queryChild(1));
- break;
- case no_eq:
- {
- IHqlExpression * lhs = expr->queryChild(0);
- //MORE: Should also handle subselects now that the constant percolator does
- if ((lhs->getOperator() != no_select) || isNewSelector(lhs) || lhs->queryChild(0) != selector)
- break;
- IHqlExpression * rhs = expr->queryChild(1);
- if (!rhs->isConstant())
- break;
- OwnedHqlExpr newSelect = createSelectExpr(LINK(mappings.querySelf()), LINK(lhs->queryChild(1)));
- mappings.addEquality(newSelect, rhs);
- break;
- }
- case no_assertkeyed:
- gatherConstantFilterMappings(mappings, selector, expr->queryChild(0));
- break;
- }
- }
- static void gatherConstantFilterMappings(HqlConstantPercolator & mappings, IHqlExpression * expr)
- {
- IHqlExpression * selector = expr->queryChild(0)->queryNormalizedSelector();
- ForEachChildFrom(i, expr, 1)
- {
- IHqlExpression * cur = expr->queryChild(i);
- if (!cur->isAttribute())
- gatherConstantFilterMappings(mappings, selector, cur);
- }
- }
- HqlConstantPercolator * CExprFolderTransformer::gatherConstants(IHqlExpression * expr)
- {
- FolderTransformInfo * extra = queryBodyExtra(expr);
- if (extra->queryGatheredConstants())
- return extra->mapping;
- //gather constants for this particular activity.....
- Owned<HqlConstantPercolator> exprMapping;
- switch (expr->getOperator())
- {
- //The following can tell nothing about the values they contain
- case no_table:
- case no_anon:
- case no_pseudods:
- case no_fail:
- case no_skip:
- case no_all:
- case no_activetable:
- case no_workunit_dataset:
- case no_getgraphresult:
- case no_getgraphloopresult:
- case no_getresult:
- case no_rows:
- case no_internalselect:
- case no_delayedselect:
- case no_unboundselect:
- case no_libraryselect:
- case no_purevirtual:
- case no_libraryinput:
- case no_translated:
- case no_id2blob:
- case no_embedbody:
- case no_pipe:
- case no_keyindex:
- case no_newkeyindex:
- case no_colon:
- case no_keyed:
- case no_nofold: // stop folding...
- case no_nohoist:
- case no_activerow:
- case no_newrow:
- case no_loop:
- case no_graphloop:
- case no_rowsetindex:
- case no_rowsetrange:
- case no_mergejoin:
- case no_nwaymerge:
- case no_temptable:
- case no_left:
- case no_right:
- case no_top:
- case no_externalcall:
- case no_call:
- case no_matchattr:
- case no_param:
- case no_deserialize:
- case no_serialize:
- case no_typetransfer:
- case no_fromxml:
- case no_fromjson:
- case no_httpcall:
- break;
- case no_null:
- //if (expr->isDatarow())
- // exprMapping.setown(HqlConstantPercolator::extractNullMapping(expr->queryRecord()));
- break;
- case no_rollup:
- {
- // transform may or may not be called, so can't just extract constants from the transform.
- // can only mark as constant if the inputDataset and the transform both assign them the same constant value
- IHqlExpression * dataset = expr->queryChild(0);
- IHqlExpression * transformExpr = queryNewColumnProvider(expr);
- OwnedHqlExpr invarientTransformExpr = percolateRollupInvariantConstants(transformExpr, dataset, no_left, querySelSeq(expr));
- exprMapping.setown(HqlConstantPercolator::extractConstantMapping(invarientTransformExpr));
- if (exprMapping)
- {
- HqlConstantPercolator * inputMapping = gatherConstants(dataset);
- if (inputMapping)
- exprMapping->intersectMapping(inputMapping);
- else
- exprMapping.clear();
- }
- break;
- }
- //The following get the values purely from the assocated transform - if it contains constant entires
- case no_xmlproject:
- case no_combine:
- case no_combinegroup:
- case no_process:
- case no_denormalize:
- case no_denormalizegroup:
- case no_fetch:
- case no_join:
- case no_selfjoin:
- case no_joincount:
- case no_iterate:
- case no_transformebcdic:
- case no_transformascii:
- case no_hqlproject:
- case no_normalize:
- case no_newparse:
- case no_newxmlparse:
- case no_rollupgroup:
- case no_soapcall:
- case no_newsoapcall:
- case no_soapcall_ds:
- case no_newsoapcall_ds:
- case no_parse:
- case no_xmlparse:
- case no_selectfields:
- case no_newaggregate:
- case no_newusertable:
- case no_usertable:
- case no_nwayjoin:
- case no_projectrow:
- case no_createrow:
- case no_dataset_from_transform:
- case no_quantile:
- {
- IHqlExpression * transform = queryNewColumnProvider(expr);
- exprMapping.setown(HqlConstantPercolator::extractConstantMapping(transform));
- break;
- }
- case no_aggregate:
- {
- if (expr->hasAttribute(mergeTransformAtom))
- break;
- IHqlExpression * transform = queryNewColumnProvider(expr);
- exprMapping.setown(HqlConstantPercolator::extractConstantMapping(transform));
- break;
- }
- case no_newtransform:
- case no_transform:
- exprMapping.setown(HqlConstantPercolator::extractConstantMapping(expr));
- break;
- //The following inherit the constant of the parent without modification
- case no_dedup:
- case no_group:
- case no_grouped:
- case no_assertgrouped:
- case no_distribute:
- case no_distributed:
- case no_unordered:
- case no_preservemeta:
- case no_assertdistributed:
- case no_keyeddistribute:
- case no_cosort:
- case no_sort:
- case no_subsort:
- case no_sorted:
- case no_assertsorted:
- case no_topn:
- case no_choosen:
- case no_choosesets:
- case no_enth:
- case no_sample:
- case no_alias_project:
- case no_alias_scope:
- case no_cachealias:
- case no_cloned:
- case no_globalscope:
- case no_sub:
- case no_thor:
- case no_nothor:
- case no_compound_indexread:
- case no_compound_diskread:
- case no_compound_disknormalize:
- case no_compound_diskaggregate:
- case no_compound_diskcount:
- case no_compound_diskgroupaggregate:
- case no_compound_indexnormalize:
- case no_compound_indexaggregate:
- case no_compound_indexcount:
- case no_compound_indexgroupaggregate:
- case no_compound_childread:
- case no_compound_childnormalize:
- case no_compound_childaggregate:
- case no_compound_childcount:
- case no_compound_childgroupaggregate:
- case no_compound_selectnew:
- case no_compound_inline:
- case no_metaactivity:
- case no_split:
- case no_spill:
- case no_readspill:
- case no_writespill:
- case no_commonspill:
- case no_throughaggregate:
- case no_limit:
- case no_keyedlimit:
- case no_compound_fetch:
- case no_preload:
- case no_alias:
- case no_assert_ds:
- case no_spillgraphresult:
- case no_forcenolocal:
- case no_allnodes:
- case no_thisnode:
- case no_forcelocal:
- case no_stepped:
- case no_cluster:
- case no_datasetfromrow:
- case no_datasetfromdictionary:
- case no_filtergroup:
- case no_section:
- case no_sectioninput:
- case no_forcegraph:
- case no_related:
- case no_executewhen:
- case no_callsideeffect:
- case no_outofline:
- case no_owned_ds:
- case no_dataset_alias:
- case no_createdictionary:
- case no_nocombine:
- case no_likely:
- case no_unlikely:
- exprMapping.set(gatherConstants(expr->queryChild(0)));
- break;
- case no_normalizegroup:
- exprMapping.set(gatherConstants(expr->queryChild(1)));
- break;
- case no_catchds:
- case no_catch:
- //all bets are off.
- break;
- case no_selectnth:
- {
- //Careful - this can create a null row if it is out of range.
- bool inherit = false;
- if (expr->hasAttribute(noBoundCheckAtom))
- inherit = true;
- else if (matchesConstantValue(expr->queryChild(1), 1) && hasSingleRow(expr->queryChild(0)))
- inherit = true;
- if (inherit)
- exprMapping.set(gatherConstants(expr->queryChild(0)));
- break;
- }
- //The following inherit the constant of second argument
- case no_comma:
- case no_compound:
- case no_mapto:
- exprMapping.set(gatherConstants(expr->queryChild(1)));
- break;
- //Intersections of the inputs...
- case no_if:
- {
- IHqlExpression * rhs = expr->queryChild(2);
- if (!rhs)
- break;
- //fall through
- }
- case no_addfiles:
- case no_regroup:
- case no_nonempty:
- case no_case:
- case no_map:
- case no_merge:
- case no_cogroup:
- case no_chooseds:
- {
- unsigned from = getFirstActivityArgument(expr);
- unsigned max = from + getNumActivityArguments(expr);
- bool allMapped = true;
- for (unsigned i=0; i < max; i++)
- {
- IHqlExpression * cur = expr->queryChild(i);
- if (cur->isDataset() && !gatherConstants(cur))
- {
- allMapped = false;
- break;
- }
- }
- if (allMapped)
- {
- for (unsigned i=0; i < max; i++)
- {
- IHqlExpression * cur = expr->queryChild(i);
- if (cur->isDataset())
- {
- HqlConstantPercolator * curMapping = gatherConstants(cur);
- if (!exprMapping)
- {
- exprMapping.setown(new HqlConstantPercolator);
- exprMapping->inheritMapping(curMapping);
- }
- else
- {
- exprMapping->intersectMapping(curMapping);
- if (exprMapping->empty())
- break;
- }
- }
- }
- }
- break;
- }
- //Now follow the special cases/
- case no_inlinetable:
- {
- IHqlExpression * transforms = expr->queryChild(0);
- unsigned numRows = transforms->numChildren();
- //MORE: Could theoretically create an intersection of the values, but not likely to be worth the processing time.
- if (numRows == 1)
- exprMapping.setown(HqlConstantPercolator::extractConstantMapping(transforms->queryChild(0)));
- break;
- }
- case no_filter:
- if (foldOptions & HFOpercolatefilters)
- {
- HqlConstantPercolator filterMappings(expr);
- gatherConstantFilterMappings(filterMappings, expr);
-
- HqlConstantPercolator * inheritedMappings = gatherConstants(expr->queryChild(0));
- if (!filterMappings.empty())
- {
- exprMapping.setown(new HqlConstantPercolator);
- if (inheritedMappings)
- exprMapping->inheritMapping(inheritedMappings);
- exprMapping->inheritMapping(&filterMappings);
- }
- else
- exprMapping.set(inheritedMappings);
- break;
- }
- else
- exprMapping.set(gatherConstants(expr->queryChild(0)));
- break;
- case no_selectmap:
- case no_select:
- case no_record:
- break;
- default:
- if (expr->isAction())
- break;
- DBGLOG("Missing entry: %s", getOpString(expr->getOperator()));
- if (expr->isDatarow())
- {
- DBGLOG("Missing entry: %s", getOpString(expr->getOperator()));
- break;
- }
- throwUnexpectedOp(expr->getOperator());
- }
- if (exprMapping)
- {
- IHqlExpression * onFail = expr->queryAttribute(onFailAtom);
- if (onFail)
- {
- HqlConstantPercolator * onFailMapping = gatherConstants(onFail->queryChild(0));
- if (onFailMapping)
- exprMapping->intersectMapping(onFailMapping);
- else
- exprMapping.clear();
- }
- if (exprMapping && !exprMapping->empty())
- extra->mapping = exprMapping.getClear();
- }
- extra->setGatheredConstants(true);
- return extra->mapping;
- }
- IHqlExpression * CExprFolderTransformer::percolateConstants(HqlConstantPercolator * mapping, IHqlExpression * expr, IHqlExpression * dataset, node_operator side)
- {
- OwnedHqlExpr selector = (side == no_none) ? LINK(dataset->queryNormalizedSelector()) : createSelector(side, dataset, querySelSeq(expr));
- return mapping->expandFields(expr, selector);
- }
- IHqlExpression * CExprFolderTransformer::percolateConstants(IHqlExpression * expr, IHqlExpression * dataset, node_operator side)
- {
- OwnedHqlExpr transformedDs = transform(dataset);
- HqlConstantPercolator * mapping = gatherConstants(transformedDs);
- if (!mapping)
- return LINK(expr);
- unsigned from = getNumChildTables(expr);
- unsigned max = expr->numChildren();
- if (from >= max)
- return LINK(expr);
- HqlExprArray temp, args;
- unwindChildren(temp, expr, from);
- for (unsigned i=0; i < from; i++)
- args.append(*LINK(expr->queryChild(i)));
- OwnedHqlExpr selector = (side == no_none) ? LINK(dataset->queryNormalizedSelector()) : createSelector(side, dataset, querySelSeq(expr));
- if (mapping->expandFields(args, temp, selector))
- return expr->clone(args);
- return LINK(expr);
- }
- IHqlExpression * CExprFolderTransformer::percolateRollupInvariantConstants(IHqlExpression * expr, HqlConstantPercolator * mapping, IHqlExpression * selector)
- {
- switch (expr->getOperator())
- {
- case no_newtransform:
- case no_transform:
- case no_assignall:
- {
- HqlExprArray children;
- ForEachChild(i, expr)
- children.append(*percolateRollupInvariantConstants(expr->queryChild(i), mapping, selector));
- return cloneOrLink(expr, children);
- }
- break;
- case no_assign:
- {
- //If the assignment is self.x := left.x, then it is ok to percolate the constant in from input dataset.
- IHqlExpression * rhs = expr->queryChild(1);
- if (rhs->getOperator() == no_select)
- {
- if (rhs->queryChild(0) == selector)
- {
- IHqlExpression * lhs = expr->queryChild(0);
- IHqlExpression * field = lhs->queryChild(1);
- if (field == rhs->queryChild(1))
- {
- OwnedHqlExpr mapped = mapping->expandField(field);
- if (mapped)
- return createAssign(LINK(lhs), mapped.getClear());
- }
- }
- }
- break;
- }
- }
- return LINK(expr);
- }
- IHqlExpression * CExprFolderTransformer::percolateRollupInvariantConstants(IHqlExpression * expr, IHqlExpression * dataset, node_operator side, IHqlExpression * selSeq)
- {
- HqlConstantPercolator * mapping = gatherConstants(dataset);
- if (!mapping)
- return LINK(expr);
- OwnedHqlExpr selector = (side == no_none) ? LINK(dataset->queryNormalizedSelector()) : createSelector(side, dataset, selSeq);
- return percolateRollupInvariantConstants(expr, mapping, selector);
- }
- //Expand a single child (used for joins)
- IHqlExpression * CExprFolderTransformer::percolateConstants(IHqlExpression * expr, IHqlExpression * dataset, node_operator side, unsigned whichChild)
- {
- OwnedHqlExpr transformedDs = transform(dataset);
- HqlConstantPercolator * mapping = gatherConstants(transformedDs);
- if (!mapping)
- return LINK(expr);
- OwnedHqlExpr selector = (side == no_none) ? LINK(dataset->queryNormalizedSelector()) : createSelector(side, dataset, querySelSeq(expr));
- HqlExprArray args;
- unwindChildren(args, expr);
- args.replace(*mapping->expandFields(&args.item(whichChild), selector), whichChild);
- return expr->clone(args);
- }
- IHqlExpression * CExprFolderTransformer::removeParentNode(IHqlExpression * expr)
- {
- IHqlExpression * child = expr->queryChild(0);
- DBGLOG("Folder: Node %s remove self (now %s)", queryNode0Text(expr), queryNode1Text(child));
- return LINK(child);
- }
- IHqlExpression * CExprFolderTransformer::replaceWithNull(IHqlExpression * expr)
- {
- IHqlExpression * ret = createNullExpr(expr);
- DBGLOG("Folder: Replace %s with %s", queryNode0Text(expr), queryNode1Text(ret));
- return ret;
- }
- IHqlExpression * CExprFolderTransformer::replaceWithNullRow(IHqlExpression * expr)
- {
- IHqlExpression * ret = createRow(no_null, LINK(expr->queryRecord()));
- DBGLOG("Folder: Replace %s with %s", queryNode0Text(expr), queryNode1Text(ret));
- return ret;
- }
- IHqlExpression * CExprFolderTransformer::replaceWithNullRowDs(IHqlExpression * expr)
- {
- assertex(!isGrouped(expr));
- return createDatasetFromRow(createRow(no_null, LINK(expr->queryRecord())));
- }
- IHqlExpression * CExprFolderTransformer::transformExpanded(IHqlExpression * expr)
- {
- return transform(expr);
- }
- //---------------------------------------------------------------------------
- IHqlExpression * foldHqlExpression(IHqlExpression * expr)
- {
- NullErrorReceiver errorProcessor;
- return foldHqlExpression(errorProcessor, expr);
- }
- IHqlExpression * foldHqlExpression(IErrorReceiver & errorProcessor, IHqlExpression * expr, ITemplateContext *templateContext, unsigned foldOptions)
- {
- if (!expr)
- return NULL;
- if (foldOptions & HFOloseannotations)
- expr = expr->queryBody();
- switch (expr->getOperator())
- {
- case no_constant:
- case no_param:
- case no_variable:
- case no_attr:
- return LINK(expr);
- case no_select:
- if (!isNewSelector(expr))
- return LINK(expr);
- break;
- }
- CExprFolderTransformer folder(errorProcessor, templateContext, foldOptions);
- #if 0
- dbglogExpr(expr);
- #endif
- IHqlExpression * ret = folder.transformRoot(expr);
- #if 0
- dbglogExpr(ret);
- #endif
- return ret;
- }
- IHqlExpression * foldScopedHqlExpression(IErrorReceiver & errorProcessor, IHqlExpression * dataset, IHqlExpression * expr, unsigned foldOptions)
- {
- if (!expr)
- return NULL;
- CExprFolderTransformer folder(errorProcessor, NULL, foldOptions);
- if (dataset)
- folder.setScope(dataset);
- IHqlExpression * ret = folder.transformRoot(expr);
- return ret;
- }
- void foldHqlExpression(IErrorReceiver & errorProcessor, HqlExprArray & tgt, HqlExprArray & src, unsigned foldOptions)
- {
- CExprFolderTransformer folder(errorProcessor, NULL, foldOptions);
- folder.transformRoot(src, tgt);
- }
- //Fold it to a constant if it is easy to otherwise return anything.
- IHqlExpression * foldExprIfConstant(IHqlExpression * expr)
- {
- if (expr->isConstant())
- return quickFoldExpression(expr);
- node_operator op = expr->getOperator();
- switch (op)
- {
- case no_and:
- // case no_or:
- {
- ForEachChild(i, expr)
- {
- IHqlExpression * cur = expr->queryChild(i);
- OwnedHqlExpr folded = foldExprIfConstant(cur);
- IValue * foldedValue = folded->queryValue();
- if (foldedValue)
- {
- bool ok = foldedValue->getBoolValue();
- if (op == no_and ? !ok : ok)
- return folded.getClear();
- }
- else
- return LINK(expr);
- }
- return createConstant(op == no_and);
- }
- case no_not:
- {
- OwnedHqlExpr folded = foldExprIfConstant(expr->queryChild(0));
- if (folded->queryValue())
- return getInverse(folded);
- break;
- }
- }
- return LINK(expr);
- }
- //---------------------------------------------------------------------------
- static HqlTransformerInfo lowerCaseTransformerInfo("LowerCaseTransformer");
- class LowerCaseTransformer : public NewHqlTransformer
- {
- public:
- LowerCaseTransformer() : NewHqlTransformer(lowerCaseTransformerInfo) {}
- virtual IHqlExpression * createTransformed(IHqlExpression * expr)
- {
- switch (expr->getOperator())
- {
- case no_constant:
- return getLowerCaseConstant(expr);
- }
- return NewHqlTransformer::createTransformed(expr);
- }
- };
- IHqlExpression * lowerCaseHqlExpr(IHqlExpression * expr)
- {
- if (expr->getOperator() == no_constant)
- return getLowerCaseConstant(expr);
- LowerCaseTransformer transformer;
- return transformer.transformRoot(expr);
- }
- static HqlTransformerInfo quickConstantTransformerInfo("QuickConstantTransformer");
- class QuickConstantTransformer : public QuickHqlTransformer
- {
- public:
- QuickConstantTransformer(ITemplateContext * _templateContext, unsigned _foldOptions) :
- QuickHqlTransformer(quickConstantTransformerInfo, NULL), templateContext(_templateContext), foldOptions(_foldOptions) {}
- virtual IHqlExpression * createTransformedBody(IHqlExpression * expr)
- {
- node_operator op = expr->getOperator();
- switch (op)
- {
- case no_if:
- {
- OwnedHqlExpr cond = transform(expr->queryChild(0));
- IValue * condValue = cond->queryValue();
- if (condValue)
- {
- unsigned idx = condValue->getBoolValue() ? 1 : 2;
- IHqlExpression * branch = expr->queryChild(idx);
- if (branch)
- return transform(branch);
- assertex(expr->isAction());
- return createValue(no_null, makeVoidType());
- }
- break;
- }
- case no_and:
- {
- OwnedHqlExpr left = transform(expr->queryChild(0));
- IValue * leftValue = left->queryValue();
- if (leftValue)
- {
- if (!leftValue->getBoolValue())
- return LINK(left);
- return transform(expr->queryChild(1));
- }
- break;
- }
- case no_or:
- {
- OwnedHqlExpr left = transform(expr->queryChild(0));
- IValue * leftValue = left->queryValue();
- if (leftValue)
- {
- if (leftValue->getBoolValue())
- return LINK(left);
- return transform(expr->queryChild(1));
- }
- break;
- }
- case no_attr:
- if (expr->queryName() == _original_Atom)
- return LINK(expr);
- break;
- }
- OwnedHqlExpr transformed = QuickHqlTransformer::createTransformedBody(expr);
- return foldConstantOperator(transformed, foldOptions, templateContext);
- }
- protected:
- ITemplateContext *templateContext;
- unsigned foldOptions;
- };
- extern HQLFOLD_API IHqlExpression * quickFoldExpression(IHqlExpression * expr, ITemplateContext *context, unsigned options)
- {
- QuickConstantTransformer transformer(context, options);
- return transformer.transform(expr);
- }
- extern HQLFOLD_API void quickFoldExpressions(HqlExprArray & target, const HqlExprArray & source, ITemplateContext *context, unsigned options)
- {
- QuickConstantTransformer transformer(context, options);
- ForEachItemIn(i, source)
- target.append(*transformer.transform(&source.item(i)));
- }
- //--------------------------------------------------------------------------------------------------------------------
- static bool valueInList(IHqlExpression * search, IHqlExpression * list)
- {
- if (list->getOperator() != no_list)
- return false;
- ForEachChild(i, list)
- {
- if (search == list->queryChild(i))
- return true;
- }
- return false;
- }
- static bool valueNotInList(IHqlExpression * search, IHqlExpression * list)
- {
- if (list->getOperator() != no_list)
- return false;
- IValue * value = search->queryValue();
- if (!value)
- return false; // can't tell
- ForEachChild(i, list)
- {
- IHqlExpression * cur = list->queryChild(i);
- IValue * curValue = cur->queryValue();
- if (!curValue || value == curValue)
- return false;
- }
- return true;
- }
- //Is it guaranteed that both conditions cannot be true at the same time. Avoid false positivies.
- //Don't try and catch all examples, just the most common possibilities.
- //This could be improved over time....
- extern HQLFOLD_API bool areExclusiveConditions(IHqlExpression * left, IHqlExpression * right)
- {
- node_operator leftOp = left->getOperator();
- node_operator rightOp = right->getOperator();
- // Check for x = constant1, x = constant2, constant1 != constant2
- if ((leftOp == no_eq) && (rightOp == no_eq))
- {
- if (left->queryChild(0) == right->queryChild(0))
- {
- //Can only really be sure if comparing against constants.
- IValue * leftValue = left->queryChild(1)->queryValue();
- IValue * rightValue = right->queryChild(1)->queryValue();
- return (leftValue && rightValue && leftValue != rightValue);
- }
- return false;
- }
- // Check for NOT x, x
- if ((leftOp == no_not) && (left->queryChild(0) == right))
- return true;
- // Check for x, NOT x
- if ((rightOp == no_not) && (right->queryChild(0) == left))
- return true;
- // two tests against the same condition (could also pass in here if both NULL..)
- if (left->queryChild(0) == right->queryChild(0))
- {
- // Check for x <op> y, x !<op> y - no need for y to be a constant.
- if (leftOp == getInverseOp(rightOp))
- {
- return left->queryChild(1) == right->queryChild(1);
- }
- // Unusual, but occured in the main example I was trying to improve
- // x = c1, x not in [c1, ....]
- // x = c1, x in [c2, c3, c4, c5]
- if ((leftOp == no_eq) && (rightOp == no_notin))
- return valueInList(left->queryChild(1), right->queryChild(1));
- if ((leftOp == no_eq) && (rightOp == no_in))
- return valueNotInList(left->queryChild(1), right->queryChild(1));
- if ((rightOp == no_eq) && (leftOp == no_notin))
- return valueInList(right->queryChild(1), left->queryChild(1));
- if ((rightOp == no_eq) && (leftOp == no_in))
- return valueNotInList(right->queryChild(1), left->queryChild(1));
- }
- return false;
- }
- bool queryCompareConstantValues(int & result, IHqlExpression * left, IHqlExpression * right)
- {
- IValue * leftValue = left->queryValue();
- IValue * rightValue = right->queryValue();
- if (!leftValue || !rightValue)
- return false;
- ITypeInfo * leftType = left->queryType();
- ITypeInfo * rightType = right->queryType();
- if (leftType != rightType)
- {
- Owned<ITypeInfo> type = ::getPromotedECLCompareType(leftType, rightType);
- OwnedHqlExpr castLeft = ensureExprType(left, type);
- OwnedHqlExpr castRight = ensureExprType(right, type);
- IValue * castLeftValue = castLeft->queryValue();
- IValue * castRightValue = castRight->queryValue();
- if (!castLeftValue || !castRightValue)
- return false;
- result = castLeftValue->compare(castRightValue);
- return true;
- }
- else
- {
- result = leftValue->compare(rightValue);
- return true;
- }
- }
- IHqlExpression * foldConstantCaseExpr(IHqlExpression * expr)
- {
- IHqlExpression * search = expr->queryChild(0);
- if (!search->isConstant())
- return LINK(expr);
- OwnedHqlExpr foldedSearch = foldHqlExpression(search);
- ForEachChildFrom(i, expr, 1)
- {
- IHqlExpression * cur = expr->queryChild(i);
- if (cur->getOperator() == no_mapto)
- {
- IHqlExpression * mapValue = cur->queryChild(0);
- if (!mapValue->isConstant())
- return LINK(expr);
- OwnedHqlExpr foldedValue = foldHqlExpression(mapValue);
- int result;
- if (!queryCompareConstantValues(result, foldedSearch, foldedValue))
- return LINK(expr);
- if (result == 0)
- return LINK(cur->queryChild(1));
- }
- else if (!cur->isAttribute())
- return LINK(cur);
- }
- return LINK(expr);
- }
- IHqlExpression * foldConstantMapExpr(IHqlExpression * expr)
- {
- ForEachChild(i, expr)
- {
- IHqlExpression * cur = expr->queryChild(i);
- if (cur->getOperator() == no_mapto)
- {
- IHqlExpression * mapValue = cur->queryChild(0);
- if (!mapValue->isConstant())
- return LINK(expr);
- OwnedHqlExpr foldedValue = foldHqlExpression(mapValue);
- IValue * value = foldedValue->queryValue();
- if (!value)
- return LINK(expr);
- if (value->getBoolValue())
- return LINK(cur->queryChild(1));
- }
- else if (!cur->isAttribute())
- return LINK(cur);
- }
- return LINK(expr);
- }
|