1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196 |
- /*
- ** 2011-08-14
- **
- ** The author disclaims copyright to this source code. In place of
- ** a legal notice, here is a blessing:
- **
- ** May you do good and not evil.
- ** May you find forgiveness for yourself and forgive others.
- ** May you share freely, never taking more than you give.
- **
- *************************************************************************
- **
- ** PAGE FORMAT:
- **
- ** The maximum page size is 65536 bytes.
- **
- ** Since all records are equal to or larger than 2 bytes in size, and
- ** some space within the page is consumed by the page footer, there must
- ** be less than 2^15 records on each page.
- **
- ** Each page ends with a footer that describes the pages contents. This
- ** footer serves as similar purpose to the page header in an SQLite database.
- ** A footer is used instead of a header because it makes it easier to
- ** populate a new page based on a sorted list of key/value pairs.
- **
- ** The footer consists of the following values (starting at the end of
- ** the page and continuing backwards towards the start). All values are
- ** stored as unsigned big-endian integers.
- **
- ** * Number of records on page (2 bytes).
- ** * Flags field (2 bytes).
- ** * Left-hand pointer value (8 bytes).
- ** * The starting offset of each record (2 bytes per record).
- **
- ** Records may span pages. Unless it happens to be an exact fit, the part
- ** of the final record that starts on page X that does not fit on page X
- ** is stored at the start of page (X+1). This means there may be pages where
- ** (N==0). And on most pages the first record that starts on the page will
- ** not start at byte offset 0. For example:
- **
- ** aaaaa bbbbb ccc <footer> cc eeeee fffff g <footer> gggg....
- **
- ** RECORD FORMAT:
- **
- ** The first byte of the record is a flags byte. It is a combination
- ** of the following flags (defined in lsmInt.h):
- **
- ** LSM_START_DELETE
- ** LSM_END_DELETE
- ** LSM_POINT_DELETE
- ** LSM_INSERT
- ** LSM_SEPARATOR
- ** LSM_SYSTEMKEY
- **
- ** Immediately following the type byte is a pointer to the smallest key
- ** in the next file that is larger than the key in the current record. The
- ** pointer is encoded as a varint. When added to the 32-bit page number
- ** stored in the footer, it is the page number of the page that contains the
- ** smallest key in the next sorted file that is larger than this key.
- **
- ** Next is the number of bytes in the key, encoded as a varint.
- **
- ** If the LSM_INSERT flag is set, the number of bytes in the value, as
- ** a varint, is next.
- **
- ** Finally, the blob of data containing the key, and for LSM_INSERT
- ** records, the value as well.
- */
- #ifndef _LSM_INT_H
- # include "lsmInt.h"
- #endif
- #define LSM_LOG_STRUCTURE 0
- #define LSM_LOG_DATA 0
- /*
- ** Macros to help decode record types.
- */
- #define rtTopic(eType) ((eType) & LSM_SYSTEMKEY)
- #define rtIsDelete(eType) (((eType) & 0x0F)==LSM_POINT_DELETE)
- #define rtIsSeparator(eType) (((eType) & LSM_SEPARATOR)!=0)
- #define rtIsWrite(eType) (((eType) & LSM_INSERT)!=0)
- #define rtIsSystem(eType) (((eType) & LSM_SYSTEMKEY)!=0)
- /*
- ** The following macros are used to access a page footer.
- */
- #define SEGMENT_NRECORD_OFFSET(pgsz) ((pgsz) - 2)
- #define SEGMENT_FLAGS_OFFSET(pgsz) ((pgsz) - 2 - 2)
- #define SEGMENT_POINTER_OFFSET(pgsz) ((pgsz) - 2 - 2 - 8)
- #define SEGMENT_CELLPTR_OFFSET(pgsz, iCell) ((pgsz) - 2 - 2 - 8 - 2 - (iCell)*2)
- #define SEGMENT_EOF(pgsz, nEntry) SEGMENT_CELLPTR_OFFSET(pgsz, nEntry-1)
- #define SEGMENT_BTREE_FLAG 0x0001
- #define PGFTR_SKIP_NEXT_FLAG 0x0002
- #define PGFTR_SKIP_THIS_FLAG 0x0004
- #ifndef LSM_SEGMENTPTR_FREE_THRESHOLD
- # define LSM_SEGMENTPTR_FREE_THRESHOLD 1024
- #endif
- typedef struct SegmentPtr SegmentPtr;
- typedef struct LsmBlob LsmBlob;
- struct LsmBlob {
- lsm_env *pEnv;
- void *pData;
- int nData;
- int nAlloc;
- };
- /*
- ** A SegmentPtr object may be used for one of two purposes:
- **
- ** * To iterate and/or seek within a single Segment (the combination of a
- ** main run and an optional sorted run).
- **
- ** * To iterate through the separators array of a segment.
- */
- struct SegmentPtr {
- Level *pLevel; /* Level object segment is part of */
- Segment *pSeg; /* Segment to access */
- /* Current page. See segmentPtrLoadPage(). */
- Page *pPg; /* Current page */
- u16 flags; /* Copy of page flags field */
- int nCell; /* Number of cells on pPg */
- LsmPgno iPtr; /* Base cascade pointer */
- /* Current cell. See segmentPtrLoadCell() */
- int iCell; /* Current record within page pPg */
- int eType; /* Type of current record */
- LsmPgno iPgPtr; /* Cascade pointer offset */
- void *pKey; int nKey; /* Key associated with current record */
- void *pVal; int nVal; /* Current record value (eType==WRITE only) */
- /* Blobs used to allocate buffers for pKey and pVal as required */
- LsmBlob blob1;
- LsmBlob blob2;
- };
- /*
- ** Used to iterate through the keys stored in a b-tree hierarchy from start
- ** to finish. Only First() and Next() operations are required.
- **
- ** btreeCursorNew()
- ** btreeCursorFirst()
- ** btreeCursorNext()
- ** btreeCursorFree()
- ** btreeCursorPosition()
- ** btreeCursorRestore()
- */
- typedef struct BtreePg BtreePg;
- typedef struct BtreeCursor BtreeCursor;
- struct BtreePg {
- Page *pPage;
- int iCell;
- };
- struct BtreeCursor {
- Segment *pSeg; /* Iterate through this segments btree */
- FileSystem *pFS; /* File system to read pages from */
- int nDepth; /* Allocated size of aPg[] */
- int iPg; /* Current entry in aPg[]. -1 -> EOF. */
- BtreePg *aPg; /* Pages from root to current location */
- /* Cache of current entry. pKey==0 for EOF. */
- void *pKey;
- int nKey;
- int eType;
- LsmPgno iPtr;
- /* Storage for key, if not local */
- LsmBlob blob;
- };
- /*
- ** A cursor used for merged searches or iterations through up to one
- ** Tree structure and any number of sorted files.
- **
- ** lsmMCursorNew()
- ** lsmMCursorSeek()
- ** lsmMCursorNext()
- ** lsmMCursorPrev()
- ** lsmMCursorFirst()
- ** lsmMCursorLast()
- ** lsmMCursorKey()
- ** lsmMCursorValue()
- ** lsmMCursorValid()
- **
- ** iFree:
- ** This variable is only used by cursors providing input data for a
- ** new top-level segment. Such cursors only ever iterate forwards, not
- ** backwards.
- */
- struct MultiCursor {
- lsm_db *pDb; /* Connection that owns this cursor */
- MultiCursor *pNext; /* Next cursor owned by connection pDb */
- int flags; /* Mask of CURSOR_XXX flags */
- int eType; /* Cache of current key type */
- LsmBlob key; /* Cache of current key (or NULL) */
- LsmBlob val; /* Cache of current value */
- /* All the component cursors: */
- TreeCursor *apTreeCsr[2]; /* Up to two tree cursors */
- int iFree; /* Next element of free-list (-ve for eof) */
- SegmentPtr *aPtr; /* Array of segment pointers */
- int nPtr; /* Size of array aPtr[] */
- BtreeCursor *pBtCsr; /* b-tree cursor (db writes only) */
- /* Comparison results */
- int nTree; /* Size of aTree[] array */
- int *aTree; /* Array of comparison results */
- /* Used by cursors flushing the in-memory tree only */
- void *pSystemVal; /* Pointer to buffer to free */
- /* Used by worker cursors only */
- LsmPgno *pPrevMergePtr;
- };
- /*
- ** The following constants are used to assign integers to each component
- ** cursor of a multi-cursor.
- */
- #define CURSOR_DATA_TREE0 0 /* Current tree cursor (apTreeCsr[0]) */
- #define CURSOR_DATA_TREE1 1 /* The "old" tree, if any (apTreeCsr[1]) */
- #define CURSOR_DATA_SYSTEM 2 /* Free-list entries (new-toplevel only) */
- #define CURSOR_DATA_SEGMENT 3 /* First segment pointer (aPtr[0]) */
- /*
- ** CURSOR_IGNORE_DELETE
- ** If set, this cursor will not visit SORTED_DELETE keys.
- **
- ** CURSOR_FLUSH_FREELIST
- ** This cursor is being used to create a new toplevel. It should also
- ** iterate through the contents of the in-memory free block list.
- **
- ** CURSOR_IGNORE_SYSTEM
- ** If set, this cursor ignores system keys.
- **
- ** CURSOR_NEXT_OK
- ** Set if it is Ok to call lsm_csr_next().
- **
- ** CURSOR_PREV_OK
- ** Set if it is Ok to call lsm_csr_prev().
- **
- ** CURSOR_READ_SEPARATORS
- ** Set if this cursor should visit the separator keys in segment
- ** aPtr[nPtr-1].
- **
- ** CURSOR_SEEK_EQ
- ** Cursor has undergone a successful lsm_csr_seek(LSM_SEEK_EQ) operation.
- ** The key and value are stored in MultiCursor.key and MultiCursor.val
- ** respectively.
- */
- #define CURSOR_IGNORE_DELETE 0x00000001
- #define CURSOR_FLUSH_FREELIST 0x00000002
- #define CURSOR_IGNORE_SYSTEM 0x00000010
- #define CURSOR_NEXT_OK 0x00000020
- #define CURSOR_PREV_OK 0x00000040
- #define CURSOR_READ_SEPARATORS 0x00000080
- #define CURSOR_SEEK_EQ 0x00000100
- typedef struct MergeWorker MergeWorker;
- typedef struct Hierarchy Hierarchy;
- struct Hierarchy {
- Page **apHier;
- int nHier;
- };
- /*
- ** aSave:
- ** When mergeWorkerNextPage() is called to advance to the next page in
- ** the output segment, if the bStore flag for an element of aSave[] is
- ** true, it is cleared and the corresponding iPgno value is set to the
- ** page number of the page just completed.
- **
- ** aSave[0] is used to record the pointer value to be pushed into the
- ** b-tree hierarchy. aSave[1] is used to save the page number of the
- ** page containing the indirect key most recently written to the b-tree.
- ** see mergeWorkerPushHierarchy() for details.
- */
- struct MergeWorker {
- lsm_db *pDb; /* Database handle */
- Level *pLevel; /* Worker snapshot Level being merged */
- MultiCursor *pCsr; /* Cursor to read new segment contents from */
- int bFlush; /* True if this is an in-memory tree flush */
- Hierarchy hier; /* B-tree hierarchy under construction */
- Page *pPage; /* Current output page */
- int nWork; /* Number of calls to mergeWorkerNextPage() */
- LsmPgno *aGobble; /* Gobble point for each input segment */
- LsmPgno iIndirect;
- struct SavedPgno {
- LsmPgno iPgno;
- int bStore;
- } aSave[2];
- };
- #ifdef LSM_DEBUG_EXPENSIVE
- static int assertPointersOk(lsm_db *, Segment *, Segment *, int);
- static int assertBtreeOk(lsm_db *, Segment *);
- static void assertRunInOrder(lsm_db *pDb, Segment *pSeg);
- #else
- #define assertRunInOrder(x,y)
- #define assertBtreeOk(x,y)
- #endif
- struct FilePage { u8 *aData; int nData; };
- static u8 *fsPageData(Page *pPg, int *pnData){
- *pnData = ((struct FilePage *)(pPg))->nData;
- return ((struct FilePage *)(pPg))->aData;
- }
- /*UNUSED static u8 *fsPageDataPtr(Page *pPg){
- return ((struct FilePage *)(pPg))->aData;
- }*/
- /*
- ** Write nVal as a 16-bit unsigned big-endian integer into buffer aOut.
- */
- void lsmPutU16(u8 *aOut, u16 nVal){
- aOut[0] = (u8)((nVal>>8) & 0xFF);
- aOut[1] = (u8)(nVal & 0xFF);
- }
- void lsmPutU32(u8 *aOut, u32 nVal){
- aOut[0] = (u8)((nVal>>24) & 0xFF);
- aOut[1] = (u8)((nVal>>16) & 0xFF);
- aOut[2] = (u8)((nVal>> 8) & 0xFF);
- aOut[3] = (u8)((nVal ) & 0xFF);
- }
- int lsmGetU16(u8 *aOut){
- return (aOut[0] << 8) + aOut[1];
- }
- u32 lsmGetU32(u8 *aOut){
- return ((u32)aOut[0] << 24)
- + ((u32)aOut[1] << 16)
- + ((u32)aOut[2] << 8)
- + ((u32)aOut[3]);
- }
- u64 lsmGetU64(u8 *aOut){
- return ((u64)aOut[0] << 56)
- + ((u64)aOut[1] << 48)
- + ((u64)aOut[2] << 40)
- + ((u64)aOut[3] << 32)
- + ((u64)aOut[4] << 24)
- + ((u32)aOut[5] << 16)
- + ((u32)aOut[6] << 8)
- + ((u32)aOut[7]);
- }
- void lsmPutU64(u8 *aOut, u64 nVal){
- aOut[0] = (u8)((nVal>>56) & 0xFF);
- aOut[1] = (u8)((nVal>>48) & 0xFF);
- aOut[2] = (u8)((nVal>>40) & 0xFF);
- aOut[3] = (u8)((nVal>>32) & 0xFF);
- aOut[4] = (u8)((nVal>>24) & 0xFF);
- aOut[5] = (u8)((nVal>>16) & 0xFF);
- aOut[6] = (u8)((nVal>> 8) & 0xFF);
- aOut[7] = (u8)((nVal ) & 0xFF);
- }
- static int sortedBlobGrow(lsm_env *pEnv, LsmBlob *pBlob, int nData){
- assert( pBlob->pEnv==pEnv || (pBlob->pEnv==0 && pBlob->pData==0) );
- if( pBlob->nAlloc<nData ){
- pBlob->pData = lsmReallocOrFree(pEnv, pBlob->pData, nData);
- if( !pBlob->pData ) return LSM_NOMEM_BKPT;
- pBlob->nAlloc = nData;
- pBlob->pEnv = pEnv;
- }
- return LSM_OK;
- }
- static int sortedBlobSet(lsm_env *pEnv, LsmBlob *pBlob, void *pData, int nData){
- if( sortedBlobGrow(pEnv, pBlob, nData) ) return LSM_NOMEM;
- memcpy(pBlob->pData, pData, nData);
- pBlob->nData = nData;
- return LSM_OK;
- }
- #if 0
- static int sortedBlobCopy(LsmBlob *pDest, LsmBlob *pSrc){
- return sortedBlobSet(pDest, pSrc->pData, pSrc->nData);
- }
- #endif
- static void sortedBlobFree(LsmBlob *pBlob){
- assert( pBlob->pEnv || pBlob->pData==0 );
- if( pBlob->pData ) lsmFree(pBlob->pEnv, pBlob->pData);
- memset(pBlob, 0, sizeof(LsmBlob));
- }
- static int sortedReadData(
- Segment *pSeg,
- Page *pPg,
- int iOff,
- int nByte,
- void **ppData,
- LsmBlob *pBlob
- ){
- int rc = LSM_OK;
- int iEnd;
- int nData;
- int nCell;
- u8 *aData;
- aData = fsPageData(pPg, &nData);
- nCell = lsmGetU16(&aData[SEGMENT_NRECORD_OFFSET(nData)]);
- iEnd = SEGMENT_EOF(nData, nCell);
- assert( iEnd>0 && iEnd<nData );
- if( iOff+nByte<=iEnd ){
- *ppData = (void *)&aData[iOff];
- }else{
- int nRem = nByte;
- int i = iOff;
- u8 *aDest;
- /* Make sure the blob is big enough to store the value being loaded. */
- rc = sortedBlobGrow(lsmPageEnv(pPg), pBlob, nByte);
- if( rc!=LSM_OK ) return rc;
- pBlob->nData = nByte;
- aDest = (u8 *)pBlob->pData;
- *ppData = pBlob->pData;
- /* Increment the pointer pages ref-count. */
- lsmFsPageRef(pPg);
- while( rc==LSM_OK ){
- Page *pNext;
- int flags;
- /* Copy data from pPg into the output buffer. */
- int nCopy = LSM_MIN(nRem, iEnd-i);
- if( nCopy>0 ){
- memcpy(&aDest[nByte-nRem], &aData[i], nCopy);
- nRem -= nCopy;
- i += nCopy;
- assert( nRem==0 || i==iEnd );
- }
- assert( nRem>=0 );
- if( nRem==0 ) break;
- i -= iEnd;
- /* Grab the next page in the segment */
- do {
- rc = lsmFsDbPageNext(pSeg, pPg, 1, &pNext);
- if( rc==LSM_OK && pNext==0 ){
- rc = LSM_CORRUPT_BKPT;
- }
- if( rc ) break;
- lsmFsPageRelease(pPg);
- pPg = pNext;
- aData = fsPageData(pPg, &nData);
- flags = lsmGetU16(&aData[SEGMENT_FLAGS_OFFSET(nData)]);
- }while( flags&SEGMENT_BTREE_FLAG );
- iEnd = SEGMENT_EOF(nData, lsmGetU16(&aData[nData-2]));
- assert( iEnd>0 && iEnd<nData );
- }
- lsmFsPageRelease(pPg);
- }
- return rc;
- }
- static int pageGetNRec(u8 *aData, int nData){
- return (int)lsmGetU16(&aData[SEGMENT_NRECORD_OFFSET(nData)]);
- }
- static LsmPgno pageGetPtr(u8 *aData, int nData){
- return (LsmPgno)lsmGetU64(&aData[SEGMENT_POINTER_OFFSET(nData)]);
- }
- static int pageGetFlags(u8 *aData, int nData){
- return (int)lsmGetU16(&aData[SEGMENT_FLAGS_OFFSET(nData)]);
- }
- static u8 *pageGetCell(u8 *aData, int nData, int iCell){
- return &aData[lsmGetU16(&aData[SEGMENT_CELLPTR_OFFSET(nData, iCell)])];
- }
- /*
- ** Return the number of cells on page pPg.
- */
- static int pageObjGetNRec(Page *pPg){
- int nData;
- u8 *aData = lsmFsPageData(pPg, &nData);
- return pageGetNRec(aData, nData);
- }
- /*
- ** Return the decoded (possibly relative) pointer value stored in cell
- ** iCell from page aData/nData.
- */
- static LsmPgno pageGetRecordPtr(u8 *aData, int nData, int iCell){
- LsmPgno iRet; /* Return value */
- u8 *aCell; /* Pointer to cell iCell */
- assert( iCell<pageGetNRec(aData, nData) && iCell>=0 );
- aCell = pageGetCell(aData, nData, iCell);
- lsmVarintGet64(&aCell[1], &iRet);
- return iRet;
- }
- static u8 *pageGetKey(
- Segment *pSeg, /* Segment pPg belongs to */
- Page *pPg, /* Page to read from */
- int iCell, /* Index of cell on page to read */
- int *piTopic, /* OUT: Topic associated with this key */
- int *pnKey, /* OUT: Size of key in bytes */
- LsmBlob *pBlob /* If required, use this for dynamic memory */
- ){
- u8 *pKey;
- i64 nDummy;
- int eType;
- u8 *aData;
- int nData;
- aData = fsPageData(pPg, &nData);
- assert( !(pageGetFlags(aData, nData) & SEGMENT_BTREE_FLAG) );
- assert( iCell<pageGetNRec(aData, nData) );
- pKey = pageGetCell(aData, nData, iCell);
- eType = *pKey++;
- pKey += lsmVarintGet64(pKey, &nDummy);
- pKey += lsmVarintGet32(pKey, pnKey);
- if( rtIsWrite(eType) ){
- pKey += lsmVarintGet64(pKey, &nDummy);
- }
- *piTopic = rtTopic(eType);
- sortedReadData(pSeg, pPg, pKey-aData, *pnKey, (void **)&pKey, pBlob);
- return pKey;
- }
- static int pageGetKeyCopy(
- lsm_env *pEnv, /* Environment handle */
- Segment *pSeg, /* Segment pPg belongs to */
- Page *pPg, /* Page to read from */
- int iCell, /* Index of cell on page to read */
- int *piTopic, /* OUT: Topic associated with this key */
- LsmBlob *pBlob /* If required, use this for dynamic memory */
- ){
- int rc = LSM_OK;
- int nKey;
- u8 *aKey;
- aKey = pageGetKey(pSeg, pPg, iCell, piTopic, &nKey, pBlob);
- assert( (void *)aKey!=pBlob->pData || nKey==pBlob->nData );
- if( (void *)aKey!=pBlob->pData ){
- rc = sortedBlobSet(pEnv, pBlob, aKey, nKey);
- }
- return rc;
- }
- static LsmPgno pageGetBtreeRef(Page *pPg, int iKey){
- LsmPgno iRef;
- u8 *aData;
- int nData;
- u8 *aCell;
- aData = fsPageData(pPg, &nData);
- aCell = pageGetCell(aData, nData, iKey);
- assert( aCell[0]==0 );
- aCell++;
- aCell += lsmVarintGet64(aCell, &iRef);
- lsmVarintGet64(aCell, &iRef);
- assert( iRef>0 );
- return iRef;
- }
- #define GETVARINT64(a, i) (((i)=((u8*)(a))[0])<=240?1:lsmVarintGet64((a), &(i)))
- #define GETVARINT32(a, i) (((i)=((u8*)(a))[0])<=240?1:lsmVarintGet32((a), &(i)))
- static int pageGetBtreeKey(
- Segment *pSeg, /* Segment page pPg belongs to */
- Page *pPg,
- int iKey,
- LsmPgno *piPtr,
- int *piTopic,
- void **ppKey,
- int *pnKey,
- LsmBlob *pBlob
- ){
- u8 *aData;
- int nData;
- u8 *aCell;
- int eType;
- aData = fsPageData(pPg, &nData);
- assert( SEGMENT_BTREE_FLAG & pageGetFlags(aData, nData) );
- assert( iKey>=0 && iKey<pageGetNRec(aData, nData) );
- aCell = pageGetCell(aData, nData, iKey);
- eType = *aCell++;
- aCell += GETVARINT64(aCell, *piPtr);
- if( eType==0 ){
- int rc;
- LsmPgno iRef; /* Page number of referenced page */
- Page *pRef;
- aCell += GETVARINT64(aCell, iRef);
- rc = lsmFsDbPageGet(lsmPageFS(pPg), pSeg, iRef, &pRef);
- if( rc!=LSM_OK ) return rc;
- pageGetKeyCopy(lsmPageEnv(pPg), pSeg, pRef, 0, &eType, pBlob);
- lsmFsPageRelease(pRef);
- *ppKey = pBlob->pData;
- *pnKey = pBlob->nData;
- }else{
- aCell += GETVARINT32(aCell, *pnKey);
- *ppKey = aCell;
- }
- if( piTopic ) *piTopic = rtTopic(eType);
- return LSM_OK;
- }
- static int btreeCursorLoadKey(BtreeCursor *pCsr){
- int rc = LSM_OK;
- if( pCsr->iPg<0 ){
- pCsr->pKey = 0;
- pCsr->nKey = 0;
- pCsr->eType = 0;
- }else{
- LsmPgno dummy;
- int iPg = pCsr->iPg;
- int iCell = pCsr->aPg[iPg].iCell;
- while( iCell<0 && (--iPg)>=0 ){
- iCell = pCsr->aPg[iPg].iCell-1;
- }
- if( iPg<0 || iCell<0 ) return LSM_CORRUPT_BKPT;
- rc = pageGetBtreeKey(
- pCsr->pSeg,
- pCsr->aPg[iPg].pPage, iCell,
- &dummy, &pCsr->eType, &pCsr->pKey, &pCsr->nKey, &pCsr->blob
- );
- pCsr->eType |= LSM_SEPARATOR;
- }
- return rc;
- }
- static LsmPgno btreeCursorPtr(u8 *aData, int nData, int iCell){
- int nCell;
- nCell = pageGetNRec(aData, nData);
- if( iCell>=nCell ){
- return pageGetPtr(aData, nData);
- }
- return pageGetRecordPtr(aData, nData, iCell);
- }
- static int btreeCursorNext(BtreeCursor *pCsr){
- int rc = LSM_OK;
- BtreePg *pPg = &pCsr->aPg[pCsr->iPg];
- int nCell;
- u8 *aData;
- int nData;
- assert( pCsr->iPg>=0 );
- assert( pCsr->iPg==pCsr->nDepth-1 );
- aData = fsPageData(pPg->pPage, &nData);
- nCell = pageGetNRec(aData, nData);
- assert( pPg->iCell<=nCell );
- pPg->iCell++;
- if( pPg->iCell==nCell ){
- LsmPgno iLoad;
- /* Up to parent. */
- lsmFsPageRelease(pPg->pPage);
- pPg->pPage = 0;
- pCsr->iPg--;
- while( pCsr->iPg>=0 ){
- pPg = &pCsr->aPg[pCsr->iPg];
- aData = fsPageData(pPg->pPage, &nData);
- if( pPg->iCell<pageGetNRec(aData, nData) ) break;
- lsmFsPageRelease(pPg->pPage);
- pCsr->iPg--;
- }
- /* Read the key */
- rc = btreeCursorLoadKey(pCsr);
- /* Unless the cursor is at EOF, descend to cell -1 (yes, negative one) of
- ** the left-most most descendent. */
- if( pCsr->iPg>=0 ){
- pCsr->aPg[pCsr->iPg].iCell++;
- iLoad = btreeCursorPtr(aData, nData, pPg->iCell);
- do {
- Page *pLoad;
- pCsr->iPg++;
- rc = lsmFsDbPageGet(pCsr->pFS, pCsr->pSeg, iLoad, &pLoad);
- pCsr->aPg[pCsr->iPg].pPage = pLoad;
- pCsr->aPg[pCsr->iPg].iCell = 0;
- if( rc==LSM_OK ){
- if( pCsr->iPg==(pCsr->nDepth-1) ) break;
- aData = fsPageData(pLoad, &nData);
- iLoad = btreeCursorPtr(aData, nData, 0);
- }
- }while( rc==LSM_OK && pCsr->iPg<(pCsr->nDepth-1) );
- pCsr->aPg[pCsr->iPg].iCell = -1;
- }
- }else{
- rc = btreeCursorLoadKey(pCsr);
- }
- if( rc==LSM_OK && pCsr->iPg>=0 ){
- aData = fsPageData(pCsr->aPg[pCsr->iPg].pPage, &nData);
- pCsr->iPtr = btreeCursorPtr(aData, nData, pCsr->aPg[pCsr->iPg].iCell+1);
- }
- return rc;
- }
- static void btreeCursorFree(BtreeCursor *pCsr){
- if( pCsr ){
- int i;
- lsm_env *pEnv = lsmFsEnv(pCsr->pFS);
- for(i=0; i<=pCsr->iPg; i++){
- lsmFsPageRelease(pCsr->aPg[i].pPage);
- }
- sortedBlobFree(&pCsr->blob);
- lsmFree(pEnv, pCsr->aPg);
- lsmFree(pEnv, pCsr);
- }
- }
- static int btreeCursorFirst(BtreeCursor *pCsr){
- int rc;
- Page *pPg = 0;
- FileSystem *pFS = pCsr->pFS;
- LsmPgno iPg = pCsr->pSeg->iRoot;
- do {
- rc = lsmFsDbPageGet(pFS, pCsr->pSeg, iPg, &pPg);
- assert( (rc==LSM_OK)==(pPg!=0) );
- if( rc==LSM_OK ){
- u8 *aData;
- int nData;
- int flags;
- aData = fsPageData(pPg, &nData);
- flags = pageGetFlags(aData, nData);
- if( (flags & SEGMENT_BTREE_FLAG)==0 ) break;
- if( (pCsr->nDepth % 8)==0 ){
- int nNew = pCsr->nDepth + 8;
- pCsr->aPg = (BtreePg *)lsmReallocOrFreeRc(
- lsmFsEnv(pFS), pCsr->aPg, sizeof(BtreePg) * nNew, &rc
- );
- if( rc==LSM_OK ){
- memset(&pCsr->aPg[pCsr->nDepth], 0, sizeof(BtreePg) * 8);
- }
- }
- if( rc==LSM_OK ){
- assert( pCsr->aPg[pCsr->nDepth].iCell==0 );
- pCsr->aPg[pCsr->nDepth].pPage = pPg;
- pCsr->nDepth++;
- iPg = pageGetRecordPtr(aData, nData, 0);
- }
- }
- }while( rc==LSM_OK );
- lsmFsPageRelease(pPg);
- pCsr->iPg = pCsr->nDepth-1;
- if( rc==LSM_OK && pCsr->nDepth ){
- pCsr->aPg[pCsr->iPg].iCell = -1;
- rc = btreeCursorNext(pCsr);
- }
- return rc;
- }
- static void btreeCursorPosition(BtreeCursor *pCsr, MergeInput *p){
- if( pCsr->iPg>=0 ){
- p->iPg = lsmFsPageNumber(pCsr->aPg[pCsr->iPg].pPage);
- p->iCell = ((pCsr->aPg[pCsr->iPg].iCell + 1) << 8) + pCsr->nDepth;
- }else{
- p->iPg = 0;
- p->iCell = 0;
- }
- }
- static void btreeCursorSplitkey(BtreeCursor *pCsr, MergeInput *p){
- int iCell = pCsr->aPg[pCsr->iPg].iCell;
- if( iCell>=0 ){
- p->iCell = iCell;
- p->iPg = lsmFsPageNumber(pCsr->aPg[pCsr->iPg].pPage);
- }else{
- int i;
- for(i=pCsr->iPg-1; i>=0; i--){
- if( pCsr->aPg[i].iCell>0 ) break;
- }
- assert( i>=0 );
- p->iCell = pCsr->aPg[i].iCell-1;
- p->iPg = lsmFsPageNumber(pCsr->aPg[i].pPage);
- }
- }
- static int sortedKeyCompare(
- int (*xCmp)(void *, int, void *, int),
- int iLhsTopic, void *pLhsKey, int nLhsKey,
- int iRhsTopic, void *pRhsKey, int nRhsKey
- ){
- int res = iLhsTopic - iRhsTopic;
- if( res==0 ){
- res = xCmp(pLhsKey, nLhsKey, pRhsKey, nRhsKey);
- }
- return res;
- }
- static int btreeCursorRestore(
- BtreeCursor *pCsr,
- int (*xCmp)(void *, int, void *, int),
- MergeInput *p
- ){
- int rc = LSM_OK;
- if( p->iPg ){
- lsm_env *pEnv = lsmFsEnv(pCsr->pFS);
- int iCell; /* Current cell number on leaf page */
- LsmPgno iLeaf; /* Page number of current leaf page */
- int nDepth; /* Depth of b-tree structure */
- Segment *pSeg = pCsr->pSeg;
- /* Decode the MergeInput structure */
- iLeaf = p->iPg;
- nDepth = (p->iCell & 0x00FF);
- iCell = (p->iCell >> 8) - 1;
- /* Allocate the BtreeCursor.aPg[] array */
- assert( pCsr->aPg==0 );
- pCsr->aPg = (BtreePg *)lsmMallocZeroRc(pEnv, sizeof(BtreePg) * nDepth, &rc);
- /* Populate the last entry of the aPg[] array */
- if( rc==LSM_OK ){
- Page **pp = &pCsr->aPg[nDepth-1].pPage;
- pCsr->iPg = nDepth-1;
- pCsr->nDepth = nDepth;
- pCsr->aPg[pCsr->iPg].iCell = iCell;
- rc = lsmFsDbPageGet(pCsr->pFS, pSeg, iLeaf, pp);
- }
- /* Populate any other aPg[] array entries */
- if( rc==LSM_OK && nDepth>1 ){
- LsmBlob blob = {0,0,0};
- void *pSeek;
- int nSeek;
- int iTopicSeek;
- int iPg = 0;
- LsmPgno iLoad = pSeg->iRoot;
- Page *pPg = pCsr->aPg[nDepth-1].pPage;
-
- if( pageObjGetNRec(pPg)==0 ){
- /* This can happen when pPg is the right-most leaf in the b-tree.
- ** In this case, set the iTopicSeek/pSeek/nSeek key to a value
- ** greater than any real key. */
- assert( iCell==-1 );
- iTopicSeek = 1000;
- pSeek = 0;
- nSeek = 0;
- }else{
- LsmPgno dummy;
- rc = pageGetBtreeKey(pSeg, pPg,
- 0, &dummy, &iTopicSeek, &pSeek, &nSeek, &pCsr->blob
- );
- }
- do {
- Page *pPg2;
- rc = lsmFsDbPageGet(pCsr->pFS, pSeg, iLoad, &pPg2);
- assert( rc==LSM_OK || pPg2==0 );
- if( rc==LSM_OK ){
- u8 *aData; /* Buffer containing page data */
- int nData; /* Size of aData[] in bytes */
- int iMin;
- int iMax;
- int iCell2;
- aData = fsPageData(pPg2, &nData);
- assert( (pageGetFlags(aData, nData) & SEGMENT_BTREE_FLAG) );
- iLoad = pageGetPtr(aData, nData);
- iCell2 = pageGetNRec(aData, nData);
- iMax = iCell2-1;
- iMin = 0;
- while( iMax>=iMin ){
- int iTry = (iMin+iMax)/2;
- void *pKey; int nKey; /* Key for cell iTry */
- int iTopic; /* Topic for key pKeyT/nKeyT */
- LsmPgno iPtr; /* Pointer for cell iTry */
- int res; /* (pSeek - pKeyT) */
- rc = pageGetBtreeKey(
- pSeg, pPg2, iTry, &iPtr, &iTopic, &pKey, &nKey, &blob
- );
- if( rc!=LSM_OK ) break;
- res = sortedKeyCompare(
- xCmp, iTopicSeek, pSeek, nSeek, iTopic, pKey, nKey
- );
- assert( res!=0 );
- if( res<0 ){
- iLoad = iPtr;
- iCell2 = iTry;
- iMax = iTry-1;
- }else{
- iMin = iTry+1;
- }
- }
- pCsr->aPg[iPg].pPage = pPg2;
- pCsr->aPg[iPg].iCell = iCell2;
- iPg++;
- assert( iPg!=nDepth-1
- || lsmFsRedirectPage(pCsr->pFS, pSeg->pRedirect, iLoad)==iLeaf
- );
- }
- }while( rc==LSM_OK && iPg<(nDepth-1) );
- sortedBlobFree(&blob);
- }
- /* Load the current key and pointer */
- if( rc==LSM_OK ){
- BtreePg *pBtreePg;
- u8 *aData;
- int nData;
- pBtreePg = &pCsr->aPg[pCsr->iPg];
- aData = fsPageData(pBtreePg->pPage, &nData);
- pCsr->iPtr = btreeCursorPtr(aData, nData, pBtreePg->iCell+1);
- if( pBtreePg->iCell<0 ){
- LsmPgno dummy;
- int i;
- for(i=pCsr->iPg-1; i>=0; i--){
- if( pCsr->aPg[i].iCell>0 ) break;
- }
- assert( i>=0 );
- rc = pageGetBtreeKey(pSeg,
- pCsr->aPg[i].pPage, pCsr->aPg[i].iCell-1,
- &dummy, &pCsr->eType, &pCsr->pKey, &pCsr->nKey, &pCsr->blob
- );
- pCsr->eType |= LSM_SEPARATOR;
- }else{
- rc = btreeCursorLoadKey(pCsr);
- }
- }
- }
- return rc;
- }
- static int btreeCursorNew(
- lsm_db *pDb,
- Segment *pSeg,
- BtreeCursor **ppCsr
- ){
- int rc = LSM_OK;
- BtreeCursor *pCsr;
-
- assert( pSeg->iRoot );
- pCsr = lsmMallocZeroRc(pDb->pEnv, sizeof(BtreeCursor), &rc);
- if( pCsr ){
- pCsr->pFS = pDb->pFS;
- pCsr->pSeg = pSeg;
- pCsr->iPg = -1;
- }
- *ppCsr = pCsr;
- return rc;
- }
- static void segmentPtrSetPage(SegmentPtr *pPtr, Page *pNext){
- lsmFsPageRelease(pPtr->pPg);
- if( pNext ){
- int nData;
- u8 *aData = fsPageData(pNext, &nData);
- pPtr->nCell = pageGetNRec(aData, nData);
- pPtr->flags = (u16)pageGetFlags(aData, nData);
- pPtr->iPtr = pageGetPtr(aData, nData);
- }
- pPtr->pPg = pNext;
- }
- /*
- ** Load a new page into the SegmentPtr object pPtr.
- */
- static int segmentPtrLoadPage(
- FileSystem *pFS,
- SegmentPtr *pPtr, /* Load page into this SegmentPtr object */
- LsmPgno iNew /* Page number of new page */
- ){
- Page *pPg = 0; /* The new page */
- int rc; /* Return Code */
- rc = lsmFsDbPageGet(pFS, pPtr->pSeg, iNew, &pPg);
- assert( rc==LSM_OK || pPg==0 );
- segmentPtrSetPage(pPtr, pPg);
- return rc;
- }
- static int segmentPtrReadData(
- SegmentPtr *pPtr,
- int iOff,
- int nByte,
- void **ppData,
- LsmBlob *pBlob
- ){
- return sortedReadData(pPtr->pSeg, pPtr->pPg, iOff, nByte, ppData, pBlob);
- }
- static int segmentPtrNextPage(
- SegmentPtr *pPtr, /* Load page into this SegmentPtr object */
- int eDir /* +1 for next(), -1 for prev() */
- ){
- Page *pNext; /* New page to load */
- int rc; /* Return code */
- assert( eDir==1 || eDir==-1 );
- assert( pPtr->pPg );
- assert( pPtr->pSeg || eDir>0 );
- rc = lsmFsDbPageNext(pPtr->pSeg, pPtr->pPg, eDir, &pNext);
- assert( rc==LSM_OK || pNext==0 );
- segmentPtrSetPage(pPtr, pNext);
- return rc;
- }
- static int segmentPtrLoadCell(
- SegmentPtr *pPtr, /* Load page into this SegmentPtr object */
- int iNew /* Cell number of new cell */
- ){
- int rc = LSM_OK;
- if( pPtr->pPg ){
- u8 *aData; /* Pointer to page data buffer */
- int iOff; /* Offset in aData[] to read from */
- int nPgsz; /* Size of page (aData[]) in bytes */
- assert( iNew<pPtr->nCell );
- pPtr->iCell = iNew;
- aData = fsPageData(pPtr->pPg, &nPgsz);
- iOff = lsmGetU16(&aData[SEGMENT_CELLPTR_OFFSET(nPgsz, pPtr->iCell)]);
- pPtr->eType = aData[iOff];
- iOff++;
- iOff += GETVARINT64(&aData[iOff], pPtr->iPgPtr);
- iOff += GETVARINT32(&aData[iOff], pPtr->nKey);
- if( rtIsWrite(pPtr->eType) ){
- iOff += GETVARINT32(&aData[iOff], pPtr->nVal);
- }
- assert( pPtr->nKey>=0 );
- rc = segmentPtrReadData(
- pPtr, iOff, pPtr->nKey, &pPtr->pKey, &pPtr->blob1
- );
- if( rc==LSM_OK && rtIsWrite(pPtr->eType) ){
- rc = segmentPtrReadData(
- pPtr, iOff+pPtr->nKey, pPtr->nVal, &pPtr->pVal, &pPtr->blob2
- );
- }else{
- pPtr->nVal = 0;
- pPtr->pVal = 0;
- }
- }
- return rc;
- }
- static Segment *sortedSplitkeySegment(Level *pLevel){
- Merge *pMerge = pLevel->pMerge;
- MergeInput *p = &pMerge->splitkey;
- Segment *pSeg;
- int i;
- for(i=0; i<pMerge->nInput; i++){
- if( p->iPg==pMerge->aInput[i].iPg ) break;
- }
- if( pMerge->nInput==(pLevel->nRight+1) && i>=(pMerge->nInput-1) ){
- pSeg = &pLevel->pNext->lhs;
- }else{
- pSeg = &pLevel->aRhs[i];
- }
- return pSeg;
- }
- static void sortedSplitkey(lsm_db *pDb, Level *pLevel, int *pRc){
- Segment *pSeg;
- Page *pPg = 0;
- lsm_env *pEnv = pDb->pEnv; /* Environment handle */
- int rc = *pRc;
- Merge *pMerge = pLevel->pMerge;
- pSeg = sortedSplitkeySegment(pLevel);
- if( rc==LSM_OK ){
- rc = lsmFsDbPageGet(pDb->pFS, pSeg, pMerge->splitkey.iPg, &pPg);
- }
- if( rc==LSM_OK ){
- int iTopic;
- LsmBlob blob = {0, 0, 0, 0};
- u8 *aData;
- int nData;
-
- aData = lsmFsPageData(pPg, &nData);
- if( pageGetFlags(aData, nData) & SEGMENT_BTREE_FLAG ){
- void *pKey;
- int nKey;
- LsmPgno dummy;
- rc = pageGetBtreeKey(pSeg,
- pPg, pMerge->splitkey.iCell, &dummy, &iTopic, &pKey, &nKey, &blob
- );
- if( rc==LSM_OK && blob.pData!=pKey ){
- rc = sortedBlobSet(pEnv, &blob, pKey, nKey);
- }
- }else{
- rc = pageGetKeyCopy(
- pEnv, pSeg, pPg, pMerge->splitkey.iCell, &iTopic, &blob
- );
- }
- pLevel->iSplitTopic = iTopic;
- pLevel->pSplitKey = blob.pData;
- pLevel->nSplitKey = blob.nData;
- lsmFsPageRelease(pPg);
- }
- *pRc = rc;
- }
- /*
- ** Reset a segment cursor. Also free its buffers if they are nThreshold
- ** bytes or larger in size.
- */
- static void segmentPtrReset(SegmentPtr *pPtr, int nThreshold){
- lsmFsPageRelease(pPtr->pPg);
- pPtr->pPg = 0;
- pPtr->nCell = 0;
- pPtr->pKey = 0;
- pPtr->nKey = 0;
- pPtr->pVal = 0;
- pPtr->nVal = 0;
- pPtr->eType = 0;
- pPtr->iCell = 0;
- if( pPtr->blob1.nAlloc>=nThreshold ) sortedBlobFree(&pPtr->blob1);
- if( pPtr->blob2.nAlloc>=nThreshold ) sortedBlobFree(&pPtr->blob2);
- }
- static int segmentPtrIgnoreSeparators(MultiCursor *pCsr, SegmentPtr *pPtr){
- return (pCsr->flags & CURSOR_READ_SEPARATORS)==0
- || (pPtr!=&pCsr->aPtr[pCsr->nPtr-1]);
- }
- static int segmentPtrAdvance(
- MultiCursor *pCsr,
- SegmentPtr *pPtr,
- int bReverse
- ){
- int eDir = (bReverse ? -1 : 1);
- Level *pLvl = pPtr->pLevel;
- do {
- int rc;
- int iCell; /* Number of new cell in page */
- int svFlags = 0; /* SegmentPtr.eType before advance */
- iCell = pPtr->iCell + eDir;
- assert( pPtr->pPg );
- assert( iCell<=pPtr->nCell && iCell>=-1 );
- if( bReverse && pPtr->pSeg!=&pPtr->pLevel->lhs ){
- svFlags = pPtr->eType;
- assert( svFlags );
- }
- if( iCell>=pPtr->nCell || iCell<0 ){
- do {
- rc = segmentPtrNextPage(pPtr, eDir);
- }while( rc==LSM_OK
- && pPtr->pPg
- && (pPtr->nCell==0 || (pPtr->flags & SEGMENT_BTREE_FLAG) )
- );
- if( rc!=LSM_OK ) return rc;
- iCell = bReverse ? (pPtr->nCell-1) : 0;
- }
- rc = segmentPtrLoadCell(pPtr, iCell);
- if( rc!=LSM_OK ) return rc;
- if( svFlags && pPtr->pPg ){
- int res = sortedKeyCompare(pCsr->pDb->xCmp,
- rtTopic(pPtr->eType), pPtr->pKey, pPtr->nKey,
- pLvl->iSplitTopic, pLvl->pSplitKey, pLvl->nSplitKey
- );
- if( res<0 ) segmentPtrReset(pPtr, LSM_SEGMENTPTR_FREE_THRESHOLD);
- }
- if( pPtr->pPg==0 && (svFlags & LSM_END_DELETE) ){
- Segment *pSeg = pPtr->pSeg;
- rc = lsmFsDbPageGet(pCsr->pDb->pFS, pSeg, pSeg->iFirst, &pPtr->pPg);
- if( rc!=LSM_OK ) return rc;
- pPtr->eType = LSM_START_DELETE | LSM_POINT_DELETE;
- pPtr->eType |= (pLvl->iSplitTopic ? LSM_SYSTEMKEY : 0);
- pPtr->pKey = pLvl->pSplitKey;
- pPtr->nKey = pLvl->nSplitKey;
- }
- }while( pCsr
- && pPtr->pPg
- && segmentPtrIgnoreSeparators(pCsr, pPtr)
- && rtIsSeparator(pPtr->eType)
- );
- return LSM_OK;
- }
- static void segmentPtrEndPage(
- FileSystem *pFS,
- SegmentPtr *pPtr,
- int bLast,
- int *pRc
- ){
- if( *pRc==LSM_OK ){
- Segment *pSeg = pPtr->pSeg;
- Page *pNew = 0;
- if( bLast ){
- *pRc = lsmFsDbPageLast(pFS, pSeg, &pNew);
- }else{
- *pRc = lsmFsDbPageGet(pFS, pSeg, pSeg->iFirst, &pNew);
- }
- segmentPtrSetPage(pPtr, pNew);
- }
- }
- /*
- ** Try to move the segment pointer passed as the second argument so that it
- ** points at either the first (bLast==0) or last (bLast==1) cell in the valid
- ** region of the segment defined by pPtr->iFirst and pPtr->iLast.
- **
- ** Return LSM_OK if successful or an lsm error code if something goes
- ** wrong (IO error, OOM etc.).
- */
- static int segmentPtrEnd(MultiCursor *pCsr, SegmentPtr *pPtr, int bLast){
- Level *pLvl = pPtr->pLevel;
- int rc = LSM_OK;
- FileSystem *pFS = pCsr->pDb->pFS;
- int bIgnore;
- segmentPtrEndPage(pFS, pPtr, bLast, &rc);
- while( rc==LSM_OK && pPtr->pPg
- && (pPtr->nCell==0 || (pPtr->flags & SEGMENT_BTREE_FLAG))
- ){
- rc = segmentPtrNextPage(pPtr, (bLast ? -1 : 1));
- }
- if( rc==LSM_OK && pPtr->pPg ){
- rc = segmentPtrLoadCell(pPtr, bLast ? (pPtr->nCell-1) : 0);
- if( rc==LSM_OK && bLast && pPtr->pSeg!=&pLvl->lhs ){
- int res = sortedKeyCompare(pCsr->pDb->xCmp,
- rtTopic(pPtr->eType), pPtr->pKey, pPtr->nKey,
- pLvl->iSplitTopic, pLvl->pSplitKey, pLvl->nSplitKey
- );
- if( res<0 ) segmentPtrReset(pPtr, LSM_SEGMENTPTR_FREE_THRESHOLD);
- }
- }
-
- bIgnore = segmentPtrIgnoreSeparators(pCsr, pPtr);
- if( rc==LSM_OK && pPtr->pPg && bIgnore && rtIsSeparator(pPtr->eType) ){
- rc = segmentPtrAdvance(pCsr, pPtr, bLast);
- }
- #if 0
- if( bLast && rc==LSM_OK && pPtr->pPg
- && pPtr->pSeg==&pLvl->lhs
- && pLvl->nRight && (pPtr->eType & LSM_START_DELETE)
- ){
- pPtr->iCell++;
- pPtr->eType = LSM_END_DELETE | (pLvl->iSplitTopic);
- pPtr->pKey = pLvl->pSplitKey;
- pPtr->nKey = pLvl->nSplitKey;
- pPtr->pVal = 0;
- pPtr->nVal = 0;
- }
- #endif
- return rc;
- }
- static void segmentPtrKey(SegmentPtr *pPtr, void **ppKey, int *pnKey){
- assert( pPtr->pPg );
- *ppKey = pPtr->pKey;
- *pnKey = pPtr->nKey;
- }
- #if 0 /* NOT USED */
- static char *keyToString(lsm_env *pEnv, void *pKey, int nKey){
- int i;
- u8 *aKey = (u8 *)pKey;
- char *zRet = (char *)lsmMalloc(pEnv, nKey+1);
- for(i=0; i<nKey; i++){
- zRet[i] = (char)(isalnum(aKey[i]) ? aKey[i] : '.');
- }
- zRet[nKey] = '\0';
- return zRet;
- }
- #endif
- #if 0 /* NOT USED */
- /*
- ** Check that the page that pPtr currently has loaded is the correct page
- ** to search for key (pKey/nKey). If it is, return 1. Otherwise, an assert
- ** fails and this function does not return.
- */
- static int assertKeyLocation(
- MultiCursor *pCsr,
- SegmentPtr *pPtr,
- void *pKey, int nKey
- ){
- lsm_env *pEnv = lsmFsEnv(pCsr->pDb->pFS);
- LsmBlob blob = {0, 0, 0};
- int eDir;
- int iTopic = 0; /* TODO: Fix me */
- for(eDir=-1; eDir<=1; eDir+=2){
- Page *pTest = pPtr->pPg;
- lsmFsPageRef(pTest);
- while( pTest ){
- Segment *pSeg = pPtr->pSeg;
- Page *pNext;
- int rc = lsmFsDbPageNext(pSeg, pTest, eDir, &pNext);
- lsmFsPageRelease(pTest);
- if( rc ) return 1;
- pTest = pNext;
- if( pTest ){
- int nData;
- u8 *aData = fsPageData(pTest, &nData);
- int nCell = pageGetNRec(aData, nData);
- int flags = pageGetFlags(aData, nData);
- if( nCell && 0==(flags&SEGMENT_BTREE_FLAG) ){
- int nPgKey;
- int iPgTopic;
- u8 *pPgKey;
- int res;
- int iCell;
- iCell = ((eDir < 0) ? (nCell-1) : 0);
- pPgKey = pageGetKey(pSeg, pTest, iCell, &iPgTopic, &nPgKey, &blob);
- res = iTopic - iPgTopic;
- if( res==0 ) res = pCsr->pDb->xCmp(pKey, nKey, pPgKey, nPgKey);
- if( (eDir==1 && res>0) || (eDir==-1 && res<0) ){
- /* Taking this branch means something has gone wrong. */
- char *zMsg = lsmMallocPrintf(pEnv, "Key \"%s\" is not on page %d",
- keyToString(pEnv, pKey, nKey), lsmFsPageNumber(pPtr->pPg)
- );
- fprintf(stderr, "%s\n", zMsg);
- assert( !"assertKeyLocation() failed" );
- }
- lsmFsPageRelease(pTest);
- pTest = 0;
- }
- }
- }
- }
- sortedBlobFree(&blob);
- return 1;
- }
- #endif
- #ifndef NDEBUG
- static int assertSeekResult(
- MultiCursor *pCsr,
- SegmentPtr *pPtr,
- int iTopic,
- void *pKey,
- int nKey,
- int eSeek
- ){
- if( pPtr->pPg ){
- int res;
- res = sortedKeyCompare(pCsr->pDb->xCmp, iTopic, pKey, nKey,
- rtTopic(pPtr->eType), pPtr->pKey, pPtr->nKey
- );
- if( eSeek==LSM_SEEK_EQ ) return (res==0);
- if( eSeek==LSM_SEEK_LE ) return (res>=0);
- if( eSeek==LSM_SEEK_GE ) return (res<=0);
- }
- return 1;
- }
- #endif
- static int segmentPtrSearchOversized(
- MultiCursor *pCsr, /* Cursor context */
- SegmentPtr *pPtr, /* Pointer to seek */
- int iTopic, /* Topic of key to search for */
- void *pKey, int nKey /* Key to seek to */
- ){
- int (*xCmp)(void *, int, void *, int) = pCsr->pDb->xCmp;
- int rc = LSM_OK;
- /* If the OVERSIZED flag is set, then there is no pointer in the
- ** upper level to the next page in the segment that contains at least
- ** one key. So compare the largest key on the current page with the
- ** key being sought (pKey/nKey). If (pKey/nKey) is larger, advance
- ** to the next page in the segment that contains at least one key.
- */
- while( rc==LSM_OK && (pPtr->flags & PGFTR_SKIP_NEXT_FLAG) ){
- u8 *pLastKey;
- int nLastKey;
- int iLastTopic;
- int res; /* Result of comparison */
- Page *pNext;
- /* Load the last key on the current page. */
- pLastKey = pageGetKey(pPtr->pSeg,
- pPtr->pPg, pPtr->nCell-1, &iLastTopic, &nLastKey, &pPtr->blob1
- );
- /* If the loaded key is >= than (pKey/nKey), break out of the loop.
- ** If (pKey/nKey) is present in this array, it must be on the current
- ** page. */
- res = sortedKeyCompare(
- xCmp, iLastTopic, pLastKey, nLastKey, iTopic, pKey, nKey
- );
- if( res>=0 ) break;
- /* Advance to the next page that contains at least one key. */
- pNext = pPtr->pPg;
- lsmFsPageRef(pNext);
- while( 1 ){
- Page *pLoad;
- u8 *aData; int nData;
- rc = lsmFsDbPageNext(pPtr->pSeg, pNext, 1, &pLoad);
- lsmFsPageRelease(pNext);
- pNext = pLoad;
- if( pNext==0 ) break;
- assert( rc==LSM_OK );
- aData = lsmFsPageData(pNext, &nData);
- if( (pageGetFlags(aData, nData) & SEGMENT_BTREE_FLAG)==0
- && pageGetNRec(aData, nData)>0
- ){
- break;
- }
- }
- if( pNext==0 ) break;
- segmentPtrSetPage(pPtr, pNext);
- /* This should probably be an LSM_CORRUPT error. */
- assert( rc!=LSM_OK || (pPtr->flags & PGFTR_SKIP_THIS_FLAG) );
- }
- return rc;
- }
- static int ptrFwdPointer(
- Page *pPage,
- int iCell,
- Segment *pSeg,
- LsmPgno *piPtr,
- int *pbFound
- ){
- Page *pPg = pPage;
- int iFirst = iCell;
- int rc = LSM_OK;
- do {
- Page *pNext = 0;
- u8 *aData;
- int nData;
- aData = lsmFsPageData(pPg, &nData);
- if( (pageGetFlags(aData, nData) & SEGMENT_BTREE_FLAG)==0 ){
- int i;
- int nCell = pageGetNRec(aData, nData);
- for(i=iFirst; i<nCell; i++){
- u8 eType = *pageGetCell(aData, nData, i);
- if( (eType & LSM_START_DELETE)==0 ){
- *pbFound = 1;
- *piPtr = pageGetRecordPtr(aData, nData, i) + pageGetPtr(aData, nData);
- lsmFsPageRelease(pPg);
- return LSM_OK;
- }
- }
- }
- rc = lsmFsDbPageNext(pSeg, pPg, 1, &pNext);
- lsmFsPageRelease(pPg);
- pPg = pNext;
- iFirst = 0;
- }while( pPg && rc==LSM_OK );
- lsmFsPageRelease(pPg);
- *pbFound = 0;
- return rc;
- }
- static int sortedRhsFirst(MultiCursor *pCsr, Level *pLvl, SegmentPtr *pPtr){
- int rc;
- rc = segmentPtrEnd(pCsr, pPtr, 0);
- while( pPtr->pPg && rc==LSM_OK ){
- int res = sortedKeyCompare(pCsr->pDb->xCmp,
- pLvl->iSplitTopic, pLvl->pSplitKey, pLvl->nSplitKey,
- rtTopic(pPtr->eType), pPtr->pKey, pPtr->nKey
- );
- if( res<=0 ) break;
- rc = segmentPtrAdvance(pCsr, pPtr, 0);
- }
- return rc;
- }
- /*
- ** This function is called as part of a SEEK_GE op on a multi-cursor if the
- ** FC pointer read from segment *pPtr comes from an entry with the
- ** LSM_START_DELETE flag set. In this case the pointer value cannot be
- ** trusted. Instead, the pointer that should be followed is that associated
- ** with the next entry in *pPtr that does not have LSM_START_DELETE set.
- **
- ** Why the pointers can't be trusted:
- **
- **
- **
- ** TODO: This is a stop-gap solution:
- **
- ** At the moment, this function is called from within segmentPtrSeek(),
- ** as part of the initial lsmMCursorSeek() call. However, consider a
- ** database where the following has occurred:
- **
- ** 1. A range delete removes keys 1..9999 using a range delete.
- ** 2. Keys 1 through 9999 are reinserted.
- ** 3. The levels containing the ops in 1. and 2. above are merged. Call
- ** this level N. Level N contains FC pointers to level N+1.
- **
- ** Then, if the user attempts to query for (key>=2 LIMIT 10), the
- ** lsmMCursorSeek() call will iterate through 9998 entries searching for a
- ** pointer down to the level N+1 that is never actually used. It would be
- ** much better if the multi-cursor could do this lazily - only seek to the
- ** level (N+1) page after the user has moved the cursor on level N passed
- ** the big range-delete.
- */
- static int segmentPtrFwdPointer(
- MultiCursor *pCsr, /* Multi-cursor pPtr belongs to */
- SegmentPtr *pPtr, /* Segment-pointer to extract FC ptr from */
- LsmPgno *piPtr /* OUT: FC pointer value */
- ){
- Level *pLvl = pPtr->pLevel;
- Level *pNext = pLvl->pNext;
- Page *pPg = pPtr->pPg;
- int rc;
- int bFound;
- LsmPgno iOut = 0;
- if( pPtr->pSeg==&pLvl->lhs || pPtr->pSeg==&pLvl->aRhs[pLvl->nRight-1] ){
- if( pNext==0
- || (pNext->nRight==0 && pNext->lhs.iRoot)
- || (pNext->nRight!=0 && pNext->aRhs[0].iRoot)
- ){
- /* Do nothing. The pointer will not be used anyway. */
- return LSM_OK;
- }
- }else{
- if( pPtr[1].pSeg->iRoot ){
- return LSM_OK;
- }
- }
- /* Search for a pointer within the current segment. */
- lsmFsPageRef(pPg);
- rc = ptrFwdPointer(pPg, pPtr->iCell, pPtr->pSeg, &iOut, &bFound);
- if( rc==LSM_OK && bFound==0 ){
- /* This case happens when pPtr points to the left-hand-side of a segment
- ** currently undergoing an incremental merge. In this case, jump to the
- ** oldest segment in the right-hand-side of the same level and continue
- ** searching. But - do not consider any keys smaller than the levels
- ** split-key. */
- SegmentPtr ptr;
- if( pPtr->pLevel->nRight==0 || pPtr->pSeg!=&pPtr->pLevel->lhs ){
- return LSM_CORRUPT_BKPT;
- }
- memset(&ptr, 0, sizeof(SegmentPtr));
- ptr.pLevel = pPtr->pLevel;
- ptr.pSeg = &ptr.pLevel->aRhs[ptr.pLevel->nRight-1];
- rc = sortedRhsFirst(pCsr, ptr.pLevel, &ptr);
- if( rc==LSM_OK ){
- rc = ptrFwdPointer(ptr.pPg, ptr.iCell, ptr.pSeg, &iOut, &bFound);
- ptr.pPg = 0;
- }
- segmentPtrReset(&ptr, 0);
- }
- *piPtr = iOut;
- return rc;
- }
- static int segmentPtrSeek(
- MultiCursor *pCsr, /* Cursor context */
- SegmentPtr *pPtr, /* Pointer to seek */
- int iTopic, /* Key topic to seek to */
- void *pKey, int nKey, /* Key to seek to */
- int eSeek, /* Search bias - see above */
- LsmPgno *piPtr, /* OUT: FC pointer */
- int *pbStop
- ){
- int (*xCmp)(void *, int, void *, int) = pCsr->pDb->xCmp;
- int res = 0; /* Result of comparison operation */
- int rc = LSM_OK;
- int iMin;
- int iMax;
- LsmPgno iPtrOut = 0;
- /* If the current page contains an oversized entry, then there are no
- ** pointers to one or more of the subsequent pages in the sorted run.
- ** The following call ensures that the segment-ptr points to the correct
- ** page in this case. */
- rc = segmentPtrSearchOversized(pCsr, pPtr, iTopic, pKey, nKey);
- iPtrOut = pPtr->iPtr;
- /* Assert that this page is the right page of this segment for the key
- ** that we are searching for. Do this by loading page (iPg-1) and testing
- ** that pKey/nKey is greater than all keys on that page, and then by
- ** loading (iPg+1) and testing that pKey/nKey is smaller than all
- ** the keys it houses.
- **
- ** TODO: With range-deletes in the tree, the test described above may fail.
- */
- #if 0
- assert( assertKeyLocation(pCsr, pPtr, pKey, nKey) );
- #endif
- assert( pPtr->nCell>0
- || pPtr->pSeg->nSize==1
- || lsmFsDbPageIsLast(pPtr->pSeg, pPtr->pPg)
- );
- if( pPtr->nCell==0 ){
- segmentPtrReset(pPtr, LSM_SEGMENTPTR_FREE_THRESHOLD);
- }else{
- iMin = 0;
- iMax = pPtr->nCell-1;
- while( 1 ){
- int iTry = (iMin+iMax)/2;
- void *pKeyT; int nKeyT; /* Key for cell iTry */
- int iTopicT;
- assert( iTry<iMax || iMin==iMax );
- rc = segmentPtrLoadCell(pPtr, iTry);
- if( rc!=LSM_OK ) break;
- segmentPtrKey(pPtr, &pKeyT, &nKeyT);
- iTopicT = rtTopic(pPtr->eType);
- res = sortedKeyCompare(xCmp, iTopicT, pKeyT, nKeyT, iTopic, pKey, nKey);
- if( res<=0 ){
- iPtrOut = pPtr->iPtr + pPtr->iPgPtr;
- }
- if( res==0 || iMin==iMax ){
- break;
- }else if( res>0 ){
- iMax = LSM_MAX(iTry-1, iMin);
- }else{
- iMin = iTry+1;
- }
- }
- if( rc==LSM_OK ){
- assert( res==0 || (iMin==iMax && iMin>=0 && iMin<pPtr->nCell) );
- if( res ){
- rc = segmentPtrLoadCell(pPtr, iMin);
- }
- assert( rc!=LSM_OK || res>0 || iPtrOut==(pPtr->iPtr + pPtr->iPgPtr) );
- if( rc==LSM_OK ){
- switch( eSeek ){
- case LSM_SEEK_EQ: {
- int eType = pPtr->eType;
- if( (res<0 && (eType & LSM_START_DELETE))
- || (res>0 && (eType & LSM_END_DELETE))
- || (res==0 && (eType & LSM_POINT_DELETE))
- ){
- *pbStop = 1;
- }else if( res==0 && (eType & LSM_INSERT) ){
- lsm_env *pEnv = pCsr->pDb->pEnv;
- *pbStop = 1;
- pCsr->eType = pPtr->eType;
- rc = sortedBlobSet(pEnv, &pCsr->key, pPtr->pKey, pPtr->nKey);
- if( rc==LSM_OK ){
- rc = sortedBlobSet(pEnv, &pCsr->val, pPtr->pVal, pPtr->nVal);
- }
- pCsr->flags |= CURSOR_SEEK_EQ;
- }
- segmentPtrReset(pPtr, LSM_SEGMENTPTR_FREE_THRESHOLD);
- break;
- }
- case LSM_SEEK_LE:
- if( res>0 ) rc = segmentPtrAdvance(pCsr, pPtr, 1);
- break;
- case LSM_SEEK_GE: {
- /* Figure out if we need to 'skip' the pointer forward or not */
- if( (res<=0 && (pPtr->eType & LSM_START_DELETE))
- || (res>0 && (pPtr->eType & LSM_END_DELETE))
- ){
- rc = segmentPtrFwdPointer(pCsr, pPtr, &iPtrOut);
- }
- if( res<0 && rc==LSM_OK ){
- rc = segmentPtrAdvance(pCsr, pPtr, 0);
- }
- break;
- }
- }
- }
- }
- /* If the cursor seek has found a separator key, and this cursor is
- ** supposed to ignore separators keys, advance to the next entry. */
- if( rc==LSM_OK && pPtr->pPg
- && segmentPtrIgnoreSeparators(pCsr, pPtr)
- && rtIsSeparator(pPtr->eType)
- ){
- assert( eSeek!=LSM_SEEK_EQ );
- rc = segmentPtrAdvance(pCsr, pPtr, eSeek==LSM_SEEK_LE);
- }
- }
- assert( rc!=LSM_OK || assertSeekResult(pCsr,pPtr,iTopic,pKey,nKey,eSeek) );
- *piPtr = iPtrOut;
- return rc;
- }
- static int seekInBtree(
- MultiCursor *pCsr, /* Multi-cursor object */
- Segment *pSeg, /* Seek within this segment */
- int iTopic,
- void *pKey, int nKey, /* Key to seek to */
- LsmPgno *aPg, /* OUT: Page numbers */
- Page **ppPg /* OUT: Leaf (sorted-run) page reference */
- ){
- int i = 0;
- int rc;
- LsmPgno iPg;
- Page *pPg = 0;
- LsmBlob blob = {0, 0, 0};
- iPg = pSeg->iRoot;
- do {
- LsmPgno *piFirst = 0;
- if( aPg ){
- aPg[i++] = iPg;
- piFirst = &aPg[i];
- }
- rc = lsmFsDbPageGet(pCsr->pDb->pFS, pSeg, iPg, &pPg);
- assert( rc==LSM_OK || pPg==0 );
- if( rc==LSM_OK ){
- u8 *aData; /* Buffer containing page data */
- int nData; /* Size of aData[] in bytes */
- int iMin;
- int iMax;
- int nRec;
- int flags;
- aData = fsPageData(pPg, &nData);
- flags = pageGetFlags(aData, nData);
- if( (flags & SEGMENT_BTREE_FLAG)==0 ) break;
- iPg = pageGetPtr(aData, nData);
- nRec = pageGetNRec(aData, nData);
- iMin = 0;
- iMax = nRec-1;
- while( iMax>=iMin ){
- int iTry = (iMin+iMax)/2;
- void *pKeyT; int nKeyT; /* Key for cell iTry */
- int iTopicT; /* Topic for key pKeyT/nKeyT */
- LsmPgno iPtr; /* Pointer associated with cell iTry */
- int res; /* (pKey - pKeyT) */
- rc = pageGetBtreeKey(
- pSeg, pPg, iTry, &iPtr, &iTopicT, &pKeyT, &nKeyT, &blob
- );
- if( rc!=LSM_OK ) break;
- if( piFirst && pKeyT==blob.pData ){
- *piFirst = pageGetBtreeRef(pPg, iTry);
- piFirst = 0;
- i++;
- }
- res = sortedKeyCompare(
- pCsr->pDb->xCmp, iTopic, pKey, nKey, iTopicT, pKeyT, nKeyT
- );
- if( res<0 ){
- iPg = iPtr;
- iMax = iTry-1;
- }else{
- iMin = iTry+1;
- }
- }
- lsmFsPageRelease(pPg);
- pPg = 0;
- }
- }while( rc==LSM_OK );
- sortedBlobFree(&blob);
- assert( (rc==LSM_OK)==(pPg!=0) );
- if( ppPg ){
- *ppPg = pPg;
- }else{
- lsmFsPageRelease(pPg);
- }
- return rc;
- }
- static int seekInSegment(
- MultiCursor *pCsr,
- SegmentPtr *pPtr,
- int iTopic,
- void *pKey, int nKey,
- LsmPgno iPg, /* Page to search */
- int eSeek, /* Search bias - see above */
- LsmPgno *piPtr, /* OUT: FC pointer */
- int *pbStop /* OUT: Stop search flag */
- ){
- LsmPgno iPtr = iPg;
- int rc = LSM_OK;
- if( pPtr->pSeg->iRoot ){
- Page *pPg;
- assert( pPtr->pSeg->iRoot!=0 );
- rc = seekInBtree(pCsr, pPtr->pSeg, iTopic, pKey, nKey, 0, &pPg);
- if( rc==LSM_OK ) segmentPtrSetPage(pPtr, pPg);
- }else{
- if( iPtr==0 ){
- iPtr = pPtr->pSeg->iFirst;
- }
- if( rc==LSM_OK ){
- rc = segmentPtrLoadPage(pCsr->pDb->pFS, pPtr, iPtr);
- }
- }
- if( rc==LSM_OK ){
- rc = segmentPtrSeek(pCsr, pPtr, iTopic, pKey, nKey, eSeek, piPtr, pbStop);
- }
- return rc;
- }
- /*
- ** Seek each segment pointer in the array of (pLvl->nRight+1) at aPtr[].
- **
- ** pbStop:
- ** This parameter is only significant if parameter eSeek is set to
- ** LSM_SEEK_EQ. In this case, it is set to true before returning if
- ** the seek operation is finished. This can happen in two ways:
- **
- ** a) A key matching (pKey/nKey) is found, or
- ** b) A point-delete or range-delete deleting the key is found.
- **
- ** In case (a), the multi-cursor CURSOR_SEEK_EQ flag is set and the pCsr->key
- ** and pCsr->val blobs populated before returning.
- */
- static int seekInLevel(
- MultiCursor *pCsr, /* Sorted cursor object to seek */
- SegmentPtr *aPtr, /* Pointer to array of (nRhs+1) SPs */
- int eSeek, /* Search bias - see above */
- int iTopic, /* Key topic to search for */
- void *pKey, int nKey, /* Key to search for */
- LsmPgno *piPgno, /* IN/OUT: fraction cascade pointer (or 0) */
- int *pbStop /* OUT: See above */
- ){
- Level *pLvl = aPtr[0].pLevel; /* Level to seek within */
- int rc = LSM_OK; /* Return code */
- LsmPgno iOut = 0; /* Pointer to return to caller */
- int res = -1; /* Result of xCmp(pKey, split) */
- int nRhs = pLvl->nRight; /* Number of right-hand-side segments */
- int bStop = 0;
- /* If this is a composite level (one currently undergoing an incremental
- ** merge), figure out if the search key is larger or smaller than the
- ** levels split-key. */
- if( nRhs ){
- res = sortedKeyCompare(pCsr->pDb->xCmp, iTopic, pKey, nKey,
- pLvl->iSplitTopic, pLvl->pSplitKey, pLvl->nSplitKey
- );
- }
- /* If (res<0), then key pKey/nKey is smaller than the split-key (or this
- ** is not a composite level and there is no split-key). Search the
- ** left-hand-side of the level in this case. */
- if( res<0 ){
- int i;
- LsmPgno iPtr = 0;
- if( nRhs==0 ) iPtr = *piPgno;
- rc = seekInSegment(
- pCsr, &aPtr[0], iTopic, pKey, nKey, iPtr, eSeek, &iOut, &bStop
- );
- if( rc==LSM_OK && nRhs>0 && eSeek==LSM_SEEK_GE && aPtr[0].pPg==0 ){
- res = 0;
- }
- for(i=1; i<=nRhs; i++){
- segmentPtrReset(&aPtr[i], LSM_SEGMENTPTR_FREE_THRESHOLD);
- }
- }
-
- if( res>=0 ){
- int bHit = 0; /* True if at least one rhs is not EOF */
- LsmPgno iPtr = *piPgno;
- int i;
- segmentPtrReset(&aPtr[0], LSM_SEGMENTPTR_FREE_THRESHOLD);
- for(i=1; rc==LSM_OK && i<=nRhs && bStop==0; i++){
- SegmentPtr *pPtr = &aPtr[i];
- iOut = 0;
- rc = seekInSegment(
- pCsr, pPtr, iTopic, pKey, nKey, iPtr, eSeek, &iOut, &bStop
- );
- iPtr = iOut;
- /* If the segment-pointer has settled on a key that is smaller than
- ** the splitkey, invalidate the segment-pointer. */
- if( pPtr->pPg ){
- res = sortedKeyCompare(pCsr->pDb->xCmp,
- rtTopic(pPtr->eType), pPtr->pKey, pPtr->nKey,
- pLvl->iSplitTopic, pLvl->pSplitKey, pLvl->nSplitKey
- );
- if( res<0 ){
- if( pPtr->eType & LSM_START_DELETE ){
- pPtr->eType &= ~LSM_INSERT;
- pPtr->pKey = pLvl->pSplitKey;
- pPtr->nKey = pLvl->nSplitKey;
- pPtr->pVal = 0;
- pPtr->nVal = 0;
- }else{
- segmentPtrReset(pPtr, LSM_SEGMENTPTR_FREE_THRESHOLD);
- }
- }
- }
- if( aPtr[i].pKey ) bHit = 1;
- }
- if( rc==LSM_OK && eSeek==LSM_SEEK_LE && bHit==0 ){
- rc = segmentPtrEnd(pCsr, &aPtr[0], 1);
- }
- }
- assert( eSeek==LSM_SEEK_EQ || bStop==0 );
- *piPgno = iOut;
- *pbStop = bStop;
- return rc;
- }
- static void multiCursorGetKey(
- MultiCursor *pCsr,
- int iKey,
- int *peType, /* OUT: Key type (SORTED_WRITE etc.) */
- void **ppKey, /* OUT: Pointer to buffer containing key */
- int *pnKey /* OUT: Size of *ppKey in bytes */
- ){
- int nKey = 0;
- void *pKey = 0;
- int eType = 0;
- switch( iKey ){
- case CURSOR_DATA_TREE0:
- case CURSOR_DATA_TREE1: {
- TreeCursor *pTreeCsr = pCsr->apTreeCsr[iKey-CURSOR_DATA_TREE0];
- if( lsmTreeCursorValid(pTreeCsr) ){
- lsmTreeCursorKey(pTreeCsr, &eType, &pKey, &nKey);
- }
- break;
- }
- case CURSOR_DATA_SYSTEM: {
- Snapshot *pWorker = pCsr->pDb->pWorker;
- if( pWorker && (pCsr->flags & CURSOR_FLUSH_FREELIST) ){
- int nEntry = pWorker->freelist.nEntry;
- if( pCsr->iFree < (nEntry*2) ){
- FreelistEntry *aEntry = pWorker->freelist.aEntry;
- int i = nEntry - 1 - (pCsr->iFree / 2);
- u32 iKey2 = 0;
- if( (pCsr->iFree % 2) ){
- eType = LSM_END_DELETE|LSM_SYSTEMKEY;
- iKey2 = aEntry[i].iBlk-1;
- }else if( aEntry[i].iId>=0 ){
- eType = LSM_INSERT|LSM_SYSTEMKEY;
- iKey2 = aEntry[i].iBlk;
- /* If the in-memory entry immediately before this one was a
- ** DELETE, and the block number is one greater than the current
- ** block number, mark this entry as an "end-delete-range". */
- if( i<(nEntry-1) && aEntry[i+1].iBlk==iKey2+1 && aEntry[i+1].iId<0 ){
- eType |= LSM_END_DELETE;
- }
- }else{
- eType = LSM_START_DELETE|LSM_SYSTEMKEY;
- iKey2 = aEntry[i].iBlk + 1;
- }
- /* If the in-memory entry immediately after this one is a
- ** DELETE, and the block number is one less than the current
- ** key, mark this entry as an "start-delete-range". */
- if( i>0 && aEntry[i-1].iBlk==iKey2-1 && aEntry[i-1].iId<0 ){
- eType |= LSM_START_DELETE;
- }
- pKey = pCsr->pSystemVal;
- nKey = 4;
- lsmPutU32(pKey, ~iKey2);
- }
- }
- break;
- }
- default: {
- int iPtr = iKey - CURSOR_DATA_SEGMENT;
- assert( iPtr>=0 );
- if( iPtr==pCsr->nPtr ){
- if( pCsr->pBtCsr ){
- pKey = pCsr->pBtCsr->pKey;
- nKey = pCsr->pBtCsr->nKey;
- eType = pCsr->pBtCsr->eType;
- }
- }else if( iPtr<pCsr->nPtr ){
- SegmentPtr *pPtr = &pCsr->aPtr[iPtr];
- if( pPtr->pPg ){
- pKey = pPtr->pKey;
- nKey = pPtr->nKey;
- eType = pPtr->eType;
- }
- }
- break;
- }
- }
- if( peType ) *peType = eType;
- if( pnKey ) *pnKey = nKey;
- if( ppKey ) *ppKey = pKey;
- }
- static int sortedDbKeyCompare(
- MultiCursor *pCsr,
- int iLhsFlags, void *pLhsKey, int nLhsKey,
- int iRhsFlags, void *pRhsKey, int nRhsKey
- ){
- int (*xCmp)(void *, int, void *, int) = pCsr->pDb->xCmp;
- int res;
- /* Compare the keys, including the system flag. */
- res = sortedKeyCompare(xCmp,
- rtTopic(iLhsFlags), pLhsKey, nLhsKey,
- rtTopic(iRhsFlags), pRhsKey, nRhsKey
- );
- /* If a key has the LSM_START_DELETE flag set, but not the LSM_INSERT or
- ** LSM_POINT_DELETE flags, it is considered a delta larger. This prevents
- ** the beginning of an open-ended set from masking a database entry or
- ** delete at a lower level. */
- if( res==0 && (pCsr->flags & CURSOR_IGNORE_DELETE) ){
- const int m = LSM_POINT_DELETE|LSM_INSERT|LSM_END_DELETE |LSM_START_DELETE;
- int iDel1 = 0;
- int iDel2 = 0;
- if( LSM_START_DELETE==(iLhsFlags & m) ) iDel1 = +1;
- if( LSM_END_DELETE ==(iLhsFlags & m) ) iDel1 = -1;
- if( LSM_START_DELETE==(iRhsFlags & m) ) iDel2 = +1;
- if( LSM_END_DELETE ==(iRhsFlags & m) ) iDel2 = -1;
- res = (iDel1 - iDel2);
- }
- return res;
- }
- static void multiCursorDoCompare(MultiCursor *pCsr, int iOut, int bReverse){
- int i1;
- int i2;
- int iRes;
- void *pKey1; int nKey1; int eType1;
- void *pKey2; int nKey2; int eType2;
- const int mul = (bReverse ? -1 : 1);
- assert( pCsr->aTree && iOut<pCsr->nTree );
- if( iOut>=(pCsr->nTree/2) ){
- i1 = (iOut - pCsr->nTree/2) * 2;
- i2 = i1 + 1;
- }else{
- i1 = pCsr->aTree[iOut*2];
- i2 = pCsr->aTree[iOut*2+1];
- }
- multiCursorGetKey(pCsr, i1, &eType1, &pKey1, &nKey1);
- multiCursorGetKey(pCsr, i2, &eType2, &pKey2, &nKey2);
- if( pKey1==0 ){
- iRes = i2;
- }else if( pKey2==0 ){
- iRes = i1;
- }else{
- int res;
- /* Compare the keys */
- res = sortedDbKeyCompare(pCsr,
- eType1, pKey1, nKey1, eType2, pKey2, nKey2
- );
- res = res * mul;
- if( res==0 ){
- /* The two keys are identical. Normally, this means that the key from
- ** the newer run clobbers the old. However, if the newer key is a
- ** separator key, or a range-delete-boundary only, do not allow it
- ** to clobber an older entry. */
- int nc1 = (eType1 & (LSM_INSERT|LSM_POINT_DELETE))==0;
- int nc2 = (eType2 & (LSM_INSERT|LSM_POINT_DELETE))==0;
- iRes = (nc1 > nc2) ? i2 : i1;
- }else if( res<0 ){
- iRes = i1;
- }else{
- iRes = i2;
- }
- }
- pCsr->aTree[iOut] = iRes;
- }
- /*
- ** This function advances segment pointer iPtr belonging to multi-cursor
- ** pCsr forward (bReverse==0) or backward (bReverse!=0).
- **
- ** If the segment pointer points to a segment that is part of a composite
- ** level, then the following special case is handled.
- **
- ** * If iPtr is the lhs of a composite level, and the cursor is being
- ** advanced forwards, and segment iPtr is at EOF, move all pointers
- ** that correspond to rhs segments of the same level to the first
- ** key in their respective data.
- */
- static int segmentCursorAdvance(
- MultiCursor *pCsr,
- int iPtr,
- int bReverse
- ){
- int rc;
- SegmentPtr *pPtr = &pCsr->aPtr[iPtr];
- Level *pLvl = pPtr->pLevel;
- int bComposite; /* True if pPtr is part of composite level */
- /* Advance the segment-pointer object. */
- rc = segmentPtrAdvance(pCsr, pPtr, bReverse);
- if( rc!=LSM_OK ) return rc;
- bComposite = (pLvl->nRight>0 && pCsr->nPtr>pLvl->nRight);
- if( bComposite && pPtr->pPg==0 ){
- int bFix = 0;
- if( (bReverse==0)==(pPtr->pSeg==&pLvl->lhs) ){
- int i;
- if( bReverse ){
- SegmentPtr *pLhs = &pCsr->aPtr[iPtr - 1 - (pPtr->pSeg - pLvl->aRhs)];
- for(i=0; i<pLvl->nRight; i++){
- if( pLhs[i+1].pPg ) break;
- }
- if( i==pLvl->nRight ){
- bFix = 1;
- rc = segmentPtrEnd(pCsr, pLhs, 1);
- }
- }else{
- bFix = 1;
- for(i=0; rc==LSM_OK && i<pLvl->nRight; i++){
- rc = sortedRhsFirst(pCsr, pLvl, &pCsr->aPtr[iPtr+1+i]);
- }
- }
- }
- if( bFix ){
- int i;
- for(i=pCsr->nTree-1; i>0; i--){
- multiCursorDoCompare(pCsr, i, bReverse);
- }
- }
- }
- #if 0
- if( bComposite && pPtr->pSeg==&pLvl->lhs /* lhs of composite level */
- && bReverse==0 /* csr advanced forwards */
- && pPtr->pPg==0 /* segment at EOF */
- ){
- int i;
- for(i=0; rc==LSM_OK && i<pLvl->nRight; i++){
- rc = sortedRhsFirst(pCsr, pLvl, &pCsr->aPtr[iPtr+1+i]);
- }
- for(i=pCsr->nTree-1; i>0; i--){
- multiCursorDoCompare(pCsr, i, 0);
- }
- }
- #endif
- return rc;
- }
- static void mcursorFreeComponents(MultiCursor *pCsr){
- int i;
- lsm_env *pEnv = pCsr->pDb->pEnv;
- /* Close the tree cursor, if any. */
- lsmTreeCursorDestroy(pCsr->apTreeCsr[0]);
- lsmTreeCursorDestroy(pCsr->apTreeCsr[1]);
- /* Reset the segment pointers */
- for(i=0; i<pCsr->nPtr; i++){
- segmentPtrReset(&pCsr->aPtr[i], 0);
- }
- /* And the b-tree cursor, if any */
- btreeCursorFree(pCsr->pBtCsr);
- /* Free allocations */
- lsmFree(pEnv, pCsr->aPtr);
- lsmFree(pEnv, pCsr->aTree);
- lsmFree(pEnv, pCsr->pSystemVal);
- /* Zero fields */
- pCsr->nPtr = 0;
- pCsr->aPtr = 0;
- pCsr->nTree = 0;
- pCsr->aTree = 0;
- pCsr->pSystemVal = 0;
- pCsr->apTreeCsr[0] = 0;
- pCsr->apTreeCsr[1] = 0;
- pCsr->pBtCsr = 0;
- }
- void lsmMCursorFreeCache(lsm_db *pDb){
- MultiCursor *p;
- MultiCursor *pNext;
- for(p=pDb->pCsrCache; p; p=pNext){
- pNext = p->pNext;
- lsmMCursorClose(p, 0);
- }
- pDb->pCsrCache = 0;
- }
- /*
- ** Close the cursor passed as the first argument.
- **
- ** If the bCache parameter is true, then shift the cursor to the pCsrCache
- ** list for possible reuse instead of actually deleting it.
- */
- void lsmMCursorClose(MultiCursor *pCsr, int bCache){
- if( pCsr ){
- lsm_db *pDb = pCsr->pDb;
- MultiCursor **pp; /* Iterator variable */
- /* The cursor may or may not be currently part of the linked list
- ** starting at lsm_db.pCsr. If it is, extract it. */
- for(pp=&pDb->pCsr; *pp; pp=&((*pp)->pNext)){
- if( *pp==pCsr ){
- *pp = pCsr->pNext;
- break;
- }
- }
- if( bCache ){
- int i; /* Used to iterate through segment-pointers */
- /* Release any page references held by this cursor. */
- assert( !pCsr->pBtCsr );
- for(i=0; i<pCsr->nPtr; i++){
- SegmentPtr *pPtr = &pCsr->aPtr[i];
- lsmFsPageRelease(pPtr->pPg);
- pPtr->pPg = 0;
- }
- /* Reset the tree cursors */
- lsmTreeCursorReset(pCsr->apTreeCsr[0]);
- lsmTreeCursorReset(pCsr->apTreeCsr[1]);
- /* Add the cursor to the pCsrCache list */
- pCsr->pNext = pDb->pCsrCache;
- pDb->pCsrCache = pCsr;
- }else{
- /* Free the allocation used to cache the current key, if any. */
- sortedBlobFree(&pCsr->key);
- sortedBlobFree(&pCsr->val);
- /* Free the component cursors */
- mcursorFreeComponents(pCsr);
- /* Free the cursor structure itself */
- lsmFree(pDb->pEnv, pCsr);
- }
- }
- }
- #define TREE_NONE 0
- #define TREE_OLD 1
- #define TREE_BOTH 2
- /*
- ** Parameter eTree is one of TREE_OLD or TREE_BOTH.
- */
- static int multiCursorAddTree(MultiCursor *pCsr, Snapshot *pSnap, int eTree){
- int rc = LSM_OK;
- lsm_db *db = pCsr->pDb;
- /* Add a tree cursor on the 'old' tree, if it exists. */
- if( eTree!=TREE_NONE
- && lsmTreeHasOld(db)
- && db->treehdr.iOldLog!=pSnap->iLogOff
- ){
- rc = lsmTreeCursorNew(db, 1, &pCsr->apTreeCsr[1]);
- }
- /* Add a tree cursor on the 'current' tree, if required. */
- if( rc==LSM_OK && eTree==TREE_BOTH ){
- rc = lsmTreeCursorNew(db, 0, &pCsr->apTreeCsr[0]);
- }
- return rc;
- }
- static int multiCursorAddRhs(MultiCursor *pCsr, Level *pLvl){
- int i;
- int nRhs = pLvl->nRight;
- assert( pLvl->nRight>0 );
- assert( pCsr->aPtr==0 );
- pCsr->aPtr = lsmMallocZero(pCsr->pDb->pEnv, sizeof(SegmentPtr) * nRhs);
- if( !pCsr->aPtr ) return LSM_NOMEM_BKPT;
- pCsr->nPtr = nRhs;
- for(i=0; i<nRhs; i++){
- pCsr->aPtr[i].pSeg = &pLvl->aRhs[i];
- pCsr->aPtr[i].pLevel = pLvl;
- }
- return LSM_OK;
- }
- static void multiCursorAddOne(MultiCursor *pCsr, Level *pLvl, int *pRc){
- if( *pRc==LSM_OK ){
- int iPtr = pCsr->nPtr;
- int i;
- pCsr->aPtr[iPtr].pLevel = pLvl;
- pCsr->aPtr[iPtr].pSeg = &pLvl->lhs;
- iPtr++;
- for(i=0; i<pLvl->nRight; i++){
- pCsr->aPtr[iPtr].pLevel = pLvl;
- pCsr->aPtr[iPtr].pSeg = &pLvl->aRhs[i];
- iPtr++;
- }
- if( pLvl->nRight && pLvl->pSplitKey==0 ){
- sortedSplitkey(pCsr->pDb, pLvl, pRc);
- }
- pCsr->nPtr = iPtr;
- }
- }
- static int multiCursorAddAll(MultiCursor *pCsr, Snapshot *pSnap){
- Level *pLvl;
- int nPtr = 0;
- int rc = LSM_OK;
- for(pLvl=pSnap->pLevel; pLvl; pLvl=pLvl->pNext){
- /* If the LEVEL_INCOMPLETE flag is set, then this function is being
- ** called (indirectly) from within a sortedNewToplevel() call to
- ** construct pLvl. In this case ignore pLvl - this cursor is going to
- ** be used to retrieve a freelist entry from the LSM, and the partially
- ** complete level may confuse it. */
- if( pLvl->flags & LEVEL_INCOMPLETE ) continue;
- nPtr += (1 + pLvl->nRight);
- }
- assert( pCsr->aPtr==0 );
- pCsr->aPtr = lsmMallocZeroRc(pCsr->pDb->pEnv, sizeof(SegmentPtr) * nPtr, &rc);
- for(pLvl=pSnap->pLevel; pLvl; pLvl=pLvl->pNext){
- if( (pLvl->flags & LEVEL_INCOMPLETE)==0 ){
- multiCursorAddOne(pCsr, pLvl, &rc);
- }
- }
- return rc;
- }
- static int multiCursorInit(MultiCursor *pCsr, Snapshot *pSnap){
- int rc;
- rc = multiCursorAddAll(pCsr, pSnap);
- if( rc==LSM_OK ){
- rc = multiCursorAddTree(pCsr, pSnap, TREE_BOTH);
- }
- pCsr->flags |= (CURSOR_IGNORE_SYSTEM | CURSOR_IGNORE_DELETE);
- return rc;
- }
- static MultiCursor *multiCursorNew(lsm_db *db, int *pRc){
- MultiCursor *pCsr;
- pCsr = (MultiCursor *)lsmMallocZeroRc(db->pEnv, sizeof(MultiCursor), pRc);
- if( pCsr ){
- pCsr->pNext = db->pCsr;
- db->pCsr = pCsr;
- pCsr->pDb = db;
- }
- return pCsr;
- }
- void lsmSortedRemap(lsm_db *pDb){
- MultiCursor *pCsr;
- for(pCsr=pDb->pCsr; pCsr; pCsr=pCsr->pNext){
- int iPtr;
- if( pCsr->pBtCsr ){
- btreeCursorLoadKey(pCsr->pBtCsr);
- }
- for(iPtr=0; iPtr<pCsr->nPtr; iPtr++){
- segmentPtrLoadCell(&pCsr->aPtr[iPtr], pCsr->aPtr[iPtr].iCell);
- }
- }
- }
- static void multiCursorReadSeparators(MultiCursor *pCsr){
- if( pCsr->nPtr>0 ){
- pCsr->flags |= CURSOR_READ_SEPARATORS;
- }
- }
- /*
- ** Have this cursor skip over SORTED_DELETE entries.
- */
- static void multiCursorIgnoreDelete(MultiCursor *pCsr){
- if( pCsr ) pCsr->flags |= CURSOR_IGNORE_DELETE;
- }
- /*
- ** If the free-block list is not empty, then have this cursor visit a key
- ** with (a) the system bit set, and (b) the key "FREELIST" and (c) a value
- ** blob containing the serialized free-block list.
- */
- static int multiCursorVisitFreelist(MultiCursor *pCsr){
- int rc = LSM_OK;
- pCsr->flags |= CURSOR_FLUSH_FREELIST;
- pCsr->pSystemVal = lsmMallocRc(pCsr->pDb->pEnv, 4 + 8, &rc);
- return rc;
- }
- /*
- ** Allocate and return a new database cursor.
- **
- ** This method should only be called to allocate user cursors. As it may
- ** recycle a cursor from lsm_db.pCsrCache.
- */
- int lsmMCursorNew(
- lsm_db *pDb, /* Database handle */
- MultiCursor **ppCsr /* OUT: Allocated cursor */
- ){
- MultiCursor *pCsr = 0;
- int rc = LSM_OK;
- if( pDb->pCsrCache ){
- int bOld; /* True if there is an old in-memory tree */
- /* Remove a cursor from the pCsrCache list and add it to the open list. */
- pCsr = pDb->pCsrCache;
- pDb->pCsrCache = pCsr->pNext;
- pCsr->pNext = pDb->pCsr;
- pDb->pCsr = pCsr;
- /* The cursor can almost be used as is, except that the old in-memory
- ** tree cursor may be present and not required, or required and not
- ** present. Fix this if required. */
- bOld = (lsmTreeHasOld(pDb) && pDb->treehdr.iOldLog!=pDb->pClient->iLogOff);
- if( !bOld && pCsr->apTreeCsr[1] ){
- lsmTreeCursorDestroy(pCsr->apTreeCsr[1]);
- pCsr->apTreeCsr[1] = 0;
- }else if( bOld && !pCsr->apTreeCsr[1] ){
- rc = lsmTreeCursorNew(pDb, 1, &pCsr->apTreeCsr[1]);
- }
- pCsr->flags = (CURSOR_IGNORE_SYSTEM | CURSOR_IGNORE_DELETE);
- }else{
- pCsr = multiCursorNew(pDb, &rc);
- if( rc==LSM_OK ) rc = multiCursorInit(pCsr, pDb->pClient);
- }
- if( rc!=LSM_OK ){
- lsmMCursorClose(pCsr, 0);
- pCsr = 0;
- }
- assert( (rc==LSM_OK)==(pCsr!=0) );
- *ppCsr = pCsr;
- return rc;
- }
- static int multiCursorGetVal(
- MultiCursor *pCsr,
- int iVal,
- void **ppVal,
- int *pnVal
- ){
- int rc = LSM_OK;
- *ppVal = 0;
- *pnVal = 0;
- switch( iVal ){
- case CURSOR_DATA_TREE0:
- case CURSOR_DATA_TREE1: {
- TreeCursor *pTreeCsr = pCsr->apTreeCsr[iVal-CURSOR_DATA_TREE0];
- if( lsmTreeCursorValid(pTreeCsr) ){
- lsmTreeCursorValue(pTreeCsr, ppVal, pnVal);
- }else{
- *ppVal = 0;
- *pnVal = 0;
- }
- break;
- }
- case CURSOR_DATA_SYSTEM: {
- Snapshot *pWorker = pCsr->pDb->pWorker;
- if( pWorker
- && (pCsr->iFree % 2)==0
- && pCsr->iFree < (pWorker->freelist.nEntry*2)
- ){
- int iEntry = pWorker->freelist.nEntry - 1 - (pCsr->iFree / 2);
- u8 *aVal = &((u8 *)(pCsr->pSystemVal))[4];
- lsmPutU64(aVal, pWorker->freelist.aEntry[iEntry].iId);
- *ppVal = aVal;
- *pnVal = 8;
- }
- break;
- }
- default: {
- int iPtr = iVal-CURSOR_DATA_SEGMENT;
- if( iPtr<pCsr->nPtr ){
- SegmentPtr *pPtr = &pCsr->aPtr[iPtr];
- if( pPtr->pPg ){
- *ppVal = pPtr->pVal;
- *pnVal = pPtr->nVal;
- }
- }
- }
- }
- assert( rc==LSM_OK || (*ppVal==0 && *pnVal==0) );
- return rc;
- }
- static int multiCursorAdvance(MultiCursor *pCsr, int bReverse);
- /*
- ** This function is called by worker connections to walk the part of the
- ** free-list stored within the LSM data structure.
- */
- int lsmSortedWalkFreelist(
- lsm_db *pDb, /* Database handle */
- int bReverse, /* True to iterate from largest to smallest */
- int (*x)(void *, int, i64), /* Callback function */
- void *pCtx /* First argument to pass to callback */
- ){
- MultiCursor *pCsr; /* Cursor used to read db */
- int rc = LSM_OK; /* Return Code */
- Snapshot *pSnap = 0;
- assert( pDb->pWorker );
- if( pDb->bIncrMerge ){
- rc = lsmCheckpointDeserialize(pDb, 0, pDb->pShmhdr->aSnap1, &pSnap);
- if( rc!=LSM_OK ) return rc;
- }else{
- pSnap = pDb->pWorker;
- }
- pCsr = multiCursorNew(pDb, &rc);
- if( pCsr ){
- rc = multiCursorAddAll(pCsr, pSnap);
- pCsr->flags |= CURSOR_IGNORE_DELETE;
- }
-
- if( rc==LSM_OK ){
- if( bReverse==0 ){
- rc = lsmMCursorLast(pCsr);
- }else{
- rc = lsmMCursorSeek(pCsr, 1, "", 0, LSM_SEEK_GE);
- }
- while( rc==LSM_OK && lsmMCursorValid(pCsr) && rtIsSystem(pCsr->eType) ){
- void *pKey; int nKey;
- void *pVal = 0; int nVal = 0;
- rc = lsmMCursorKey(pCsr, &pKey, &nKey);
- if( rc==LSM_OK ) rc = lsmMCursorValue(pCsr, &pVal, &nVal);
- if( rc==LSM_OK && (nKey!=4 || nVal!=8) ) rc = LSM_CORRUPT_BKPT;
- if( rc==LSM_OK ){
- int iBlk;
- i64 iSnap;
- iBlk = (int)(~(lsmGetU32((u8 *)pKey)));
- iSnap = (i64)lsmGetU64((u8 *)pVal);
- if( x(pCtx, iBlk, iSnap) ) break;
- rc = multiCursorAdvance(pCsr, !bReverse);
- }
- }
- }
- lsmMCursorClose(pCsr, 0);
- if( pSnap!=pDb->pWorker ){
- lsmFreeSnapshot(pDb->pEnv, pSnap);
- }
- return rc;
- }
- int lsmSortedLoadFreelist(
- lsm_db *pDb, /* Database handle (must be worker) */
- void **ppVal, /* OUT: Blob containing LSM free-list */
- int *pnVal /* OUT: Size of *ppVal blob in bytes */
- ){
- MultiCursor *pCsr; /* Cursor used to retreive free-list */
- int rc = LSM_OK; /* Return Code */
- assert( pDb->pWorker );
- assert( *ppVal==0 && *pnVal==0 );
- pCsr = multiCursorNew(pDb, &rc);
- if( pCsr ){
- rc = multiCursorAddAll(pCsr, pDb->pWorker);
- pCsr->flags |= CURSOR_IGNORE_DELETE;
- }
-
- if( rc==LSM_OK ){
- rc = lsmMCursorLast(pCsr);
- if( rc==LSM_OK
- && rtIsWrite(pCsr->eType) && rtIsSystem(pCsr->eType)
- && pCsr->key.nData==8
- && 0==memcmp(pCsr->key.pData, "FREELIST", 8)
- ){
- void *pVal; int nVal; /* Value read from database */
- rc = lsmMCursorValue(pCsr, &pVal, &nVal);
- if( rc==LSM_OK ){
- *ppVal = lsmMallocRc(pDb->pEnv, nVal, &rc);
- if( *ppVal ){
- memcpy(*ppVal, pVal, nVal);
- *pnVal = nVal;
- }
- }
- }
- lsmMCursorClose(pCsr, 0);
- }
- return rc;
- }
- static int multiCursorAllocTree(MultiCursor *pCsr){
- int rc = LSM_OK;
- if( pCsr->aTree==0 ){
- int nByte; /* Bytes of space to allocate */
- int nMin; /* Total number of cursors being merged */
- nMin = CURSOR_DATA_SEGMENT + pCsr->nPtr + (pCsr->pBtCsr!=0);
- pCsr->nTree = 2;
- while( pCsr->nTree<nMin ){
- pCsr->nTree = pCsr->nTree*2;
- }
- nByte = sizeof(int)*pCsr->nTree*2;
- pCsr->aTree = (int *)lsmMallocZeroRc(pCsr->pDb->pEnv, nByte, &rc);
- }
- return rc;
- }
- static void multiCursorCacheKey(MultiCursor *pCsr, int *pRc){
- if( *pRc==LSM_OK ){
- void *pKey;
- int nKey;
- multiCursorGetKey(pCsr, pCsr->aTree[1], &pCsr->eType, &pKey, &nKey);
- *pRc = sortedBlobSet(pCsr->pDb->pEnv, &pCsr->key, pKey, nKey);
- }
- }
- #ifdef LSM_DEBUG_EXPENSIVE
- static void assertCursorTree(MultiCursor *pCsr){
- int bRev = !!(pCsr->flags & CURSOR_PREV_OK);
- int *aSave = pCsr->aTree;
- int nSave = pCsr->nTree;
- int rc;
- pCsr->aTree = 0;
- pCsr->nTree = 0;
- rc = multiCursorAllocTree(pCsr);
- if( rc==LSM_OK ){
- int i;
- for(i=pCsr->nTree-1; i>0; i--){
- multiCursorDoCompare(pCsr, i, bRev);
- }
- assert( nSave==pCsr->nTree
- && 0==memcmp(aSave, pCsr->aTree, sizeof(int)*nSave)
- );
- lsmFree(pCsr->pDb->pEnv, pCsr->aTree);
- }
- pCsr->aTree = aSave;
- pCsr->nTree = nSave;
- }
- #else
- # define assertCursorTree(x)
- #endif
- static int mcursorLocationOk(MultiCursor *pCsr, int bDeleteOk){
- int eType = pCsr->eType;
- int iKey;
- int i;
- int rdmask;
-
- assert( pCsr->flags & (CURSOR_NEXT_OK|CURSOR_PREV_OK) );
- assertCursorTree(pCsr);
- rdmask = (pCsr->flags & CURSOR_NEXT_OK) ? LSM_END_DELETE : LSM_START_DELETE;
- /* If the cursor does not currently point to an actual database key (i.e.
- ** it points to a delete key, or the start or end of a range-delete), and
- ** the CURSOR_IGNORE_DELETE flag is set, skip past this entry. */
- if( (pCsr->flags & CURSOR_IGNORE_DELETE) && bDeleteOk==0 ){
- if( (eType & LSM_INSERT)==0 ) return 0;
- }
- /* If the cursor points to a system key (free-list entry), and the
- ** CURSOR_IGNORE_SYSTEM flag is set, skip thie entry. */
- if( (pCsr->flags & CURSOR_IGNORE_SYSTEM) && rtTopic(eType)!=0 ){
- return 0;
- }
- #ifndef NDEBUG
- /* This block fires assert() statements to check one of the assumptions
- ** in the comment below - that if the lhs sub-cursor of a level undergoing
- ** a merge is valid, then all the rhs sub-cursors must be at EOF.
- **
- ** Also assert that all rhs sub-cursors are either at EOF or point to
- ** a key that is not less than the level split-key. */
- for(i=0; i<pCsr->nPtr; i++){
- SegmentPtr *pPtr = &pCsr->aPtr[i];
- Level *pLvl = pPtr->pLevel;
- if( pLvl->nRight && pPtr->pPg ){
- if( pPtr->pSeg==&pLvl->lhs ){
- int j;
- for(j=0; j<pLvl->nRight; j++) assert( pPtr[j+1].pPg==0 );
- }else{
- int res = sortedKeyCompare(pCsr->pDb->xCmp,
- rtTopic(pPtr->eType), pPtr->pKey, pPtr->nKey,
- pLvl->iSplitTopic, pLvl->pSplitKey, pLvl->nSplitKey
- );
- assert( res>=0 );
- }
- }
- }
- #endif
- /* Now check if this key has already been deleted by a range-delete. If
- ** so, skip past it.
- **
- ** Assume, for the moment, that the tree contains no levels currently
- ** undergoing incremental merge, and that this cursor is iterating forwards
- ** through the database keys. The cursor currently points to a key in
- ** level L. This key has already been deleted if any of the sub-cursors
- ** that point to levels newer than L (or to the in-memory tree) point to
- ** a key greater than the current key with the LSM_END_DELETE flag set.
- **
- ** Or, if the cursor is iterating backwards through data keys, if any
- ** such sub-cursor points to a key smaller than the current key with the
- ** LSM_START_DELETE flag set.
- **
- ** Why it works with levels undergoing a merge too:
- **
- ** When a cursor iterates forwards, the sub-cursors for the rhs of a
- ** level are only activated once the lhs reaches EOF. So when iterating
- ** forwards, the keys visited are the same as if the level was completely
- ** merged.
- **
- ** If the cursor is iterating backwards, then the lhs sub-cursor is not
- ** initialized until the last of the rhs sub-cursors has reached EOF.
- ** Additionally, if the START_DELETE flag is set on the last entry (in
- ** reverse order - so the entry with the smallest key) of a rhs sub-cursor,
- ** then a pseudo-key equal to the levels split-key with the END_DELETE
- ** flag set is visited by the sub-cursor.
- */
- iKey = pCsr->aTree[1];
- for(i=0; i<iKey; i++){
- int csrflags;
- multiCursorGetKey(pCsr, i, &csrflags, 0, 0);
- if( (rdmask & csrflags) ){
- const int SD_ED = (LSM_START_DELETE|LSM_END_DELETE);
- if( (csrflags & SD_ED)==SD_ED
- || (pCsr->flags & CURSOR_IGNORE_DELETE)==0
- ){
- void *pKey; int nKey;
- multiCursorGetKey(pCsr, i, 0, &pKey, &nKey);
- if( 0==sortedKeyCompare(pCsr->pDb->xCmp,
- rtTopic(eType), pCsr->key.pData, pCsr->key.nData,
- rtTopic(csrflags), pKey, nKey
- )){
- continue;
- }
- }
- return 0;
- }
- }
- /* The current cursor position is one this cursor should visit. Return 1. */
- return 1;
- }
- static int multiCursorSetupTree(MultiCursor *pCsr, int bRev){
- int rc;
- rc = multiCursorAllocTree(pCsr);
- if( rc==LSM_OK ){
- int i;
- for(i=pCsr->nTree-1; i>0; i--){
- multiCursorDoCompare(pCsr, i, bRev);
- }
- }
- assertCursorTree(pCsr);
- multiCursorCacheKey(pCsr, &rc);
- if( rc==LSM_OK && mcursorLocationOk(pCsr, 0)==0 ){
- rc = multiCursorAdvance(pCsr, bRev);
- }
- return rc;
- }
- static int multiCursorEnd(MultiCursor *pCsr, int bLast){
- int rc = LSM_OK;
- int i;
- pCsr->flags &= ~(CURSOR_NEXT_OK | CURSOR_PREV_OK | CURSOR_SEEK_EQ);
- pCsr->flags |= (bLast ? CURSOR_PREV_OK : CURSOR_NEXT_OK);
- pCsr->iFree = 0;
- /* Position the two in-memory tree cursors */
- for(i=0; rc==LSM_OK && i<2; i++){
- if( pCsr->apTreeCsr[i] ){
- rc = lsmTreeCursorEnd(pCsr->apTreeCsr[i], bLast);
- }
- }
- for(i=0; rc==LSM_OK && i<pCsr->nPtr; i++){
- SegmentPtr *pPtr = &pCsr->aPtr[i];
- Level *pLvl = pPtr->pLevel;
- int iRhs;
- int bHit = 0;
- if( bLast ){
- for(iRhs=0; iRhs<pLvl->nRight && rc==LSM_OK; iRhs++){
- rc = segmentPtrEnd(pCsr, &pPtr[iRhs+1], 1);
- if( pPtr[iRhs+1].pPg ) bHit = 1;
- }
- if( bHit==0 && rc==LSM_OK ){
- rc = segmentPtrEnd(pCsr, pPtr, 1);
- }else{
- segmentPtrReset(pPtr, LSM_SEGMENTPTR_FREE_THRESHOLD);
- }
- }else{
- int bLhs = (pPtr->pSeg==&pLvl->lhs);
- assert( pPtr->pSeg==&pLvl->lhs || pPtr->pSeg==&pLvl->aRhs[0] );
- if( bLhs ){
- rc = segmentPtrEnd(pCsr, pPtr, 0);
- if( pPtr->pKey ) bHit = 1;
- }
- for(iRhs=0; iRhs<pLvl->nRight && rc==LSM_OK; iRhs++){
- if( bHit ){
- segmentPtrReset(&pPtr[iRhs+1], LSM_SEGMENTPTR_FREE_THRESHOLD);
- }else{
- rc = sortedRhsFirst(pCsr, pLvl, &pPtr[iRhs+bLhs]);
- }
- }
- }
- i += pLvl->nRight;
- }
- /* And the b-tree cursor, if applicable */
- if( rc==LSM_OK && pCsr->pBtCsr ){
- assert( bLast==0 );
- rc = btreeCursorFirst(pCsr->pBtCsr);
- }
- if( rc==LSM_OK ){
- rc = multiCursorSetupTree(pCsr, bLast);
- }
-
- return rc;
- }
- int mcursorSave(MultiCursor *pCsr){
- int rc = LSM_OK;
- if( pCsr->aTree ){
- int iTree = pCsr->aTree[1];
- if( iTree==CURSOR_DATA_TREE0 || iTree==CURSOR_DATA_TREE1 ){
- multiCursorCacheKey(pCsr, &rc);
- }
- }
- mcursorFreeComponents(pCsr);
- return rc;
- }
- int mcursorRestore(lsm_db *pDb, MultiCursor *pCsr){
- int rc;
- rc = multiCursorInit(pCsr, pDb->pClient);
- if( rc==LSM_OK && pCsr->key.pData ){
- rc = lsmMCursorSeek(pCsr,
- rtTopic(pCsr->eType), pCsr->key.pData, pCsr->key.nData, +1
- );
- }
- return rc;
- }
- int lsmSaveCursors(lsm_db *pDb){
- int rc = LSM_OK;
- MultiCursor *pCsr;
- for(pCsr=pDb->pCsr; rc==LSM_OK && pCsr; pCsr=pCsr->pNext){
- rc = mcursorSave(pCsr);
- }
- return rc;
- }
- int lsmRestoreCursors(lsm_db *pDb){
- int rc = LSM_OK;
- MultiCursor *pCsr;
- for(pCsr=pDb->pCsr; rc==LSM_OK && pCsr; pCsr=pCsr->pNext){
- rc = mcursorRestore(pDb, pCsr);
- }
- return rc;
- }
- int lsmMCursorFirst(MultiCursor *pCsr){
- return multiCursorEnd(pCsr, 0);
- }
- int lsmMCursorLast(MultiCursor *pCsr){
- return multiCursorEnd(pCsr, 1);
- }
- lsm_db *lsmMCursorDb(MultiCursor *pCsr){
- return pCsr->pDb;
- }
- void lsmMCursorReset(MultiCursor *pCsr){
- int i;
- lsmTreeCursorReset(pCsr->apTreeCsr[0]);
- lsmTreeCursorReset(pCsr->apTreeCsr[1]);
- for(i=0; i<pCsr->nPtr; i++){
- segmentPtrReset(&pCsr->aPtr[i], LSM_SEGMENTPTR_FREE_THRESHOLD);
- }
- pCsr->key.nData = 0;
- }
- static int treeCursorSeek(
- MultiCursor *pCsr,
- TreeCursor *pTreeCsr,
- void *pKey, int nKey,
- int eSeek,
- int *pbStop
- ){
- int rc = LSM_OK;
- if( pTreeCsr ){
- int res = 0;
- lsmTreeCursorSeek(pTreeCsr, pKey, nKey, &res);
- switch( eSeek ){
- case LSM_SEEK_EQ: {
- int eType = lsmTreeCursorFlags(pTreeCsr);
- if( (res<0 && (eType & LSM_START_DELETE))
- || (res>0 && (eType & LSM_END_DELETE))
- || (res==0 && (eType & LSM_POINT_DELETE))
- ){
- *pbStop = 1;
- }else if( res==0 && (eType & LSM_INSERT) ){
- lsm_env *pEnv = pCsr->pDb->pEnv;
- void *p; int n; /* Key/value from tree-cursor */
- *pbStop = 1;
- pCsr->flags |= CURSOR_SEEK_EQ;
- rc = lsmTreeCursorKey(pTreeCsr, &pCsr->eType, &p, &n);
- if( rc==LSM_OK ) rc = sortedBlobSet(pEnv, &pCsr->key, p, n);
- if( rc==LSM_OK ) rc = lsmTreeCursorValue(pTreeCsr, &p, &n);
- if( rc==LSM_OK ) rc = sortedBlobSet(pEnv, &pCsr->val, p, n);
- }
- lsmTreeCursorReset(pTreeCsr);
- break;
- }
- case LSM_SEEK_GE:
- if( res<0 && lsmTreeCursorValid(pTreeCsr) ){
- lsmTreeCursorNext(pTreeCsr);
- }
- break;
- default:
- if( res>0 ){
- assert( lsmTreeCursorValid(pTreeCsr) );
- lsmTreeCursorPrev(pTreeCsr);
- }
- break;
- }
- }
- return rc;
- }
- /*
- ** Seek the cursor.
- */
- int lsmMCursorSeek(
- MultiCursor *pCsr,
- int iTopic,
- void *pKey, int nKey,
- int eSeek
- ){
- int eESeek = eSeek; /* Effective eSeek parameter */
- int bStop = 0; /* Set to true to halt search operation */
- int rc = LSM_OK; /* Return code */
- int iPtr = 0; /* Used to iterate through pCsr->aPtr[] */
- LsmPgno iPgno = 0; /* FC pointer value */
- assert( pCsr->apTreeCsr[0]==0 || iTopic==0 );
- assert( pCsr->apTreeCsr[1]==0 || iTopic==0 );
- if( eESeek==LSM_SEEK_LEFAST ) eESeek = LSM_SEEK_LE;
- assert( eESeek==LSM_SEEK_EQ || eESeek==LSM_SEEK_LE || eESeek==LSM_SEEK_GE );
- assert( (pCsr->flags & CURSOR_FLUSH_FREELIST)==0 );
- assert( pCsr->nPtr==0 || pCsr->aPtr[0].pLevel );
- pCsr->flags &= ~(CURSOR_NEXT_OK | CURSOR_PREV_OK | CURSOR_SEEK_EQ);
- rc = treeCursorSeek(pCsr, pCsr->apTreeCsr[0], pKey, nKey, eESeek, &bStop);
- if( rc==LSM_OK && bStop==0 ){
- rc = treeCursorSeek(pCsr, pCsr->apTreeCsr[1], pKey, nKey, eESeek, &bStop);
- }
- /* Seek all segment pointers. */
- for(iPtr=0; iPtr<pCsr->nPtr && rc==LSM_OK && bStop==0; iPtr++){
- SegmentPtr *pPtr = &pCsr->aPtr[iPtr];
- assert( pPtr->pSeg==&pPtr->pLevel->lhs );
- rc = seekInLevel(pCsr, pPtr, eESeek, iTopic, pKey, nKey, &iPgno, &bStop);
- iPtr += pPtr->pLevel->nRight;
- }
- if( eSeek!=LSM_SEEK_EQ ){
- if( rc==LSM_OK ){
- rc = multiCursorAllocTree(pCsr);
- }
- if( rc==LSM_OK ){
- int i;
- for(i=pCsr->nTree-1; i>0; i--){
- multiCursorDoCompare(pCsr, i, eESeek==LSM_SEEK_LE);
- }
- if( eSeek==LSM_SEEK_GE ) pCsr->flags |= CURSOR_NEXT_OK;
- if( eSeek==LSM_SEEK_LE ) pCsr->flags |= CURSOR_PREV_OK;
- }
- multiCursorCacheKey(pCsr, &rc);
- if( rc==LSM_OK && eSeek!=LSM_SEEK_LEFAST && 0==mcursorLocationOk(pCsr, 0) ){
- switch( eESeek ){
- case LSM_SEEK_EQ:
- lsmMCursorReset(pCsr);
- break;
- case LSM_SEEK_GE:
- rc = lsmMCursorNext(pCsr);
- break;
- default:
- rc = lsmMCursorPrev(pCsr);
- break;
- }
- }
- }
- return rc;
- }
- int lsmMCursorValid(MultiCursor *pCsr){
- int res = 0;
- if( pCsr->flags & CURSOR_SEEK_EQ ){
- res = 1;
- }else if( pCsr->aTree ){
- int iKey = pCsr->aTree[1];
- if( iKey==CURSOR_DATA_TREE0 || iKey==CURSOR_DATA_TREE1 ){
- res = lsmTreeCursorValid(pCsr->apTreeCsr[iKey-CURSOR_DATA_TREE0]);
- }else{
- void *pKey;
- multiCursorGetKey(pCsr, iKey, 0, &pKey, 0);
- res = pKey!=0;
- }
- }
- return res;
- }
- static int mcursorAdvanceOk(
- MultiCursor *pCsr,
- int bReverse,
- int *pRc
- ){
- void *pNew; /* Pointer to buffer containing new key */
- int nNew; /* Size of buffer pNew in bytes */
- int eNewType; /* Type of new record */
- if( *pRc ) return 1;
- /* Check the current key value. If it is not greater than (if bReverse==0)
- ** or less than (if bReverse!=0) the key currently cached in pCsr->key,
- ** then the cursor has not yet been successfully advanced.
- */
- multiCursorGetKey(pCsr, pCsr->aTree[1], &eNewType, &pNew, &nNew);
- if( pNew ){
- int typemask = (pCsr->flags & CURSOR_IGNORE_DELETE) ? ~(0) : LSM_SYSTEMKEY;
- int res = sortedDbKeyCompare(pCsr,
- eNewType & typemask, pNew, nNew,
- pCsr->eType & typemask, pCsr->key.pData, pCsr->key.nData
- );
- if( (bReverse==0 && res<=0) || (bReverse!=0 && res>=0) ){
- return 0;
- }
- multiCursorCacheKey(pCsr, pRc);
- assert( pCsr->eType==eNewType );
- /* If this cursor is configured to skip deleted keys, and the current
- ** cursor points to a SORTED_DELETE entry, then the cursor has not been
- ** successfully advanced.
- **
- ** Similarly, if the cursor is configured to skip system keys and the
- ** current cursor points to a system key, it has not yet been advanced.
- */
- if( *pRc==LSM_OK && 0==mcursorLocationOk(pCsr, 0) ) return 0;
- }
- return 1;
- }
- static void flCsrAdvance(MultiCursor *pCsr){
- assert( pCsr->flags & CURSOR_FLUSH_FREELIST );
- if( pCsr->iFree % 2 ){
- pCsr->iFree++;
- }else{
- int nEntry = pCsr->pDb->pWorker->freelist.nEntry;
- FreelistEntry *aEntry = pCsr->pDb->pWorker->freelist.aEntry;
- int i = nEntry - 1 - (pCsr->iFree / 2);
- /* If the current entry is a delete and the "end-delete" key will not
- ** be attached to the next entry, increment iFree by 1 only. */
- if( aEntry[i].iId<0 ){
- while( 1 ){
- if( i==0 || aEntry[i-1].iBlk!=aEntry[i].iBlk-1 ){
- pCsr->iFree--;
- break;
- }
- if( aEntry[i-1].iId>=0 ) break;
- pCsr->iFree += 2;
- i--;
- }
- }
- pCsr->iFree += 2;
- }
- }
- static int multiCursorAdvance(MultiCursor *pCsr, int bReverse){
- int rc = LSM_OK; /* Return Code */
- if( lsmMCursorValid(pCsr) ){
- do {
- int iKey = pCsr->aTree[1];
- assertCursorTree(pCsr);
- /* If this multi-cursor is advancing forwards, and the sub-cursor
- ** being advanced is the one that separator keys may be being read
- ** from, record the current absolute pointer value. */
- if( pCsr->pPrevMergePtr ){
- if( iKey==(CURSOR_DATA_SEGMENT+pCsr->nPtr) ){
- assert( pCsr->pBtCsr );
- *pCsr->pPrevMergePtr = pCsr->pBtCsr->iPtr;
- }else if( pCsr->pBtCsr==0 && pCsr->nPtr>0
- && iKey==(CURSOR_DATA_SEGMENT+pCsr->nPtr-1)
- ){
- SegmentPtr *pPtr = &pCsr->aPtr[iKey-CURSOR_DATA_SEGMENT];
- *pCsr->pPrevMergePtr = pPtr->iPtr+pPtr->iPgPtr;
- }
- }
- if( iKey==CURSOR_DATA_TREE0 || iKey==CURSOR_DATA_TREE1 ){
- TreeCursor *pTreeCsr = pCsr->apTreeCsr[iKey-CURSOR_DATA_TREE0];
- if( bReverse ){
- rc = lsmTreeCursorPrev(pTreeCsr);
- }else{
- rc = lsmTreeCursorNext(pTreeCsr);
- }
- }else if( iKey==CURSOR_DATA_SYSTEM ){
- assert( pCsr->flags & CURSOR_FLUSH_FREELIST );
- assert( bReverse==0 );
- flCsrAdvance(pCsr);
- }else if( iKey==(CURSOR_DATA_SEGMENT+pCsr->nPtr) ){
- assert( bReverse==0 && pCsr->pBtCsr );
- rc = btreeCursorNext(pCsr->pBtCsr);
- }else{
- rc = segmentCursorAdvance(pCsr, iKey-CURSOR_DATA_SEGMENT, bReverse);
- }
- if( rc==LSM_OK ){
- int i;
- for(i=(iKey+pCsr->nTree)/2; i>0; i=i/2){
- multiCursorDoCompare(pCsr, i, bReverse);
- }
- assertCursorTree(pCsr);
- }
- }while( mcursorAdvanceOk(pCsr, bReverse, &rc)==0 );
- }
- return rc;
- }
- int lsmMCursorNext(MultiCursor *pCsr){
- if( (pCsr->flags & CURSOR_NEXT_OK)==0 ) return LSM_MISUSE_BKPT;
- return multiCursorAdvance(pCsr, 0);
- }
- int lsmMCursorPrev(MultiCursor *pCsr){
- if( (pCsr->flags & CURSOR_PREV_OK)==0 ) return LSM_MISUSE_BKPT;
- return multiCursorAdvance(pCsr, 1);
- }
- int lsmMCursorKey(MultiCursor *pCsr, void **ppKey, int *pnKey){
- if( (pCsr->flags & CURSOR_SEEK_EQ) || pCsr->aTree==0 ){
- *pnKey = pCsr->key.nData;
- *ppKey = pCsr->key.pData;
- }else{
- int iKey = pCsr->aTree[1];
- if( iKey==CURSOR_DATA_TREE0 || iKey==CURSOR_DATA_TREE1 ){
- TreeCursor *pTreeCsr = pCsr->apTreeCsr[iKey-CURSOR_DATA_TREE0];
- lsmTreeCursorKey(pTreeCsr, 0, ppKey, pnKey);
- }else{
- int nKey;
- #ifndef NDEBUG
- void *pKey;
- int eType;
- multiCursorGetKey(pCsr, iKey, &eType, &pKey, &nKey);
- assert( eType==pCsr->eType );
- assert( nKey==pCsr->key.nData );
- assert( memcmp(pKey, pCsr->key.pData, nKey)==0 );
- #endif
- nKey = pCsr->key.nData;
- if( nKey==0 ){
- *ppKey = 0;
- }else{
- *ppKey = pCsr->key.pData;
- }
- *pnKey = nKey;
- }
- }
- return LSM_OK;
- }
- /*
- ** Compare the current key that cursor csr points to with pKey/nKey. Set
- ** *piRes to the result and return LSM_OK.
- */
- int lsm_csr_cmp(lsm_cursor *csr, const void *pKey, int nKey, int *piRes){
- MultiCursor *pCsr = (MultiCursor *)csr;
- void *pCsrkey; int nCsrkey;
- int rc;
- rc = lsmMCursorKey(pCsr, &pCsrkey, &nCsrkey);
- if( rc==LSM_OK ){
- int (*xCmp)(void *, int, void *, int) = pCsr->pDb->xCmp;
- *piRes = sortedKeyCompare(xCmp, 0, pCsrkey, nCsrkey, 0, (void *)pKey, nKey);
- }
- return rc;
- }
- int lsmMCursorValue(MultiCursor *pCsr, void **ppVal, int *pnVal){
- void *pVal;
- int nVal;
- int rc;
- if( (pCsr->flags & CURSOR_SEEK_EQ) || pCsr->aTree==0 ){
- rc = LSM_OK;
- nVal = pCsr->val.nData;
- pVal = pCsr->val.pData;
- }else{
- assert( pCsr->aTree );
- assert( mcursorLocationOk(pCsr, (pCsr->flags & CURSOR_IGNORE_DELETE)) );
- rc = multiCursorGetVal(pCsr, pCsr->aTree[1], &pVal, &nVal);
- if( pVal && rc==LSM_OK ){
- rc = sortedBlobSet(pCsr->pDb->pEnv, &pCsr->val, pVal, nVal);
- pVal = pCsr->val.pData;
- }
- if( rc!=LSM_OK ){
- pVal = 0;
- nVal = 0;
- }
- }
- *ppVal = pVal;
- *pnVal = nVal;
- return rc;
- }
- int lsmMCursorType(MultiCursor *pCsr, int *peType){
- assert( pCsr->aTree );
- multiCursorGetKey(pCsr, pCsr->aTree[1], peType, 0, 0);
- return LSM_OK;
- }
- /*
- ** Buffer aData[], size nData, is assumed to contain a valid b-tree
- ** hierarchy page image. Return the offset in aData[] of the next free
- ** byte in the data area (where a new cell may be written if there is
- ** space).
- */
- static int mergeWorkerPageOffset(u8 *aData, int nData){
- int nRec;
- int iOff;
- int nKey;
- int eType;
- i64 nDummy;
- nRec = lsmGetU16(&aData[SEGMENT_NRECORD_OFFSET(nData)]);
- iOff = lsmGetU16(&aData[SEGMENT_CELLPTR_OFFSET(nData, nRec-1)]);
- eType = aData[iOff++];
- assert( eType==0
- || eType==(LSM_SYSTEMKEY|LSM_SEPARATOR)
- || eType==(LSM_SEPARATOR)
- );
- iOff += lsmVarintGet64(&aData[iOff], &nDummy);
- iOff += lsmVarintGet32(&aData[iOff], &nKey);
- return iOff + (eType ? nKey : 0);
- }
- /*
- ** Following a checkpoint operation, database pages that are part of the
- ** checkpointed state of the LSM are deemed read-only. This includes the
- ** right-most page of the b-tree hierarchy of any separators array under
- ** construction, and all pages between it and the b-tree root, inclusive.
- ** This is a problem, as when further pages are appended to the separators
- ** array, entries must be added to the indicated b-tree hierarchy pages.
- **
- ** This function copies all such b-tree pages to new locations, so that
- ** they can be modified as required.
- **
- ** The complication is that not all database pages are the same size - due
- ** to the way the file.c module works some (the first and last in each block)
- ** are 4 bytes smaller than the others.
- */
- static int mergeWorkerMoveHierarchy(
- MergeWorker *pMW, /* Merge worker */
- int bSep /* True for separators run */
- ){
- lsm_db *pDb = pMW->pDb; /* Database handle */
- int rc = LSM_OK; /* Return code */
- int i;
- Page **apHier = pMW->hier.apHier;
- int nHier = pMW->hier.nHier;
- for(i=0; rc==LSM_OK && i<nHier; i++){
- Page *pNew = 0;
- rc = lsmFsSortedAppend(pDb->pFS, pDb->pWorker, pMW->pLevel, 1, &pNew);
- assert( rc==LSM_OK );
- if( rc==LSM_OK ){
- u8 *a1; int n1;
- u8 *a2; int n2;
- a1 = fsPageData(pNew, &n1);
- a2 = fsPageData(apHier[i], &n2);
- assert( n1==n2 || n1+4==n2 );
- if( n1==n2 ){
- memcpy(a1, a2, n2);
- }else{
- int nEntry = pageGetNRec(a2, n2);
- int iEof1 = SEGMENT_EOF(n1, nEntry);
- int iEof2 = SEGMENT_EOF(n2, nEntry);
- memcpy(a1, a2, iEof2 - 4);
- memcpy(&a1[iEof1], &a2[iEof2], n2 - iEof2);
- }
- lsmFsPageRelease(apHier[i]);
- apHier[i] = pNew;
- #if 0
- assert( n1==n2 || n1+4==n2 || n2+4==n1 );
- if( n1>=n2 ){
- /* If n1 (size of the new page) is equal to or greater than n2 (the
- ** size of the old page), then copy the data into the new page. If
- ** n1==n2, this could be done with a single memcpy(). However,
- ** since sometimes n1>n2, the page content and footer must be copied
- ** separately. */
- int nEntry = pageGetNRec(a2, n2);
- int iEof1 = SEGMENT_EOF(n1, nEntry);
- int iEof2 = SEGMENT_EOF(n2, nEntry);
- memcpy(a1, a2, iEof2);
- memcpy(&a1[iEof1], &a2[iEof2], n2 - iEof2);
- lsmFsPageRelease(apHier[i]);
- apHier[i] = pNew;
- }else{
- lsmPutU16(&a1[SEGMENT_FLAGS_OFFSET(n1)], SEGMENT_BTREE_FLAG);
- lsmPutU16(&a1[SEGMENT_NRECORD_OFFSET(n1)], 0);
- lsmPutU64(&a1[SEGMENT_POINTER_OFFSET(n1)], 0);
- i = i - 1;
- lsmFsPageRelease(pNew);
- }
- #endif
- }
- }
- #ifdef LSM_DEBUG
- if( rc==LSM_OK ){
- for(i=0; i<nHier; i++) assert( lsmFsPageWritable(apHier[i]) );
- }
- #endif
- return rc;
- }
- /*
- ** Allocate and populate the MergeWorker.apHier[] array.
- */
- static int mergeWorkerLoadHierarchy(MergeWorker *pMW){
- int rc = LSM_OK;
- Segment *pSeg;
- Hierarchy *p;
-
- pSeg = &pMW->pLevel->lhs;
- p = &pMW->hier;
- if( p->apHier==0 && pSeg->iRoot!=0 ){
- FileSystem *pFS = pMW->pDb->pFS;
- lsm_env *pEnv = pMW->pDb->pEnv;
- Page **apHier = 0;
- int nHier = 0;
- LsmPgno iPg = pSeg->iRoot;
- do {
- Page *pPg = 0;
- u8 *aData;
- int nData;
- int flags;
- rc = lsmFsDbPageGet(pFS, pSeg, iPg, &pPg);
- if( rc!=LSM_OK ) break;
- aData = fsPageData(pPg, &nData);
- flags = pageGetFlags(aData, nData);
- if( flags&SEGMENT_BTREE_FLAG ){
- Page **apNew = (Page **)lsmRealloc(
- pEnv, apHier, sizeof(Page *)*(nHier+1)
- );
- if( apNew==0 ){
- rc = LSM_NOMEM_BKPT;
- break;
- }
- apHier = apNew;
- memmove(&apHier[1], &apHier[0], sizeof(Page *) * nHier);
- nHier++;
- apHier[0] = pPg;
- iPg = pageGetPtr(aData, nData);
- }else{
- lsmFsPageRelease(pPg);
- break;
- }
- }while( 1 );
- if( rc==LSM_OK ){
- u8 *aData;
- int nData;
- aData = fsPageData(apHier[0], &nData);
- pMW->aSave[0].iPgno = pageGetPtr(aData, nData);
- p->nHier = nHier;
- p->apHier = apHier;
- rc = mergeWorkerMoveHierarchy(pMW, 0);
- }else{
- int i;
- for(i=0; i<nHier; i++){
- lsmFsPageRelease(apHier[i]);
- }
- lsmFree(pEnv, apHier);
- }
- }
- return rc;
- }
- /*
- ** B-tree pages use almost the same format as regular pages. The
- ** differences are:
- **
- ** 1. The record format is (usually, see below) as follows:
- **
- ** + Type byte (always SORTED_SEPARATOR or SORTED_SYSTEM_SEPARATOR),
- ** + Absolute pointer value (varint),
- ** + Number of bytes in key (varint),
- ** + LsmBlob containing key data.
- **
- ** 2. All pointer values are stored as absolute values (not offsets
- ** relative to the footer pointer value).
- **
- ** 3. Each pointer that is part of a record points to a page that
- ** contains keys smaller than the records key (note: not "equal to or
- ** smaller than - smaller than").
- **
- ** 4. The pointer in the page footer of a b-tree page points to a page
- ** that contains keys equal to or larger than the largest key on the
- ** b-tree page.
- **
- ** The reason for having the page footer pointer point to the right-child
- ** (instead of the left) is that doing things this way makes the
- ** mergeWorkerMoveHierarchy() operation less complicated (since the pointers
- ** that need to be updated are all stored as fixed-size integers within the
- ** page footer, not varints in page records).
- **
- ** Records may not span b-tree pages. If this function is called to add a
- ** record larger than (page-size / 4) bytes, then a pointer to the indexed
- ** array page that contains the main record is added to the b-tree instead.
- ** In this case the record format is:
- **
- ** + 0x00 byte (1 byte)
- ** + Absolute pointer value (varint),
- ** + Absolute page number of page containing key (varint).
- **
- ** See function seekInBtree() for the code that traverses b-tree pages.
- */
- static int mergeWorkerBtreeWrite(
- MergeWorker *pMW,
- u8 eType,
- LsmPgno iPtr,
- LsmPgno iKeyPg,
- void *pKey,
- int nKey
- ){
- Hierarchy *p = &pMW->hier;
- lsm_db *pDb = pMW->pDb; /* Database handle */
- int rc = LSM_OK; /* Return Code */
- int iLevel; /* Level of b-tree hierachy to write to */
- int nData; /* Size of aData[] in bytes */
- u8 *aData; /* Page data for level iLevel */
- int iOff; /* Offset on b-tree page to write record to */
- int nRec; /* Initial number of records on b-tree page */
- /* iKeyPg should be zero for an ordinary b-tree key, or non-zero for an
- ** indirect key. The flags byte for an indirect key is 0x00. */
- assert( (eType==0)==(iKeyPg!=0) );
- /* The MergeWorker.apHier[] array contains the right-most leaf of the b-tree
- ** hierarchy, the root node, and all nodes that lie on the path between.
- ** apHier[0] is the right-most leaf and apHier[pMW->nHier-1] is the current
- ** root page.
- **
- ** This loop searches for a node with enough space to store the key on,
- ** starting with the leaf and iterating up towards the root. When the loop
- ** exits, the key may be written to apHier[iLevel]. */
- for(iLevel=0; iLevel<=p->nHier; iLevel++){
- int nByte; /* Number of free bytes required */
- if( iLevel==p->nHier ){
- /* Extend the array and allocate a new root page. */
- Page **aNew;
- aNew = (Page **)lsmRealloc(
- pMW->pDb->pEnv, p->apHier, sizeof(Page *)*(p->nHier+1)
- );
- if( !aNew ){
- return LSM_NOMEM_BKPT;
- }
- p->apHier = aNew;
- }else{
- Page *pOld;
- int nFree;
- /* If the key will fit on this page, break out of the loop here.
- ** The new entry will be written to page apHier[iLevel]. */
- pOld = p->apHier[iLevel];
- assert( lsmFsPageWritable(pOld) );
- aData = fsPageData(pOld, &nData);
- if( eType==0 ){
- nByte = 2 + 1 + lsmVarintLen64(iPtr) + lsmVarintLen64(iKeyPg);
- }else{
- nByte = 2 + 1 + lsmVarintLen64(iPtr) + lsmVarintLen32(nKey) + nKey;
- }
- nRec = pageGetNRec(aData, nData);
- nFree = SEGMENT_EOF(nData, nRec) - mergeWorkerPageOffset(aData, nData);
- if( nByte<=nFree ) break;
- /* Otherwise, this page is full. Set the right-hand-child pointer
- ** to iPtr and release it. */
- lsmPutU64(&aData[SEGMENT_POINTER_OFFSET(nData)], iPtr);
- assert( lsmFsPageNumber(pOld)==0 );
- rc = lsmFsPagePersist(pOld);
- if( rc==LSM_OK ){
- iPtr = lsmFsPageNumber(pOld);
- lsmFsPageRelease(pOld);
- }
- }
- /* Allocate a new page for apHier[iLevel]. */
- p->apHier[iLevel] = 0;
- if( rc==LSM_OK ){
- rc = lsmFsSortedAppend(
- pDb->pFS, pDb->pWorker, pMW->pLevel, 1, &p->apHier[iLevel]
- );
- }
- if( rc!=LSM_OK ) return rc;
- aData = fsPageData(p->apHier[iLevel], &nData);
- memset(aData, 0, nData);
- lsmPutU16(&aData[SEGMENT_FLAGS_OFFSET(nData)], SEGMENT_BTREE_FLAG);
- lsmPutU16(&aData[SEGMENT_NRECORD_OFFSET(nData)], 0);
- if( iLevel==p->nHier ){
- p->nHier++;
- break;
- }
- }
- /* Write the key into page apHier[iLevel]. */
- aData = fsPageData(p->apHier[iLevel], &nData);
- iOff = mergeWorkerPageOffset(aData, nData);
- nRec = pageGetNRec(aData, nData);
- lsmPutU16(&aData[SEGMENT_CELLPTR_OFFSET(nData, nRec)], (u16)iOff);
- lsmPutU16(&aData[SEGMENT_NRECORD_OFFSET(nData)], (u16)(nRec+1));
- if( eType==0 ){
- aData[iOff++] = 0x00;
- iOff += lsmVarintPut64(&aData[iOff], iPtr);
- iOff += lsmVarintPut64(&aData[iOff], iKeyPg);
- }else{
- aData[iOff++] = eType;
- iOff += lsmVarintPut64(&aData[iOff], iPtr);
- iOff += lsmVarintPut32(&aData[iOff], nKey);
- memcpy(&aData[iOff], pKey, nKey);
- }
- return rc;
- }
- static int mergeWorkerBtreeIndirect(MergeWorker *pMW){
- int rc = LSM_OK;
- if( pMW->iIndirect ){
- LsmPgno iKeyPg = pMW->aSave[1].iPgno;
- rc = mergeWorkerBtreeWrite(pMW, 0, pMW->iIndirect, iKeyPg, 0, 0);
- pMW->iIndirect = 0;
- }
- return rc;
- }
- /*
- ** Append the database key (iTopic/pKey/nKey) to the b-tree under
- ** construction. This key has not yet been written to a segment page.
- ** The pointer that will accompany the new key in the b-tree - that
- ** points to the completed segment page that contains keys smaller than
- ** (pKey/nKey) is currently stored in pMW->aSave[0].iPgno.
- */
- static int mergeWorkerPushHierarchy(
- MergeWorker *pMW, /* Merge worker object */
- int iTopic, /* Topic value for this key */
- void *pKey, /* Pointer to key buffer */
- int nKey /* Size of pKey buffer in bytes */
- ){
- int rc = LSM_OK; /* Return Code */
- LsmPgno iPtr; /* Pointer value to accompany pKey/nKey */
- assert( pMW->aSave[0].bStore==0 );
- assert( pMW->aSave[1].bStore==0 );
- rc = mergeWorkerBtreeIndirect(pMW);
- /* Obtain the absolute pointer value to store along with the key in the
- ** page body. This pointer points to a page that contains keys that are
- ** smaller than pKey/nKey. */
- iPtr = pMW->aSave[0].iPgno;
- assert( iPtr!=0 );
- /* Determine if the indirect format should be used. */
- if( (nKey*4 > lsmFsPageSize(pMW->pDb->pFS)) ){
- pMW->iIndirect = iPtr;
- pMW->aSave[1].bStore = 1;
- }else{
- rc = mergeWorkerBtreeWrite(
- pMW, (u8)(iTopic | LSM_SEPARATOR), iPtr, 0, pKey, nKey
- );
- }
- /* Ensure that the SortedRun.iRoot field is correct. */
- return rc;
- }
- static int mergeWorkerFinishHierarchy(
- MergeWorker *pMW /* Merge worker object */
- ){
- int i; /* Used to loop through apHier[] */
- int rc = LSM_OK; /* Return code */
- LsmPgno iPtr; /* New right-hand-child pointer value */
- iPtr = pMW->aSave[0].iPgno;
- for(i=0; i<pMW->hier.nHier && rc==LSM_OK; i++){
- Page *pPg = pMW->hier.apHier[i];
- int nData; /* Size of aData[] in bytes */
- u8 *aData; /* Page data for pPg */
- aData = fsPageData(pPg, &nData);
- lsmPutU64(&aData[SEGMENT_POINTER_OFFSET(nData)], iPtr);
- rc = lsmFsPagePersist(pPg);
- iPtr = lsmFsPageNumber(pPg);
- lsmFsPageRelease(pPg);
- }
- if( pMW->hier.nHier ){
- pMW->pLevel->lhs.iRoot = iPtr;
- lsmFree(pMW->pDb->pEnv, pMW->hier.apHier);
- pMW->hier.apHier = 0;
- pMW->hier.nHier = 0;
- }
- return rc;
- }
- static int mergeWorkerAddPadding(
- MergeWorker *pMW /* Merge worker object */
- ){
- FileSystem *pFS = pMW->pDb->pFS;
- return lsmFsSortedPadding(pFS, pMW->pDb->pWorker, &pMW->pLevel->lhs);
- }
- /*
- ** Release all page references currently held by the merge-worker passed
- ** as the only argument. Unless an error has occurred, all pages have
- ** already been released.
- */
- static void mergeWorkerReleaseAll(MergeWorker *pMW){
- int i;
- lsmFsPageRelease(pMW->pPage);
- pMW->pPage = 0;
- for(i=0; i<pMW->hier.nHier; i++){
- lsmFsPageRelease(pMW->hier.apHier[i]);
- pMW->hier.apHier[i] = 0;
- }
- lsmFree(pMW->pDb->pEnv, pMW->hier.apHier);
- pMW->hier.apHier = 0;
- pMW->hier.nHier = 0;
- }
- static int keyszToSkip(FileSystem *pFS, int nKey){
- int nPgsz; /* Nominal database page size */
- nPgsz = lsmFsPageSize(pFS);
- return LSM_MIN(((nKey * 4) / nPgsz), 3);
- }
- /*
- ** Release the reference to the current output page of merge-worker *pMW
- ** (reference pMW->pPage). Set the page number values in aSave[] as
- ** required (see comments above struct MergeWorker for details).
- */
- static int mergeWorkerPersistAndRelease(MergeWorker *pMW){
- int rc;
- int i;
- assert( pMW->pPage || (pMW->aSave[0].bStore==0 && pMW->aSave[1].bStore==0) );
- /* Persist the page */
- rc = lsmFsPagePersist(pMW->pPage);
- /* If required, save the page number. */
- for(i=0; i<2; i++){
- if( pMW->aSave[i].bStore ){
- pMW->aSave[i].iPgno = lsmFsPageNumber(pMW->pPage);
- pMW->aSave[i].bStore = 0;
- }
- }
- /* Release the completed output page. */
- lsmFsPageRelease(pMW->pPage);
- pMW->pPage = 0;
- return rc;
- }
- /*
- ** Advance to the next page of an output run being populated by merge-worker
- ** pMW. The footer of the new page is initialized to indicate that it contains
- ** zero records. The flags field is cleared. The page footer pointer field
- ** is set to iFPtr.
- **
- ** If successful, LSM_OK is returned. Otherwise, an error code.
- */
- static int mergeWorkerNextPage(
- MergeWorker *pMW, /* Merge worker object to append page to */
- LsmPgno iFPtr /* Pointer value for footer of new page */
- ){
- int rc = LSM_OK; /* Return code */
- Page *pNext = 0; /* New page appended to run */
- lsm_db *pDb = pMW->pDb; /* Database handle */
- rc = lsmFsSortedAppend(pDb->pFS, pDb->pWorker, pMW->pLevel, 0, &pNext);
- assert( rc || pMW->pLevel->lhs.iFirst>0 || pMW->pDb->compress.xCompress );
- if( rc==LSM_OK ){
- u8 *aData; /* Data buffer belonging to page pNext */
- int nData; /* Size of aData[] in bytes */
- rc = mergeWorkerPersistAndRelease(pMW);
- pMW->pPage = pNext;
- pMW->pLevel->pMerge->iOutputOff = 0;
- aData = fsPageData(pNext, &nData);
- lsmPutU16(&aData[SEGMENT_NRECORD_OFFSET(nData)], 0);
- lsmPutU16(&aData[SEGMENT_FLAGS_OFFSET(nData)], 0);
- lsmPutU64(&aData[SEGMENT_POINTER_OFFSET(nData)], iFPtr);
- pMW->nWork++;
- }
- return rc;
- }
- /*
- ** Write a blob of data into an output segment being populated by a
- ** merge-worker object. If argument bSep is true, write into the separators
- ** array. Otherwise, the main array.
- **
- ** This function is used to write the blobs of data for keys and values.
- */
- static int mergeWorkerData(
- MergeWorker *pMW, /* Merge worker object */
- int bSep, /* True to write to separators run */
- LsmPgno iFPtr, /* Footer ptr for new pages */
- u8 *aWrite, /* Write data from this buffer */
- int nWrite /* Size of aWrite[] in bytes */
- ){
- int rc = LSM_OK; /* Return code */
- int nRem = nWrite; /* Number of bytes still to write */
- while( rc==LSM_OK && nRem>0 ){
- Merge *pMerge = pMW->pLevel->pMerge;
- int nCopy; /* Number of bytes to copy */
- u8 *aData; /* Pointer to buffer of current output page */
- int nData; /* Size of aData[] in bytes */
- int nRec; /* Number of records on current output page */
- int iOff; /* Offset in aData[] to write to */
- assert( lsmFsPageWritable(pMW->pPage) );
-
- aData = fsPageData(pMW->pPage, &nData);
- nRec = pageGetNRec(aData, nData);
- iOff = pMerge->iOutputOff;
- nCopy = LSM_MIN(nRem, SEGMENT_EOF(nData, nRec) - iOff);
- memcpy(&aData[iOff], &aWrite[nWrite-nRem], nCopy);
- nRem -= nCopy;
- if( nRem>0 ){
- rc = mergeWorkerNextPage(pMW, iFPtr);
- }else{
- pMerge->iOutputOff = iOff + nCopy;
- }
- }
- return rc;
- }
- /*
- ** The MergeWorker passed as the only argument is working to merge two or
- ** more existing segments together (not to flush an in-memory tree). It
- ** has not yet written the first key to the first page of the output.
- */
- static int mergeWorkerFirstPage(MergeWorker *pMW){
- int rc = LSM_OK; /* Return code */
- Page *pPg = 0; /* First page of run pSeg */
- LsmPgno iFPtr = 0; /* Pointer value read from footer of pPg */
- MultiCursor *pCsr = pMW->pCsr;
- assert( pMW->pPage==0 );
- if( pCsr->pBtCsr ){
- rc = LSM_OK;
- iFPtr = pMW->pLevel->pNext->lhs.iFirst;
- }else if( pCsr->nPtr>0 ){
- Segment *pSeg;
- pSeg = pCsr->aPtr[pCsr->nPtr-1].pSeg;
- rc = lsmFsDbPageGet(pMW->pDb->pFS, pSeg, pSeg->iFirst, &pPg);
- if( rc==LSM_OK ){
- u8 *aData; /* Buffer for page pPg */
- int nData; /* Size of aData[] in bytes */
- aData = fsPageData(pPg, &nData);
- iFPtr = pageGetPtr(aData, nData);
- lsmFsPageRelease(pPg);
- }
- }
- if( rc==LSM_OK ){
- rc = mergeWorkerNextPage(pMW, iFPtr);
- if( pCsr->pPrevMergePtr ) *pCsr->pPrevMergePtr = iFPtr;
- pMW->aSave[0].bStore = 1;
- }
- return rc;
- }
- static int mergeWorkerWrite(
- MergeWorker *pMW, /* Merge worker object to write into */
- int eType, /* One of SORTED_SEPARATOR, WRITE or DELETE */
- void *pKey, int nKey, /* Key value */
- void *pVal, int nVal, /* Value value */
- LsmPgno iPtr /* Absolute value of page pointer, or 0 */
- ){
- int rc = LSM_OK; /* Return code */
- Merge *pMerge; /* Persistent part of level merge state */
- int nHdr; /* Space required for this record header */
- Page *pPg; /* Page to write to */
- u8 *aData; /* Data buffer for page pWriter->pPage */
- int nData = 0; /* Size of buffer aData[] in bytes */
- int nRec = 0; /* Number of records on page pPg */
- LsmPgno iFPtr = 0; /* Value of pointer in footer of pPg */
- LsmPgno iRPtr = 0; /* Value of pointer written into record */
- int iOff = 0; /* Current write offset within page pPg */
- Segment *pSeg; /* Segment being written */
- int flags = 0; /* If != 0, flags value for page footer */
- int bFirst = 0; /* True for first key of output run */
- pMerge = pMW->pLevel->pMerge;
- pSeg = &pMW->pLevel->lhs;
- if( pSeg->iFirst==0 && pMW->pPage==0 ){
- rc = mergeWorkerFirstPage(pMW);
- bFirst = 1;
- }
- pPg = pMW->pPage;
- if( pPg ){
- aData = fsPageData(pPg, &nData);
- nRec = pageGetNRec(aData, nData);
- iFPtr = pageGetPtr(aData, nData);
- iRPtr = iPtr ? (iPtr - iFPtr) : 0;
- }
-
- /* Figure out how much space is required by the new record. The space
- ** required is divided into two sections: the header and the body. The
- ** header consists of the intial varint fields. The body are the blobs
- ** of data that correspond to the key and value data. The entire header
- ** must be stored on the page. The body may overflow onto the next and
- ** subsequent pages.
- **
- ** The header space is:
- **
- ** 1) record type - 1 byte.
- ** 2) Page-pointer-offset - 1 varint
- ** 3) Key size - 1 varint
- ** 4) Value size - 1 varint (only if LSM_INSERT flag is set)
- */
- if( rc==LSM_OK ){
- nHdr = 1 + lsmVarintLen64(iRPtr) + lsmVarintLen32(nKey);
- if( rtIsWrite(eType) ) nHdr += lsmVarintLen32(nVal);
- /* If the entire header will not fit on page pPg, or if page pPg is
- ** marked read-only, advance to the next page of the output run. */
- iOff = pMerge->iOutputOff;
- if( iOff<0 || pPg==0 || iOff+nHdr > SEGMENT_EOF(nData, nRec+1) ){
- if( iOff>=0 && pPg ){
- /* Zero any free space on the page */
- assert( aData );
- memset(&aData[iOff], 0, SEGMENT_EOF(nData, nRec)-iOff);
- }
- iFPtr = *pMW->pCsr->pPrevMergePtr;
- iRPtr = iPtr ? (iPtr - iFPtr) : 0;
- iOff = 0;
- nRec = 0;
- rc = mergeWorkerNextPage(pMW, iFPtr);
- pPg = pMW->pPage;
- }
- }
- /* If this record header will be the first on the page, and the page is
- ** not the very first in the entire run, add a copy of the key to the
- ** b-tree hierarchy.
- */
- if( rc==LSM_OK && nRec==0 && bFirst==0 ){
- assert( pMerge->nSkip>=0 );
- if( pMerge->nSkip==0 ){
- rc = mergeWorkerPushHierarchy(pMW, rtTopic(eType), pKey, nKey);
- assert( pMW->aSave[0].bStore==0 );
- pMW->aSave[0].bStore = 1;
- pMerge->nSkip = keyszToSkip(pMW->pDb->pFS, nKey);
- }else{
- pMerge->nSkip--;
- flags = PGFTR_SKIP_THIS_FLAG;
- }
- if( pMerge->nSkip ) flags |= PGFTR_SKIP_NEXT_FLAG;
- }
- /* Update the output segment */
- if( rc==LSM_OK ){
- aData = fsPageData(pPg, &nData);
- /* Update the page footer. */
- lsmPutU16(&aData[SEGMENT_NRECORD_OFFSET(nData)], (u16)(nRec+1));
- lsmPutU16(&aData[SEGMENT_CELLPTR_OFFSET(nData, nRec)], (u16)iOff);
- if( flags ) lsmPutU16(&aData[SEGMENT_FLAGS_OFFSET(nData)], (u16)flags);
- /* Write the entry header into the current page. */
- aData[iOff++] = (u8)eType; /* 1 */
- iOff += lsmVarintPut64(&aData[iOff], iRPtr); /* 2 */
- iOff += lsmVarintPut32(&aData[iOff], nKey); /* 3 */
- if( rtIsWrite(eType) ) iOff += lsmVarintPut32(&aData[iOff], nVal); /* 4 */
- pMerge->iOutputOff = iOff;
- /* Write the key and data into the segment. */
- assert( iFPtr==pageGetPtr(aData, nData) );
- rc = mergeWorkerData(pMW, 0, iFPtr+iRPtr, pKey, nKey);
- if( rc==LSM_OK && rtIsWrite(eType) ){
- if( rc==LSM_OK ){
- rc = mergeWorkerData(pMW, 0, iFPtr+iRPtr, pVal, nVal);
- }
- }
- }
- return rc;
- }
- /*
- ** Free all resources allocated by mergeWorkerInit().
- */
- static void mergeWorkerShutdown(MergeWorker *pMW, int *pRc){
- int i; /* Iterator variable */
- int rc = *pRc;
- MultiCursor *pCsr = pMW->pCsr;
- /* Unless the merge has finished, save the cursor position in the
- ** Merge.aInput[] array. See function mergeWorkerInit() for the
- ** code to restore a cursor position based on aInput[]. */
- if( rc==LSM_OK && pCsr ){
- Merge *pMerge = pMW->pLevel->pMerge;
- if( lsmMCursorValid(pCsr) ){
- int bBtree = (pCsr->pBtCsr!=0);
- int iPtr;
- /* pMerge->nInput==0 indicates that this is a FlushTree() operation. */
- assert( pMerge->nInput==0 || pMW->pLevel->nRight>0 );
- assert( pMerge->nInput==0 || pMerge->nInput==(pCsr->nPtr+bBtree) );
- for(i=0; i<(pMerge->nInput-bBtree); i++){
- SegmentPtr *pPtr = &pCsr->aPtr[i];
- if( pPtr->pPg ){
- pMerge->aInput[i].iPg = lsmFsPageNumber(pPtr->pPg);
- pMerge->aInput[i].iCell = pPtr->iCell;
- }else{
- pMerge->aInput[i].iPg = 0;
- pMerge->aInput[i].iCell = 0;
- }
- }
- if( bBtree && pMerge->nInput ){
- assert( i==pCsr->nPtr );
- btreeCursorPosition(pCsr->pBtCsr, &pMerge->aInput[i]);
- }
- /* Store the location of the split-key */
- iPtr = pCsr->aTree[1] - CURSOR_DATA_SEGMENT;
- if( iPtr<pCsr->nPtr ){
- pMerge->splitkey = pMerge->aInput[iPtr];
- }else{
- btreeCursorSplitkey(pCsr->pBtCsr, &pMerge->splitkey);
- }
- }
- /* Zero any free space left on the final page. This helps with
- ** compression if using a compression hook. And prevents valgrind
- ** from complaining about uninitialized byte passed to write(). */
- if( pMW->pPage ){
- int nData;
- u8 *aData = fsPageData(pMW->pPage, &nData);
- int iOff = pMerge->iOutputOff;
- int iEof = SEGMENT_EOF(nData, pageGetNRec(aData, nData));
- memset(&aData[iOff], 0, iEof - iOff);
- }
-
- pMerge->iOutputOff = -1;
- }
- lsmMCursorClose(pCsr, 0);
- /* Persist and release the output page. */
- if( rc==LSM_OK ) rc = mergeWorkerPersistAndRelease(pMW);
- if( rc==LSM_OK ) rc = mergeWorkerBtreeIndirect(pMW);
- if( rc==LSM_OK ) rc = mergeWorkerFinishHierarchy(pMW);
- if( rc==LSM_OK ) rc = mergeWorkerAddPadding(pMW);
- lsmFsFlushWaiting(pMW->pDb->pFS, &rc);
- mergeWorkerReleaseAll(pMW);
- lsmFree(pMW->pDb->pEnv, pMW->aGobble);
- pMW->aGobble = 0;
- pMW->pCsr = 0;
- *pRc = rc;
- }
- /*
- ** The cursor passed as the first argument is being used as the input for
- ** a merge operation. When this function is called, *piFlags contains the
- ** database entry flags for the current entry. The entry about to be written
- ** to the output.
- **
- ** Note that this function only has to work for cursors configured to
- ** iterate forwards (not backwards).
- */
- static void mergeRangeDeletes(MultiCursor *pCsr, int *piVal, int *piFlags){
- int f = *piFlags;
- int iKey = pCsr->aTree[1];
- int i;
- assert( pCsr->flags & CURSOR_NEXT_OK );
- if( pCsr->flags & CURSOR_IGNORE_DELETE ){
- /* The ignore-delete flag is set when the output of the merge will form
- ** the oldest level in the database. In this case there is no point in
- ** retaining any range-delete flags. */
- assert( (f & LSM_POINT_DELETE)==0 );
- f &= ~(LSM_START_DELETE|LSM_END_DELETE);
- }else{
- for(i=0; i<(CURSOR_DATA_SEGMENT + pCsr->nPtr); i++){
- if( i!=iKey ){
- int eType;
- void *pKey;
- int nKey;
- int res;
- multiCursorGetKey(pCsr, i, &eType, &pKey, &nKey);
- if( pKey ){
- res = sortedKeyCompare(pCsr->pDb->xCmp,
- rtTopic(pCsr->eType), pCsr->key.pData, pCsr->key.nData,
- rtTopic(eType), pKey, nKey
- );
- assert( res<=0 );
- if( res==0 ){
- if( (f & (LSM_INSERT|LSM_POINT_DELETE))==0 ){
- if( eType & LSM_INSERT ){
- f |= LSM_INSERT;
- *piVal = i;
- }
- else if( eType & LSM_POINT_DELETE ){
- f |= LSM_POINT_DELETE;
- }
- }
- f |= (eType & (LSM_END_DELETE|LSM_START_DELETE));
- }
- if( i>iKey && (eType & LSM_END_DELETE) && res<0 ){
- if( f & (LSM_INSERT|LSM_POINT_DELETE) ){
- f |= (LSM_END_DELETE|LSM_START_DELETE);
- }else{
- f = 0;
- }
- break;
- }
- }
- }
- }
- assert( (f & LSM_INSERT)==0 || (f & LSM_POINT_DELETE)==0 );
- if( (f & LSM_START_DELETE)
- && (f & LSM_END_DELETE)
- && (f & LSM_POINT_DELETE )
- ){
- f = 0;
- }
- }
- *piFlags = f;
- }
- static int mergeWorkerStep(MergeWorker *pMW){
- lsm_db *pDb = pMW->pDb; /* Database handle */
- MultiCursor *pCsr; /* Cursor to read input data from */
- int rc = LSM_OK; /* Return code */
- int eType; /* SORTED_SEPARATOR, WRITE or DELETE */
- void *pKey; int nKey; /* Key */
- LsmPgno iPtr;
- int iVal;
- pCsr = pMW->pCsr;
- /* Pull the next record out of the source cursor. */
- lsmMCursorKey(pCsr, &pKey, &nKey);
- eType = pCsr->eType;
- /* Figure out if the output record may have a different pointer value
- ** than the previous. This is the case if the current key is identical to
- ** a key that appears in the lowest level run being merged. If so, set
- ** iPtr to the absolute pointer value. If not, leave iPtr set to zero,
- ** indicating that the output pointer value should be a copy of the pointer
- ** value written with the previous key. */
- iPtr = (pCsr->pPrevMergePtr ? *pCsr->pPrevMergePtr : 0);
- if( pCsr->pBtCsr ){
- BtreeCursor *pBtCsr = pCsr->pBtCsr;
- if( pBtCsr->pKey ){
- int res = rtTopic(pBtCsr->eType) - rtTopic(eType);
- if( res==0 ) res = pDb->xCmp(pBtCsr->pKey, pBtCsr->nKey, pKey, nKey);
- if( 0==res ) iPtr = pBtCsr->iPtr;
- assert( res>=0 );
- }
- }else if( pCsr->nPtr ){
- SegmentPtr *pPtr = &pCsr->aPtr[pCsr->nPtr-1];
- if( pPtr->pPg
- && 0==pDb->xCmp(pPtr->pKey, pPtr->nKey, pKey, nKey)
- ){
- iPtr = pPtr->iPtr+pPtr->iPgPtr;
- }
- }
- iVal = pCsr->aTree[1];
- mergeRangeDeletes(pCsr, &iVal, &eType);
- if( eType!=0 ){
- if( pMW->aGobble ){
- int iGobble = pCsr->aTree[1] - CURSOR_DATA_SEGMENT;
- if( iGobble<pCsr->nPtr && iGobble>=0 ){
- SegmentPtr *pGobble = &pCsr->aPtr[iGobble];
- if( (pGobble->flags & PGFTR_SKIP_THIS_FLAG)==0 ){
- pMW->aGobble[iGobble] = lsmFsPageNumber(pGobble->pPg);
- }
- }
- }
- /* If this is a separator key and we know that the output pointer has not
- ** changed, there is no point in writing an output record. Otherwise,
- ** proceed. */
- if( rc==LSM_OK && (rtIsSeparator(eType)==0 || iPtr!=0) ){
- /* Write the record into the main run. */
- void *pVal; int nVal;
- rc = multiCursorGetVal(pCsr, iVal, &pVal, &nVal);
- if( pVal && rc==LSM_OK ){
- assert( nVal>=0 );
- rc = sortedBlobSet(pDb->pEnv, &pCsr->val, pVal, nVal);
- pVal = pCsr->val.pData;
- }
- if( rc==LSM_OK ){
- rc = mergeWorkerWrite(pMW, eType, pKey, nKey, pVal, nVal, iPtr);
- }
- }
- }
- /* Advance the cursor to the next input record (assuming one exists). */
- assert( lsmMCursorValid(pMW->pCsr) );
- if( rc==LSM_OK ) rc = lsmMCursorNext(pMW->pCsr);
- return rc;
- }
- static int mergeWorkerDone(MergeWorker *pMW){
- return pMW->pCsr==0 || !lsmMCursorValid(pMW->pCsr);
- }
- static void sortedFreeLevel(lsm_env *pEnv, Level *p){
- if( p ){
- lsmFree(pEnv, p->pSplitKey);
- lsmFree(pEnv, p->pMerge);
- lsmFree(pEnv, p->aRhs);
- lsmFree(pEnv, p);
- }
- }
- static void sortedInvokeWorkHook(lsm_db *pDb){
- if( pDb->xWork ){
- pDb->xWork(pDb, pDb->pWorkCtx);
- }
- }
- static int sortedNewToplevel(
- lsm_db *pDb, /* Connection handle */
- int eTree, /* One of the TREE_XXX constants */
- int *pnWrite /* OUT: Number of database pages written */
- ){
- int rc = LSM_OK; /* Return Code */
- MultiCursor *pCsr = 0;
- Level *pNext = 0; /* The current top level */
- Level *pNew; /* The new level itself */
- Segment *pLinked = 0; /* Delete separators from this segment */
- Level *pDel = 0; /* Delete this entire level */
- int nWrite = 0; /* Number of database pages written */
- Freelist freelist;
- if( eTree!=TREE_NONE ){
- rc = lsmShmCacheChunks(pDb, pDb->treehdr.nChunk);
- }
- assert( pDb->bUseFreelist==0 );
- pDb->pFreelist = &freelist;
- pDb->bUseFreelist = 1;
- memset(&freelist, 0, sizeof(freelist));
- /* Allocate the new level structure to write to. */
- pNext = lsmDbSnapshotLevel(pDb->pWorker);
- pNew = (Level *)lsmMallocZeroRc(pDb->pEnv, sizeof(Level), &rc);
- if( pNew ){
- pNew->pNext = pNext;
- lsmDbSnapshotSetLevel(pDb->pWorker, pNew);
- }
- /* Create a cursor to gather the data required by the new segment. The new
- ** segment contains everything in the tree and pointers to the next segment
- ** in the database (if any). */
- pCsr = multiCursorNew(pDb, &rc);
- if( pCsr ){
- pCsr->pDb = pDb;
- rc = multiCursorVisitFreelist(pCsr);
- if( rc==LSM_OK ){
- rc = multiCursorAddTree(pCsr, pDb->pWorker, eTree);
- }
- if( rc==LSM_OK && pNext && pNext->pMerge==0 ){
- if( (pNext->flags & LEVEL_FREELIST_ONLY) ){
- pDel = pNext;
- pCsr->aPtr = lsmMallocZeroRc(pDb->pEnv, sizeof(SegmentPtr), &rc);
- multiCursorAddOne(pCsr, pNext, &rc);
- }else if( eTree!=TREE_NONE && pNext->lhs.iRoot ){
- pLinked = &pNext->lhs;
- rc = btreeCursorNew(pDb, pLinked, &pCsr->pBtCsr);
- }
- }
- /* If this will be the only segment in the database, discard any delete
- ** markers present in the in-memory tree. */
- if( pNext==0 ){
- multiCursorIgnoreDelete(pCsr);
- }
- }
- if( rc!=LSM_OK ){
- lsmMCursorClose(pCsr, 0);
- }else{
- LsmPgno iLeftPtr = 0;
- Merge merge; /* Merge object used to create new level */
- MergeWorker mergeworker; /* MergeWorker object for the same purpose */
- memset(&merge, 0, sizeof(Merge));
- memset(&mergeworker, 0, sizeof(MergeWorker));
- pNew->pMerge = &merge;
- pNew->flags |= LEVEL_INCOMPLETE;
- mergeworker.pDb = pDb;
- mergeworker.pLevel = pNew;
- mergeworker.pCsr = pCsr;
- pCsr->pPrevMergePtr = &iLeftPtr;
- /* Mark the separators array for the new level as a "phantom". */
- mergeworker.bFlush = 1;
- /* Do the work to create the new merged segment on disk */
- if( rc==LSM_OK ) rc = lsmMCursorFirst(pCsr);
- while( rc==LSM_OK && mergeWorkerDone(&mergeworker)==0 ){
- rc = mergeWorkerStep(&mergeworker);
- }
- mergeWorkerShutdown(&mergeworker, &rc);
- assert( rc!=LSM_OK || mergeworker.nWork==0 || pNew->lhs.iFirst );
- if( rc==LSM_OK && pNew->lhs.iFirst ){
- rc = lsmFsSortedFinish(pDb->pFS, &pNew->lhs);
- }
- nWrite = mergeworker.nWork;
- pNew->flags &= ~LEVEL_INCOMPLETE;
- if( eTree==TREE_NONE ){
- pNew->flags |= LEVEL_FREELIST_ONLY;
- }
- pNew->pMerge = 0;
- }
- if( rc!=LSM_OK || pNew->lhs.iFirst==0 ){
- assert( rc!=LSM_OK || pDb->pWorker->freelist.nEntry==0 );
- lsmDbSnapshotSetLevel(pDb->pWorker, pNext);
- sortedFreeLevel(pDb->pEnv, pNew);
- }else{
- if( pLinked ){
- pLinked->iRoot = 0;
- }else if( pDel ){
- assert( pNew->pNext==pDel );
- pNew->pNext = pDel->pNext;
- lsmFsSortedDelete(pDb->pFS, pDb->pWorker, 1, &pDel->lhs);
- sortedFreeLevel(pDb->pEnv, pDel);
- }
- #if LSM_LOG_STRUCTURE
- lsmSortedDumpStructure(pDb, pDb->pWorker, LSM_LOG_DATA, 0, "new-toplevel");
- #endif
- if( freelist.nEntry ){
- Freelist *p = &pDb->pWorker->freelist;
- lsmFree(pDb->pEnv, p->aEntry);
- memcpy(p, &freelist, sizeof(freelist));
- freelist.aEntry = 0;
- }else{
- pDb->pWorker->freelist.nEntry = 0;
- }
- assertBtreeOk(pDb, &pNew->lhs);
- sortedInvokeWorkHook(pDb);
- }
- if( pnWrite ) *pnWrite = nWrite;
- pDb->pWorker->nWrite += nWrite;
- pDb->pFreelist = 0;
- pDb->bUseFreelist = 0;
- lsmFree(pDb->pEnv, freelist.aEntry);
- return rc;
- }
- /*
- ** The nMerge levels in the LSM beginning with pLevel consist of a
- ** left-hand-side segment only. Replace these levels with a single new
- ** level consisting of a new empty segment on the left-hand-side and the
- ** nMerge segments from the replaced levels on the right-hand-side.
- **
- ** Also, allocate and populate a Merge object and set Level.pMerge to
- ** point to it.
- */
- static int sortedMergeSetup(
- lsm_db *pDb, /* Database handle */
- Level *pLevel, /* First level to merge */
- int nMerge, /* Merge this many levels together */
- Level **ppNew /* New, merged, level */
- ){
- int rc = LSM_OK; /* Return Code */
- Level *pNew; /* New Level object */
- int bUseNext = 0; /* True to link in next separators */
- Merge *pMerge; /* New Merge object */
- int nByte; /* Bytes of space allocated at pMerge */
- #ifdef LSM_DEBUG
- int iLevel;
- Level *pX = pLevel;
- for(iLevel=0; iLevel<nMerge; iLevel++){
- assert( pX->nRight==0 );
- pX = pX->pNext;
- }
- #endif
- /* Allocate the new Level object */
- pNew = (Level *)lsmMallocZeroRc(pDb->pEnv, sizeof(Level), &rc);
- if( pNew ){
- pNew->aRhs = (Segment *)lsmMallocZeroRc(pDb->pEnv,
- nMerge * sizeof(Segment), &rc);
- }
- /* Populate the new Level object */
- if( rc==LSM_OK ){
- Level *pNext = 0; /* Level following pNew */
- int i;
- int bFreeOnly = 1;
- Level *pTopLevel;
- Level *p = pLevel;
- Level **pp;
- pNew->nRight = nMerge;
- pNew->iAge = pLevel->iAge+1;
- for(i=0; i<nMerge; i++){
- assert( p->nRight==0 );
- pNext = p->pNext;
- pNew->aRhs[i] = p->lhs;
- if( (p->flags & LEVEL_FREELIST_ONLY)==0 ) bFreeOnly = 0;
- sortedFreeLevel(pDb->pEnv, p);
- p = pNext;
- }
- if( bFreeOnly ) pNew->flags |= LEVEL_FREELIST_ONLY;
- /* Replace the old levels with the new. */
- pTopLevel = lsmDbSnapshotLevel(pDb->pWorker);
- pNew->pNext = p;
- for(pp=&pTopLevel; *pp!=pLevel; pp=&((*pp)->pNext));
- *pp = pNew;
- lsmDbSnapshotSetLevel(pDb->pWorker, pTopLevel);
- /* Determine whether or not the next separators will be linked in */
- if( pNext && pNext->pMerge==0 && pNext->lhs.iRoot && pNext
- && (bFreeOnly==0 || (pNext->flags & LEVEL_FREELIST_ONLY))
- ){
- bUseNext = 1;
- }
- }
- /* Allocate the merge object */
- nByte = sizeof(Merge) + sizeof(MergeInput) * (nMerge + bUseNext);
- pMerge = (Merge *)lsmMallocZeroRc(pDb->pEnv, nByte, &rc);
- if( pMerge ){
- pMerge->aInput = (MergeInput *)&pMerge[1];
- pMerge->nInput = nMerge + bUseNext;
- pNew->pMerge = pMerge;
- }
- *ppNew = pNew;
- return rc;
- }
- static int mergeWorkerInit(
- lsm_db *pDb, /* Db connection to do merge work */
- Level *pLevel, /* Level to work on merging */
- MergeWorker *pMW /* Object to initialize */
- ){
- int rc = LSM_OK; /* Return code */
- Merge *pMerge = pLevel->pMerge; /* Persistent part of merge state */
- MultiCursor *pCsr = 0; /* Cursor opened for pMW */
- Level *pNext = pLevel->pNext; /* Next level in LSM */
- assert( pDb->pWorker );
- assert( pLevel->pMerge );
- assert( pLevel->nRight>0 );
- memset(pMW, 0, sizeof(MergeWorker));
- pMW->pDb = pDb;
- pMW->pLevel = pLevel;
- pMW->aGobble = lsmMallocZeroRc(pDb->pEnv, sizeof(LsmPgno)*pLevel->nRight,&rc);
- /* Create a multi-cursor to read the data to write to the new
- ** segment. The new segment contains:
- **
- ** 1. Records from LHS of each of the nMerge levels being merged.
- ** 2. Separators from either the last level being merged, or the
- ** separators attached to the LHS of the following level, or neither.
- **
- ** If the new level is the lowest (oldest) in the db, discard any
- ** delete keys. Key annihilation.
- */
- pCsr = multiCursorNew(pDb, &rc);
- if( pCsr ){
- pCsr->flags |= CURSOR_NEXT_OK;
- rc = multiCursorAddRhs(pCsr, pLevel);
- }
- if( rc==LSM_OK && pMerge->nInput > pLevel->nRight ){
- rc = btreeCursorNew(pDb, &pNext->lhs, &pCsr->pBtCsr);
- }else if( pNext ){
- multiCursorReadSeparators(pCsr);
- }else{
- multiCursorIgnoreDelete(pCsr);
- }
- assert( rc!=LSM_OK || pMerge->nInput==(pCsr->nPtr+(pCsr->pBtCsr!=0)) );
- pMW->pCsr = pCsr;
- /* Load the b-tree hierarchy into memory. */
- if( rc==LSM_OK ) rc = mergeWorkerLoadHierarchy(pMW);
- if( rc==LSM_OK && pMW->hier.nHier==0 ){
- pMW->aSave[0].iPgno = pLevel->lhs.iFirst;
- }
- /* Position the cursor. */
- if( rc==LSM_OK ){
- pCsr->pPrevMergePtr = &pMerge->iCurrentPtr;
- if( pLevel->lhs.iFirst==0 ){
- /* The output array is still empty. So position the cursor at the very
- ** start of the input. */
- rc = multiCursorEnd(pCsr, 0);
- }else{
- /* The output array is non-empty. Position the cursor based on the
- ** page/cell data saved in the Merge.aInput[] array. */
- int i;
- for(i=0; rc==LSM_OK && i<pCsr->nPtr; i++){
- MergeInput *pInput = &pMerge->aInput[i];
- if( pInput->iPg ){
- SegmentPtr *pPtr;
- assert( pCsr->aPtr[i].pPg==0 );
- pPtr = &pCsr->aPtr[i];
- rc = segmentPtrLoadPage(pDb->pFS, pPtr, pInput->iPg);
- if( rc==LSM_OK && pPtr->nCell>0 ){
- rc = segmentPtrLoadCell(pPtr, pInput->iCell);
- }
- }
- }
- if( rc==LSM_OK && pCsr->pBtCsr ){
- int (*xCmp)(void *, int, void *, int) = pCsr->pDb->xCmp;
- assert( i==pCsr->nPtr );
- rc = btreeCursorRestore(pCsr->pBtCsr, xCmp, &pMerge->aInput[i]);
- }
- if( rc==LSM_OK ){
- rc = multiCursorSetupTree(pCsr, 0);
- }
- }
- pCsr->flags |= CURSOR_NEXT_OK;
- }
- return rc;
- }
- static int sortedBtreeGobble(
- lsm_db *pDb, /* Worker connection */
- MultiCursor *pCsr, /* Multi-cursor being used for a merge */
- int iGobble /* pCsr->aPtr[] entry to operate on */
- ){
- int rc = LSM_OK;
- if( rtTopic(pCsr->eType)==0 ){
- Segment *pSeg = pCsr->aPtr[iGobble].pSeg;
- LsmPgno *aPg;
- int nPg;
- /* Seek from the root of the b-tree to the segment leaf that may contain
- ** a key equal to the one multi-cursor currently points to. Record the
- ** page number of each b-tree page and the leaf. The segment may be
- ** gobbled up to (but not including) the first of these page numbers.
- */
- assert( pSeg->iRoot>0 );
- aPg = lsmMallocZeroRc(pDb->pEnv, sizeof(LsmPgno)*32, &rc);
- if( rc==LSM_OK ){
- rc = seekInBtree(pCsr, pSeg,
- rtTopic(pCsr->eType), pCsr->key.pData, pCsr->key.nData, aPg, 0
- );
- }
- if( rc==LSM_OK ){
- for(nPg=0; aPg[nPg]; nPg++);
- lsmFsGobble(pDb, pSeg, aPg, nPg);
- }
- lsmFree(pDb->pEnv, aPg);
- }
- return rc;
- }
- /*
- ** Argument p points to a level of age N. Return the number of levels in
- ** the linked list starting at p that have age=N (always at least 1).
- */
- static int sortedCountLevels(Level *p){
- int iAge = p->iAge;
- int nRet = 0;
- do {
- nRet++;
- p = p->pNext;
- }while( p && p->iAge==iAge );
- return nRet;
- }
- static int sortedSelectLevel(lsm_db *pDb, int nMerge, Level **ppOut){
- Level *pTopLevel = lsmDbSnapshotLevel(pDb->pWorker);
- int rc = LSM_OK;
- Level *pLevel = 0; /* Output value */
- Level *pBest = 0; /* Best level to work on found so far */
- int nBest; /* Number of segments merged at pBest */
- Level *pThis = 0; /* First in run of levels with age=iAge */
- int nThis = 0; /* Number of levels starting at pThis */
- assert( nMerge>=1 );
- nBest = LSM_MAX(1, nMerge-1);
- /* Find the longest contiguous run of levels not currently undergoing a
- ** merge with the same age in the structure. Or the level being merged
- ** with the largest number of right-hand segments. Work on it. */
- for(pLevel=pTopLevel; pLevel; pLevel=pLevel->pNext){
- if( pLevel->nRight==0 && pThis && pLevel->iAge==pThis->iAge ){
- nThis++;
- }else{
- if( nThis>nBest ){
- if( (pLevel->iAge!=pThis->iAge+1)
- || (pLevel->nRight==0 && sortedCountLevels(pLevel)<=pDb->nMerge)
- ){
- pBest = pThis;
- nBest = nThis;
- }
- }
- if( pLevel->nRight ){
- if( pLevel->nRight>nBest ){
- nBest = pLevel->nRight;
- pBest = pLevel;
- }
- nThis = 0;
- pThis = 0;
- }else{
- pThis = pLevel;
- nThis = 1;
- }
- }
- }
- if( nThis>nBest ){
- assert( pThis );
- pBest = pThis;
- nBest = nThis;
- }
- if( pBest==0 && nMerge==1 ){
- int nFree = 0;
- int nUsr = 0;
- for(pLevel=pTopLevel; pLevel; pLevel=pLevel->pNext){
- assert( !pLevel->nRight );
- if( pLevel->flags & LEVEL_FREELIST_ONLY ){
- nFree++;
- }else{
- nUsr++;
- }
- }
- if( nUsr>1 ){
- pBest = pTopLevel;
- nBest = nFree + nUsr;
- }
- }
- if( pBest ){
- if( pBest->nRight==0 ){
- rc = sortedMergeSetup(pDb, pBest, nBest, ppOut);
- }else{
- *ppOut = pBest;
- }
- }
- return rc;
- }
- static int sortedDbIsFull(lsm_db *pDb){
- Level *pTop = lsmDbSnapshotLevel(pDb->pWorker);
- if( lsmDatabaseFull(pDb) ) return 1;
- if( pTop && pTop->iAge==0
- && (pTop->nRight || sortedCountLevels(pTop)>=pDb->nMerge)
- ){
- return 1;
- }
- return 0;
- }
- typedef struct MoveBlockCtx MoveBlockCtx;
- struct MoveBlockCtx {
- int iSeen; /* Previous free block on list */
- int iFrom; /* Total number of blocks in file */
- };
- static int moveBlockCb(void *pCtx, int iBlk, i64 iSnapshot){
- MoveBlockCtx *p = (MoveBlockCtx *)pCtx;
- assert( p->iFrom==0 );
- if( iBlk==(p->iSeen-1) ){
- p->iSeen = iBlk;
- return 0;
- }
- p->iFrom = p->iSeen-1;
- return 1;
- }
- /*
- ** This function is called to further compact a database for which all
- ** of the content has already been merged into a single segment. If
- ** possible, it moves the contents of a single block from the end of the
- ** file to a free-block that lies closer to the start of the file (allowing
- ** the file to be eventually truncated).
- */
- static int sortedMoveBlock(lsm_db *pDb, int *pnWrite){
- Snapshot *p = pDb->pWorker;
- Level *pLvl = lsmDbSnapshotLevel(p);
- int iFrom; /* Block to move */
- int iTo; /* Destination to move block to */
- int rc; /* Return code */
- MoveBlockCtx sCtx;
- assert( pLvl->pNext==0 && pLvl->nRight==0 );
- assert( p->redirect.n<=LSM_MAX_BLOCK_REDIRECTS );
- *pnWrite = 0;
- /* Check that the redirect array is not already full. If it is, return
- ** without moving any database content. */
- if( p->redirect.n>=LSM_MAX_BLOCK_REDIRECTS ) return LSM_OK;
- /* Find the last block of content in the database file. Do this by
- ** traversing the free-list in reverse (descending block number) order.
- ** The first block not on the free list is the one that will be moved.
- ** Since the db consists of a single segment, there is no ambiguity as
- ** to which segment the block belongs to. */
- sCtx.iSeen = p->nBlock+1;
- sCtx.iFrom = 0;
- rc = lsmWalkFreelist(pDb, 1, moveBlockCb, &sCtx);
- if( rc!=LSM_OK || sCtx.iFrom==0 ) return rc;
- iFrom = sCtx.iFrom;
- /* Find the first free block in the database, ignoring block 1. Block
- ** 1 is tricky as it is smaller than the other blocks. */
- rc = lsmBlockAllocate(pDb, iFrom, &iTo);
- if( rc!=LSM_OK || iTo==0 ) return rc;
- assert( iTo!=1 && iTo<iFrom );
- rc = lsmFsMoveBlock(pDb->pFS, &pLvl->lhs, iTo, iFrom);
- if( rc==LSM_OK ){
- if( p->redirect.a==0 ){
- int nByte = sizeof(struct RedirectEntry) * LSM_MAX_BLOCK_REDIRECTS;
- p->redirect.a = lsmMallocZeroRc(pDb->pEnv, nByte, &rc);
- }
- if( rc==LSM_OK ){
- /* Check if the block just moved was already redirected. */
- int i;
- for(i=0; i<p->redirect.n; i++){
- if( p->redirect.a[i].iTo==iFrom ) break;
- }
- if( i==p->redirect.n ){
- /* Block iFrom was not already redirected. Add a new array entry. */
- memmove(&p->redirect.a[1], &p->redirect.a[0],
- sizeof(struct RedirectEntry) * p->redirect.n
- );
- p->redirect.a[0].iFrom = iFrom;
- p->redirect.a[0].iTo = iTo;
- p->redirect.n++;
- }else{
- /* Block iFrom was already redirected. Overwrite existing entry. */
- p->redirect.a[i].iTo = iTo;
- }
- rc = lsmBlockFree(pDb, iFrom);
- *pnWrite = lsmFsBlockSize(pDb->pFS) / lsmFsPageSize(pDb->pFS);
- pLvl->lhs.pRedirect = &p->redirect;
- }
- }
- #if LSM_LOG_STRUCTURE
- if( rc==LSM_OK ){
- char aBuf[64];
- sprintf(aBuf, "move-block %d/%d", p->redirect.n-1, LSM_MAX_BLOCK_REDIRECTS);
- lsmSortedDumpStructure(pDb, pDb->pWorker, LSM_LOG_DATA, 0, aBuf);
- }
- #endif
- return rc;
- }
- /*
- */
- static int mergeInsertFreelistSegments(
- lsm_db *pDb,
- int nFree,
- MergeWorker *pMW
- ){
- int rc = LSM_OK;
- if( nFree>0 ){
- MultiCursor *pCsr = pMW->pCsr;
- Level *pLvl = pMW->pLevel;
- SegmentPtr *aNew1;
- Segment *aNew2;
- Level *pIter;
- Level *pNext;
- int i = 0;
- aNew1 = (SegmentPtr *)lsmMallocZeroRc(
- pDb->pEnv, sizeof(SegmentPtr) * (pCsr->nPtr+nFree), &rc
- );
- if( rc ) return rc;
- memcpy(&aNew1[nFree], pCsr->aPtr, sizeof(SegmentPtr)*pCsr->nPtr);
- pCsr->nPtr += nFree;
- lsmFree(pDb->pEnv, pCsr->aTree);
- lsmFree(pDb->pEnv, pCsr->aPtr);
- pCsr->aTree = 0;
- pCsr->aPtr = aNew1;
- aNew2 = (Segment *)lsmMallocZeroRc(
- pDb->pEnv, sizeof(Segment) * (pLvl->nRight+nFree), &rc
- );
- if( rc ) return rc;
- memcpy(&aNew2[nFree], pLvl->aRhs, sizeof(Segment)*pLvl->nRight);
- pLvl->nRight += nFree;
- lsmFree(pDb->pEnv, pLvl->aRhs);
- pLvl->aRhs = aNew2;
- for(pIter=pDb->pWorker->pLevel; rc==LSM_OK && pIter!=pLvl; pIter=pNext){
- Segment *pSeg = &pLvl->aRhs[i];
- memcpy(pSeg, &pIter->lhs, sizeof(Segment));
- pCsr->aPtr[i].pSeg = pSeg;
- pCsr->aPtr[i].pLevel = pLvl;
- rc = segmentPtrEnd(pCsr, &pCsr->aPtr[i], 0);
- pDb->pWorker->pLevel = pNext = pIter->pNext;
- sortedFreeLevel(pDb->pEnv, pIter);
- i++;
- }
- assert( i==nFree );
- assert( rc!=LSM_OK || pDb->pWorker->pLevel==pLvl );
- for(i=nFree; i<pCsr->nPtr; i++){
- pCsr->aPtr[i].pSeg = &pLvl->aRhs[i];
- }
- lsmFree(pDb->pEnv, pMW->aGobble);
- pMW->aGobble = 0;
- }
- return rc;
- }
- static int sortedWork(
- lsm_db *pDb, /* Database handle. Must be worker. */
- int nWork, /* Number of pages of work to do */
- int nMerge, /* Try to merge this many levels at once */
- int bFlush, /* Set if call is to make room for a flush */
- int *pnWrite /* OUT: Actual number of pages written */
- ){
- int rc = LSM_OK; /* Return Code */
- int nRemaining = nWork; /* Units of work to do before returning */
- Snapshot *pWorker = pDb->pWorker;
- assert( pWorker );
- if( lsmDbSnapshotLevel(pWorker)==0 ) return LSM_OK;
- while( nRemaining>0 ){
- Level *pLevel = 0;
- /* Find a level to work on. */
- rc = sortedSelectLevel(pDb, nMerge, &pLevel);
- assert( rc==LSM_OK || pLevel==0 );
- if( pLevel==0 ){
- int nDone = 0;
- Level *pTopLevel = lsmDbSnapshotLevel(pDb->pWorker);
- if( bFlush==0 && nMerge==1 && pTopLevel && pTopLevel->pNext==0 ){
- rc = sortedMoveBlock(pDb, &nDone);
- }
- nRemaining -= nDone;
- /* Could not find any work to do. Finished. */
- if( nDone==0 ) break;
- }else{
- int bSave = 0;
- Freelist freelist = {0, 0, 0};
- MergeWorker mergeworker; /* State used to work on the level merge */
- assert( pDb->bIncrMerge==0 );
- assert( pDb->pFreelist==0 && pDb->bUseFreelist==0 );
- pDb->bIncrMerge = 1;
- rc = mergeWorkerInit(pDb, pLevel, &mergeworker);
- assert( mergeworker.nWork==0 );
-
- while( rc==LSM_OK
- && 0==mergeWorkerDone(&mergeworker)
- && (mergeworker.nWork<nRemaining || pDb->bUseFreelist)
- ){
- int eType = rtTopic(mergeworker.pCsr->eType);
- rc = mergeWorkerStep(&mergeworker);
- /* If the cursor now points at the first entry past the end of the
- ** user data (i.e. either to EOF or to the first free-list entry
- ** that will be added to the run), then check if it is possible to
- ** merge in any free-list entries that are either in-memory or in
- ** free-list-only blocks. */
- if( rc==LSM_OK && nMerge==1 && eType==0
- && (rtTopic(mergeworker.pCsr->eType) || mergeWorkerDone(&mergeworker))
- ){
- int nFree = 0; /* Number of free-list-only levels to merge */
- Level *pLvl;
- assert( pDb->pFreelist==0 && pDb->bUseFreelist==0 );
- /* Now check if all levels containing data newer than this one
- ** are single-segment free-list only levels. If so, they will be
- ** merged in now. */
- for(pLvl=pDb->pWorker->pLevel;
- pLvl!=mergeworker.pLevel && (pLvl->flags & LEVEL_FREELIST_ONLY);
- pLvl=pLvl->pNext
- ){
- assert( pLvl->nRight==0 );
- nFree++;
- }
- if( pLvl==mergeworker.pLevel ){
- rc = mergeInsertFreelistSegments(pDb, nFree, &mergeworker);
- if( rc==LSM_OK ){
- rc = multiCursorVisitFreelist(mergeworker.pCsr);
- }
- if( rc==LSM_OK ){
- rc = multiCursorSetupTree(mergeworker.pCsr, 0);
- pDb->pFreelist = &freelist;
- pDb->bUseFreelist = 1;
- }
- }
- }
- }
- nRemaining -= LSM_MAX(mergeworker.nWork, 1);
- if( rc==LSM_OK ){
- /* Check if the merge operation is completely finished. If not,
- ** gobble up (declare eligible for recycling) any pages from rhs
- ** segments for which the content has been completely merged into
- ** the lhs of the level. */
- if( mergeWorkerDone(&mergeworker)==0 ){
- int i;
- for(i=0; i<pLevel->nRight; i++){
- SegmentPtr *pGobble = &mergeworker.pCsr->aPtr[i];
- if( pGobble->pSeg->iRoot ){
- rc = sortedBtreeGobble(pDb, mergeworker.pCsr, i);
- }else if( mergeworker.aGobble[i] ){
- lsmFsGobble(pDb, pGobble->pSeg, &mergeworker.aGobble[i], 1);
- }
- }
- }else{
- int i;
- int bEmpty;
- mergeWorkerShutdown(&mergeworker, &rc);
- bEmpty = (pLevel->lhs.iFirst==0);
- if( bEmpty==0 && rc==LSM_OK ){
- rc = lsmFsSortedFinish(pDb->pFS, &pLevel->lhs);
- }
- if( pDb->bUseFreelist ){
- Freelist *p = &pDb->pWorker->freelist;
- lsmFree(pDb->pEnv, p->aEntry);
- memcpy(p, &freelist, sizeof(freelist));
- pDb->bUseFreelist = 0;
- pDb->pFreelist = 0;
- bSave = 1;
- }
- for(i=0; i<pLevel->nRight; i++){
- lsmFsSortedDelete(pDb->pFS, pWorker, 1, &pLevel->aRhs[i]);
- }
- if( bEmpty ){
- /* If the new level is completely empty, remove it from the
- ** database snapshot. This can only happen if all input keys were
- ** annihilated. Since keys are only annihilated if the new level
- ** is the last in the linked list (contains the most ancient of
- ** database content), this guarantees that pLevel->pNext==0. */
- Level *pTop; /* Top level of worker snapshot */
- Level **pp; /* Read/write iterator for Level.pNext list */
- assert( pLevel->pNext==0 );
- /* Remove the level from the worker snapshot. */
- pTop = lsmDbSnapshotLevel(pWorker);
- for(pp=&pTop; *pp!=pLevel; pp=&((*pp)->pNext));
- *pp = pLevel->pNext;
- lsmDbSnapshotSetLevel(pWorker, pTop);
- /* Free the Level structure. */
- sortedFreeLevel(pDb->pEnv, pLevel);
- }else{
- /* Free the separators of the next level, if required. */
- if( pLevel->pMerge->nInput > pLevel->nRight ){
- assert( pLevel->pNext->lhs.iRoot );
- pLevel->pNext->lhs.iRoot = 0;
- }
- /* Zero the right-hand-side of pLevel */
- lsmFree(pDb->pEnv, pLevel->aRhs);
- pLevel->nRight = 0;
- pLevel->aRhs = 0;
- /* Free the Merge object */
- lsmFree(pDb->pEnv, pLevel->pMerge);
- pLevel->pMerge = 0;
- }
- if( bSave && rc==LSM_OK ){
- pDb->bIncrMerge = 0;
- rc = lsmSaveWorker(pDb, 0);
- }
- }
- }
- /* Clean up the MergeWorker object initialized above. If no error
- ** has occurred, invoke the work-hook to inform the application that
- ** the database structure has changed. */
- mergeWorkerShutdown(&mergeworker, &rc);
- pDb->bIncrMerge = 0;
- if( rc==LSM_OK ) sortedInvokeWorkHook(pDb);
- #if LSM_LOG_STRUCTURE
- lsmSortedDumpStructure(pDb, pDb->pWorker, LSM_LOG_DATA, 0, "work");
- #endif
- assertBtreeOk(pDb, &pLevel->lhs);
- assertRunInOrder(pDb, &pLevel->lhs);
- /* If bFlush is true and the database is no longer considered "full",
- ** break out of the loop even if nRemaining is still greater than
- ** zero. The caller has an in-memory tree to flush to disk. */
- if( bFlush && sortedDbIsFull(pDb)==0 ) break;
- }
- }
- if( pnWrite ) *pnWrite = (nWork - nRemaining);
- pWorker->nWrite += (nWork - nRemaining);
- #ifdef LSM_LOG_WORK
- lsmLogMessage(pDb, rc, "sortedWork(): %d pages", (nWork-nRemaining));
- #endif
- return rc;
- }
- /*
- ** The database connection passed as the first argument must be a worker
- ** connection. This function checks if there exists an "old" in-memory tree
- ** ready to be flushed to disk. If so, true is returned. Otherwise false.
- **
- ** If an error occurs, *pRc is set to an LSM error code before returning.
- ** It is assumed that *pRc is set to LSM_OK when this function is called.
- */
- static int sortedTreeHasOld(lsm_db *pDb, int *pRc){
- int rc = LSM_OK;
- int bRet = 0;
- assert( pDb->pWorker );
- if( *pRc==LSM_OK ){
- if( rc==LSM_OK
- && pDb->treehdr.iOldShmid
- && pDb->treehdr.iOldLog!=pDb->pWorker->iLogOff
- ){
- bRet = 1;
- }else{
- bRet = 0;
- }
- *pRc = rc;
- }
- assert( *pRc==LSM_OK || bRet==0 );
- return bRet;
- }
- /*
- ** Create a new free-list only top-level segment. Return LSM_OK if successful
- ** or an LSM error code if some error occurs.
- */
- static int sortedNewFreelistOnly(lsm_db *pDb){
- return sortedNewToplevel(pDb, TREE_NONE, 0);
- }
- int lsmSaveWorker(lsm_db *pDb, int bFlush){
- Snapshot *p = pDb->pWorker;
- if( p->freelist.nEntry>pDb->nMaxFreelist ){
- int rc = sortedNewFreelistOnly(pDb);
- if( rc!=LSM_OK ) return rc;
- }
- return lsmCheckpointSaveWorker(pDb, bFlush);
- }
- static int doLsmSingleWork(
- lsm_db *pDb,
- int bShutdown,
- int nMerge, /* Minimum segments to merge together */
- int nPage, /* Number of pages to write to disk */
- int *pnWrite, /* OUT: Pages actually written to disk */
- int *pbCkpt /* OUT: True if an auto-checkpoint is req. */
- ){
- Snapshot *pWorker; /* Worker snapshot */
- int rc = LSM_OK; /* Return code */
- int bDirty = 0;
- int nMax = nPage; /* Maximum pages to write to disk */
- int nRem = nPage;
- int bCkpt = 0;
- assert( nPage>0 );
- /* Open the worker 'transaction'. It will be closed before this function
- ** returns. */
- assert( pDb->pWorker==0 );
- rc = lsmBeginWork(pDb);
- if( rc!=LSM_OK ) return rc;
- pWorker = pDb->pWorker;
- /* If this connection is doing auto-checkpoints, set nMax (and nRem) so
- ** that this call stops writing when the auto-checkpoint is due. The
- ** caller will do the checkpoint, then possibly call this function again. */
- if( bShutdown==0 && pDb->nAutockpt ){
- u32 nSync;
- u32 nUnsync;
- int nPgsz;
- lsmCheckpointSynced(pDb, 0, 0, &nSync);
- nUnsync = lsmCheckpointNWrite(pDb->pShmhdr->aSnap1, 0);
- nPgsz = lsmCheckpointPgsz(pDb->pShmhdr->aSnap1);
- nMax = (int)LSM_MIN(nMax, (pDb->nAutockpt/nPgsz) - (int)(nUnsync-nSync));
- if( nMax<nRem ){
- bCkpt = 1;
- nRem = LSM_MAX(nMax, 0);
- }
- }
- /* If there exists in-memory data ready to be flushed to disk, attempt
- ** to flush it now. */
- if( pDb->nTransOpen==0 ){
- rc = lsmTreeLoadHeader(pDb, 0);
- }
- if( sortedTreeHasOld(pDb, &rc) ){
- /* sortedDbIsFull() returns non-zero if either (a) there are too many
- ** levels in total in the db, or (b) there are too many levels with the
- ** the same age in the db. Either way, call sortedWork() to merge
- ** existing segments together until this condition is cleared. */
- if( sortedDbIsFull(pDb) ){
- int nPg = 0;
- rc = sortedWork(pDb, nRem, nMerge, 1, &nPg);
- nRem -= nPg;
- assert( rc!=LSM_OK || nRem<=0 || !sortedDbIsFull(pDb) );
- bDirty = 1;
- }
- if( rc==LSM_OK && nRem>0 ){
- int nPg = 0;
- rc = sortedNewToplevel(pDb, TREE_OLD, &nPg);
- nRem -= nPg;
- if( rc==LSM_OK ){
- if( pDb->nTransOpen>0 ){
- lsmTreeDiscardOld(pDb);
- }
- rc = lsmSaveWorker(pDb, 1);
- bDirty = 0;
- }
- }
- }
- /* If nPage is still greater than zero, do some merging. */
- if( rc==LSM_OK && nRem>0 && bShutdown==0 ){
- int nPg = 0;
- rc = sortedWork(pDb, nRem, nMerge, 0, &nPg);
- nRem -= nPg;
- if( nPg ) bDirty = 1;
- }
- /* If the in-memory part of the free-list is too large, write a new
- ** top-level containing just the in-memory free-list entries to disk. */
- if( rc==LSM_OK && pDb->pWorker->freelist.nEntry > pDb->nMaxFreelist ){
- while( rc==LSM_OK && lsmDatabaseFull(pDb) ){
- int nPg = 0;
- rc = sortedWork(pDb, 16, nMerge, 1, &nPg);
- nRem -= nPg;
- }
- if( rc==LSM_OK ){
- rc = sortedNewFreelistOnly(pDb);
- }
- bDirty = 1;
- }
- if( rc==LSM_OK ){
- *pnWrite = (nMax - nRem);
- *pbCkpt = (bCkpt && nRem<=0);
- if( nMerge==1 && pDb->nAutockpt>0 && *pnWrite>0
- && pWorker->pLevel
- && pWorker->pLevel->nRight==0
- && pWorker->pLevel->pNext==0
- ){
- *pbCkpt = 1;
- }
- }
- if( rc==LSM_OK && bDirty ){
- lsmFinishWork(pDb, 0, &rc);
- }else{
- int rcdummy = LSM_BUSY;
- lsmFinishWork(pDb, 0, &rcdummy);
- *pnWrite = 0;
- }
- assert( pDb->pWorker==0 );
- return rc;
- }
- static int doLsmWork(lsm_db *pDb, int nMerge, int nPage, int *pnWrite){
- int rc = LSM_OK; /* Return code */
- int nWrite = 0; /* Number of pages written */
- assert( nMerge>=1 );
- if( nPage!=0 ){
- int bCkpt = 0;
- do {
- int nThis = 0;
- int nReq = (nPage>=0) ? (nPage-nWrite) : ((int)0x7FFFFFFF);
- bCkpt = 0;
- rc = doLsmSingleWork(pDb, 0, nMerge, nReq, &nThis, &bCkpt);
- nWrite += nThis;
- if( rc==LSM_OK && bCkpt ){
- rc = lsm_checkpoint(pDb, 0);
- }
- }while( rc==LSM_OK && bCkpt && (nWrite<nPage || nPage<0) );
- }
- if( pnWrite ){
- if( rc==LSM_OK ){
- *pnWrite = nWrite;
- }else{
- *pnWrite = 0;
- }
- }
- return rc;
- }
- /*
- ** Perform work to merge database segments together.
- */
- int lsm_work(lsm_db *pDb, int nMerge, int nKB, int *pnWrite){
- int rc; /* Return code */
- int nPgsz; /* Nominal page size in bytes */
- int nPage; /* Equivalent of nKB in pages */
- int nWrite = 0; /* Number of pages written */
- /* This function may not be called if pDb has an open read or write
- ** transaction. Return LSM_MISUSE if an application attempts this. */
- if( pDb->nTransOpen || pDb->pCsr ) return LSM_MISUSE_BKPT;
- if( nMerge<=0 ) nMerge = pDb->nMerge;
- lsmFsPurgeCache(pDb->pFS);
- /* Convert from KB to pages */
- nPgsz = lsmFsPageSize(pDb->pFS);
- if( nKB>=0 ){
- nPage = ((i64)nKB * 1024 + nPgsz - 1) / nPgsz;
- }else{
- nPage = -1;
- }
- rc = doLsmWork(pDb, nMerge, nPage, &nWrite);
-
- if( pnWrite ){
- /* Convert back from pages to KB */
- *pnWrite = (int)(((i64)nWrite * 1024 + nPgsz - 1) / nPgsz);
- }
- return rc;
- }
- int lsm_flush(lsm_db *db){
- int rc;
- if( db->nTransOpen>0 || db->pCsr ){
- rc = LSM_MISUSE_BKPT;
- }else{
- rc = lsmBeginWriteTrans(db);
- if( rc==LSM_OK ){
- lsmFlushTreeToDisk(db);
- lsmTreeDiscardOld(db);
- lsmTreeMakeOld(db);
- lsmTreeDiscardOld(db);
- }
- if( rc==LSM_OK ){
- rc = lsmFinishWriteTrans(db, 1);
- }else{
- lsmFinishWriteTrans(db, 0);
- }
- lsmFinishReadTrans(db);
- }
- return rc;
- }
- /*
- ** This function is called in auto-work mode to perform merging work on
- ** the data structure. It performs enough merging work to prevent the
- ** height of the tree from growing indefinitely assuming that roughly
- ** nUnit database pages worth of data have been written to the database
- ** (i.e. the in-memory tree) since the last call.
- */
- int lsmSortedAutoWork(
- lsm_db *pDb, /* Database handle */
- int nUnit /* Pages of data written to in-memory tree */
- ){
- int rc = LSM_OK; /* Return code */
- int nDepth = 0; /* Current height of tree (longest path) */
- Level *pLevel; /* Used to iterate through levels */
- int bRestore = 0;
- assert( pDb->pWorker==0 );
- assert( pDb->nTransOpen>0 );
- /* Determine how many units of work to do before returning. One unit of
- ** work is achieved by writing one page (~4KB) of merged data. */
- for(pLevel=lsmDbSnapshotLevel(pDb->pClient); pLevel; pLevel=pLevel->pNext){
- /* nDepth += LSM_MAX(1, pLevel->nRight); */
- nDepth += 1;
- }
- if( lsmTreeHasOld(pDb) ){
- nDepth += 1;
- bRestore = 1;
- rc = lsmSaveCursors(pDb);
- if( rc!=LSM_OK ) return rc;
- }
- if( nDepth>0 ){
- int nRemaining; /* Units of work to do before returning */
- nRemaining = nUnit * nDepth;
- #ifdef LSM_LOG_WORK
- lsmLogMessage(pDb, rc, "lsmSortedAutoWork(): %d*%d = %d pages",
- nUnit, nDepth, nRemaining);
- #endif
- assert( nRemaining>=0 );
- rc = doLsmWork(pDb, pDb->nMerge, nRemaining, 0);
- if( rc==LSM_BUSY ) rc = LSM_OK;
- if( bRestore && pDb->pCsr ){
- lsmMCursorFreeCache(pDb);
- lsmFreeSnapshot(pDb->pEnv, pDb->pClient);
- pDb->pClient = 0;
- if( rc==LSM_OK ){
- rc = lsmCheckpointLoad(pDb, 0);
- }
- if( rc==LSM_OK ){
- rc = lsmCheckpointDeserialize(pDb, 0, pDb->aSnapshot, &pDb->pClient);
- }
- if( rc==LSM_OK ){
- rc = lsmRestoreCursors(pDb);
- }
- }
- }
- return rc;
- }
- /*
- ** This function is only called during system shutdown. The contents of
- ** any in-memory trees present (old or current) are written out to disk.
- */
- int lsmFlushTreeToDisk(lsm_db *pDb){
- int rc;
- rc = lsmBeginWork(pDb);
- while( rc==LSM_OK && sortedDbIsFull(pDb) ){
- rc = sortedWork(pDb, 256, pDb->nMerge, 1, 0);
- }
- if( rc==LSM_OK ){
- rc = sortedNewToplevel(pDb, TREE_BOTH, 0);
- }
- lsmFinishWork(pDb, 1, &rc);
- return rc;
- }
- /*
- ** Return a string representation of the segment passed as the only argument.
- ** Space for the returned string is allocated using lsmMalloc(), and should
- ** be freed by the caller using lsmFree().
- */
- static char *segToString(lsm_env *pEnv, Segment *pSeg, int nMin){
- LsmPgno nSize = pSeg->nSize;
- LsmPgno iRoot = pSeg->iRoot;
- LsmPgno iFirst = pSeg->iFirst;
- LsmPgno iLast = pSeg->iLastPg;
- char *z;
- char *z1;
- char *z2;
- int nPad;
- z1 = lsmMallocPrintf(pEnv, "%d.%d", iFirst, iLast);
- if( iRoot ){
- z2 = lsmMallocPrintf(pEnv, "root=%lld", iRoot);
- }else{
- z2 = lsmMallocPrintf(pEnv, "size=%lld", nSize);
- }
- nPad = nMin - 2 - strlen(z1) - 1 - strlen(z2);
- nPad = LSM_MAX(0, nPad);
- if( iRoot ){
- z = lsmMallocPrintf(pEnv, "/%s %*s%s\\", z1, nPad, "", z2);
- }else{
- z = lsmMallocPrintf(pEnv, "|%s %*s%s|", z1, nPad, "", z2);
- }
- lsmFree(pEnv, z1);
- lsmFree(pEnv, z2);
- return z;
- }
- static int fileToString(
- lsm_db *pDb, /* For xMalloc() */
- char *aBuf,
- int nBuf,
- int nMin,
- Segment *pSeg
- ){
- int i = 0;
- if( pSeg ){
- char *zSeg;
- zSeg = segToString(pDb->pEnv, pSeg, nMin);
- snprintf(&aBuf[i], nBuf-i, "%s", zSeg);
- i += strlen(&aBuf[i]);
- lsmFree(pDb->pEnv, zSeg);
- #ifdef LSM_LOG_FREELIST
- lsmInfoArrayStructure(pDb, 1, pSeg->iFirst, &zSeg);
- snprintf(&aBuf[i], nBuf-1, " (%s)", zSeg);
- i += strlen(&aBuf[i]);
- lsmFree(pDb->pEnv, zSeg);
- #endif
- aBuf[nBuf] = 0;
- }else{
- aBuf[0] = '\0';
- }
- return i;
- }
- void sortedDumpPage(lsm_db *pDb, Segment *pRun, Page *pPg, int bVals){
- LsmBlob blob = {0, 0, 0}; /* LsmBlob used for keys */
- LsmString s;
- int i;
- int nRec;
- LsmPgno iPtr;
- int flags;
- u8 *aData;
- int nData;
- aData = fsPageData(pPg, &nData);
- nRec = pageGetNRec(aData, nData);
- iPtr = pageGetPtr(aData, nData);
- flags = pageGetFlags(aData, nData);
- lsmStringInit(&s, pDb->pEnv);
- lsmStringAppendf(&s,"nCell=%d iPtr=%lld flags=%d {", nRec, iPtr, flags);
- if( flags&SEGMENT_BTREE_FLAG ) iPtr = 0;
- for(i=0; i<nRec; i++){
- Page *pRef = 0; /* Pointer to page iRef */
- int iChar;
- u8 *aKey; int nKey = 0; /* Key */
- u8 *aVal = 0; int nVal = 0; /* Value */
- int iTopic;
- u8 *aCell;
- i64 iPgPtr;
- int eType;
- aCell = pageGetCell(aData, nData, i);
- eType = *aCell++;
- assert( (flags & SEGMENT_BTREE_FLAG) || eType!=0 );
- aCell += lsmVarintGet64(aCell, &iPgPtr);
- if( eType==0 ){
- LsmPgno iRef; /* Page number of referenced page */
- aCell += lsmVarintGet64(aCell, &iRef);
- lsmFsDbPageGet(pDb->pFS, pRun, iRef, &pRef);
- aKey = pageGetKey(pRun, pRef, 0, &iTopic, &nKey, &blob);
- }else{
- aCell += lsmVarintGet32(aCell, &nKey);
- if( rtIsWrite(eType) ) aCell += lsmVarintGet32(aCell, &nVal);
- sortedReadData(0, pPg, (aCell-aData), nKey+nVal, (void **)&aKey, &blob);
- aVal = &aKey[nKey];
- iTopic = eType;
- }
- lsmStringAppendf(&s, "%s%2X:", (i==0?"":" "), iTopic);
- for(iChar=0; iChar<nKey; iChar++){
- lsmStringAppendf(&s, "%c", isalnum(aKey[iChar]) ? aKey[iChar] : '.');
- }
- if( nVal>0 && bVals ){
- lsmStringAppendf(&s, "##");
- for(iChar=0; iChar<nVal; iChar++){
- lsmStringAppendf(&s, "%c", isalnum(aVal[iChar]) ? aVal[iChar] : '.');
- }
- }
- lsmStringAppendf(&s, " %lld", iPgPtr+iPtr);
- lsmFsPageRelease(pRef);
- }
- lsmStringAppend(&s, "}", 1);
- lsmLogMessage(pDb, LSM_OK, " Page %d: %s", lsmFsPageNumber(pPg), s.z);
- lsmStringClear(&s);
- sortedBlobFree(&blob);
- }
- static void infoCellDump(
- lsm_db *pDb, /* Database handle */
- Segment *pSeg, /* Segment page belongs to */
- int bIndirect, /* True to follow indirect refs */
- Page *pPg,
- int iCell,
- int *peType,
- int *piPgPtr,
- u8 **paKey, int *pnKey,
- u8 **paVal, int *pnVal,
- LsmBlob *pBlob
- ){
- u8 *aData; int nData; /* Page data */
- u8 *aKey; int nKey = 0; /* Key */
- u8 *aVal = 0; int nVal = 0; /* Value */
- int eType;
- int iPgPtr;
- Page *pRef = 0; /* Pointer to page iRef */
- u8 *aCell;
- aData = fsPageData(pPg, &nData);
- aCell = pageGetCell(aData, nData, iCell);
- eType = *aCell++;
- aCell += lsmVarintGet32(aCell, &iPgPtr);
- if( eType==0 ){
- int dummy;
- LsmPgno iRef; /* Page number of referenced page */
- aCell += lsmVarintGet64(aCell, &iRef);
- if( bIndirect ){
- lsmFsDbPageGet(pDb->pFS, pSeg, iRef, &pRef);
- pageGetKeyCopy(pDb->pEnv, pSeg, pRef, 0, &dummy, pBlob);
- aKey = (u8 *)pBlob->pData;
- nKey = pBlob->nData;
- lsmFsPageRelease(pRef);
- }else{
- aKey = (u8 *)"<indirect>";
- nKey = 11;
- }
- }else{
- aCell += lsmVarintGet32(aCell, &nKey);
- if( rtIsWrite(eType) ) aCell += lsmVarintGet32(aCell, &nVal);
- sortedReadData(pSeg, pPg, (aCell-aData), nKey+nVal, (void **)&aKey, pBlob);
- aVal = &aKey[nKey];
- }
- if( peType ) *peType = eType;
- if( piPgPtr ) *piPgPtr = iPgPtr;
- if( paKey ) *paKey = aKey;
- if( paVal ) *paVal = aVal;
- if( pnKey ) *pnKey = nKey;
- if( pnVal ) *pnVal = nVal;
- }
- static int infoAppendBlob(LsmString *pStr, int bHex, u8 *z, int n){
- int iChar;
- for(iChar=0; iChar<n; iChar++){
- if( bHex ){
- lsmStringAppendf(pStr, "%02X", z[iChar]);
- }else{
- lsmStringAppendf(pStr, "%c", isalnum(z[iChar]) ?z[iChar] : '.');
- }
- }
- return LSM_OK;
- }
- #define INFO_PAGE_DUMP_DATA 0x01
- #define INFO_PAGE_DUMP_VALUES 0x02
- #define INFO_PAGE_DUMP_HEX 0x04
- #define INFO_PAGE_DUMP_INDIRECT 0x08
- static int infoPageDump(
- lsm_db *pDb, /* Database handle */
- LsmPgno iPg, /* Page number of page to dump */
- int flags,
- char **pzOut /* OUT: lsmMalloc'd string */
- ){
- int rc = LSM_OK; /* Return code */
- Page *pPg = 0; /* Handle for page iPg */
- int i, j; /* Loop counters */
- const int perLine = 16; /* Bytes per line in the raw hex dump */
- Segment *pSeg = 0;
- Snapshot *pSnap;
- int bValues = (flags & INFO_PAGE_DUMP_VALUES);
- int bHex = (flags & INFO_PAGE_DUMP_HEX);
- int bData = (flags & INFO_PAGE_DUMP_DATA);
- int bIndirect = (flags & INFO_PAGE_DUMP_INDIRECT);
- *pzOut = 0;
- if( iPg==0 ) return LSM_ERROR;
- assert( pDb->pClient || pDb->pWorker );
- pSnap = pDb->pClient;
- if( pSnap==0 ) pSnap = pDb->pWorker;
- if( pSnap->redirect.n>0 ){
- Level *pLvl;
- int bUse = 0;
- for(pLvl=pSnap->pLevel; pLvl->pNext; pLvl=pLvl->pNext);
- pSeg = (pLvl->nRight==0 ? &pLvl->lhs : &pLvl->aRhs[pLvl->nRight-1]);
- rc = lsmFsSegmentContainsPg(pDb->pFS, pSeg, iPg, &bUse);
- if( bUse==0 ){
- pSeg = 0;
- }
- }
- /* iPg is a real page number (not subject to redirection). So it is safe
- ** to pass a NULL in place of the segment pointer as the second argument
- ** to lsmFsDbPageGet() here. */
- if( rc==LSM_OK ){
- rc = lsmFsDbPageGet(pDb->pFS, 0, iPg, &pPg);
- }
- if( rc==LSM_OK ){
- LsmBlob blob = {0, 0, 0, 0};
- int nKeyWidth = 0;
- LsmString str;
- int nRec;
- LsmPgno iPtr;
- int flags2;
- int iCell;
- u8 *aData; int nData; /* Page data and size thereof */
- aData = fsPageData(pPg, &nData);
- nRec = pageGetNRec(aData, nData);
- iPtr = pageGetPtr(aData, nData);
- flags2 = pageGetFlags(aData, nData);
- lsmStringInit(&str, pDb->pEnv);
- lsmStringAppendf(&str, "Page : %lld (%d bytes)\n", iPg, nData);
- lsmStringAppendf(&str, "nRec : %d\n", nRec);
- lsmStringAppendf(&str, "iPtr : %lld\n", iPtr);
- lsmStringAppendf(&str, "flags: %04x\n", flags2);
- lsmStringAppendf(&str, "\n");
- for(iCell=0; iCell<nRec; iCell++){
- int nKey;
- infoCellDump(
- pDb, pSeg, bIndirect, pPg, iCell, 0, 0, 0, &nKey, 0, 0, &blob
- );
- if( nKey>nKeyWidth ) nKeyWidth = nKey;
- }
- if( bHex ) nKeyWidth = nKeyWidth * 2;
- for(iCell=0; iCell<nRec; iCell++){
- u8 *aKey; int nKey = 0; /* Key */
- u8 *aVal; int nVal = 0; /* Value */
- int iPgPtr;
- int eType;
- LsmPgno iAbsPtr;
- char zFlags[8];
- infoCellDump(pDb, pSeg, bIndirect, pPg, iCell, &eType, &iPgPtr,
- &aKey, &nKey, &aVal, &nVal, &blob
- );
- iAbsPtr = iPgPtr + ((flags2 & SEGMENT_BTREE_FLAG) ? 0 : iPtr);
- lsmFlagsToString(eType, zFlags);
- lsmStringAppendf(&str, "%s %d (%s) ",
- zFlags, iAbsPtr, (rtTopic(eType) ? "sys" : "usr")
- );
- infoAppendBlob(&str, bHex, aKey, nKey);
- if( nVal>0 && bValues ){
- lsmStringAppendf(&str, "%*s", nKeyWidth - (nKey*(1+bHex)), "");
- lsmStringAppendf(&str, " ");
- infoAppendBlob(&str, bHex, aVal, nVal);
- }
- if( rtTopic(eType) ){
- int iBlk = (int)~lsmGetU32(aKey);
- lsmStringAppendf(&str, " (block=%d", iBlk);
- if( nVal>0 ){
- i64 iSnap = lsmGetU64(aVal);
- lsmStringAppendf(&str, " snapshot=%lld", iSnap);
- }
- lsmStringAppendf(&str, ")");
- }
- lsmStringAppendf(&str, "\n");
- }
- if( bData ){
- lsmStringAppendf(&str, "\n-------------------"
- "-------------------------------------------------------------\n");
- lsmStringAppendf(&str, "Page %d\n",
- iPg, (iPg-1)*nData, iPg*nData - 1);
- for(i=0; i<nData; i += perLine){
- lsmStringAppendf(&str, "%04x: ", i);
- for(j=0; j<perLine; j++){
- if( i+j>nData ){
- lsmStringAppendf(&str, " ");
- }else{
- lsmStringAppendf(&str, "%02x ", aData[i+j]);
- }
- }
- lsmStringAppendf(&str, " ");
- for(j=0; j<perLine; j++){
- if( i+j>nData ){
- lsmStringAppendf(&str, " ");
- }else{
- lsmStringAppendf(&str,"%c", isprint(aData[i+j]) ? aData[i+j] : '.');
- }
- }
- lsmStringAppendf(&str,"\n");
- }
- }
- *pzOut = str.z;
- sortedBlobFree(&blob);
- lsmFsPageRelease(pPg);
- }
- return rc;
- }
- int lsmInfoPageDump(
- lsm_db *pDb, /* Database handle */
- LsmPgno iPg, /* Page number of page to dump */
- int bHex, /* True to output key/value in hex form */
- char **pzOut /* OUT: lsmMalloc'd string */
- ){
- int flags = INFO_PAGE_DUMP_DATA | INFO_PAGE_DUMP_VALUES;
- if( bHex ) flags |= INFO_PAGE_DUMP_HEX;
- return infoPageDump(pDb, iPg, flags, pzOut);
- }
- void sortedDumpSegment(lsm_db *pDb, Segment *pRun, int bVals){
- assert( pDb->xLog );
- if( pRun && pRun->iFirst ){
- int flags = (bVals ? INFO_PAGE_DUMP_VALUES : 0);
- char *zSeg;
- Page *pPg;
- zSeg = segToString(pDb->pEnv, pRun, 0);
- lsmLogMessage(pDb, LSM_OK, "Segment: %s", zSeg);
- lsmFree(pDb->pEnv, zSeg);
- lsmFsDbPageGet(pDb->pFS, pRun, pRun->iFirst, &pPg);
- while( pPg ){
- Page *pNext;
- char *z = 0;
- infoPageDump(pDb, lsmFsPageNumber(pPg), flags, &z);
- lsmLogMessage(pDb, LSM_OK, "%s", z);
- lsmFree(pDb->pEnv, z);
- #if 0
- sortedDumpPage(pDb, pRun, pPg, bVals);
- #endif
- lsmFsDbPageNext(pRun, pPg, 1, &pNext);
- lsmFsPageRelease(pPg);
- pPg = pNext;
- }
- }
- }
- /*
- ** Invoke the log callback zero or more times with messages that describe
- ** the current database structure.
- */
- void lsmSortedDumpStructure(
- lsm_db *pDb, /* Database handle (used for xLog callback) */
- Snapshot *pSnap, /* Snapshot to dump */
- int bKeys, /* Output the keys from each segment */
- int bVals, /* Output the values from each segment */
- const char *zWhy /* Caption to print near top of dump */
- ){
- Snapshot *pDump = pSnap;
- Level *pTopLevel;
- char *zFree = 0;
- assert( pSnap );
- pTopLevel = lsmDbSnapshotLevel(pDump);
- if( pDb->xLog && pTopLevel ){
- static int nCall = 0;
- Level *pLevel;
- int iLevel = 0;
- nCall++;
- lsmLogMessage(pDb, LSM_OK, "Database structure %d (%s)", nCall, zWhy);
- #if 0
- if( nCall==1031 || nCall==1032 ) bKeys=1;
- #endif
- for(pLevel=pTopLevel; pLevel; pLevel=pLevel->pNext){
- char zLeft[1024];
- char zRight[1024];
- int i = 0;
- Segment *aLeft[24];
- Segment *aRight[24];
- int nLeft = 0;
- int nRight = 0;
- Segment *pSeg = &pLevel->lhs;
- aLeft[nLeft++] = pSeg;
- for(i=0; i<pLevel->nRight; i++){
- aRight[nRight++] = &pLevel->aRhs[i];
- }
- #ifdef LSM_LOG_FREELIST
- if( nRight ){
- memmove(&aRight[1], aRight, sizeof(aRight[0])*nRight);
- aRight[0] = 0;
- nRight++;
- }
- #endif
- for(i=0; i<nLeft || i<nRight; i++){
- int iPad = 0;
- char zLevel[32];
- zLeft[0] = '\0';
- zRight[0] = '\0';
- if( i<nLeft ){
- fileToString(pDb, zLeft, sizeof(zLeft), 24, aLeft[i]);
- }
- if( i<nRight ){
- fileToString(pDb, zRight, sizeof(zRight), 24, aRight[i]);
- }
- if( i==0 ){
- snprintf(zLevel, sizeof(zLevel), "L%d: (age=%d) (flags=%.4x)",
- iLevel, (int)pLevel->iAge, (int)pLevel->flags
- );
- }else{
- zLevel[0] = '\0';
- }
- if( nRight==0 ){
- iPad = 10;
- }
- lsmLogMessage(pDb, LSM_OK, "% 25s % *s% -35s %s",
- zLevel, iPad, "", zLeft, zRight
- );
- }
- iLevel++;
- }
- if( bKeys ){
- for(pLevel=pTopLevel; pLevel; pLevel=pLevel->pNext){
- int i;
- sortedDumpSegment(pDb, &pLevel->lhs, bVals);
- for(i=0; i<pLevel->nRight; i++){
- sortedDumpSegment(pDb, &pLevel->aRhs[i], bVals);
- }
- }
- }
- }
- lsmInfoFreelist(pDb, &zFree);
- lsmLogMessage(pDb, LSM_OK, "Freelist: %s", zFree);
- lsmFree(pDb->pEnv, zFree);
- assert( lsmFsIntegrityCheck(pDb) );
- }
- void lsmSortedFreeLevel(lsm_env *pEnv, Level *pLevel){
- Level *pNext;
- Level *p;
- for(p=pLevel; p; p=pNext){
- pNext = p->pNext;
- sortedFreeLevel(pEnv, p);
- }
- }
- void lsmSortedSaveTreeCursors(lsm_db *pDb){
- MultiCursor *pCsr;
- for(pCsr=pDb->pCsr; pCsr; pCsr=pCsr->pNext){
- lsmTreeCursorSave(pCsr->apTreeCsr[0]);
- lsmTreeCursorSave(pCsr->apTreeCsr[1]);
- }
- }
- void lsmSortedExpandBtreePage(Page *pPg, int nOrig){
- u8 *aData;
- int nData;
- int nEntry;
- int iHdr;
- aData = lsmFsPageData(pPg, &nData);
- nEntry = pageGetNRec(aData, nOrig);
- iHdr = SEGMENT_EOF(nOrig, nEntry);
- memmove(&aData[iHdr + (nData-nOrig)], &aData[iHdr], nOrig-iHdr);
- }
- #ifdef LSM_DEBUG_EXPENSIVE
- static void assertRunInOrder(lsm_db *pDb, Segment *pSeg){
- Page *pPg = 0;
- LsmBlob blob1 = {0, 0, 0, 0};
- LsmBlob blob2 = {0, 0, 0, 0};
- lsmFsDbPageGet(pDb->pFS, pSeg, pSeg->iFirst, &pPg);
- while( pPg ){
- u8 *aData; int nData;
- Page *pNext;
- aData = lsmFsPageData(pPg, &nData);
- if( 0==(pageGetFlags(aData, nData) & SEGMENT_BTREE_FLAG) ){
- int i;
- int nRec = pageGetNRec(aData, nData);
- for(i=0; i<nRec; i++){
- int iTopic1, iTopic2;
- pageGetKeyCopy(pDb->pEnv, pSeg, pPg, i, &iTopic1, &blob1);
- if( i==0 && blob2.nData ){
- assert( sortedKeyCompare(
- pDb->xCmp, iTopic2, blob2.pData, blob2.nData,
- iTopic1, blob1.pData, blob1.nData
- )<0 );
- }
- if( i<(nRec-1) ){
- pageGetKeyCopy(pDb->pEnv, pSeg, pPg, i+1, &iTopic2, &blob2);
- assert( sortedKeyCompare(
- pDb->xCmp, iTopic1, blob1.pData, blob1.nData,
- iTopic2, blob2.pData, blob2.nData
- )<0 );
- }
- }
- }
- lsmFsDbPageNext(pSeg, pPg, 1, &pNext);
- lsmFsPageRelease(pPg);
- pPg = pNext;
- }
- sortedBlobFree(&blob1);
- sortedBlobFree(&blob2);
- }
- #endif
- #ifdef LSM_DEBUG_EXPENSIVE
- /*
- ** This function is only included in the build if LSM_DEBUG_EXPENSIVE is
- ** defined. Its only purpose is to evaluate various assert() statements to
- ** verify that the database is well formed in certain respects.
- **
- ** More specifically, it checks that the array pOne contains the required
- ** pointers to pTwo. Array pTwo must be a main array. pOne may be either a
- ** separators array or another main array. If pOne does not contain the
- ** correct set of pointers, an assert() statement fails.
- */
- static int assertPointersOk(
- lsm_db *pDb, /* Database handle */
- Segment *pOne, /* Segment containing pointers */
- Segment *pTwo, /* Segment containing pointer targets */
- int bRhs /* True if pTwo may have been Gobble()d */
- ){
- int rc = LSM_OK; /* Error code */
- SegmentPtr ptr1; /* Iterates through pOne */
- SegmentPtr ptr2; /* Iterates through pTwo */
- LsmPgno iPrev;
- assert( pOne && pTwo );
- memset(&ptr1, 0, sizeof(ptr1));
- memset(&ptr2, 0, sizeof(ptr1));
- ptr1.pSeg = pOne;
- ptr2.pSeg = pTwo;
- segmentPtrEndPage(pDb->pFS, &ptr1, 0, &rc);
- segmentPtrEndPage(pDb->pFS, &ptr2, 0, &rc);
- /* Check that the footer pointer of the first page of pOne points to
- ** the first page of pTwo. */
- iPrev = pTwo->iFirst;
- if( ptr1.iPtr!=iPrev && !bRhs ){
- assert( 0 );
- }
- if( rc==LSM_OK && ptr1.nCell>0 ){
- rc = segmentPtrLoadCell(&ptr1, 0);
- }
-
- while( rc==LSM_OK && ptr2.pPg ){
- LsmPgno iThis;
- /* Advance to the next page of segment pTwo that contains at least
- ** one cell. Break out of the loop if the iterator reaches EOF. */
- do{
- rc = segmentPtrNextPage(&ptr2, 1);
- assert( rc==LSM_OK );
- }while( rc==LSM_OK && ptr2.pPg && ptr2.nCell==0 );
- if( rc!=LSM_OK || ptr2.pPg==0 ) break;
- iThis = lsmFsPageNumber(ptr2.pPg);
- if( (ptr2.flags & (PGFTR_SKIP_THIS_FLAG|SEGMENT_BTREE_FLAG))==0 ){
- /* Load the first cell in the array pTwo page. */
- rc = segmentPtrLoadCell(&ptr2, 0);
- /* Iterate forwards through pOne, searching for a key that matches the
- ** key ptr2.pKey/nKey. This key should have a pointer to the page that
- ** ptr2 currently points to. */
- while( rc==LSM_OK ){
- int res = rtTopic(ptr1.eType) - rtTopic(ptr2.eType);
- if( res==0 ){
- res = pDb->xCmp(ptr1.pKey, ptr1.nKey, ptr2.pKey, ptr2.nKey);
- }
- if( res<0 ){
- assert( bRhs || ptr1.iPtr+ptr1.iPgPtr==iPrev );
- }else if( res>0 ){
- assert( 0 );
- }else{
- assert( ptr1.iPtr+ptr1.iPgPtr==iThis );
- iPrev = iThis;
- break;
- }
- rc = segmentPtrAdvance(0, &ptr1, 0);
- if( ptr1.pPg==0 ){
- assert( 0 );
- }
- }
- }
- }
- segmentPtrReset(&ptr1, 0);
- segmentPtrReset(&ptr2, 0);
- return LSM_OK;
- }
- /*
- ** This function is only included in the build if LSM_DEBUG_EXPENSIVE is
- ** defined. Its only purpose is to evaluate various assert() statements to
- ** verify that the database is well formed in certain respects.
- **
- ** More specifically, it checks that the b-tree embedded in array pRun
- ** contains the correct keys. If not, an assert() fails.
- */
- static int assertBtreeOk(
- lsm_db *pDb,
- Segment *pSeg
- ){
- int rc = LSM_OK; /* Return code */
- if( pSeg->iRoot ){
- LsmBlob blob = {0, 0, 0}; /* Buffer used to cache overflow keys */
- FileSystem *pFS = pDb->pFS; /* File system to read from */
- Page *pPg = 0; /* Main run page */
- BtreeCursor *pCsr = 0; /* Btree cursor */
- rc = btreeCursorNew(pDb, pSeg, &pCsr);
- if( rc==LSM_OK ){
- rc = btreeCursorFirst(pCsr);
- }
- if( rc==LSM_OK ){
- rc = lsmFsDbPageGet(pFS, pSeg, pSeg->iFirst, &pPg);
- }
- while( rc==LSM_OK ){
- Page *pNext;
- u8 *aData;
- int nData;
- int flags;
- rc = lsmFsDbPageNext(pSeg, pPg, 1, &pNext);
- lsmFsPageRelease(pPg);
- pPg = pNext;
- if( pPg==0 ) break;
- aData = fsPageData(pPg, &nData);
- flags = pageGetFlags(aData, nData);
- if( rc==LSM_OK
- && 0==((SEGMENT_BTREE_FLAG|PGFTR_SKIP_THIS_FLAG) & flags)
- && 0!=pageGetNRec(aData, nData)
- ){
- u8 *pKey;
- int nKey;
- int iTopic;
- pKey = pageGetKey(pSeg, pPg, 0, &iTopic, &nKey, &blob);
- assert( nKey==pCsr->nKey && 0==memcmp(pKey, pCsr->pKey, nKey) );
- assert( lsmFsPageNumber(pPg)==pCsr->iPtr );
- rc = btreeCursorNext(pCsr);
- }
- }
- assert( rc!=LSM_OK || pCsr->pKey==0 );
- if( pPg ) lsmFsPageRelease(pPg);
- btreeCursorFree(pCsr);
- sortedBlobFree(&blob);
- }
- return rc;
- }
- #endif /* ifdef LSM_DEBUG_EXPENSIVE */
|