database_test.test 161 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683
  1. <?php
  2. /**
  3. * Dummy class for fetching into a class.
  4. *
  5. * PDO supports using a new instance of an arbitrary class for records
  6. * rather than just a stdClass or array. This class is for testing that
  7. * functionality. (See testQueryFetchClass() below)
  8. */
  9. #[AllowDynamicProperties]
  10. class FakeRecord { }
  11. /**
  12. * Base test class for databases.
  13. *
  14. * Because all database tests share the same test data, we can centralize that
  15. * here.
  16. */
  17. class DatabaseTestCase extends DrupalWebTestCase {
  18. protected $profile = 'testing';
  19. function setUp() {
  20. parent::setUp('database_test');
  21. $schema['test'] = drupal_get_schema('test');
  22. $schema['test_classtype'] = drupal_get_schema('test_classtype');
  23. $schema['test_people'] = drupal_get_schema('test_people');
  24. $schema['test_people_copy'] = drupal_get_schema('test_people_copy');
  25. $schema['test_one_blob'] = drupal_get_schema('test_one_blob');
  26. $schema['test_two_blobs'] = drupal_get_schema('test_two_blobs');
  27. $schema['test_task'] = drupal_get_schema('test_task');
  28. $schema['TEST_UPPERCASE'] = drupal_get_schema('TEST_UPPERCASE');
  29. $this->installTables($schema);
  30. $this->addSampleData();
  31. }
  32. /**
  33. * Set up several tables needed by a certain test.
  34. *
  35. * @param $schema
  36. * An array of table definitions to install.
  37. */
  38. function installTables($schema) {
  39. // This ends up being a test for table drop and create, too, which is nice.
  40. foreach ($schema as $name => $data) {
  41. if (db_table_exists($name)) {
  42. db_drop_table($name);
  43. }
  44. db_create_table($name, $data);
  45. }
  46. foreach ($schema as $name => $data) {
  47. $this->assertTrue(db_table_exists($name), format_string('Table @name created successfully.', array('@name' => $name)));
  48. }
  49. }
  50. /**
  51. * Set up tables for NULL handling.
  52. */
  53. function ensureSampleDataNull() {
  54. $schema['test_null'] = drupal_get_schema('test_null');
  55. $this->installTables($schema);
  56. db_insert('test_null')
  57. ->fields(array('name', 'age'))
  58. ->values(array(
  59. 'name' => 'Kermit',
  60. 'age' => 25,
  61. ))
  62. ->values(array(
  63. 'name' => 'Fozzie',
  64. 'age' => NULL,
  65. ))
  66. ->values(array(
  67. 'name' => 'Gonzo',
  68. 'age' => 27,
  69. ))
  70. ->execute();
  71. }
  72. /**
  73. * Setup our sample data.
  74. *
  75. * These are added using db_query(), since we're not trying to test the
  76. * INSERT operations here, just populate.
  77. */
  78. function addSampleData() {
  79. // We need the IDs, so we can't use a multi-insert here.
  80. $john = db_insert('test')
  81. ->fields(array(
  82. 'name' => 'John',
  83. 'age' => 25,
  84. 'job' => 'Singer',
  85. ))
  86. ->execute();
  87. $george = db_insert('test')
  88. ->fields(array(
  89. 'name' => 'George',
  90. 'age' => 27,
  91. 'job' => 'Singer',
  92. ))
  93. ->execute();
  94. $ringo = db_insert('test')
  95. ->fields(array(
  96. 'name' => 'Ringo',
  97. 'age' => 28,
  98. 'job' => 'Drummer',
  99. ))
  100. ->execute();
  101. $paul = db_insert('test')
  102. ->fields(array(
  103. 'name' => 'Paul',
  104. 'age' => 26,
  105. 'job' => 'Songwriter',
  106. ))
  107. ->execute();
  108. db_insert('test_classtype')
  109. ->fields(array(
  110. 'classname' => 'FakeRecord',
  111. 'name' => 'Kay',
  112. 'age' => 26,
  113. 'job' => 'Web Developer',
  114. ))
  115. ->execute();
  116. db_insert('test_people')
  117. ->fields(array(
  118. 'name' => 'Meredith',
  119. 'age' => 30,
  120. 'job' => 'Speaker',
  121. ))
  122. ->execute();
  123. db_insert('test_task')
  124. ->fields(array('pid', 'task', 'priority'))
  125. ->values(array(
  126. 'pid' => $john,
  127. 'task' => 'eat',
  128. 'priority' => 3,
  129. ))
  130. ->values(array(
  131. 'pid' => $john,
  132. 'task' => 'sleep',
  133. 'priority' => 4,
  134. ))
  135. ->values(array(
  136. 'pid' => $john,
  137. 'task' => 'code',
  138. 'priority' => 1,
  139. ))
  140. ->values(array(
  141. 'pid' => $george,
  142. 'task' => 'sing',
  143. 'priority' => 2,
  144. ))
  145. ->values(array(
  146. 'pid' => $george,
  147. 'task' => 'sleep',
  148. 'priority' => 2,
  149. ))
  150. ->values(array(
  151. 'pid' => $paul,
  152. 'task' => 'found new band',
  153. 'priority' => 1,
  154. ))
  155. ->values(array(
  156. 'pid' => $paul,
  157. 'task' => 'perform at superbowl',
  158. 'priority' => 3,
  159. ))
  160. ->execute();
  161. db_insert('virtual')
  162. ->fields(array(
  163. 'function' => 'Function value 1',
  164. ))
  165. ->execute();
  166. }
  167. }
  168. /**
  169. * Test connection management.
  170. */
  171. class DatabaseConnectionTestCase extends DatabaseTestCase {
  172. public static function getInfo() {
  173. return array(
  174. 'name' => 'Connection tests',
  175. 'description' => 'Tests of the core database system.',
  176. 'group' => 'Database',
  177. );
  178. }
  179. /**
  180. * Test that connections return appropriate connection objects.
  181. */
  182. function testConnectionRouting() {
  183. // Clone the master credentials to a slave connection.
  184. // Note this will result in two independent connection objects that happen
  185. // to point to the same place.
  186. $connection_info = Database::getConnectionInfo('default');
  187. Database::addConnectionInfo('default', 'slave', $connection_info['default']);
  188. $db1 = Database::getConnection('default', 'default');
  189. $db2 = Database::getConnection('slave', 'default');
  190. $this->assertNotNull($db1, 'default connection is a real connection object.');
  191. $this->assertNotNull($db2, 'slave connection is a real connection object.');
  192. $this->assertNotIdentical($db1, $db2, 'Each target refers to a different connection.');
  193. // Try to open those targets another time, that should return the same objects.
  194. $db1b = Database::getConnection('default', 'default');
  195. $db2b = Database::getConnection('slave', 'default');
  196. $this->assertIdentical($db1, $db1b, 'A second call to getConnection() returns the same object.');
  197. $this->assertIdentical($db2, $db2b, 'A second call to getConnection() returns the same object.');
  198. // Try to open an unknown target.
  199. $unknown_target = $this->randomName();
  200. $db3 = Database::getConnection($unknown_target, 'default');
  201. $this->assertNotNull($db3, 'Opening an unknown target returns a real connection object.');
  202. $this->assertIdentical($db1, $db3, 'An unknown target opens the default connection.');
  203. // Try to open that unknown target another time, that should return the same object.
  204. $db3b = Database::getConnection($unknown_target, 'default');
  205. $this->assertIdentical($db3, $db3b, 'A second call to getConnection() returns the same object.');
  206. }
  207. /**
  208. * Test that connections return appropriate connection objects.
  209. */
  210. function testConnectionRoutingOverride() {
  211. // Clone the master credentials to a slave connection.
  212. // Note this will result in two independent connection objects that happen
  213. // to point to the same place.
  214. $connection_info = Database::getConnectionInfo('default');
  215. Database::addConnectionInfo('default', 'slave', $connection_info['default']);
  216. Database::ignoreTarget('default', 'slave');
  217. $db1 = Database::getConnection('default', 'default');
  218. $db2 = Database::getConnection('slave', 'default');
  219. $this->assertIdentical($db1, $db2, 'Both targets refer to the same connection.');
  220. }
  221. /**
  222. * Tests the closing of a database connection.
  223. */
  224. function testConnectionClosing() {
  225. // Open the default target so we have an object to compare.
  226. $db1 = Database::getConnection('default', 'default');
  227. // Try to close the default connection, then open a new one.
  228. Database::closeConnection('default', 'default');
  229. $db2 = Database::getConnection('default', 'default');
  230. // Opening a connection after closing it should yield an object different than the original.
  231. $this->assertNotIdentical($db1, $db2, 'Opening the default connection after it is closed returns a new object.');
  232. }
  233. /**
  234. * Tests the connection options of the active database.
  235. */
  236. function testConnectionOptions() {
  237. $connection_info = Database::getConnectionInfo('default');
  238. // Be sure we're connected to the default database.
  239. $db = Database::getConnection('default', 'default');
  240. $connectionOptions = $db->getConnectionOptions();
  241. // In the MySQL driver, the port can be different, so check individual
  242. // options.
  243. $this->assertEqual($connection_info['default']['driver'], $connectionOptions['driver'], 'The default connection info driver matches the current connection options driver.');
  244. $this->assertEqual($connection_info['default']['database'], $connectionOptions['database'], 'The default connection info database matches the current connection options database.');
  245. // Set up identical slave and confirm connection options are identical.
  246. Database::addConnectionInfo('default', 'slave', $connection_info['default']);
  247. $db2 = Database::getConnection('slave', 'default');
  248. $connectionOptions2 = $db2->getConnectionOptions();
  249. // Get a fresh copy of the default connection options.
  250. $connectionOptions = $db->getConnectionOptions();
  251. $this->assertIdentical($connectionOptions, $connectionOptions2, 'The default and slave connection options are identical.');
  252. // Set up a new connection with different connection info.
  253. $test = $connection_info['default'];
  254. $test['database'] .= 'test';
  255. Database::addConnectionInfo('test', 'default', $test);
  256. $connection_info = Database::getConnectionInfo('test');
  257. // Get a fresh copy of the default connection options.
  258. $connectionOptions = $db->getConnectionOptions();
  259. $this->assertNotEqual($connection_info['default']['database'], $connectionOptions['database'], 'The test connection info database does not match the current connection options database.');
  260. }
  261. }
  262. /**
  263. * Test cloning Select queries.
  264. */
  265. class DatabaseSelectCloneTest extends DatabaseTestCase {
  266. public static function getInfo() {
  267. return array(
  268. 'name' => 'Select tests, cloning',
  269. 'description' => 'Test cloning Select queries.',
  270. 'group' => 'Database',
  271. );
  272. }
  273. /**
  274. * Test that subqueries as value within conditions are cloned properly.
  275. */
  276. function testSelectConditionSubQueryCloning() {
  277. $subquery = db_select('test', 't');
  278. $subquery->addField('t', 'id', 'id');
  279. $subquery->condition('age', 28, '<');
  280. $query = db_select('test', 't');
  281. $query->addField('t', 'name', 'name');
  282. $query->condition('id', $subquery, 'IN');
  283. $clone = clone $query;
  284. // Cloned query should have a different unique identifier.
  285. $this->assertNotEqual($query->uniqueIdentifier(), $clone->uniqueIdentifier());
  286. // Cloned query should not be altered by the following modification
  287. // happening on original query.
  288. $subquery->condition('age', 25, '>');
  289. $clone_result = $clone->countQuery()->execute()->fetchField();
  290. $query_result = $query->countQuery()->execute()->fetchField();
  291. // Make sure the cloned query has not been modified
  292. $this->assertEqual(3, $clone_result, 'The cloned query returns the expected number of rows');
  293. $this->assertEqual(2, $query_result, 'The query returns the expected number of rows');
  294. }
  295. /**
  296. * Tests that nested SELECT queries are cloned properly.
  297. */
  298. public function testNestedQueryCloning() {
  299. $sub_query = db_select('test', 't');
  300. $sub_query->addField('t', 'id', 'id');
  301. $sub_query->condition('age', 28, '<');
  302. $query = db_select($sub_query, 't');
  303. $clone = clone $query;
  304. // Cloned query should have a different unique identifier.
  305. $this->assertNotEqual($query->uniqueIdentifier(), $clone->uniqueIdentifier());
  306. // Cloned query should not be altered by the following modification
  307. // happening on original query.
  308. $sub_query->condition('age', 25, '>');
  309. $clone_result = $clone->countQuery()->execute()->fetchField();
  310. $query_result = $query->countQuery()->execute()->fetchField();
  311. // Make sure the cloned query has not been modified.
  312. $this->assertEqual(3, $clone_result, 'The cloned query returns the expected number of rows');
  313. $this->assertEqual(2, $query_result, 'The query returns the expected number of rows');
  314. }
  315. }
  316. /**
  317. * Test fetch actions, part 1.
  318. *
  319. * We get timeout errors if we try to run too many tests at once.
  320. */
  321. class DatabaseFetchTestCase extends DatabaseTestCase {
  322. public static function getInfo() {
  323. return array(
  324. 'name' => 'Fetch tests',
  325. 'description' => 'Test the Database system\'s various fetch capabilities.',
  326. 'group' => 'Database',
  327. );
  328. }
  329. /**
  330. * Confirm that we can fetch a record properly in default object mode.
  331. */
  332. function testQueryFetchDefault() {
  333. $records = array();
  334. $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25));
  335. $this->assertTrue($result instanceof DatabaseStatementInterface, 'Result set is a Drupal statement object.');
  336. foreach ($result as $record) {
  337. $records[] = $record;
  338. $this->assertTrue(is_object($record), 'Record is an object.');
  339. $this->assertIdentical($record->name, 'John', '25 year old is John.');
  340. }
  341. $this->assertIdentical(count($records), 1, 'There is only one record.');
  342. }
  343. /**
  344. * Confirm that we can fetch a record to an object explicitly.
  345. */
  346. function testQueryFetchObject() {
  347. $records = array();
  348. $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_OBJ));
  349. foreach ($result as $record) {
  350. $records[] = $record;
  351. $this->assertTrue(is_object($record), 'Record is an object.');
  352. $this->assertIdentical($record->name, 'John', '25 year old is John.');
  353. }
  354. $this->assertIdentical(count($records), 1, 'There is only one record.');
  355. }
  356. /**
  357. * Confirm that we can fetch a record to an array associative explicitly.
  358. */
  359. function testQueryFetchArray() {
  360. $records = array();
  361. $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_ASSOC));
  362. foreach ($result as $record) {
  363. $records[] = $record;
  364. if ($this->assertTrue(is_array($record), 'Record is an array.')) {
  365. $this->assertIdentical($record['name'], 'John', 'Record can be accessed associatively.');
  366. }
  367. }
  368. $this->assertIdentical(count($records), 1, 'There is only one record.');
  369. }
  370. /**
  371. * Confirm that we can fetch a record into a new instance of a custom class.
  372. *
  373. * @see FakeRecord
  374. */
  375. function testQueryFetchClass() {
  376. $records = array();
  377. $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => 'FakeRecord'));
  378. foreach ($result as $record) {
  379. $records[] = $record;
  380. if ($this->assertTrue($record instanceof FakeRecord, 'Record is an object of class FakeRecord.')) {
  381. $this->assertIdentical($record->name, 'John', '25 year old is John.');
  382. }
  383. }
  384. $this->assertIdentical(count($records), 1, 'There is only one record.');
  385. }
  386. /**
  387. * Confirms that we can fetch a record into a new instance of a custom class.
  388. * The name of the class is determined from a value of the first column.
  389. *
  390. * @see FakeRecord
  391. */
  392. function testQueryFetchClasstype() {
  393. $records = array();
  394. $result = db_query('SELECT classname, name, job FROM {test_classtype} WHERE age = :age', array(':age' => 26), array('fetch' => PDO::FETCH_CLASS | PDO::FETCH_CLASSTYPE));
  395. foreach ($result as $record) {
  396. $records[] = $record;
  397. if ($this->assertTrue($record instanceof FakeRecord, 'Record is an object of class FakeRecord.')) {
  398. $this->assertIdentical($record->name, 'Kay', 'Kay is found.');
  399. $this->assertIdentical($record->job, 'Web Developer', 'A 26 year old Web Developer.');
  400. }
  401. $this->assertFalse(isset($record->classname), 'Classname field not found, as intended.');
  402. }
  403. $this->assertIdentical(count($records), 1, 'There is only one record.');
  404. }
  405. }
  406. /**
  407. * Test fetch actions, part 2.
  408. *
  409. * We get timeout errors if we try to run too many tests at once.
  410. */
  411. class DatabaseFetch2TestCase extends DatabaseTestCase {
  412. public static function getInfo() {
  413. return array(
  414. 'name' => 'Fetch tests, part 2',
  415. 'description' => 'Test the Database system\'s various fetch capabilities.',
  416. 'group' => 'Database',
  417. );
  418. }
  419. function setUp() {
  420. parent::setUp();
  421. }
  422. // Confirm that we can fetch a record into an indexed array explicitly.
  423. function testQueryFetchNum() {
  424. $records = array();
  425. $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_NUM));
  426. foreach ($result as $record) {
  427. $records[] = $record;
  428. if ($this->assertTrue(is_array($record), 'Record is an array.')) {
  429. $this->assertIdentical($record[0], 'John', 'Record can be accessed numerically.');
  430. }
  431. }
  432. $this->assertIdentical(count($records), 1, 'There is only one record');
  433. }
  434. /**
  435. * Confirm that we can fetch a record into a doubly-keyed array explicitly.
  436. */
  437. function testQueryFetchBoth() {
  438. $records = array();
  439. $result = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 25), array('fetch' => PDO::FETCH_BOTH));
  440. foreach ($result as $record) {
  441. $records[] = $record;
  442. if ($this->assertTrue(is_array($record), 'Record is an array.')) {
  443. $this->assertIdentical($record[0], 'John', 'Record can be accessed numerically.');
  444. $this->assertIdentical($record['name'], 'John', 'Record can be accessed associatively.');
  445. }
  446. }
  447. $this->assertIdentical(count($records), 1, 'There is only one record.');
  448. }
  449. /**
  450. * Confirm that we can fetch an entire column of a result set at once.
  451. */
  452. function testQueryFetchCol() {
  453. $records = array();
  454. $result = db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25));
  455. $column = $result->fetchCol();
  456. $this->assertIdentical(count($column), 3, 'fetchCol() returns the right number of records.');
  457. $result = db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25));
  458. $i = 0;
  459. foreach ($result as $record) {
  460. $this->assertIdentical($record->name, $column[$i++], 'Column matches direct accesss.');
  461. }
  462. }
  463. }
  464. /**
  465. * Test the insert builder.
  466. */
  467. class DatabaseInsertTestCase extends DatabaseTestCase {
  468. public static function getInfo() {
  469. return array(
  470. 'name' => 'Insert tests',
  471. 'description' => 'Test the Insert query builder.',
  472. 'group' => 'Database',
  473. );
  474. }
  475. /**
  476. * Test the very basic insert functionality.
  477. */
  478. function testSimpleInsert() {
  479. $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
  480. $query = db_insert('test');
  481. $query->fields(array(
  482. 'name' => 'Yoko',
  483. 'age' => '29',
  484. ));
  485. $query->execute();
  486. $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
  487. $this->assertIdentical($num_records_before + 1, (int) $num_records_after, 'Record inserts correctly.');
  488. $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Yoko'))->fetchField();
  489. $this->assertIdentical($saved_age, '29', 'Can retrieve after inserting.');
  490. }
  491. /**
  492. * Test that we can insert multiple records in one query object.
  493. */
  494. function testMultiInsert() {
  495. $num_records_before = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
  496. $query = db_insert('test');
  497. $query->fields(array(
  498. 'name' => 'Larry',
  499. 'age' => '30',
  500. ));
  501. // We should be able to specify values in any order if named.
  502. $query->values(array(
  503. 'age' => '31',
  504. 'name' => 'Curly',
  505. ));
  506. // We should be able to say "use the field order".
  507. // This is not the recommended mechanism for most cases, but it should work.
  508. $query->values(array('Moe', '32'));
  509. $query->execute();
  510. $num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
  511. $this->assertIdentical($num_records_before + 3, $num_records_after, 'Record inserts correctly.');
  512. $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField();
  513. $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
  514. $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField();
  515. $this->assertIdentical($saved_age, '31', 'Can retrieve after inserting.');
  516. $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField();
  517. $this->assertIdentical($saved_age, '32', 'Can retrieve after inserting.');
  518. }
  519. /**
  520. * Test that an insert object can be reused with new data after it executes.
  521. */
  522. function testRepeatedInsert() {
  523. $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
  524. $query = db_insert('test');
  525. $query->fields(array(
  526. 'name' => 'Larry',
  527. 'age' => '30',
  528. ));
  529. $query->execute(); // This should run the insert, but leave the fields intact.
  530. // We should be able to specify values in any order if named.
  531. $query->values(array(
  532. 'age' => '31',
  533. 'name' => 'Curly',
  534. ));
  535. $query->execute();
  536. // We should be able to say "use the field order".
  537. $query->values(array('Moe', '32'));
  538. $query->execute();
  539. $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
  540. $this->assertIdentical((int) $num_records_before + 3, (int) $num_records_after, 'Record inserts correctly.');
  541. $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField();
  542. $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
  543. $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField();
  544. $this->assertIdentical($saved_age, '31', 'Can retrieve after inserting.');
  545. $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField();
  546. $this->assertIdentical($saved_age, '32', 'Can retrieve after inserting.');
  547. }
  548. /**
  549. * Test that we can specify fields without values and specify values later.
  550. */
  551. function testInsertFieldOnlyDefinintion() {
  552. // This is useful for importers, when we want to create a query and define
  553. // its fields once, then loop over a multi-insert execution.
  554. db_insert('test')
  555. ->fields(array('name', 'age'))
  556. ->values(array('Larry', '30'))
  557. ->values(array('Curly', '31'))
  558. ->values(array('Moe', '32'))
  559. ->execute();
  560. $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Larry'))->fetchField();
  561. $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
  562. $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Curly'))->fetchField();
  563. $this->assertIdentical($saved_age, '31', 'Can retrieve after inserting.');
  564. $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Moe'))->fetchField();
  565. $this->assertIdentical($saved_age, '32', 'Can retrieve after inserting.');
  566. }
  567. /**
  568. * Test that inserts return the proper auto-increment ID.
  569. */
  570. function testInsertLastInsertID() {
  571. $id = db_insert('test')
  572. ->fields(array(
  573. 'name' => 'Larry',
  574. 'age' => '30',
  575. ))
  576. ->execute();
  577. $this->assertIdentical($id, '5', 'Auto-increment ID returned successfully.');
  578. }
  579. /**
  580. * Test that the INSERT INTO ... SELECT (fields) ... syntax works.
  581. */
  582. function testInsertSelectFields() {
  583. $query = db_select('test_people', 'tp');
  584. // The query builder will always append expressions after fields.
  585. // Add the expression first to test that the insert fields are correctly
  586. // re-ordered.
  587. $query->addExpression('tp.age', 'age');
  588. $query
  589. ->fields('tp', array('name','job'))
  590. ->condition('tp.name', 'Meredith');
  591. // The resulting query should be equivalent to:
  592. // INSERT INTO test (age, name, job)
  593. // SELECT tp.age AS age, tp.name AS name, tp.job AS job
  594. // FROM test_people tp
  595. // WHERE tp.name = 'Meredith'
  596. db_insert('test')
  597. ->from($query)
  598. ->execute();
  599. $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Meredith'))->fetchField();
  600. $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
  601. }
  602. /**
  603. * Tests that the INSERT INTO ... SELECT * ... syntax works.
  604. */
  605. function testInsertSelectAll() {
  606. $query = db_select('test_people', 'tp')
  607. ->fields('tp')
  608. ->condition('tp.name', 'Meredith');
  609. // The resulting query should be equivalent to:
  610. // INSERT INTO test_people_copy
  611. // SELECT *
  612. // FROM test_people tp
  613. // WHERE tp.name = 'Meredith'
  614. db_insert('test_people_copy')
  615. ->from($query)
  616. ->execute();
  617. $saved_age = db_query('SELECT age FROM {test_people_copy} WHERE name = :name', array(':name' => 'Meredith'))->fetchField();
  618. $this->assertIdentical($saved_age, '30', 'Can retrieve after inserting.');
  619. }
  620. }
  621. /**
  622. * Insert tests using LOB fields, which are weird on some databases.
  623. */
  624. class DatabaseInsertLOBTestCase extends DatabaseTestCase {
  625. public static function getInfo() {
  626. return array(
  627. 'name' => 'Insert tests, LOB fields',
  628. 'description' => 'Test the Insert query builder with LOB fields.',
  629. 'group' => 'Database',
  630. );
  631. }
  632. /**
  633. * Test that we can insert a single blob field successfully.
  634. */
  635. function testInsertOneBlob() {
  636. $data = "This is\000a test.";
  637. $this->assertTrue(strlen($data) === 15, 'Test data contains a NULL.');
  638. $id = db_insert('test_one_blob')
  639. ->fields(array('blob1' => $data))
  640. ->execute();
  641. $r = db_query('SELECT * FROM {test_one_blob} WHERE id = :id', array(':id' => $id))->fetchAssoc();
  642. $this->assertTrue($r['blob1'] === $data, format_string('Can insert a blob: id @id, @data.', array('@id' => $id, '@data' => serialize($r))));
  643. }
  644. /**
  645. * Test that we can insert multiple blob fields in the same query.
  646. */
  647. function testInsertMultipleBlob() {
  648. $id = db_insert('test_two_blobs')
  649. ->fields(array(
  650. 'blob1' => 'This is',
  651. 'blob2' => 'a test',
  652. ))
  653. ->execute();
  654. $r = db_query('SELECT * FROM {test_two_blobs} WHERE id = :id', array(':id' => $id))->fetchAssoc();
  655. $this->assertTrue($r['blob1'] === 'This is' && $r['blob2'] === 'a test', 'Can insert multiple blobs per row.');
  656. }
  657. }
  658. /**
  659. * Insert tests for "database default" values.
  660. */
  661. class DatabaseInsertDefaultsTestCase extends DatabaseTestCase {
  662. public static function getInfo() {
  663. return array(
  664. 'name' => 'Insert tests, default fields',
  665. 'description' => 'Test the Insert query builder with default values.',
  666. 'group' => 'Database',
  667. );
  668. }
  669. /**
  670. * Test that we can run a query that is "default values for everything".
  671. */
  672. function testDefaultInsert() {
  673. $query = db_insert('test')->useDefaults(array('job'));
  674. $id = $query->execute();
  675. $schema = drupal_get_schema('test');
  676. $job = db_query('SELECT job FROM {test} WHERE id = :id', array(':id' => $id))->fetchField();
  677. $this->assertEqual($job, $schema['fields']['job']['default'], 'Default field value is set.');
  678. }
  679. /**
  680. * Test that no action will be preformed if no fields are specified.
  681. */
  682. function testDefaultEmptyInsert() {
  683. $num_records_before = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
  684. try {
  685. $result = db_insert('test')->execute();
  686. // This is only executed if no exception has been thrown.
  687. $this->fail('Expected exception NoFieldsException has not been thrown.');
  688. } catch (NoFieldsException $e) {
  689. $this->pass('Expected exception NoFieldsException has been thrown.');
  690. }
  691. $num_records_after = (int) db_query('SELECT COUNT(*) FROM {test}')->fetchField();
  692. $this->assertIdentical($num_records_before, $num_records_after, 'Do nothing as no fields are specified.');
  693. }
  694. /**
  695. * Test that we can insert fields with values and defaults in the same query.
  696. */
  697. function testDefaultInsertWithFields() {
  698. $query = db_insert('test')
  699. ->fields(array('name' => 'Bob'))
  700. ->useDefaults(array('job'));
  701. $id = $query->execute();
  702. $schema = drupal_get_schema('test');
  703. $job = db_query('SELECT job FROM {test} WHERE id = :id', array(':id' => $id))->fetchField();
  704. $this->assertEqual($job, $schema['fields']['job']['default'], 'Default field value is set.');
  705. }
  706. }
  707. /**
  708. * Update builder tests.
  709. */
  710. class DatabaseUpdateTestCase extends DatabaseTestCase {
  711. public static function getInfo() {
  712. return array(
  713. 'name' => 'Update tests',
  714. 'description' => 'Test the Update query builder.',
  715. 'group' => 'Database',
  716. );
  717. }
  718. /**
  719. * Confirm that we can update a single record successfully.
  720. */
  721. function testSimpleUpdate() {
  722. $num_updated = db_update('test')
  723. ->fields(array('name' => 'Tiffany'))
  724. ->condition('id', 1)
  725. ->execute();
  726. $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
  727. $saved_name = db_query('SELECT name FROM {test} WHERE id = :id', array(':id' => 1))->fetchField();
  728. $this->assertIdentical($saved_name, 'Tiffany', 'Updated name successfully.');
  729. }
  730. /**
  731. * Confirm updating to NULL.
  732. */
  733. function testSimpleNullUpdate() {
  734. $this->ensureSampleDataNull();
  735. $num_updated = db_update('test_null')
  736. ->fields(array('age' => NULL))
  737. ->condition('name', 'Kermit')
  738. ->execute();
  739. $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
  740. $saved_age = db_query('SELECT age FROM {test_null} WHERE name = :name', array(':name' => 'Kermit'))->fetchField();
  741. $this->assertNull($saved_age, 'Updated name successfully.');
  742. }
  743. /**
  744. * Confirm that we can update a multiple records successfully.
  745. */
  746. function testMultiUpdate() {
  747. $num_updated = db_update('test')
  748. ->fields(array('job' => 'Musician'))
  749. ->condition('job', 'Singer')
  750. ->execute();
  751. $this->assertIdentical($num_updated, 2, 'Updated 2 records.');
  752. $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
  753. $this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
  754. }
  755. /**
  756. * Confirm that we can update a multiple records with a non-equality condition.
  757. */
  758. function testMultiGTUpdate() {
  759. $num_updated = db_update('test')
  760. ->fields(array('job' => 'Musician'))
  761. ->condition('age', 26, '>')
  762. ->execute();
  763. $this->assertIdentical($num_updated, 2, 'Updated 2 records.');
  764. $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
  765. $this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
  766. }
  767. /**
  768. * Confirm that we can update a multiple records with a where call.
  769. */
  770. function testWhereUpdate() {
  771. $num_updated = db_update('test')
  772. ->fields(array('job' => 'Musician'))
  773. ->where('age > :age', array(':age' => 26))
  774. ->execute();
  775. $this->assertIdentical($num_updated, 2, 'Updated 2 records.');
  776. $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
  777. $this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
  778. }
  779. /**
  780. * Confirm that we can stack condition and where calls.
  781. */
  782. function testWhereAndConditionUpdate() {
  783. $update = db_update('test')
  784. ->fields(array('job' => 'Musician'))
  785. ->where('age > :age', array(':age' => 26))
  786. ->condition('name', 'Ringo');
  787. $num_updated = $update->execute();
  788. $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
  789. $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
  790. $this->assertIdentical($num_matches, '1', 'Updated fields successfully.');
  791. }
  792. /**
  793. * Test updating with expressions.
  794. */
  795. function testExpressionUpdate() {
  796. // Set age = 1 for a single row for this test to work.
  797. db_update('test')
  798. ->condition('id', 1)
  799. ->fields(array('age' => 1))
  800. ->execute();
  801. // Ensure that expressions are handled properly. This should set every
  802. // record's age to a square of itself. The number of affected rows returned
  803. // by db_update() will vary across different database engines and versions:
  804. // - MySQL / InnoDB: changed rows only
  805. // - MySQL / MyISAM: changed rows only
  806. // - PostgreSQL: all rows matched by the query
  807. // - SQLite: changed rows only (see workaround in UpdateQuery_sqlite)
  808. // All database engines will change only three of the four records in the
  809. // table since 1*1 = 1. However, the pgsql driver will return the number of
  810. // rows matched rather than the number changed.
  811. // @see https://www.drupal.org/project/drupal/issues/805858
  812. // @see https://www.drupal.org/project/drupal/issues/3264471
  813. $connection = Database::getConnection()->getConnectionOptions();
  814. $expected_rows = 3;
  815. if ($connection['driver'] === 'pgsql') {
  816. $expected_rows ++;
  817. }
  818. $num_rows = db_update('test')
  819. ->expression('age', 'age * age')
  820. ->execute();
  821. $this->assertIdentical($num_rows, $expected_rows, 'Number of affected rows are returned.');
  822. $saved_name = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => pow(26, 2)))->fetchField();
  823. $this->assertIdentical('Paul', $saved_name, 'Successfully updated values using an algebraic expression.');
  824. }
  825. }
  826. /**
  827. * Tests for more complex update statements.
  828. */
  829. class DatabaseUpdateComplexTestCase extends DatabaseTestCase {
  830. public static function getInfo() {
  831. return array(
  832. 'name' => 'Update tests, Complex',
  833. 'description' => 'Test the Update query builder, complex queries.',
  834. 'group' => 'Database',
  835. );
  836. }
  837. /**
  838. * Test updates with OR conditionals.
  839. */
  840. function testOrConditionUpdate() {
  841. $update = db_update('test')
  842. ->fields(array('job' => 'Musician'))
  843. ->condition(db_or()
  844. ->condition('name', 'John')
  845. ->condition('name', 'Paul')
  846. );
  847. $num_updated = $update->execute();
  848. $this->assertIdentical($num_updated, 2, 'Updated 2 records.');
  849. $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
  850. $this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
  851. }
  852. /**
  853. * Test WHERE IN clauses.
  854. */
  855. function testInConditionUpdate() {
  856. $num_updated = db_update('test')
  857. ->fields(array('job' => 'Musician'))
  858. ->condition('name', array('John', 'Paul'), 'IN')
  859. ->execute();
  860. $this->assertIdentical($num_updated, 2, 'Updated 2 records.');
  861. $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
  862. $this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
  863. }
  864. /**
  865. * Test WHERE NOT IN clauses.
  866. */
  867. function testNotInConditionUpdate() {
  868. // The o is lowercase in the 'NoT IN' operator, to make sure the operators
  869. // work in mixed case.
  870. $num_updated = db_update('test')
  871. ->fields(array('job' => 'Musician'))
  872. ->condition('name', array('John', 'Paul', 'George'), 'NoT IN')
  873. ->execute();
  874. $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
  875. $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
  876. $this->assertIdentical($num_matches, '1', 'Updated fields successfully.');
  877. }
  878. /**
  879. * Test BETWEEN conditional clauses.
  880. */
  881. function testBetweenConditionUpdate() {
  882. $num_updated = db_update('test')
  883. ->fields(array('job' => 'Musician'))
  884. ->condition('age', array(25, 26), 'BETWEEN')
  885. ->execute();
  886. $this->assertIdentical($num_updated, 2, 'Updated 2 records.');
  887. $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
  888. $this->assertIdentical($num_matches, '2', 'Updated fields successfully.');
  889. }
  890. /**
  891. * Test LIKE conditionals.
  892. */
  893. function testLikeConditionUpdate() {
  894. $num_updated = db_update('test')
  895. ->fields(array('job' => 'Musician'))
  896. ->condition('name', '%ge%', 'LIKE')
  897. ->execute();
  898. $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
  899. $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
  900. $this->assertIdentical($num_matches, '1', 'Updated fields successfully.');
  901. }
  902. /**
  903. * Test update with expression values.
  904. */
  905. function testUpdateExpression() {
  906. $before_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
  907. $GLOBALS['larry_test'] = 1;
  908. $num_updated = db_update('test')
  909. ->condition('name', 'Ringo')
  910. ->fields(array('job' => 'Musician'))
  911. ->expression('age', 'age + :age', array(':age' => 4))
  912. ->execute();
  913. $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
  914. $num_matches = db_query('SELECT COUNT(*) FROM {test} WHERE job = :job', array(':job' => 'Musician'))->fetchField();
  915. $this->assertIdentical($num_matches, '1', 'Updated fields successfully.');
  916. $person = db_query('SELECT * FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetch();
  917. $this->assertEqual($person->name, 'Ringo', 'Name set correctly.');
  918. $this->assertEqual($person->age, $before_age + 4, 'Age set correctly.');
  919. $this->assertEqual($person->job, 'Musician', 'Job set correctly.');
  920. $GLOBALS['larry_test'] = 0;
  921. }
  922. /**
  923. * Test update with only expression values.
  924. */
  925. function testUpdateOnlyExpression() {
  926. $before_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
  927. $num_updated = db_update('test')
  928. ->condition('name', 'Ringo')
  929. ->expression('age', 'age + :age', array(':age' => 4))
  930. ->execute();
  931. $this->assertIdentical($num_updated, 1, 'Updated 1 record.');
  932. $after_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchField();
  933. $this->assertEqual($before_age + 4, $after_age, 'Age updated correctly');
  934. }
  935. }
  936. /**
  937. * Test update queries involving LOB values.
  938. */
  939. class DatabaseUpdateLOBTestCase extends DatabaseTestCase {
  940. public static function getInfo() {
  941. return array(
  942. 'name' => 'Update tests, LOB',
  943. 'description' => 'Test the Update query builder with LOB fields.',
  944. 'group' => 'Database',
  945. );
  946. }
  947. /**
  948. * Confirm that we can update a blob column.
  949. */
  950. function testUpdateOneBlob() {
  951. $data = "This is\000a test.";
  952. $this->assertTrue(strlen($data) === 15, 'Test data contains a NULL.');
  953. $id = db_insert('test_one_blob')
  954. ->fields(array('blob1' => $data))
  955. ->execute();
  956. $data .= $data;
  957. db_update('test_one_blob')
  958. ->condition('id', $id)
  959. ->fields(array('blob1' => $data))
  960. ->execute();
  961. $r = db_query('SELECT * FROM {test_one_blob} WHERE id = :id', array(':id' => $id))->fetchAssoc();
  962. $this->assertTrue($r['blob1'] === $data, format_string('Can update a blob: id @id, @data.', array('@id' => $id, '@data' => serialize($r))));
  963. }
  964. /**
  965. * Confirm that we can update two blob columns in the same table.
  966. */
  967. function testUpdateMultipleBlob() {
  968. $id = db_insert('test_two_blobs')
  969. ->fields(array(
  970. 'blob1' => 'This is',
  971. 'blob2' => 'a test',
  972. ))
  973. ->execute();
  974. db_update('test_two_blobs')
  975. ->condition('id', $id)
  976. ->fields(array('blob1' => 'and so', 'blob2' => 'is this'))
  977. ->execute();
  978. $r = db_query('SELECT * FROM {test_two_blobs} WHERE id = :id', array(':id' => $id))->fetchAssoc();
  979. $this->assertTrue($r['blob1'] === 'and so' && $r['blob2'] === 'is this', 'Can update multiple blobs per row.');
  980. }
  981. }
  982. /**
  983. * Delete/Truncate tests.
  984. *
  985. * The DELETE tests are not as extensive, as all of the interesting code for
  986. * DELETE queries is in the conditional which is identical to the UPDATE and
  987. * SELECT conditional handling.
  988. *
  989. * The TRUNCATE tests are not extensive either, because the behavior of
  990. * TRUNCATE queries is not consistent across database engines. We only test
  991. * that a TRUNCATE query actually deletes all rows from the target table.
  992. */
  993. class DatabaseDeleteTruncateTestCase extends DatabaseTestCase {
  994. public static function getInfo() {
  995. return array(
  996. 'name' => 'Delete/Truncate tests',
  997. 'description' => 'Test the Delete and Truncate query builders.',
  998. 'group' => 'Database',
  999. );
  1000. }
  1001. /**
  1002. * Confirm that we can use a subselect in a delete successfully.
  1003. */
  1004. function testSubselectDelete() {
  1005. $num_records_before = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField();
  1006. $pid_to_delete = db_query("SELECT * FROM {test_task} WHERE task = 'sleep'")->fetchField();
  1007. $subquery = db_select('test', 't')
  1008. ->fields('t', array('id'))
  1009. ->condition('t.id', array($pid_to_delete), 'IN');
  1010. $delete = db_delete('test_task')
  1011. ->condition('task', 'sleep')
  1012. ->condition('pid', $subquery, 'IN');
  1013. $num_deleted = $delete->execute();
  1014. $this->assertEqual($num_deleted, 1, "Deleted 1 record.");
  1015. $num_records_after = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField();
  1016. $this->assertEqual($num_records_before, $num_records_after + $num_deleted, 'Deletion adds up.');
  1017. }
  1018. /**
  1019. * Confirm that we can delete a single record successfully.
  1020. */
  1021. function testSimpleDelete() {
  1022. $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
  1023. $num_deleted = db_delete('test')
  1024. ->condition('id', 1)
  1025. ->execute();
  1026. $this->assertIdentical($num_deleted, 1, 'Deleted 1 record.');
  1027. $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
  1028. $this->assertEqual($num_records_before, $num_records_after + $num_deleted, 'Deletion adds up.');
  1029. }
  1030. /**
  1031. * Confirm that we can truncate a whole table successfully.
  1032. */
  1033. function testTruncate() {
  1034. $num_records_before = db_query("SELECT COUNT(*) FROM {test}")->fetchField();
  1035. db_truncate('test')->execute();
  1036. $num_records_after = db_query("SELECT COUNT(*) FROM {test}")->fetchField();
  1037. $this->assertEqual(0, $num_records_after, 'Truncate really deletes everything.');
  1038. }
  1039. }
  1040. /**
  1041. * Test the MERGE query builder.
  1042. */
  1043. class DatabaseMergeTestCase extends DatabaseTestCase {
  1044. public static function getInfo() {
  1045. return array(
  1046. 'name' => 'Merge tests',
  1047. 'description' => 'Test the Merge query builder.',
  1048. 'group' => 'Database',
  1049. );
  1050. }
  1051. /**
  1052. * Confirm that we can merge-insert a record successfully.
  1053. */
  1054. function testMergeInsert() {
  1055. $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
  1056. $result = db_merge('test_people')
  1057. ->key(array('job' => 'Presenter'))
  1058. ->fields(array(
  1059. 'age' => 31,
  1060. 'name' => 'Tiffany',
  1061. ))
  1062. ->execute();
  1063. $this->assertEqual($result, MergeQuery::STATUS_INSERT, 'Insert status returned.');
  1064. $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
  1065. $this->assertEqual($num_records_before + 1, $num_records_after, 'Merge inserted properly.');
  1066. $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch();
  1067. $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.');
  1068. $this->assertEqual($person->age, 31, 'Age set correctly.');
  1069. $this->assertEqual($person->job, 'Presenter', 'Job set correctly.');
  1070. }
  1071. /**
  1072. * Confirm that we can merge-update a record successfully.
  1073. */
  1074. function testMergeUpdate() {
  1075. $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
  1076. $result = db_merge('test_people')
  1077. ->key(array('job' => 'Speaker'))
  1078. ->fields(array(
  1079. 'age' => 31,
  1080. 'name' => 'Tiffany',
  1081. ))
  1082. ->execute();
  1083. $this->assertEqual($result, MergeQuery::STATUS_UPDATE, 'Update status returned.');
  1084. $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
  1085. $this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.');
  1086. $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
  1087. $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.');
  1088. $this->assertEqual($person->age, 31, 'Age set correctly.');
  1089. $this->assertEqual($person->job, 'Speaker', 'Job set correctly.');
  1090. }
  1091. /**
  1092. * Confirm that we can merge-update a record successfully, with different insert and update.
  1093. */
  1094. function testMergeUpdateExcept() {
  1095. $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
  1096. db_merge('test_people')
  1097. ->key(array('job' => 'Speaker'))
  1098. ->insertFields(array('age' => 31))
  1099. ->updateFields(array('name' => 'Tiffany'))
  1100. ->execute();
  1101. $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
  1102. $this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.');
  1103. $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
  1104. $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.');
  1105. $this->assertEqual($person->age, 30, 'Age skipped correctly.');
  1106. $this->assertEqual($person->job, 'Speaker', 'Job set correctly.');
  1107. }
  1108. /**
  1109. * Confirm that we can merge-update a record successfully, with alternate replacement.
  1110. */
  1111. function testMergeUpdateExplicit() {
  1112. $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
  1113. db_merge('test_people')
  1114. ->key(array('job' => 'Speaker'))
  1115. ->insertFields(array(
  1116. 'age' => 31,
  1117. 'name' => 'Tiffany',
  1118. ))
  1119. ->updateFields(array(
  1120. 'name' => 'Joe',
  1121. ))
  1122. ->execute();
  1123. $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
  1124. $this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.');
  1125. $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
  1126. $this->assertEqual($person->name, 'Joe', 'Name set correctly.');
  1127. $this->assertEqual($person->age, 30, 'Age skipped correctly.');
  1128. $this->assertEqual($person->job, 'Speaker', 'Job set correctly.');
  1129. }
  1130. /**
  1131. * Confirm that we can merge-update a record successfully, with expressions.
  1132. */
  1133. function testMergeUpdateExpression() {
  1134. $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
  1135. $age_before = db_query('SELECT age FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetchField();
  1136. // This is a very contrived example, as I have no idea why you'd want to
  1137. // change age this way, but that's beside the point.
  1138. // Note that we are also double-setting age here, once as a literal and
  1139. // once as an expression. This test will only pass if the expression wins,
  1140. // which is what is supposed to happen.
  1141. db_merge('test_people')
  1142. ->key(array('job' => 'Speaker'))
  1143. ->fields(array('name' => 'Tiffany'))
  1144. ->insertFields(array('age' => 31))
  1145. ->expression('age', 'age + :age', array(':age' => 4))
  1146. ->execute();
  1147. $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
  1148. $this->assertEqual($num_records_before, $num_records_after, 'Merge updated properly.');
  1149. $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
  1150. $this->assertEqual($person->name, 'Tiffany', 'Name set correctly.');
  1151. $this->assertEqual($person->age, $age_before + 4, 'Age updated correctly.');
  1152. $this->assertEqual($person->job, 'Speaker', 'Job set correctly.');
  1153. }
  1154. /**
  1155. * Test that we can merge-insert without any update fields.
  1156. */
  1157. function testMergeInsertWithoutUpdate() {
  1158. $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
  1159. db_merge('test_people')
  1160. ->key(array('job' => 'Presenter'))
  1161. ->execute();
  1162. $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
  1163. $this->assertEqual($num_records_before + 1, $num_records_after, 'Merge inserted properly.');
  1164. $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Presenter'))->fetch();
  1165. $this->assertEqual($person->name, '', 'Name set correctly.');
  1166. $this->assertEqual($person->age, 0, 'Age set correctly.');
  1167. $this->assertEqual($person->job, 'Presenter', 'Job set correctly.');
  1168. }
  1169. /**
  1170. * Confirm that we can merge-update without any update fields.
  1171. */
  1172. function testMergeUpdateWithoutUpdate() {
  1173. $num_records_before = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
  1174. db_merge('test_people')
  1175. ->key(array('job' => 'Speaker'))
  1176. ->execute();
  1177. $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
  1178. $this->assertEqual($num_records_before, $num_records_after, 'Merge skipped properly.');
  1179. $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
  1180. $this->assertEqual($person->name, 'Meredith', 'Name skipped correctly.');
  1181. $this->assertEqual($person->age, 30, 'Age skipped correctly.');
  1182. $this->assertEqual($person->job, 'Speaker', 'Job skipped correctly.');
  1183. db_merge('test_people')
  1184. ->key(array('job' => 'Speaker'))
  1185. ->insertFields(array('age' => 31))
  1186. ->execute();
  1187. $num_records_after = db_query('SELECT COUNT(*) FROM {test_people}')->fetchField();
  1188. $this->assertEqual($num_records_before, $num_records_after, 'Merge skipped properly.');
  1189. $person = db_query('SELECT * FROM {test_people} WHERE job = :job', array(':job' => 'Speaker'))->fetch();
  1190. $this->assertEqual($person->name, 'Meredith', 'Name skipped correctly.');
  1191. $this->assertEqual($person->age, 30, 'Age skipped correctly.');
  1192. $this->assertEqual($person->job, 'Speaker', 'Job skipped correctly.');
  1193. }
  1194. /**
  1195. * Test that an invalid merge query throws an exception like it is supposed to.
  1196. */
  1197. function testInvalidMerge() {
  1198. try {
  1199. // This query should die because there is no key field specified.
  1200. db_merge('test_people')
  1201. ->fields(array(
  1202. 'age' => 31,
  1203. 'name' => 'Tiffany',
  1204. ))
  1205. ->execute();
  1206. }
  1207. catch (InvalidMergeQueryException $e) {
  1208. $this->pass('InvalidMergeQueryException thrown for invalid query.');
  1209. return;
  1210. }
  1211. $this->fail('No InvalidMergeQueryException thrown');
  1212. }
  1213. }
  1214. /**
  1215. * Test the SELECT builder.
  1216. */
  1217. class DatabaseSelectTestCase extends DatabaseTestCase {
  1218. public static function getInfo() {
  1219. return array(
  1220. 'name' => 'Select tests',
  1221. 'description' => 'Test the Select query builder.',
  1222. 'group' => 'Database',
  1223. );
  1224. }
  1225. /**
  1226. * Test rudimentary SELECT statements.
  1227. */
  1228. function testSimpleSelect() {
  1229. $query = db_select('test');
  1230. $name_field = $query->addField('test', 'name');
  1231. $age_field = $query->addField('test', 'age', 'age');
  1232. $result = $query->execute();
  1233. $num_records = 0;
  1234. foreach ($result as $record) {
  1235. $num_records++;
  1236. }
  1237. $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
  1238. }
  1239. /**
  1240. * Test rudimentary SELECT statement with a COMMENT.
  1241. */
  1242. function testSimpleComment() {
  1243. $query = db_select('test')->comment('Testing query comments');
  1244. $name_field = $query->addField('test', 'name');
  1245. $age_field = $query->addField('test', 'age', 'age');
  1246. $result = $query->execute();
  1247. $num_records = 0;
  1248. foreach ($result as $record) {
  1249. $num_records++;
  1250. }
  1251. $query = (string)$query;
  1252. $expected = "/* Testing query comments */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test";
  1253. $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
  1254. $this->assertEqual($query, $expected, 'The flattened query contains the comment string.');
  1255. }
  1256. /**
  1257. * Test query COMMENT system against vulnerabilities.
  1258. */
  1259. function testVulnerableComment() {
  1260. $query = db_select('test')->comment('Testing query comments */ SELECT nid FROM {node}; --');
  1261. $name_field = $query->addField('test', 'name');
  1262. $age_field = $query->addField('test', 'age', 'age');
  1263. $result = $query->execute();
  1264. $num_records = 0;
  1265. foreach ($result as $record) {
  1266. $num_records++;
  1267. }
  1268. $query = (string)$query;
  1269. $expected = "/* Testing query comments * / SELECT nid FROM {node}; -- */ SELECT test.name AS name, test.age AS age\nFROM \n{test} test";
  1270. $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
  1271. $this->assertEqual($query, $expected, 'The flattened query contains the sanitised comment string.');
  1272. $connection = Database::getConnection();
  1273. foreach ($this->makeCommentsProvider() as $test_set) {
  1274. list($expected, $comments) = $test_set;
  1275. $this->assertEqual($expected, $connection->makeComment($comments));
  1276. }
  1277. }
  1278. /**
  1279. * Provides expected and input values for testVulnerableComment().
  1280. */
  1281. function makeCommentsProvider() {
  1282. return array(
  1283. array(
  1284. '/* */ ',
  1285. array(''),
  1286. ),
  1287. // Try and close the comment early.
  1288. array(
  1289. '/* Exploit * / DROP TABLE node; -- */ ',
  1290. array('Exploit */ DROP TABLE node; --'),
  1291. ),
  1292. // Variations on comment closing.
  1293. array(
  1294. '/* Exploit * / * / DROP TABLE node; -- */ ',
  1295. array('Exploit */*/ DROP TABLE node; --'),
  1296. ),
  1297. array(
  1298. '/* Exploit * * // DROP TABLE node; -- */ ',
  1299. array('Exploit **// DROP TABLE node; --'),
  1300. ),
  1301. // Try closing the comment in the second string which is appended.
  1302. array(
  1303. '/* Exploit * / DROP TABLE node; --; Another try * / DROP TABLE node; -- */ ',
  1304. array('Exploit */ DROP TABLE node; --', 'Another try */ DROP TABLE node; --'),
  1305. ),
  1306. );
  1307. }
  1308. /**
  1309. * Test basic conditionals on SELECT statements.
  1310. */
  1311. function testSimpleSelectConditional() {
  1312. $query = db_select('test');
  1313. $name_field = $query->addField('test', 'name');
  1314. $age_field = $query->addField('test', 'age', 'age');
  1315. $query->condition('age', 27);
  1316. $result = $query->execute();
  1317. // Check that the aliases are being created the way we want.
  1318. $this->assertEqual($name_field, 'name', 'Name field alias is correct.');
  1319. $this->assertEqual($age_field, 'age', 'Age field alias is correct.');
  1320. // Ensure that we got the right record.
  1321. $record = $result->fetch();
  1322. $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
  1323. $this->assertEqual($record->$age_field, 27, 'Fetched age is correct.');
  1324. }
  1325. /**
  1326. * Test SELECT statements with expressions.
  1327. */
  1328. function testSimpleSelectExpression() {
  1329. $query = db_select('test');
  1330. $name_field = $query->addField('test', 'name');
  1331. $age_field = $query->addExpression("age*2", 'double_age');
  1332. $query->condition('age', 27);
  1333. $result = $query->execute();
  1334. // Check that the aliases are being created the way we want.
  1335. $this->assertEqual($name_field, 'name', 'Name field alias is correct.');
  1336. $this->assertEqual($age_field, 'double_age', 'Age field alias is correct.');
  1337. // Ensure that we got the right record.
  1338. $record = $result->fetch();
  1339. $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
  1340. $this->assertEqual($record->$age_field, 27*2, 'Fetched age expression is correct.');
  1341. }
  1342. /**
  1343. * Test SELECT statements with multiple expressions.
  1344. */
  1345. function testSimpleSelectExpressionMultiple() {
  1346. $query = db_select('test');
  1347. $name_field = $query->addField('test', 'name');
  1348. $age_double_field = $query->addExpression("age*2");
  1349. $age_triple_field = $query->addExpression("age*3");
  1350. $query->condition('age', 27);
  1351. $result = $query->execute();
  1352. // Check that the aliases are being created the way we want.
  1353. $this->assertEqual($age_double_field, 'expression', 'Double age field alias is correct.');
  1354. $this->assertEqual($age_triple_field, 'expression_2', 'Triple age field alias is correct.');
  1355. // Ensure that we got the right record.
  1356. $record = $result->fetch();
  1357. $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
  1358. $this->assertEqual($record->$age_double_field, 27*2, 'Fetched double age expression is correct.');
  1359. $this->assertEqual($record->$age_triple_field, 27*3, 'Fetched triple age expression is correct.');
  1360. }
  1361. /**
  1362. * Test adding multiple fields to a select statement at the same time.
  1363. */
  1364. function testSimpleSelectMultipleFields() {
  1365. $record = db_select('test')
  1366. ->fields('test', array('id', 'name', 'age', 'job'))
  1367. ->condition('age', 27)
  1368. ->execute()->fetchObject();
  1369. // Check that all fields we asked for are present.
  1370. $this->assertNotNull($record->id, 'ID field is present.');
  1371. $this->assertNotNull($record->name, 'Name field is present.');
  1372. $this->assertNotNull($record->age, 'Age field is present.');
  1373. $this->assertNotNull($record->job, 'Job field is present.');
  1374. // Ensure that we got the right record.
  1375. // Check that all fields we asked for are present.
  1376. $this->assertEqual($record->id, 2, 'ID field has the correct value.');
  1377. $this->assertEqual($record->name, 'George', 'Name field has the correct value.');
  1378. $this->assertEqual($record->age, 27, 'Age field has the correct value.');
  1379. $this->assertEqual($record->job, 'Singer', 'Job field has the correct value.');
  1380. }
  1381. /**
  1382. * Test adding all fields from a given table to a select statement.
  1383. */
  1384. function testSimpleSelectAllFields() {
  1385. $record = db_select('test')
  1386. ->fields('test')
  1387. ->condition('age', 27)
  1388. ->execute()->fetchObject();
  1389. // Check that all fields we asked for are present.
  1390. $this->assertNotNull($record->id, 'ID field is present.');
  1391. $this->assertNotNull($record->name, 'Name field is present.');
  1392. $this->assertNotNull($record->age, 'Age field is present.');
  1393. $this->assertNotNull($record->job, 'Job field is present.');
  1394. // Ensure that we got the right record.
  1395. // Check that all fields we asked for are present.
  1396. $this->assertEqual($record->id, 2, 'ID field has the correct value.');
  1397. $this->assertEqual($record->name, 'George', 'Name field has the correct value.');
  1398. $this->assertEqual($record->age, 27, 'Age field has the correct value.');
  1399. $this->assertEqual($record->job, 'Singer', 'Job field has the correct value.');
  1400. }
  1401. /**
  1402. * Test that we can find a record with a NULL value.
  1403. */
  1404. function testNullCondition() {
  1405. $this->ensureSampleDataNull();
  1406. $names = db_select('test_null', 'tn')
  1407. ->fields('tn', array('name'))
  1408. ->isNull('age')
  1409. ->execute()->fetchCol();
  1410. $this->assertEqual(count($names), 1, 'Correct number of records found with NULL age.');
  1411. $this->assertEqual($names[0], 'Fozzie', 'Correct record returned for NULL age.');
  1412. }
  1413. /**
  1414. * Test that we can find a record without a NULL value.
  1415. */
  1416. function testNotNullCondition() {
  1417. $this->ensureSampleDataNull();
  1418. $names = db_select('test_null', 'tn')
  1419. ->fields('tn', array('name'))
  1420. ->isNotNull('tn.age')
  1421. ->orderBy('name')
  1422. ->execute()->fetchCol();
  1423. $this->assertEqual(count($names), 2, 'Correct number of records found withNOT NULL age.');
  1424. $this->assertEqual($names[0], 'Gonzo', 'Correct record returned for NOT NULL age.');
  1425. $this->assertEqual($names[1], 'Kermit', 'Correct record returned for NOT NULL age.');
  1426. }
  1427. /**
  1428. * Test that we can UNION multiple Select queries together. This is
  1429. * semantically equal to UNION DISTINCT, so we don't explicitly test that.
  1430. */
  1431. function testUnion() {
  1432. $query_1 = db_select('test', 't')
  1433. ->fields('t', array('name'))
  1434. ->condition('age', array(27, 28), 'IN');
  1435. $query_2 = db_select('test', 't')
  1436. ->fields('t', array('name'))
  1437. ->condition('age', 28)
  1438. ->orderBy('name');
  1439. $query_1->union($query_2);
  1440. $names = $query_1->execute()->fetchCol();
  1441. // Ensure we only get 2 records.
  1442. $this->assertEqual(count($names), 2, 'UNION correctly discarded duplicates.');
  1443. $this->assertEqual($names[0], 'George', 'First query returned correct name.');
  1444. $this->assertEqual($names[1], 'Ringo', 'Second query returned correct name.');
  1445. }
  1446. /**
  1447. * Test that we can UNION ALL multiple Select queries together.
  1448. */
  1449. function testUnionAll() {
  1450. $query_1 = db_select('test', 't')
  1451. ->fields('t', array('name'))
  1452. ->condition('age', array(27, 28), 'IN');
  1453. $query_2 = db_select('test', 't')
  1454. ->fields('t', array('name'))
  1455. ->condition('age', 28);
  1456. $query_1->union($query_2, 'ALL');
  1457. $names = $query_1->execute()->fetchCol();
  1458. // Ensure we get all 3 records.
  1459. $this->assertEqual(count($names), 3, 'UNION ALL correctly preserved duplicates.');
  1460. $this->assertEqual($names[0], 'George', 'First query returned correct first name.');
  1461. $this->assertEqual($names[1], 'Ringo', 'Second query returned correct second name.');
  1462. $this->assertEqual($names[2], 'Ringo', 'Third query returned correct name.');
  1463. }
  1464. /**
  1465. * Tests that we can UNION multiple Select queries together and set the ORDER.
  1466. */
  1467. function testUnionOrder() {
  1468. // This gives George and Ringo.
  1469. $query_1 = db_select('test', 't')
  1470. ->fields('t', array('name'))
  1471. ->condition('age', array(27, 28), 'IN');
  1472. // This gives Paul.
  1473. $query_2 = db_select('test', 't')
  1474. ->fields('t', array('name'))
  1475. ->condition('age', 26);
  1476. $query_1->union($query_2);
  1477. $query_1->orderBy('name', 'DESC');
  1478. $names = $query_1->execute()->fetchCol();
  1479. // Ensure we get all 3 records.
  1480. $this->assertEqual(count($names), 3, 'UNION returned rows from both queries.');
  1481. // Ensure that the names are in the correct reverse alphabetical order,
  1482. // regardless of which query they came from.
  1483. $this->assertEqual($names[0], 'Ringo', 'First query returned correct name.');
  1484. $this->assertEqual($names[1], 'Paul', 'Second query returned correct name.');
  1485. $this->assertEqual($names[2], 'George', 'Third query returned correct name.');
  1486. }
  1487. /**
  1488. * Tests that we can UNION multiple Select queries together with a LIMIT.
  1489. */
  1490. function testUnionOrderLimit() {
  1491. // This gives George and Ringo.
  1492. $query_1 = db_select('test', 't')
  1493. ->fields('t', array('name'))
  1494. ->condition('age', array(27, 28), 'IN');
  1495. // This gives Paul.
  1496. $query_2 = db_select('test', 't')
  1497. ->fields('t', array('name'))
  1498. ->condition('age', 26);
  1499. $query_1->union($query_2);
  1500. $query_1->orderBy('name', 'DESC');
  1501. $query_1->range(0, 2);
  1502. $names = $query_1->execute()->fetchCol();
  1503. // Ensure we get only 2 of the 3 records.
  1504. $this->assertEqual(count($names), 2, 'UNION with a limit returned rows from both queries.');
  1505. // Ensure that the names are in the correct reverse alphabetical order,
  1506. // regardless of which query they came from.
  1507. $this->assertEqual($names[0], 'Ringo', 'First query returned correct name.');
  1508. $this->assertEqual($names[1], 'Paul', 'Second query returned correct name.');
  1509. }
  1510. /**
  1511. * Test that random ordering of queries works.
  1512. *
  1513. * We take the approach of testing the Drupal layer only, rather than trying
  1514. * to test that the database's random number generator actually produces
  1515. * random queries (which is very difficult to do without an unacceptable risk
  1516. * of the test failing by accident).
  1517. *
  1518. * Therefore, in this test we simply run the same query twice and assert that
  1519. * the two results are reordered versions of each other (as well as of the
  1520. * same query without the random ordering). It is reasonable to assume that
  1521. * if we run the same select query twice and the results are in a different
  1522. * order each time, the only way this could happen is if we have successfully
  1523. * triggered the database's random ordering functionality.
  1524. */
  1525. function testRandomOrder() {
  1526. // Use 52 items, so the chance that this test fails by accident will be the
  1527. // same as the chance that a deck of cards will come out in the same order
  1528. // after shuffling it (in other words, nearly impossible).
  1529. $number_of_items = 52;
  1530. while (db_query("SELECT MAX(id) FROM {test}")->fetchField() < $number_of_items) {
  1531. db_insert('test')->fields(array('name' => $this->randomName()))->execute();
  1532. }
  1533. // First select the items in order and make sure we get an ordered list.
  1534. $expected_ids = range(1, $number_of_items);
  1535. $ordered_ids = db_select('test', 't')
  1536. ->fields('t', array('id'))
  1537. ->range(0, $number_of_items)
  1538. ->orderBy('id')
  1539. ->execute()
  1540. ->fetchCol();
  1541. $this->assertEqual($ordered_ids, $expected_ids, 'A query without random ordering returns IDs in the correct order.');
  1542. // Now perform the same query, but instead choose a random ordering. We
  1543. // expect this to contain a differently ordered version of the original
  1544. // result.
  1545. $randomized_ids = db_select('test', 't')
  1546. ->fields('t', array('id'))
  1547. ->range(0, $number_of_items)
  1548. ->orderRandom()
  1549. ->execute()
  1550. ->fetchCol();
  1551. $this->assertNotEqual($randomized_ids, $ordered_ids, 'A query with random ordering returns an unordered set of IDs.');
  1552. $sorted_ids = $randomized_ids;
  1553. sort($sorted_ids);
  1554. $this->assertEqual($sorted_ids, $ordered_ids, 'After sorting the random list, the result matches the original query.');
  1555. // Now perform the exact same query again, and make sure the order is
  1556. // different.
  1557. $randomized_ids_second_set = db_select('test', 't')
  1558. ->fields('t', array('id'))
  1559. ->range(0, $number_of_items)
  1560. ->orderRandom()
  1561. ->execute()
  1562. ->fetchCol();
  1563. $this->assertNotEqual($randomized_ids_second_set, $randomized_ids, 'Performing the query with random ordering a second time returns IDs in a different order.');
  1564. $sorted_ids_second_set = $randomized_ids_second_set;
  1565. sort($sorted_ids_second_set);
  1566. $this->assertEqual($sorted_ids_second_set, $sorted_ids, 'After sorting the second random list, the result matches the sorted version of the first random list.');
  1567. }
  1568. /**
  1569. * Test that aliases are renamed when duplicates.
  1570. */
  1571. function testSelectDuplicateAlias() {
  1572. $query = db_select('test', 't');
  1573. $alias1 = $query->addField('t', 'name', 'the_alias');
  1574. $alias2 = $query->addField('t', 'age', 'the_alias');
  1575. $this->assertNotIdentical($alias1, $alias2, 'Duplicate aliases are renamed.');
  1576. }
  1577. }
  1578. /**
  1579. * Test case for subselects in a dynamic SELECT query.
  1580. */
  1581. class DatabaseSelectSubqueryTestCase extends DatabaseTestCase {
  1582. public static function getInfo() {
  1583. return array(
  1584. 'name' => 'Select tests, subqueries',
  1585. 'description' => 'Test the Select query builder.',
  1586. 'group' => 'Database',
  1587. );
  1588. }
  1589. /**
  1590. * Test that we can use a subquery in a FROM clause.
  1591. */
  1592. function testFromSubquerySelect() {
  1593. // Create a subquery, which is just a normal query object.
  1594. $subquery = db_select('test_task', 'tt');
  1595. $subquery->addField('tt', 'pid', 'pid');
  1596. $subquery->addField('tt', 'task', 'task');
  1597. $subquery->condition('priority', 1);
  1598. for ($i = 0; $i < 2; $i++) {
  1599. // Create another query that joins against the virtual table resulting
  1600. // from the subquery.
  1601. $select = db_select($subquery, 'tt2');
  1602. $select->join('test', 't', 't.id=tt2.pid');
  1603. $select->addField('t', 'name');
  1604. if ($i) {
  1605. // Use a different number of conditions here to confuse the subquery
  1606. // placeholder counter, testing http://drupal.org/node/1112854.
  1607. $select->condition('name', 'John');
  1608. }
  1609. $select->condition('task', 'code');
  1610. // The resulting query should be equivalent to:
  1611. // SELECT t.name
  1612. // FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt WHERE priority=1) tt
  1613. // INNER JOIN test t ON t.id=tt.pid
  1614. // WHERE tt.task = 'code'
  1615. $people = $select->execute()->fetchCol();
  1616. $this->assertEqual(count($people), 1, 'Returned the correct number of rows.');
  1617. }
  1618. }
  1619. /**
  1620. * Test that we can use a subquery in a FROM clause with a limit.
  1621. */
  1622. function testFromSubquerySelectWithLimit() {
  1623. // Create a subquery, which is just a normal query object.
  1624. $subquery = db_select('test_task', 'tt');
  1625. $subquery->addField('tt', 'pid', 'pid');
  1626. $subquery->addField('tt', 'task', 'task');
  1627. $subquery->orderBy('priority', 'DESC');
  1628. $subquery->range(0, 1);
  1629. // Create another query that joins against the virtual table resulting
  1630. // from the subquery.
  1631. $select = db_select($subquery, 'tt2');
  1632. $select->join('test', 't', 't.id=tt2.pid');
  1633. $select->addField('t', 'name');
  1634. // The resulting query should be equivalent to:
  1635. // SELECT t.name
  1636. // FROM (SELECT tt.pid AS pid, tt.task AS task FROM test_task tt ORDER BY priority DESC LIMIT 1 OFFSET 0) tt
  1637. // INNER JOIN test t ON t.id=tt.pid
  1638. $people = $select->execute()->fetchCol();
  1639. $this->assertEqual(count($people), 1, 'Returned the correct number of rows.');
  1640. }
  1641. /**
  1642. * Test that we can use a subquery in a WHERE clause.
  1643. */
  1644. function testConditionSubquerySelect() {
  1645. // Create a subquery, which is just a normal query object.
  1646. $subquery = db_select('test_task', 'tt');
  1647. $subquery->addField('tt', 'pid', 'pid');
  1648. $subquery->condition('tt.priority', 1);
  1649. // Create another query that joins against the virtual table resulting
  1650. // from the subquery.
  1651. $select = db_select('test_task', 'tt2');
  1652. $select->addField('tt2', 'task');
  1653. $select->condition('tt2.pid', $subquery, 'IN');
  1654. // The resulting query should be equivalent to:
  1655. // SELECT tt2.name
  1656. // FROM test tt2
  1657. // WHERE tt2.pid IN (SELECT tt.pid AS pid FROM test_task tt WHERE tt.priority=1)
  1658. $people = $select->execute()->fetchCol();
  1659. $this->assertEqual(count($people), 5, 'Returned the correct number of rows.');
  1660. }
  1661. /**
  1662. * Test that we can use a subquery in a JOIN clause.
  1663. */
  1664. function testJoinSubquerySelect() {
  1665. // Create a subquery, which is just a normal query object.
  1666. $subquery = db_select('test_task', 'tt');
  1667. $subquery->addField('tt', 'pid', 'pid');
  1668. $subquery->condition('priority', 1);
  1669. // Create another query that joins against the virtual table resulting
  1670. // from the subquery.
  1671. $select = db_select('test', 't');
  1672. $select->join($subquery, 'tt', 't.id=tt.pid');
  1673. $select->addField('t', 'name');
  1674. // The resulting query should be equivalent to:
  1675. // SELECT t.name
  1676. // FROM test t
  1677. // INNER JOIN (SELECT tt.pid AS pid FROM test_task tt WHERE priority=1) tt ON t.id=tt.pid
  1678. $people = $select->execute()->fetchCol();
  1679. $this->assertEqual(count($people), 2, 'Returned the correct number of rows.');
  1680. }
  1681. /**
  1682. * Test EXISTS subquery conditionals on SELECT statements.
  1683. *
  1684. * We essentially select all rows from the {test} table that have matching
  1685. * rows in the {test_people} table based on the shared name column.
  1686. */
  1687. function testExistsSubquerySelect() {
  1688. // Put George into {test_people}.
  1689. db_insert('test_people')
  1690. ->fields(array(
  1691. 'name' => 'George',
  1692. 'age' => 27,
  1693. 'job' => 'Singer',
  1694. ))
  1695. ->execute();
  1696. // Base query to {test}.
  1697. $query = db_select('test', 't')
  1698. ->fields('t', array('name'));
  1699. // Subquery to {test_people}.
  1700. $subquery = db_select('test_people', 'tp')
  1701. ->fields('tp', array('name'))
  1702. ->where('tp.name = t.name');
  1703. $query->exists($subquery);
  1704. $result = $query->execute();
  1705. // Ensure that we got the right record.
  1706. $record = $result->fetch();
  1707. $this->assertEqual($record->name, 'George', 'Fetched name is correct using EXISTS query.');
  1708. }
  1709. /**
  1710. * Test NOT EXISTS subquery conditionals on SELECT statements.
  1711. *
  1712. * We essentially select all rows from the {test} table that don't have
  1713. * matching rows in the {test_people} table based on the shared name column.
  1714. */
  1715. function testNotExistsSubquerySelect() {
  1716. // Put George into {test_people}.
  1717. db_insert('test_people')
  1718. ->fields(array(
  1719. 'name' => 'George',
  1720. 'age' => 27,
  1721. 'job' => 'Singer',
  1722. ))
  1723. ->execute();
  1724. // Base query to {test}.
  1725. $query = db_select('test', 't')
  1726. ->fields('t', array('name'));
  1727. // Subquery to {test_people}.
  1728. $subquery = db_select('test_people', 'tp')
  1729. ->fields('tp', array('name'))
  1730. ->where('tp.name = t.name');
  1731. $query->notExists($subquery);
  1732. // Ensure that we got the right number of records.
  1733. $people = $query->execute()->fetchCol();
  1734. $this->assertEqual(count($people), 3, 'NOT EXISTS query returned the correct results.');
  1735. }
  1736. }
  1737. /**
  1738. * Test select with order by clauses.
  1739. */
  1740. class DatabaseSelectOrderedTestCase extends DatabaseTestCase {
  1741. public static function getInfo() {
  1742. return array(
  1743. 'name' => 'Select tests, ordered',
  1744. 'description' => 'Test the Select query builder.',
  1745. 'group' => 'Database',
  1746. );
  1747. }
  1748. /**
  1749. * Test basic order by.
  1750. */
  1751. function testSimpleSelectOrdered() {
  1752. $query = db_select('test');
  1753. $name_field = $query->addField('test', 'name');
  1754. $age_field = $query->addField('test', 'age', 'age');
  1755. $query->orderBy($age_field);
  1756. $result = $query->execute();
  1757. $num_records = 0;
  1758. $last_age = 0;
  1759. foreach ($result as $record) {
  1760. $num_records++;
  1761. $this->assertTrue($record->age >= $last_age, 'Results returned in correct order.');
  1762. $last_age = $record->age;
  1763. }
  1764. $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
  1765. }
  1766. /**
  1767. * Test multiple order by.
  1768. */
  1769. function testSimpleSelectMultiOrdered() {
  1770. $query = db_select('test');
  1771. $name_field = $query->addField('test', 'name');
  1772. $age_field = $query->addField('test', 'age', 'age');
  1773. $job_field = $query->addField('test', 'job');
  1774. $query->orderBy($job_field);
  1775. $query->orderBy($age_field);
  1776. $result = $query->execute();
  1777. $num_records = 0;
  1778. $expected = array(
  1779. array('Ringo', 28, 'Drummer'),
  1780. array('John', 25, 'Singer'),
  1781. array('George', 27, 'Singer'),
  1782. array('Paul', 26, 'Songwriter'),
  1783. );
  1784. $results = $result->fetchAll(PDO::FETCH_NUM);
  1785. foreach ($expected as $k => $record) {
  1786. $num_records++;
  1787. foreach ($record as $kk => $col) {
  1788. if ($expected[$k][$kk] != $results[$k][$kk]) {
  1789. $this->assertTrue(FALSE, 'Results returned in correct order.');
  1790. }
  1791. }
  1792. }
  1793. $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
  1794. }
  1795. /**
  1796. * Test order by descending.
  1797. */
  1798. function testSimpleSelectOrderedDesc() {
  1799. $query = db_select('test');
  1800. $name_field = $query->addField('test', 'name');
  1801. $age_field = $query->addField('test', 'age', 'age');
  1802. $query->orderBy($age_field, 'DESC');
  1803. $result = $query->execute();
  1804. $num_records = 0;
  1805. $last_age = 100000000;
  1806. foreach ($result as $record) {
  1807. $num_records++;
  1808. $this->assertTrue($record->age <= $last_age, 'Results returned in correct order.');
  1809. $last_age = $record->age;
  1810. }
  1811. $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
  1812. }
  1813. /**
  1814. * Tests that the sort direction is sanitized properly.
  1815. */
  1816. function testOrderByEscaping() {
  1817. $query = db_select('test')->orderBy('name', 'invalid direction');
  1818. $order_bys = $query->getOrderBy();
  1819. $this->assertEqual($order_bys['name'], 'ASC', 'Invalid order by direction is converted to ASC.');
  1820. }
  1821. }
  1822. /**
  1823. * Test more complex select statements.
  1824. */
  1825. class DatabaseSelectComplexTestCase extends DatabaseTestCase {
  1826. public static function getInfo() {
  1827. return array(
  1828. 'name' => 'Select tests, complex',
  1829. 'description' => 'Test the Select query builder with more complex queries.',
  1830. 'group' => 'Database',
  1831. );
  1832. }
  1833. /**
  1834. * Test simple JOIN statements.
  1835. */
  1836. function testDefaultJoin() {
  1837. $query = db_select('test_task', 't');
  1838. $people_alias = $query->join('test', 'p', 't.pid = p.id');
  1839. $name_field = $query->addField($people_alias, 'name', 'name');
  1840. $task_field = $query->addField('t', 'task', 'task');
  1841. $priority_field = $query->addField('t', 'priority', 'priority');
  1842. $query->orderBy($priority_field);
  1843. $result = $query->execute();
  1844. $num_records = 0;
  1845. $last_priority = 0;
  1846. foreach ($result as $record) {
  1847. $num_records++;
  1848. $this->assertTrue($record->$priority_field >= $last_priority, 'Results returned in correct order.');
  1849. $this->assertNotEqual($record->$name_field, 'Ringo', 'Taskless person not selected.');
  1850. $last_priority = $record->$priority_field;
  1851. }
  1852. $this->assertEqual($num_records, 7, 'Returned the correct number of rows.');
  1853. }
  1854. /**
  1855. * Test LEFT OUTER joins.
  1856. */
  1857. function testLeftOuterJoin() {
  1858. $query = db_select('test', 'p');
  1859. $people_alias = $query->leftJoin('test_task', 't', 't.pid = p.id');
  1860. $name_field = $query->addField('p', 'name', 'name');
  1861. $task_field = $query->addField($people_alias, 'task', 'task');
  1862. $priority_field = $query->addField($people_alias, 'priority', 'priority');
  1863. $query->orderBy($name_field);
  1864. $result = $query->execute();
  1865. $num_records = 0;
  1866. $last_name = 0;
  1867. foreach ($result as $record) {
  1868. $num_records++;
  1869. $this->assertTrue(strcmp($record->$name_field, $last_name) >= 0, 'Results returned in correct order.');
  1870. $last_priority = $record->$name_field;
  1871. }
  1872. $this->assertEqual($num_records, 8, 'Returned the correct number of rows.');
  1873. }
  1874. /**
  1875. * Test GROUP BY clauses.
  1876. */
  1877. function testGroupBy() {
  1878. $query = db_select('test_task', 't');
  1879. $count_field = $query->addExpression('COUNT(task)', 'num');
  1880. $task_field = $query->addField('t', 'task');
  1881. $query->orderBy($count_field);
  1882. $query->groupBy($task_field);
  1883. $result = $query->execute();
  1884. $num_records = 0;
  1885. $last_count = 0;
  1886. $records = array();
  1887. foreach ($result as $record) {
  1888. $num_records++;
  1889. $this->assertTrue($record->$count_field >= $last_count, 'Results returned in correct order.');
  1890. $last_count = $record->$count_field;
  1891. $records[$record->$task_field] = $record->$count_field;
  1892. }
  1893. $correct_results = array(
  1894. 'eat' => 1,
  1895. 'sleep' => 2,
  1896. 'code' => 1,
  1897. 'found new band' => 1,
  1898. 'perform at superbowl' => 1,
  1899. );
  1900. foreach ($correct_results as $task => $count) {
  1901. $this->assertEqual($records[$task], $count, format_string("Correct number of '@task' records found.", array('@task' => $task)));
  1902. }
  1903. $this->assertEqual($num_records, 6, 'Returned the correct number of total rows.');
  1904. }
  1905. /**
  1906. * Test GROUP BY and HAVING clauses together.
  1907. */
  1908. function testGroupByAndHaving() {
  1909. $query = db_select('test_task', 't');
  1910. $count_field = $query->addExpression('COUNT(task)', 'num');
  1911. $task_field = $query->addField('t', 'task');
  1912. $query->orderBy($count_field);
  1913. $query->groupBy($task_field);
  1914. $query->having('COUNT(task) >= 2');
  1915. $result = $query->execute();
  1916. $num_records = 0;
  1917. $last_count = 0;
  1918. $records = array();
  1919. foreach ($result as $record) {
  1920. $num_records++;
  1921. $this->assertTrue($record->$count_field >= 2, 'Record has the minimum count.');
  1922. $this->assertTrue($record->$count_field >= $last_count, 'Results returned in correct order.');
  1923. $last_count = $record->$count_field;
  1924. $records[$record->$task_field] = $record->$count_field;
  1925. }
  1926. $correct_results = array(
  1927. 'sleep' => 2,
  1928. );
  1929. foreach ($correct_results as $task => $count) {
  1930. $this->assertEqual($records[$task], $count, format_string("Correct number of '@task' records found.", array('@task' => $task)));
  1931. }
  1932. $this->assertEqual($num_records, 1, 'Returned the correct number of total rows.');
  1933. }
  1934. /**
  1935. * Test range queries. The SQL clause varies with the database.
  1936. */
  1937. function testRange() {
  1938. $query = db_select('test');
  1939. $name_field = $query->addField('test', 'name');
  1940. $age_field = $query->addField('test', 'age', 'age');
  1941. $query->range(0, 2);
  1942. $result = $query->execute();
  1943. $num_records = 0;
  1944. foreach ($result as $record) {
  1945. $num_records++;
  1946. }
  1947. $this->assertEqual($num_records, 2, 'Returned the correct number of rows.');
  1948. }
  1949. /**
  1950. * Test distinct queries.
  1951. */
  1952. function testDistinct() {
  1953. $query = db_select('test_task');
  1954. $task_field = $query->addField('test_task', 'task');
  1955. $query->distinct();
  1956. $result = $query->execute();
  1957. $num_records = 0;
  1958. foreach ($result as $record) {
  1959. $num_records++;
  1960. }
  1961. $this->assertEqual($num_records, 6, 'Returned the correct number of rows.');
  1962. }
  1963. /**
  1964. * Test that we can generate a count query from a built query.
  1965. */
  1966. function testCountQuery() {
  1967. $query = db_select('test');
  1968. $name_field = $query->addField('test', 'name');
  1969. $age_field = $query->addField('test', 'age', 'age');
  1970. $query->orderBy('name');
  1971. $count = $query->countQuery()->execute()->fetchField();
  1972. $this->assertEqual($count, 4, 'Counted the correct number of records.');
  1973. // Now make sure we didn't break the original query! We should still have
  1974. // all of the fields we asked for.
  1975. $record = $query->execute()->fetch();
  1976. $this->assertEqual($record->$name_field, 'George', 'Correct data retrieved.');
  1977. $this->assertEqual($record->$age_field, 27, 'Correct data retrieved.');
  1978. }
  1979. function testHavingCountQuery() {
  1980. $query = db_select('test')
  1981. ->extend('PagerDefault')
  1982. ->groupBy('age')
  1983. ->having('age + 1 > 0');
  1984. $query->addField('test', 'age');
  1985. $query->addExpression('age + 1');
  1986. $count = count($query->execute()->fetchCol());
  1987. $this->assertEqual($count, 4, 'Counted the correct number of records.');
  1988. }
  1989. /**
  1990. * Test that countQuery properly removes 'all_fields' statements and
  1991. * ordering clauses.
  1992. */
  1993. function testCountQueryRemovals() {
  1994. $query = db_select('test');
  1995. $query->fields('test');
  1996. $query->orderBy('name');
  1997. $count = $query->countQuery();
  1998. // Check that the 'all_fields' statement is handled properly.
  1999. $tables = $query->getTables();
  2000. $this->assertEqual($tables['test']['all_fields'], 1, 'Query correctly sets \'all_fields\' statement.');
  2001. $tables = $count->getTables();
  2002. $this->assertFalse(isset($tables['test']['all_fields']), 'Count query correctly unsets \'all_fields\' statement.');
  2003. // Check that the ordering clause is handled properly.
  2004. $orderby = $query->getOrderBy();
  2005. $this->assertEqual($orderby['name'], 'ASC', 'Query correctly sets ordering clause.');
  2006. $orderby = $count->getOrderBy();
  2007. $this->assertFalse(isset($orderby['name']), 'Count query correctly unsets ordering caluse.');
  2008. // Make sure that the count query works.
  2009. $count = $count->execute()->fetchField();
  2010. $this->assertEqual($count, 4, 'Counted the correct number of records.');
  2011. }
  2012. /**
  2013. * Test that countQuery properly removes fields and expressions.
  2014. */
  2015. function testCountQueryFieldRemovals() {
  2016. // countQuery should remove all fields and expressions, so this can be
  2017. // tested by adding a non-existent field and expression: if it ends
  2018. // up in the query, an error will be thrown. If not, it will return the
  2019. // number of records, which in this case happens to be 4 (there are four
  2020. // records in the {test} table).
  2021. $query = db_select('test');
  2022. $query->fields('test', array('fail'));
  2023. $this->assertEqual(4, $query->countQuery()->execute()->fetchField(), 'Count Query removed fields');
  2024. $query = db_select('test');
  2025. $query->addExpression('fail');
  2026. $this->assertEqual(4, $query->countQuery()->execute()->fetchField(), 'Count Query removed expressions');
  2027. }
  2028. /**
  2029. * Test that we can generate a count query from a query with distinct.
  2030. */
  2031. function testCountQueryDistinct() {
  2032. $query = db_select('test_task');
  2033. $task_field = $query->addField('test_task', 'task');
  2034. $query->distinct();
  2035. $count = $query->countQuery()->execute()->fetchField();
  2036. $this->assertEqual($count, 6, 'Counted the correct number of records.');
  2037. }
  2038. /**
  2039. * Test that we can generate a count query from a query with GROUP BY.
  2040. */
  2041. function testCountQueryGroupBy() {
  2042. $query = db_select('test_task');
  2043. $pid_field = $query->addField('test_task', 'pid');
  2044. $query->groupBy('pid');
  2045. $count = $query->countQuery()->execute()->fetchField();
  2046. $this->assertEqual($count, 3, 'Counted the correct number of records.');
  2047. // Use a column alias as, without one, the query can succeed for the wrong
  2048. // reason.
  2049. $query = db_select('test_task');
  2050. $pid_field = $query->addField('test_task', 'pid', 'pid_alias');
  2051. $query->addExpression('COUNT(test_task.task)', 'count');
  2052. $query->groupBy('pid_alias');
  2053. $query->orderBy('pid_alias', 'asc');
  2054. $count = $query->countQuery()->execute()->fetchField();
  2055. $this->assertEqual($count, 3, 'Counted the correct number of records.');
  2056. }
  2057. /**
  2058. * Confirm that we can properly nest conditional clauses.
  2059. */
  2060. function testNestedConditions() {
  2061. // This query should translate to:
  2062. // "SELECT job FROM {test} WHERE name = 'Paul' AND (age = 26 OR age = 27)"
  2063. // That should find only one record. Yes it's a non-optimal way of writing
  2064. // that query but that's not the point!
  2065. $query = db_select('test');
  2066. $query->addField('test', 'job');
  2067. $query->condition('name', 'Paul');
  2068. $query->condition(db_or()->condition('age', 26)->condition('age', 27));
  2069. $job = $query->execute()->fetchField();
  2070. $this->assertEqual($job, 'Songwriter', 'Correct data retrieved.');
  2071. }
  2072. /**
  2073. * Confirm we can join on a single table twice with a dynamic alias.
  2074. */
  2075. function testJoinTwice() {
  2076. $query = db_select('test')->fields('test');
  2077. $alias = $query->join('test', 'test', 'test.job = %alias.job');
  2078. $query->addField($alias, 'name', 'othername');
  2079. $query->addField($alias, 'job', 'otherjob');
  2080. $query->where("$alias.name <> test.name");
  2081. $crowded_job = $query->execute()->fetch();
  2082. $this->assertEqual($crowded_job->job, $crowded_job->otherjob, 'Correctly joined same table twice.');
  2083. $this->assertNotEqual($crowded_job->name, $crowded_job->othername, 'Correctly joined same table twice.');
  2084. }
  2085. /**
  2086. * Test that join conditions can use Condition objects.
  2087. */
  2088. function testJoinConditionObject() {
  2089. // Same test as testDefaultJoin, but with a Condition object.
  2090. $query = db_select('test_task', 't');
  2091. $join_cond = db_and()->where('t.pid = p.id');
  2092. $people_alias = $query->join('test', 'p', $join_cond);
  2093. $name_field = $query->addField($people_alias, 'name', 'name');
  2094. $query->addField('t', 'task', 'task');
  2095. $priority_field = $query->addField('t', 'priority', 'priority');
  2096. $query->orderBy($priority_field);
  2097. $result = $query->execute();
  2098. $num_records = 0;
  2099. $last_priority = 0;
  2100. foreach ($result as $record) {
  2101. $num_records++;
  2102. $this->assertTrue($record->$priority_field >= $last_priority, 'Results returned in correct order.');
  2103. $this->assertNotEqual($record->$name_field, 'Ringo', 'Taskless person not selected.');
  2104. $last_priority = $record->$priority_field;
  2105. }
  2106. $this->assertEqual($num_records, 7, 'Returned the correct number of rows.');
  2107. // Test a condition object that creates placeholders.
  2108. $t1_name = 'John';
  2109. $t2_name = 'George';
  2110. $join_cond = db_and()
  2111. ->condition('t1.name', $t1_name)
  2112. ->condition('t2.name', $t2_name);
  2113. $query = db_select('test', 't1');
  2114. $query->innerJoin('test', 't2', $join_cond);
  2115. $query->addField('t1', 'name', 't1_name');
  2116. $query->addField('t2', 'name', 't2_name');
  2117. $num_records = $query->countQuery()->execute()->fetchField();
  2118. $this->assertEqual($num_records, 1, 'Query expected to return 1 row. Actual: ' . $num_records);
  2119. if ($num_records == 1) {
  2120. $record = $query->execute()->fetchObject();
  2121. $this->assertEqual($record->t1_name, $t1_name, 'Query expected to retrieve name ' . $t1_name . ' from table t1. Actual: ' . $record->t1_name);
  2122. $this->assertEqual($record->t2_name, $t2_name, 'Query expected to retrieve name ' . $t2_name . ' from table t2. Actual: ' . $record->t2_name);
  2123. }
  2124. }
  2125. }
  2126. /**
  2127. * Test more complex select statements, part 2.
  2128. */
  2129. class DatabaseSelectComplexTestCase2 extends DatabaseTestCase {
  2130. public static function getInfo() {
  2131. return array(
  2132. 'name' => 'Select tests, complex 2',
  2133. 'description' => 'Test the Select query builder with even more complex queries.',
  2134. 'group' => 'Database',
  2135. );
  2136. }
  2137. function setUp() {
  2138. DrupalWebTestCase::setUp('database_test', 'node_access_test');
  2139. $schema['test'] = drupal_get_schema('test');
  2140. $schema['test_people'] = drupal_get_schema('test_people');
  2141. $schema['test_one_blob'] = drupal_get_schema('test_one_blob');
  2142. $schema['test_two_blobs'] = drupal_get_schema('test_two_blobs');
  2143. $schema['test_task'] = drupal_get_schema('test_task');
  2144. $this->installTables($schema);
  2145. $this->addSampleData();
  2146. }
  2147. /**
  2148. * Test that we can join on a query.
  2149. */
  2150. function testJoinSubquery() {
  2151. $acct = $this->drupalCreateUser(array('access content'));
  2152. $this->drupalLogin($acct);
  2153. $query = db_select('test_task', 'tt', array('target' => 'slave'));
  2154. $query->addExpression('tt.pid + 1', 'abc');
  2155. $query->condition('priority', 1, '>');
  2156. $query->condition('priority', 100, '<');
  2157. $subquery = db_select('test', 'tp');
  2158. $subquery->join('test_one_blob', 'tpb', 'tp.id = tpb.id');
  2159. $subquery->join('node', 'n', 'tp.id = n.nid');
  2160. $subquery->addTag('node_access');
  2161. $subquery->addMetaData('account', $acct);
  2162. $subquery->addField('tp', 'id');
  2163. $subquery->condition('age', 5, '>');
  2164. $subquery->condition('age', 500, '<');
  2165. $query->leftJoin($subquery, 'sq', 'tt.pid = sq.id');
  2166. $query->join('test_one_blob', 'tb3', 'tt.pid = tb3.id');
  2167. // Construct the query string.
  2168. // This is the same sequence that SelectQuery::execute() goes through.
  2169. $query->preExecute();
  2170. $query->getArguments();
  2171. $str = (string) $query;
  2172. // Verify that the string only has one copy of condition placeholder 0.
  2173. $pos = strpos($str, 'db_condition_placeholder_0', 0);
  2174. $pos2 = strpos($str, 'db_condition_placeholder_0', $pos + 1);
  2175. $this->assertFalse($pos2, 'Condition placeholder is not repeated.');
  2176. }
  2177. }
  2178. class DatabaseSelectPagerDefaultTestCase extends DatabaseTestCase {
  2179. public static function getInfo() {
  2180. return array(
  2181. 'name' => 'Pager query tests',
  2182. 'description' => 'Test the pager query extender.',
  2183. 'group' => 'Database',
  2184. );
  2185. }
  2186. /**
  2187. * Confirm that a pager query returns the correct results.
  2188. *
  2189. * Note that we have to make an HTTP request to a test page handler
  2190. * because the pager depends on GET parameters.
  2191. */
  2192. function testEvenPagerQuery() {
  2193. // To keep the test from being too brittle, we determine up front
  2194. // what the page count should be dynamically, and pass the control
  2195. // information forward to the actual query on the other side of the
  2196. // HTTP request.
  2197. $limit = 2;
  2198. $count = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
  2199. $correct_number = $limit;
  2200. $num_pages = floor($count / $limit);
  2201. // If there is no remainder from rounding, subtract 1 since we index from 0.
  2202. if (!($num_pages * $limit < $count)) {
  2203. $num_pages--;
  2204. }
  2205. for ($page = 0; $page <= $num_pages; ++$page) {
  2206. $this->drupalGet('database_test/pager_query_even/' . $limit, array('query' => array('page' => $page)));
  2207. $data = json_decode($this->drupalGetContent());
  2208. if ($page == $num_pages) {
  2209. $correct_number = $count - ($limit * $page);
  2210. }
  2211. $this->assertEqual(count($data->names), $correct_number, format_string('Correct number of records returned by pager: @number', array('@number' => $correct_number)));
  2212. }
  2213. }
  2214. /**
  2215. * Confirm that a pager query returns the correct results.
  2216. *
  2217. * Note that we have to make an HTTP request to a test page handler
  2218. * because the pager depends on GET parameters.
  2219. */
  2220. function testOddPagerQuery() {
  2221. // To keep the test from being too brittle, we determine up front
  2222. // what the page count should be dynamically, and pass the control
  2223. // information forward to the actual query on the other side of the
  2224. // HTTP request.
  2225. $limit = 2;
  2226. $count = db_query('SELECT COUNT(*) FROM {test_task}')->fetchField();
  2227. $correct_number = $limit;
  2228. $num_pages = floor($count / $limit);
  2229. // If there is no remainder from rounding, subtract 1 since we index from 0.
  2230. if (!($num_pages * $limit < $count)) {
  2231. $num_pages--;
  2232. }
  2233. for ($page = 0; $page <= $num_pages; ++$page) {
  2234. $this->drupalGet('database_test/pager_query_odd/' . $limit, array('query' => array('page' => $page)));
  2235. $data = json_decode($this->drupalGetContent());
  2236. if ($page == $num_pages) {
  2237. $correct_number = $count - ($limit * $page);
  2238. }
  2239. $this->assertEqual(count($data->names), $correct_number, format_string('Correct number of records returned by pager: @number', array('@number' => $correct_number)));
  2240. }
  2241. }
  2242. /**
  2243. * Confirm that a pager query with inner pager query returns valid results.
  2244. *
  2245. * This is a regression test for #467984.
  2246. */
  2247. function testInnerPagerQuery() {
  2248. $query = db_select('test', 't')->extend('PagerDefault');
  2249. $query
  2250. ->fields('t', array('age'))
  2251. ->orderBy('age')
  2252. ->limit(5);
  2253. $outer_query = db_select($query);
  2254. $outer_query->addField('subquery', 'age');
  2255. $ages = $outer_query
  2256. ->execute()
  2257. ->fetchCol();
  2258. $this->assertEqual($ages, array(25, 26, 27, 28), 'Inner pager query returned the correct ages.');
  2259. }
  2260. /**
  2261. * Confirm that a paging query with a having expression returns valid results.
  2262. *
  2263. * This is a regression test for #467984.
  2264. */
  2265. function testHavingPagerQuery() {
  2266. $query = db_select('test', 't')->extend('PagerDefault');
  2267. $query
  2268. ->fields('t', array('name'))
  2269. ->orderBy('name')
  2270. ->groupBy('name')
  2271. ->having('MAX(age) > :count', array(':count' => 26))
  2272. ->limit(5);
  2273. $ages = $query
  2274. ->execute()
  2275. ->fetchCol();
  2276. $this->assertEqual($ages, array('George', 'Ringo'), 'Pager query with having expression returned the correct ages.');
  2277. }
  2278. /**
  2279. * Confirm that every pager gets a valid non-overlaping element ID.
  2280. */
  2281. function testElementNumbers() {
  2282. $_GET['page'] = '3, 2, 1, 0';
  2283. $query = db_select('test', 't')->extend('PagerDefault');
  2284. $query->element(2)
  2285. ->fields('t', array('name'))
  2286. ->orderBy('age')
  2287. ->limit(1);
  2288. $this->assertEqual(2, $query->getElement());
  2289. $name = $query->execute()->fetchField();
  2290. $this->assertEqual($name, 'Paul', 'Pager query #1 with a specified element ID returned the correct results.');
  2291. // Setting an element smaller than the previous one
  2292. // should not overwrite the pager $maxElement with a smaller value.
  2293. $query = db_select('test', 't')->extend('PagerDefault');
  2294. $query->element(1)
  2295. ->fields('t', array('name'))
  2296. ->orderBy('age')
  2297. ->limit(1);
  2298. $this->assertEqual(1, $query->getElement());
  2299. $name = $query->execute()->fetchField();
  2300. $this->assertEqual($name, 'George', 'Pager query #2 with a specified element ID returned the correct results.');
  2301. $query = db_select('test', 't')->extend('PagerDefault');
  2302. $query->fields('t', array('name'))
  2303. ->orderBy('age')
  2304. ->limit(1);
  2305. $this->assertEqual(3, $query->getElement());
  2306. $name = $query->execute()->fetchField();
  2307. $this->assertEqual($name, 'John', 'Pager query #3 with a generated element ID returned the correct results.');
  2308. unset($_GET['page']);
  2309. }
  2310. }
  2311. class DatabaseSelectTableSortDefaultTestCase extends DatabaseTestCase {
  2312. public static function getInfo() {
  2313. return array(
  2314. 'name' => 'Tablesort query tests',
  2315. 'description' => 'Test the tablesort query extender.',
  2316. 'group' => 'Database',
  2317. );
  2318. }
  2319. /**
  2320. * Confirm that a tablesort query returns the correct results.
  2321. *
  2322. * Note that we have to make an HTTP request to a test page handler
  2323. * because the pager depends on GET parameters.
  2324. */
  2325. function testTableSortQuery() {
  2326. $sorts = array(
  2327. array('field' => t('Task ID'), 'sort' => 'desc', 'first' => 'perform at superbowl', 'last' => 'eat'),
  2328. array('field' => t('Task ID'), 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'),
  2329. array('field' => t('Task'), 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'),
  2330. array('field' => t('Task'), 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'),
  2331. // more elements here
  2332. );
  2333. foreach ($sorts as $sort) {
  2334. $this->drupalGet('database_test/tablesort/', array('query' => array('order' => $sort['field'], 'sort' => $sort['sort'])));
  2335. $data = json_decode($this->drupalGetContent());
  2336. $first = array_shift($data->tasks);
  2337. $last = array_pop($data->tasks);
  2338. $this->assertEqual($first->task, $sort['first'], 'Items appear in the correct order.');
  2339. $this->assertEqual($last->task, $sort['last'], 'Items appear in the correct order.');
  2340. }
  2341. }
  2342. /**
  2343. * Confirm that if a tablesort's orderByHeader is called before another orderBy, that the header happens first.
  2344. *
  2345. */
  2346. function testTableSortQueryFirst() {
  2347. $sorts = array(
  2348. array('field' => t('Task ID'), 'sort' => 'desc', 'first' => 'perform at superbowl', 'last' => 'eat'),
  2349. array('field' => t('Task ID'), 'sort' => 'asc', 'first' => 'eat', 'last' => 'perform at superbowl'),
  2350. array('field' => t('Task'), 'sort' => 'asc', 'first' => 'code', 'last' => 'sleep'),
  2351. array('field' => t('Task'), 'sort' => 'desc', 'first' => 'sleep', 'last' => 'code'),
  2352. // more elements here
  2353. );
  2354. foreach ($sorts as $sort) {
  2355. $this->drupalGet('database_test/tablesort_first/', array('query' => array('order' => $sort['field'], 'sort' => $sort['sort'])));
  2356. $data = json_decode($this->drupalGetContent());
  2357. $first = array_shift($data->tasks);
  2358. $last = array_pop($data->tasks);
  2359. $this->assertEqual($first->task, $sort['first'], format_string('Items appear in the correct order sorting by @field @sort.', array('@field' => $sort['field'], '@sort' => $sort['sort'])));
  2360. $this->assertEqual($last->task, $sort['last'], format_string('Items appear in the correct order sorting by @field @sort.', array('@field' => $sort['field'], '@sort' => $sort['sort'])));
  2361. }
  2362. }
  2363. /**
  2364. * Confirm that if a sort is not set in a tableselect form there is no error thrown when using the default.
  2365. */
  2366. function testTableSortDefaultSort() {
  2367. $this->drupalGet('database_test/tablesort_default_sort');
  2368. // Any PHP errors or notices thrown would trigger a simpletest exception, so
  2369. // no additional assertions are needed.
  2370. }
  2371. }
  2372. /**
  2373. * Select tagging tests.
  2374. *
  2375. * Tags are a way to flag queries for alter hooks so they know
  2376. * what type of query it is, such as "node_access".
  2377. */
  2378. class DatabaseTaggingTestCase extends DatabaseTestCase {
  2379. public static function getInfo() {
  2380. return array(
  2381. 'name' => 'Query tagging tests',
  2382. 'description' => 'Test the tagging capabilities of the Select builder.',
  2383. 'group' => 'Database',
  2384. );
  2385. }
  2386. /**
  2387. * Confirm that a query has a "tag" added to it.
  2388. */
  2389. function testHasTag() {
  2390. $query = db_select('test');
  2391. $query->addField('test', 'name');
  2392. $query->addField('test', 'age', 'age');
  2393. $query->addTag('test');
  2394. $this->assertTrue($query->hasTag('test'), 'hasTag() returned true.');
  2395. $this->assertFalse($query->hasTag('other'), 'hasTag() returned false.');
  2396. }
  2397. /**
  2398. * Test query tagging "has all of these tags" functionality.
  2399. */
  2400. function testHasAllTags() {
  2401. $query = db_select('test');
  2402. $query->addField('test', 'name');
  2403. $query->addField('test', 'age', 'age');
  2404. $query->addTag('test');
  2405. $query->addTag('other');
  2406. $this->assertTrue($query->hasAllTags('test', 'other'), 'hasAllTags() returned true.');
  2407. $this->assertFalse($query->hasAllTags('test', 'stuff'), 'hasAllTags() returned false.');
  2408. }
  2409. /**
  2410. * Test query tagging "has at least one of these tags" functionality.
  2411. */
  2412. function testHasAnyTag() {
  2413. $query = db_select('test');
  2414. $query->addField('test', 'name');
  2415. $query->addField('test', 'age', 'age');
  2416. $query->addTag('test');
  2417. $this->assertTrue($query->hasAnyTag('test', 'other'), 'hasAnyTag() returned true.');
  2418. $this->assertFalse($query->hasAnyTag('other', 'stuff'), 'hasAnyTag() returned false.');
  2419. }
  2420. /**
  2421. * Confirm that an extended query has a "tag" added to it.
  2422. */
  2423. function testExtenderHasTag() {
  2424. $query = db_select('test')
  2425. ->extend('SelectQueryExtender');
  2426. $query->addField('test', 'name');
  2427. $query->addField('test', 'age', 'age');
  2428. $query->addTag('test');
  2429. $this->assertTrue($query->hasTag('test'), 'hasTag() returned true.');
  2430. $this->assertFalse($query->hasTag('other'), 'hasTag() returned false.');
  2431. }
  2432. /**
  2433. * Test extended query tagging "has all of these tags" functionality.
  2434. */
  2435. function testExtenderHasAllTags() {
  2436. $query = db_select('test')
  2437. ->extend('SelectQueryExtender');
  2438. $query->addField('test', 'name');
  2439. $query->addField('test', 'age', 'age');
  2440. $query->addTag('test');
  2441. $query->addTag('other');
  2442. $this->assertTrue($query->hasAllTags('test', 'other'), 'hasAllTags() returned true.');
  2443. $this->assertFalse($query->hasAllTags('test', 'stuff'), 'hasAllTags() returned false.');
  2444. }
  2445. /**
  2446. * Test extended query tagging "has at least one of these tags" functionality.
  2447. */
  2448. function testExtenderHasAnyTag() {
  2449. $query = db_select('test')
  2450. ->extend('SelectQueryExtender');
  2451. $query->addField('test', 'name');
  2452. $query->addField('test', 'age', 'age');
  2453. $query->addTag('test');
  2454. $this->assertTrue($query->hasAnyTag('test', 'other'), 'hasAnyTag() returned true.');
  2455. $this->assertFalse($query->hasAnyTag('other', 'stuff'), 'hasAnyTag() returned false.');
  2456. }
  2457. /**
  2458. * Test that we can attach meta data to a query object.
  2459. *
  2460. * This is how we pass additional context to alter hooks.
  2461. */
  2462. function testMetaData() {
  2463. $query = db_select('test');
  2464. $query->addField('test', 'name');
  2465. $query->addField('test', 'age', 'age');
  2466. $data = array(
  2467. 'a' => 'A',
  2468. 'b' => 'B',
  2469. );
  2470. $query->addMetaData('test', $data);
  2471. $return = $query->getMetaData('test');
  2472. $this->assertEqual($data, $return, 'Corect metadata returned.');
  2473. $return = $query->getMetaData('nothere');
  2474. $this->assertNull($return, 'Non-existent key returned NULL.');
  2475. }
  2476. }
  2477. /**
  2478. * Select alter tests.
  2479. *
  2480. * @see database_test_query_alter()
  2481. */
  2482. class DatabaseAlterTestCase extends DatabaseTestCase {
  2483. public static function getInfo() {
  2484. return array(
  2485. 'name' => 'Query altering tests',
  2486. 'description' => 'Test the hook_query_alter capabilities of the Select builder.',
  2487. 'group' => 'Database',
  2488. );
  2489. }
  2490. /**
  2491. * Test that we can do basic alters.
  2492. */
  2493. function testSimpleAlter() {
  2494. $query = db_select('test');
  2495. $query->addField('test', 'name');
  2496. $query->addField('test', 'age', 'age');
  2497. $query->addTag('database_test_alter_add_range');
  2498. $result = $query->execute();
  2499. $num_records = 0;
  2500. foreach ($result as $record) {
  2501. $num_records++;
  2502. }
  2503. $this->assertEqual($num_records, 2, 'Returned the correct number of rows.');
  2504. }
  2505. /**
  2506. * Test that we can alter the joins on a query.
  2507. */
  2508. function testAlterWithJoin() {
  2509. $query = db_select('test_task');
  2510. $tid_field = $query->addField('test_task', 'tid');
  2511. $task_field = $query->addField('test_task', 'task');
  2512. $query->orderBy($task_field);
  2513. $query->addTag('database_test_alter_add_join');
  2514. $result = $query->execute();
  2515. $records = $result->fetchAll();
  2516. $this->assertEqual(count($records), 2, 'Returned the correct number of rows.');
  2517. $this->assertEqual($records[0]->name, 'George', 'Correct data retrieved.');
  2518. $this->assertEqual($records[0]->$tid_field, 4, 'Correct data retrieved.');
  2519. $this->assertEqual($records[0]->$task_field, 'sing', 'Correct data retrieved.');
  2520. $this->assertEqual($records[1]->name, 'George', 'Correct data retrieved.');
  2521. $this->assertEqual($records[1]->$tid_field, 5, 'Correct data retrieved.');
  2522. $this->assertEqual($records[1]->$task_field, 'sleep', 'Correct data retrieved.');
  2523. }
  2524. /**
  2525. * Test that we can alter a query's conditionals.
  2526. */
  2527. function testAlterChangeConditional() {
  2528. $query = db_select('test_task');
  2529. $tid_field = $query->addField('test_task', 'tid');
  2530. $pid_field = $query->addField('test_task', 'pid');
  2531. $task_field = $query->addField('test_task', 'task');
  2532. $people_alias = $query->join('test', 'people', "test_task.pid = people.id");
  2533. $name_field = $query->addField($people_alias, 'name', 'name');
  2534. $query->condition('test_task.tid', '1');
  2535. $query->orderBy($tid_field);
  2536. $query->addTag('database_test_alter_change_conditional');
  2537. $result = $query->execute();
  2538. $records = $result->fetchAll();
  2539. $this->assertEqual(count($records), 1, 'Returned the correct number of rows.');
  2540. $this->assertEqual($records[0]->$name_field, 'John', 'Correct data retrieved.');
  2541. $this->assertEqual($records[0]->$tid_field, 2, 'Correct data retrieved.');
  2542. $this->assertEqual($records[0]->$pid_field, 1, 'Correct data retrieved.');
  2543. $this->assertEqual($records[0]->$task_field, 'sleep', 'Correct data retrieved.');
  2544. }
  2545. /**
  2546. * Test that we can alter the fields of a query.
  2547. */
  2548. function testAlterChangeFields() {
  2549. $query = db_select('test');
  2550. $name_field = $query->addField('test', 'name');
  2551. $age_field = $query->addField('test', 'age', 'age');
  2552. $query->orderBy('name');
  2553. $query->addTag('database_test_alter_change_fields');
  2554. $record = $query->execute()->fetch();
  2555. $this->assertEqual($record->$name_field, 'George', 'Correct data retrieved.');
  2556. $this->assertFalse(isset($record->$age_field), 'Age field not found, as intended.');
  2557. }
  2558. /**
  2559. * Test that we can alter expressions in the query.
  2560. */
  2561. function testAlterExpression() {
  2562. $query = db_select('test');
  2563. $name_field = $query->addField('test', 'name');
  2564. $age_field = $query->addExpression("age*2", 'double_age');
  2565. $query->condition('age', 27);
  2566. $query->addTag('database_test_alter_change_expressions');
  2567. $result = $query->execute();
  2568. // Ensure that we got the right record.
  2569. $record = $result->fetch();
  2570. $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
  2571. $this->assertEqual($record->$age_field, 27*3, 'Fetched age expression is correct.');
  2572. }
  2573. /**
  2574. * Test that we can remove a range() value from a query. This also tests hook_query_TAG_alter().
  2575. */
  2576. function testAlterRemoveRange() {
  2577. $query = db_select('test');
  2578. $query->addField('test', 'name');
  2579. $query->addField('test', 'age', 'age');
  2580. $query->range(0, 2);
  2581. $query->addTag('database_test_alter_remove_range');
  2582. $num_records = count($query->execute()->fetchAll());
  2583. $this->assertEqual($num_records, 4, 'Returned the correct number of rows.');
  2584. }
  2585. /**
  2586. * Test that we can do basic alters on subqueries.
  2587. */
  2588. function testSimpleAlterSubquery() {
  2589. // Create a sub-query with an alter tag.
  2590. $subquery = db_select('test', 'p');
  2591. $subquery->addField('p', 'name');
  2592. $subquery->addField('p', 'id');
  2593. // Pick out George.
  2594. $subquery->condition('age', 27);
  2595. $subquery->addExpression("age*2", 'double_age');
  2596. // This query alter should change it to age * 3.
  2597. $subquery->addTag('database_test_alter_change_expressions');
  2598. // Create a main query and join to sub-query.
  2599. $query = db_select('test_task', 'tt');
  2600. $query->join($subquery, 'pq', 'pq.id = tt.pid');
  2601. $age_field = $query->addField('pq', 'double_age');
  2602. $name_field = $query->addField('pq', 'name');
  2603. $record = $query->execute()->fetch();
  2604. $this->assertEqual($record->$name_field, 'George', 'Fetched name is correct.');
  2605. $this->assertEqual($record->$age_field, 27*3, 'Fetched age expression is correct.');
  2606. }
  2607. }
  2608. /**
  2609. * Regression tests.
  2610. */
  2611. class DatabaseRegressionTestCase extends DatabaseTestCase {
  2612. public static function getInfo() {
  2613. return array(
  2614. 'name' => 'Regression tests',
  2615. 'description' => 'Regression tests cases for the database layer.',
  2616. 'group' => 'Database',
  2617. );
  2618. }
  2619. /**
  2620. * Regression test for #310447.
  2621. *
  2622. * Tries to insert non-ascii UTF-8 data in a database column and checks
  2623. * if its stored properly.
  2624. */
  2625. function testRegression_310447() {
  2626. // That's a 255 character UTF-8 string.
  2627. $name = str_repeat("é", 255);
  2628. db_insert('test')
  2629. ->fields(array(
  2630. 'name' => $name,
  2631. 'age' => 20,
  2632. 'job' => 'Dancer',
  2633. ))->execute();
  2634. $from_database = db_query('SELECT name FROM {test} WHERE name = :name', array(':name' => $name))->fetchField();
  2635. $this->assertIdentical($name, $from_database, "The database handles UTF-8 characters cleanly.");
  2636. }
  2637. /**
  2638. * Test the db_table_exists() function.
  2639. */
  2640. function testDBTableExists() {
  2641. $this->assertIdentical(TRUE, db_table_exists('node'), 'Returns true for existent table.');
  2642. $this->assertIdentical(FALSE, db_table_exists('nosuchtable'), 'Returns false for nonexistent table.');
  2643. }
  2644. /**
  2645. * Test the db_field_exists() function.
  2646. */
  2647. function testDBFieldExists() {
  2648. $this->assertIdentical(TRUE, db_field_exists('node', 'nid'), 'Returns true for existent column.');
  2649. $this->assertIdentical(FALSE, db_field_exists('node', 'nosuchcolumn'), 'Returns false for nonexistent column.');
  2650. }
  2651. /**
  2652. * Test the db_index_exists() function.
  2653. */
  2654. function testDBIndexExists() {
  2655. $this->assertIdentical(TRUE, db_index_exists('node', 'node_created'), 'Returns true for existent index.');
  2656. $this->assertIdentical(FALSE, db_index_exists('node', 'nosuchindex'), 'Returns false for nonexistent index.');
  2657. }
  2658. }
  2659. /**
  2660. * Query logging tests.
  2661. */
  2662. class DatabaseLoggingTestCase extends DatabaseTestCase {
  2663. public static function getInfo() {
  2664. return array(
  2665. 'name' => 'Query logging',
  2666. 'description' => 'Test the query logging facility.',
  2667. 'group' => 'Database',
  2668. );
  2669. }
  2670. /**
  2671. * Test that we can log the existence of a query.
  2672. */
  2673. function testEnableLogging() {
  2674. $log = Database::startLog('testing');
  2675. db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
  2676. db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchCol();
  2677. // Trigger a call that does not have file in the backtrace.
  2678. call_user_func_array('db_query', array('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo')))->fetchCol();
  2679. $queries = Database::getLog('testing', 'default');
  2680. $this->assertEqual(count($queries), 3, 'Correct number of queries recorded.');
  2681. foreach ($queries as $query) {
  2682. $this->assertEqual($query['caller']['function'], __FUNCTION__, 'Correct function in query log.');
  2683. }
  2684. }
  2685. /**
  2686. * Test that we can run two logs in parallel.
  2687. */
  2688. function testEnableMultiLogging() {
  2689. Database::startLog('testing1');
  2690. db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
  2691. Database::startLog('testing2');
  2692. db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'))->fetchCol();
  2693. $queries1 = Database::getLog('testing1');
  2694. $queries2 = Database::getLog('testing2');
  2695. $this->assertEqual(count($queries1), 2, 'Correct number of queries recorded for log 1.');
  2696. $this->assertEqual(count($queries2), 1, 'Correct number of queries recorded for log 2.');
  2697. }
  2698. /**
  2699. * Test that we can log queries against multiple targets on the same connection.
  2700. */
  2701. function testEnableTargetLogging() {
  2702. // Clone the master credentials to a slave connection and to another fake
  2703. // connection.
  2704. $connection_info = Database::getConnectionInfo('default');
  2705. Database::addConnectionInfo('default', 'slave', $connection_info['default']);
  2706. Database::startLog('testing1');
  2707. db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
  2708. db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'slave'));//->fetchCol();
  2709. $queries1 = Database::getLog('testing1');
  2710. $this->assertEqual(count($queries1), 2, 'Recorded queries from all targets.');
  2711. $this->assertEqual($queries1[0]['target'], 'default', 'First query used default target.');
  2712. $this->assertEqual($queries1[1]['target'], 'slave', 'Second query used slave target.');
  2713. }
  2714. /**
  2715. * Test that logs to separate targets collapse to the same connection properly.
  2716. *
  2717. * This test is identical to the one above, except that it doesn't create
  2718. * a fake target so the query should fall back to running on the default
  2719. * target.
  2720. */
  2721. function testEnableTargetLoggingNoTarget() {
  2722. Database::startLog('testing1');
  2723. db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
  2724. // We use "fake" here as a target because any non-existent target will do.
  2725. // However, because all of the tests in this class share a single page
  2726. // request there is likely to be a target of "slave" from one of the other
  2727. // unit tests, so we use a target here that we know with absolute certainty
  2728. // does not exist.
  2729. db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'fake'))->fetchCol();
  2730. $queries1 = Database::getLog('testing1');
  2731. $this->assertEqual(count($queries1), 2, 'Recorded queries from all targets.');
  2732. $this->assertEqual($queries1[0]['target'], 'default', 'First query used default target.');
  2733. $this->assertEqual($queries1[1]['target'], 'default', 'Second query used default target as fallback.');
  2734. }
  2735. /**
  2736. * Test that we can log queries separately on different connections.
  2737. */
  2738. function testEnableMultiConnectionLogging() {
  2739. // Clone the master credentials to a fake connection.
  2740. // That both connections point to the same physical database is irrelevant.
  2741. $connection_info = Database::getConnectionInfo('default');
  2742. Database::addConnectionInfo('test2', 'default', $connection_info['default']);
  2743. Database::startLog('testing1');
  2744. Database::startLog('testing1', 'test2');
  2745. db_query('SELECT name FROM {test} WHERE age > :age', array(':age' => 25))->fetchCol();
  2746. $old_key = db_set_active('test2');
  2747. db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'Ringo'), array('target' => 'slave'))->fetchCol();
  2748. db_set_active($old_key);
  2749. $queries1 = Database::getLog('testing1');
  2750. $queries2 = Database::getLog('testing1', 'test2');
  2751. $this->assertEqual(count($queries1), 1, 'Correct number of queries recorded for first connection.');
  2752. $this->assertEqual(count($queries2), 1, 'Correct number of queries recorded for second connection.');
  2753. }
  2754. }
  2755. /**
  2756. * Query serialization tests.
  2757. */
  2758. class DatabaseSerializeQueryTestCase extends DatabaseTestCase {
  2759. public static function getInfo() {
  2760. return array(
  2761. 'name' => 'Serialize query',
  2762. 'description' => 'Test serializing and unserializing a query.',
  2763. 'group' => 'Database',
  2764. );
  2765. }
  2766. /**
  2767. * Confirm that a query can be serialized and unserialized.
  2768. */
  2769. function testSerializeQuery() {
  2770. $query = db_select('test');
  2771. $query->addField('test', 'age');
  2772. $query->condition('name', 'Ringo');
  2773. // If this doesn't work, it will throw an exception, so no need for an
  2774. // assertion.
  2775. $query = unserialize(serialize($query));
  2776. $results = $query->execute()->fetchCol();
  2777. $this->assertEqual($results[0], 28, 'Query properly executed after unserialization.');
  2778. }
  2779. }
  2780. /**
  2781. * Range query tests.
  2782. */
  2783. class DatabaseRangeQueryTestCase extends DrupalWebTestCase {
  2784. public static function getInfo() {
  2785. return array(
  2786. 'name' => 'Range query test',
  2787. 'description' => 'Test the Range query functionality.',
  2788. 'group' => 'Database',
  2789. );
  2790. }
  2791. function setUp() {
  2792. parent::setUp('database_test');
  2793. }
  2794. /**
  2795. * Confirm that range query work and return correct result.
  2796. */
  2797. function testRangeQuery() {
  2798. // Test if return correct number of rows.
  2799. $range_rows = db_query_range("SELECT name FROM {system} ORDER BY name", 2, 3)->fetchAll();
  2800. $this->assertEqual(count($range_rows), 3, 'Range query work and return correct number of rows.');
  2801. // Test if return target data.
  2802. $raw_rows = db_query('SELECT name FROM {system} ORDER BY name')->fetchAll();
  2803. $raw_rows = array_slice($raw_rows, 2, 3);
  2804. $this->assertEqual($range_rows, $raw_rows, 'Range query work and return target data.');
  2805. }
  2806. }
  2807. /**
  2808. * Temporary query tests.
  2809. */
  2810. class DatabaseTemporaryQueryTestCase extends DrupalWebTestCase {
  2811. public static function getInfo() {
  2812. return array(
  2813. 'name' => 'Temporary query test',
  2814. 'description' => 'Test the temporary query functionality.',
  2815. 'group' => 'Database',
  2816. );
  2817. }
  2818. function setUp() {
  2819. parent::setUp('database_test');
  2820. }
  2821. /**
  2822. * Return the number of rows of a table.
  2823. */
  2824. function countTableRows($table_name) {
  2825. return db_select($table_name)->countQuery()->execute()->fetchField();
  2826. }
  2827. /**
  2828. * Confirm that temporary tables work and are limited to one request.
  2829. */
  2830. function testTemporaryQuery() {
  2831. $this->drupalGet('database_test/db_query_temporary');
  2832. $data = json_decode($this->drupalGetContent());
  2833. if ($data) {
  2834. $this->assertEqual($this->countTableRows("system"), $data->row_count, 'The temporary table contains the correct amount of rows.');
  2835. $this->assertFalse(db_table_exists($data->table_name), 'The temporary table is, indeed, temporary.');
  2836. }
  2837. else {
  2838. $this->fail("The creation of the temporary table failed.");
  2839. }
  2840. // Now try to run two db_query_temporary() in the same request.
  2841. $table_name_system = db_query_temporary('SELECT status FROM {system}', array());
  2842. $table_name_users = db_query_temporary('SELECT uid FROM {users}', array());
  2843. $this->assertEqual($this->countTableRows($table_name_system), $this->countTableRows("system"), 'A temporary table was created successfully in this request.');
  2844. $this->assertEqual($this->countTableRows($table_name_users), $this->countTableRows("users"), 'A second temporary table was created successfully in this request.');
  2845. // Check that leading whitespace and comments do not cause problems
  2846. // in the modified query.
  2847. $sql = "
  2848. -- Let's select some rows into a temporary table
  2849. SELECT name FROM {test}
  2850. ";
  2851. $table_name_test = db_query_temporary($sql, array());
  2852. $this->assertEqual($this->countTableRows($table_name_test), $this->countTableRows('test'), 'Leading white space and comments do not interfere with temporary table creation.');
  2853. }
  2854. }
  2855. /**
  2856. * Test how the current database driver interprets the SQL syntax.
  2857. *
  2858. * In order to ensure consistent SQL handling throughout Drupal
  2859. * across multiple kinds of database systems, we test that the
  2860. * database system interprets SQL syntax in an expected fashion.
  2861. */
  2862. class DatabaseBasicSyntaxTestCase extends DatabaseTestCase {
  2863. public static function getInfo() {
  2864. return array(
  2865. 'name' => 'Basic SQL syntax tests',
  2866. 'description' => 'Test SQL syntax interpretation.',
  2867. 'group' => 'Database',
  2868. );
  2869. }
  2870. function setUp() {
  2871. parent::setUp('database_test');
  2872. }
  2873. /**
  2874. * Test for string concatenation.
  2875. */
  2876. function testBasicConcat() {
  2877. $result = db_query('SELECT CONCAT(:a1, CONCAT(:a2, CONCAT(:a3, CONCAT(:a4, :a5))))', array(
  2878. ':a1' => 'This',
  2879. ':a2' => ' ',
  2880. ':a3' => 'is',
  2881. ':a4' => ' a ',
  2882. ':a5' => 'test.',
  2883. ));
  2884. $this->assertIdentical($result->fetchField(), 'This is a test.', 'Basic CONCAT works.');
  2885. }
  2886. /**
  2887. * Test for string concatenation with field values.
  2888. */
  2889. function testFieldConcat() {
  2890. $result = db_query('SELECT CONCAT(:a1, CONCAT(name, CONCAT(:a2, CONCAT(age, :a3)))) FROM {test} WHERE age = :age', array(
  2891. ':a1' => 'The age of ',
  2892. ':a2' => ' is ',
  2893. ':a3' => '.',
  2894. ':age' => 25,
  2895. ));
  2896. $this->assertIdentical($result->fetchField(), 'The age of John is 25.', 'Field CONCAT works.');
  2897. }
  2898. /**
  2899. * Test escaping of LIKE wildcards.
  2900. */
  2901. function testLikeEscape() {
  2902. db_insert('test')
  2903. ->fields(array(
  2904. 'name' => 'Ring_',
  2905. ))
  2906. ->execute();
  2907. // Match both "Ringo" and "Ring_".
  2908. $num_matches = db_select('test', 't')
  2909. ->condition('name', 'Ring_', 'LIKE')
  2910. ->countQuery()
  2911. ->execute()
  2912. ->fetchField();
  2913. $this->assertIdentical($num_matches, '2', 'Found 2 records.');
  2914. // Match only "Ring_" using a LIKE expression with no wildcards.
  2915. $num_matches = db_select('test', 't')
  2916. ->condition('name', db_like('Ring_'), 'LIKE')
  2917. ->countQuery()
  2918. ->execute()
  2919. ->fetchField();
  2920. $this->assertIdentical($num_matches, '1', 'Found 1 record.');
  2921. }
  2922. /**
  2923. * Test LIKE query containing a backslash.
  2924. */
  2925. function testLikeBackslash() {
  2926. db_insert('test')
  2927. ->fields(array('name'))
  2928. ->values(array(
  2929. 'name' => 'abcde\f',
  2930. ))
  2931. ->values(array(
  2932. 'name' => 'abc%\_',
  2933. ))
  2934. ->execute();
  2935. // Match both rows using a LIKE expression with two wildcards and a verbatim
  2936. // backslash.
  2937. $num_matches = db_select('test', 't')
  2938. ->condition('name', 'abc%\\\\_', 'LIKE')
  2939. ->countQuery()
  2940. ->execute()
  2941. ->fetchField();
  2942. $this->assertIdentical($num_matches, '2', 'Found 2 records.');
  2943. // Match only the former using a LIKE expression with no wildcards.
  2944. $num_matches = db_select('test', 't')
  2945. ->condition('name', db_like('abc%\_'), 'LIKE')
  2946. ->countQuery()
  2947. ->execute()
  2948. ->fetchField();
  2949. $this->assertIdentical($num_matches, '1', 'Found 1 record.');
  2950. }
  2951. }
  2952. /**
  2953. * Test case sensitivity handling.
  2954. */
  2955. class DatabaseCaseSensitivityTestCase extends DatabaseTestCase {
  2956. public static function getInfo() {
  2957. return array(
  2958. 'name' => 'Case sensitivity',
  2959. 'description' => 'Test handling case sensitive collation.',
  2960. 'group' => 'Database',
  2961. );
  2962. }
  2963. /**
  2964. * Test BINARY collation in MySQL.
  2965. */
  2966. function testCaseSensitiveInsert() {
  2967. $num_records_before = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
  2968. $john = db_insert('test')
  2969. ->fields(array(
  2970. 'name' => 'john', // <- A record already exists with name 'John'.
  2971. 'age' => 2,
  2972. 'job' => 'Baby',
  2973. ))
  2974. ->execute();
  2975. $num_records_after = db_query('SELECT COUNT(*) FROM {test}')->fetchField();
  2976. $this->assertIdentical($num_records_before + 1, (int) $num_records_after, 'Record inserts correctly.');
  2977. $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'john'))->fetchField();
  2978. $this->assertIdentical($saved_age, '2', 'Can retrieve after inserting.');
  2979. }
  2980. }
  2981. /**
  2982. * Test invalid data handling.
  2983. */
  2984. class DatabaseInvalidDataTestCase extends DatabaseTestCase {
  2985. public static function getInfo() {
  2986. return array(
  2987. 'name' => 'Invalid data',
  2988. 'description' => 'Test handling of some invalid data.',
  2989. 'group' => 'Database',
  2990. );
  2991. }
  2992. function setUp() {
  2993. parent::setUp('database_test');
  2994. }
  2995. /**
  2996. * Traditional SQL database systems abort inserts when invalid data is encountered.
  2997. */
  2998. function testInsertDuplicateData() {
  2999. // Try to insert multiple records where at least one has bad data.
  3000. try {
  3001. db_insert('test')
  3002. ->fields(array('name', 'age', 'job'))
  3003. ->values(array(
  3004. 'name' => 'Elvis',
  3005. 'age' => 63,
  3006. 'job' => 'Singer',
  3007. ))->values(array(
  3008. 'name' => 'John', // <-- Duplicate value on unique field.
  3009. 'age' => 17,
  3010. 'job' => 'Consultant',
  3011. ))
  3012. ->values(array(
  3013. 'name' => 'Frank',
  3014. 'age' => 75,
  3015. 'job' => 'Singer',
  3016. ))
  3017. ->execute();
  3018. $this->fail('Insert succeedded when it should not have.');
  3019. }
  3020. catch (Exception $e) {
  3021. // Check if the first record was inserted.
  3022. $name = db_query('SELECT name FROM {test} WHERE age = :age', array(':age' => 63))->fetchField();
  3023. if ($name == 'Elvis') {
  3024. if (!Database::getConnection()->supportsTransactions()) {
  3025. // This is an expected fail.
  3026. // Database engines that don't support transactions can leave partial
  3027. // inserts in place when an error occurs. This is the case for MySQL
  3028. // when running on a MyISAM table.
  3029. $this->pass("The whole transaction has not been rolled-back when a duplicate key insert occurs, this is expected because the database doesn't support transactions");
  3030. }
  3031. else {
  3032. $this->fail('The whole transaction is rolled back when a duplicate key insert occurs.');
  3033. }
  3034. }
  3035. else {
  3036. $this->pass('The whole transaction is rolled back when a duplicate key insert occurs.');
  3037. }
  3038. // Ensure the other values were not inserted.
  3039. $record = db_select('test')
  3040. ->fields('test', array('name', 'age'))
  3041. ->condition('age', array(17, 75), 'IN')
  3042. ->execute()->fetchObject();
  3043. $this->assertFalse($record, 'The rest of the insert aborted as expected.');
  3044. }
  3045. }
  3046. }
  3047. /**
  3048. * Drupal-specific SQL syntax tests.
  3049. */
  3050. class DatabaseQueryTestCase extends DatabaseTestCase {
  3051. public static function getInfo() {
  3052. return array(
  3053. 'name' => 'Custom query syntax tests',
  3054. 'description' => 'Test Drupal\'s extended prepared statement syntax..',
  3055. 'group' => 'Database',
  3056. );
  3057. }
  3058. function setUp() {
  3059. parent::setUp('database_test');
  3060. }
  3061. /**
  3062. * Test that we can specify an array of values in the query by simply passing in an array.
  3063. */
  3064. function testArraySubstitution() {
  3065. $names = db_query('SELECT name FROM {test} WHERE age IN (:ages) ORDER BY age', array(':ages' => array(25, 26, 27)))->fetchAll();
  3066. $this->assertEqual(count($names), 3, 'Correct number of names returned');
  3067. }
  3068. /**
  3069. * Test SQL injection via database query array arguments.
  3070. */
  3071. public function testArrayArgumentsSQLInjection() {
  3072. // Attempt SQL injection and verify that it does not work.
  3073. $condition = array(
  3074. "1 ;INSERT INTO {test} (name) VALUES ('test12345678'); -- " => '',
  3075. '1' => '',
  3076. );
  3077. try {
  3078. db_query("SELECT * FROM {test} WHERE name = :name", array(':name' => $condition))->fetchObject();
  3079. $this->fail('SQL injection attempt via array arguments should result in a PDOException.');
  3080. }
  3081. catch (PDOException $e) {
  3082. $this->pass('SQL injection attempt via array arguments should result in a PDOException.');
  3083. }
  3084. // Test that the insert query that was used in the SQL injection attempt did
  3085. // not result in a row being inserted in the database.
  3086. $result = db_select('test')
  3087. ->condition('name', 'test12345678')
  3088. ->countQuery()
  3089. ->execute()
  3090. ->fetchField();
  3091. $this->assertFalse($result, 'SQL injection attempt did not result in a row being inserted in the database table.');
  3092. }
  3093. /**
  3094. * Tests SQL injection via condition operator.
  3095. */
  3096. public function testConditionOperatorArgumentsSQLInjection() {
  3097. $injection = "IS NOT NULL); INSERT INTO {test} (name) VALUES ('test12345678'); -- ";
  3098. try {
  3099. $result = db_select('test', 't')
  3100. ->fields('t')
  3101. ->condition('name', 1, $injection)
  3102. ->execute();
  3103. $this->fail('Should not be able to attempt SQL injection via condition operator.');
  3104. }
  3105. catch (InvalidQueryConditionOperatorException $e) {
  3106. $this->pass('SQL injection attempt via condition arguments should result in a database exception.');
  3107. }
  3108. // Test that the insert query that was used in the SQL injection attempt did
  3109. // not result in a row being inserted in the database.
  3110. $result = db_select('test')
  3111. ->condition('name', 'test12345678')
  3112. ->countQuery()
  3113. ->execute()
  3114. ->fetchField();
  3115. $this->assertFalse($result, 'SQL injection attempt did not result in a row being inserted in the database table.');
  3116. // Attempt SQLi via union query with no unsafe characters.
  3117. db_insert('test')
  3118. ->fields(array('name' => '123456'))
  3119. ->execute();
  3120. $injection = "= 1 UNION ALL SELECT password FROM user WHERE uid =";
  3121. try {
  3122. $result = db_select('test', 't')
  3123. ->fields('t', array('name', 'name'))
  3124. ->condition('name', 1, $injection)
  3125. ->execute();
  3126. $this->fail('Should not be able to attempt SQL injection via operator.');
  3127. }
  3128. catch (InvalidQueryConditionOperatorException $e) {
  3129. $this->pass('SQL injection attempt via condition arguments should result in a database exception.');
  3130. }
  3131. // Attempt SQLi via union query - uppercase tablename.
  3132. db_insert('TEST_UPPERCASE')
  3133. ->fields(array('name' => 'secrets'))
  3134. ->execute();
  3135. $injection = "IS NOT NULL) UNION ALL SELECT name FROM {TEST_UPPERCASE} -- ";
  3136. try {
  3137. $result = db_select('test', 't')
  3138. ->fields('t', array('name'))
  3139. ->condition('name', 1, $injection)
  3140. ->execute();
  3141. $this->fail('Should not be able to attempt SQL injection via operator.');
  3142. }
  3143. catch (InvalidQueryConditionOperatorException $e) {
  3144. $this->pass('SQL injection attempt via condition arguments should result in a database exception.');
  3145. }
  3146. }
  3147. }
  3148. /**
  3149. * Test transaction support, particularly nesting.
  3150. *
  3151. * We test nesting by having two transaction layers, an outer and inner. The
  3152. * outer layer encapsulates the inner layer. Our transaction nesting abstraction
  3153. * should allow the outer layer function to call any function it wants,
  3154. * especially the inner layer that starts its own transaction, and be
  3155. * confident that, when the function it calls returns, its own transaction
  3156. * is still "alive."
  3157. *
  3158. * Call structure:
  3159. * transactionOuterLayer()
  3160. * Start transaction
  3161. * transactionInnerLayer()
  3162. * Start transaction (does nothing in database)
  3163. * [Maybe decide to roll back]
  3164. * Do more stuff
  3165. * Should still be in transaction A
  3166. *
  3167. */
  3168. class DatabaseTransactionTestCase extends DatabaseTestCase {
  3169. public static function getInfo() {
  3170. return array(
  3171. 'name' => 'Transaction tests',
  3172. 'description' => 'Test the transaction abstraction system.',
  3173. 'group' => 'Database',
  3174. );
  3175. }
  3176. /**
  3177. * Helper method for transaction unit test.
  3178. *
  3179. * This "outer layer" transaction starts and then encapsulates the
  3180. * "inner layer" transaction. This nesting is used to evaluate whether the
  3181. * database transaction API properly supports nesting. By "properly supports,"
  3182. * we mean the outer transaction continues to exist regardless of what
  3183. * functions are called and whether those functions start their own
  3184. * transactions.
  3185. *
  3186. * In contrast, a typical database would commit the outer transaction, start
  3187. * a new transaction for the inner layer, commit the inner layer transaction,
  3188. * and then be confused when the outer layer transaction tries to commit its
  3189. * transaction (which was already committed when the inner transaction
  3190. * started).
  3191. *
  3192. * @param $suffix
  3193. * Suffix to add to field values to differentiate tests.
  3194. * @param $rollback
  3195. * Whether or not to try rolling back the transaction when we're done.
  3196. * @param $ddl_statement
  3197. * Whether to execute a DDL statement during the inner transaction.
  3198. */
  3199. protected function transactionOuterLayer($suffix, $rollback = FALSE, $ddl_statement = FALSE) {
  3200. $connection = Database::getConnection();
  3201. $depth = $connection->transactionDepth();
  3202. $txn = db_transaction();
  3203. // Insert a single row into the testing table.
  3204. db_insert('test')
  3205. ->fields(array(
  3206. 'name' => 'David' . $suffix,
  3207. 'age' => '24',
  3208. ))
  3209. ->execute();
  3210. $this->assertTrue($connection->inTransaction(), 'In transaction before calling nested transaction.');
  3211. // We're already in a transaction, but we call ->transactionInnerLayer
  3212. // to nest another transaction inside the current one.
  3213. $this->transactionInnerLayer($suffix, $rollback, $ddl_statement);
  3214. $this->assertTrue($connection->inTransaction(), 'In transaction after calling nested transaction.');
  3215. if ($rollback) {
  3216. // Roll back the transaction, if requested.
  3217. // This rollback should propagate to the last savepoint.
  3218. $txn->rollback();
  3219. $this->assertTrue(($connection->transactionDepth() == $depth), 'Transaction has rolled back to the last savepoint after calling rollback().');
  3220. }
  3221. }
  3222. /**
  3223. * Helper method for transaction unit tests. This "inner layer" transaction
  3224. * is either used alone or nested inside of the "outer layer" transaction.
  3225. *
  3226. * @param $suffix
  3227. * Suffix to add to field values to differentiate tests.
  3228. * @param $rollback
  3229. * Whether or not to try rolling back the transaction when we're done.
  3230. * @param $ddl_statement
  3231. * Whether to execute a DDL statement during the transaction.
  3232. */
  3233. protected function transactionInnerLayer($suffix, $rollback = FALSE, $ddl_statement = FALSE) {
  3234. $connection = Database::getConnection();
  3235. $depth = $connection->transactionDepth();
  3236. // Start a transaction. If we're being called from ->transactionOuterLayer,
  3237. // then we're already in a transaction. Normally, that would make starting
  3238. // a transaction here dangerous, but the database API handles this problem
  3239. // for us by tracking the nesting and avoiding the danger.
  3240. $txn = db_transaction();
  3241. $depth2 = $connection->transactionDepth();
  3242. $this->assertTrue($depth < $depth2, 'Transaction depth is has increased with new transaction.');
  3243. // Insert a single row into the testing table.
  3244. db_insert('test')
  3245. ->fields(array(
  3246. 'name' => 'Daniel' . $suffix,
  3247. 'age' => '19',
  3248. ))
  3249. ->execute();
  3250. $this->assertTrue($connection->inTransaction(), 'In transaction inside nested transaction.');
  3251. if ($ddl_statement) {
  3252. $table = array(
  3253. 'fields' => array(
  3254. 'id' => array(
  3255. 'type' => 'serial',
  3256. 'unsigned' => TRUE,
  3257. 'not null' => TRUE,
  3258. ),
  3259. ),
  3260. 'primary key' => array('id'),
  3261. );
  3262. db_create_table('database_test_1', $table);
  3263. $this->assertTrue($connection->inTransaction(), 'In transaction inside nested transaction.');
  3264. }
  3265. if ($rollback) {
  3266. // Roll back the transaction, if requested.
  3267. // This rollback should propagate to the last savepoint.
  3268. $txn->rollback();
  3269. $this->assertTrue(($connection->transactionDepth() == $depth), 'Transaction has rolled back to the last savepoint after calling rollback().');
  3270. }
  3271. }
  3272. /**
  3273. * Test transaction rollback on a database that supports transactions.
  3274. *
  3275. * If the active connection does not support transactions, this test does nothing.
  3276. */
  3277. function testTransactionRollBackSupported() {
  3278. // This test won't work right if transactions are not supported.
  3279. if (!Database::getConnection()->supportsTransactions()) {
  3280. return;
  3281. }
  3282. try {
  3283. // Create two nested transactions. Roll back from the inner one.
  3284. $this->transactionOuterLayer('B', TRUE);
  3285. // Neither of the rows we inserted in the two transaction layers
  3286. // should be present in the tables post-rollback.
  3287. $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidB'))->fetchField();
  3288. $this->assertNotIdentical($saved_age, '24', 'Cannot retrieve DavidB row after commit.');
  3289. $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielB'))->fetchField();
  3290. $this->assertNotIdentical($saved_age, '19', 'Cannot retrieve DanielB row after commit.');
  3291. }
  3292. catch (Exception $e) {
  3293. $this->fail($e->getMessage());
  3294. }
  3295. }
  3296. /**
  3297. * Test transaction rollback on a database that does not support transactions.
  3298. *
  3299. * If the active driver supports transactions, this test does nothing.
  3300. */
  3301. function testTransactionRollBackNotSupported() {
  3302. // This test won't work right if transactions are supported.
  3303. if (Database::getConnection()->supportsTransactions()) {
  3304. return;
  3305. }
  3306. try {
  3307. // Create two nested transactions. Attempt to roll back from the inner one.
  3308. $this->transactionOuterLayer('B', TRUE);
  3309. // Because our current database claims to not support transactions,
  3310. // the inserted rows should be present despite the attempt to roll back.
  3311. $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidB'))->fetchField();
  3312. $this->assertIdentical($saved_age, '24', 'DavidB not rolled back, since transactions are not supported.');
  3313. $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielB'))->fetchField();
  3314. $this->assertIdentical($saved_age, '19', 'DanielB not rolled back, since transactions are not supported.');
  3315. }
  3316. catch (Exception $e) {
  3317. $this->fail($e->getMessage());
  3318. }
  3319. }
  3320. /**
  3321. * Test committed transaction.
  3322. *
  3323. * The behavior of this test should be identical for connections that support
  3324. * transactions and those that do not.
  3325. */
  3326. function testCommittedTransaction() {
  3327. try {
  3328. // Create two nested transactions. The changes should be committed.
  3329. $this->transactionOuterLayer('A');
  3330. // Because we committed, both of the inserted rows should be present.
  3331. $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DavidA'))->fetchField();
  3332. $this->assertIdentical($saved_age, '24', 'Can retrieve DavidA row after commit.');
  3333. $saved_age = db_query('SELECT age FROM {test} WHERE name = :name', array(':name' => 'DanielA'))->fetchField();
  3334. $this->assertIdentical($saved_age, '19', 'Can retrieve DanielA row after commit.');
  3335. }
  3336. catch (Exception $e) {
  3337. $this->fail($e->getMessage());
  3338. }
  3339. }
  3340. /**
  3341. * Test the compatibility of transactions with DDL statements.
  3342. */
  3343. function testTransactionWithDdlStatement() {
  3344. // First, test that a commit works normally, even with DDL statements.
  3345. $transaction = db_transaction();
  3346. $this->insertRow('row');
  3347. $this->executeDDLStatement();
  3348. unset($transaction);
  3349. $this->assertRowPresent('row');
  3350. // Even in different order.
  3351. $this->cleanUp();
  3352. $transaction = db_transaction();
  3353. $this->executeDDLStatement();
  3354. $this->insertRow('row');
  3355. unset($transaction);
  3356. $this->assertRowPresent('row');
  3357. // Even with stacking.
  3358. $this->cleanUp();
  3359. $transaction = db_transaction();
  3360. $transaction2 = db_transaction();
  3361. $this->executeDDLStatement();
  3362. unset($transaction2);
  3363. $transaction3 = db_transaction();
  3364. $this->insertRow('row');
  3365. unset($transaction3);
  3366. unset($transaction);
  3367. $this->assertRowPresent('row');
  3368. // A transaction after a DDL statement should still work the same.
  3369. $this->cleanUp();
  3370. $transaction = db_transaction();
  3371. $transaction2 = db_transaction();
  3372. $this->executeDDLStatement();
  3373. unset($transaction2);
  3374. $transaction3 = db_transaction();
  3375. $this->insertRow('row');
  3376. $transaction3->rollback();
  3377. unset($transaction3);
  3378. unset($transaction);
  3379. $this->assertRowAbsent('row');
  3380. // The behavior of a rollback depends on the type of database server.
  3381. if (Database::getConnection()->supportsTransactionalDDL()) {
  3382. // For database servers that support transactional DDL, a rollback
  3383. // of a transaction including DDL statements should be possible.
  3384. $this->cleanUp();
  3385. $transaction = db_transaction();
  3386. $this->insertRow('row');
  3387. $this->executeDDLStatement();
  3388. $transaction->rollback();
  3389. unset($transaction);
  3390. $this->assertRowAbsent('row');
  3391. // Including with stacking.
  3392. $this->cleanUp();
  3393. $transaction = db_transaction();
  3394. $transaction2 = db_transaction();
  3395. $this->executeDDLStatement();
  3396. unset($transaction2);
  3397. $transaction3 = db_transaction();
  3398. $this->insertRow('row');
  3399. unset($transaction3);
  3400. $transaction->rollback();
  3401. unset($transaction);
  3402. $this->assertRowAbsent('row');
  3403. }
  3404. else {
  3405. // For database servers that do not support transactional DDL,
  3406. // the DDL statement should commit the transaction stack.
  3407. $this->cleanUp();
  3408. $transaction = db_transaction();
  3409. $this->insertRow('row');
  3410. $this->executeDDLStatement();
  3411. set_error_handler(array($this, 'rollBackWithoutTransactionErrorHandler'));
  3412. try {
  3413. // Rollback the outer transaction.
  3414. $transaction->rollback();
  3415. // @see \DatabaseConnection_mysql::rollback()
  3416. if (PHP_VERSION_ID >= 80000) {
  3417. $this->fail('Rolling back a transaction containing DDL should produce a warning.');
  3418. }
  3419. }
  3420. catch (Exception $e) {
  3421. $this->assertEqual('Rollback attempted when there is no active transaction.', $e->getMessage());
  3422. }
  3423. restore_error_handler();
  3424. unset($transaction);
  3425. $this->assertRowPresent('row');
  3426. }
  3427. }
  3428. /**
  3429. * Special handling of "rollback without transaction" errors.
  3430. */
  3431. public function rollBackWithoutTransactionErrorHandler($error_level, $message, $filename, $line) {
  3432. // Throw an exception if this is a "rollback without transaction" error.
  3433. if (strpos($message, 'Rollback attempted when there is no active transaction.') !== FALSE ) {
  3434. throw new Exception('Rollback attempted when there is no active transaction.');
  3435. }
  3436. _drupal_error_handler($error_level, $message, $filename, $line);
  3437. }
  3438. /**
  3439. * Insert a single row into the testing table.
  3440. */
  3441. protected function insertRow($name) {
  3442. db_insert('test')
  3443. ->fields(array(
  3444. 'name' => $name,
  3445. ))
  3446. ->execute();
  3447. }
  3448. /**
  3449. * Execute a DDL statement.
  3450. */
  3451. protected function executeDDLStatement() {
  3452. static $count = 0;
  3453. $table = array(
  3454. 'fields' => array(
  3455. 'id' => array(
  3456. 'type' => 'serial',
  3457. 'unsigned' => TRUE,
  3458. 'not null' => TRUE,
  3459. ),
  3460. ),
  3461. 'primary key' => array('id'),
  3462. );
  3463. db_create_table('database_test_' . ++$count, $table);
  3464. }
  3465. /**
  3466. * Start over for a new test.
  3467. */
  3468. protected function cleanUp() {
  3469. db_truncate('test')
  3470. ->execute();
  3471. }
  3472. /**
  3473. * Assert that a given row is present in the test table.
  3474. *
  3475. * @param $name
  3476. * The name of the row.
  3477. * @param $message
  3478. * The message to log for the assertion.
  3479. */
  3480. function assertRowPresent($name, $message = NULL) {
  3481. if (!isset($message)) {
  3482. $message = format_string('Row %name is present.', array('%name' => $name));
  3483. }
  3484. $present = (boolean) db_query('SELECT 1 FROM {test} WHERE name = :name', array(':name' => $name))->fetchField();
  3485. return $this->assertTrue($present, $message);
  3486. }
  3487. /**
  3488. * Assert that a given row is absent from the test table.
  3489. *
  3490. * @param $name
  3491. * The name of the row.
  3492. * @param $message
  3493. * The message to log for the assertion.
  3494. */
  3495. function assertRowAbsent($name, $message = NULL) {
  3496. if (!isset($message)) {
  3497. $message = format_string('Row %name is absent.', array('%name' => $name));
  3498. }
  3499. $present = (boolean) db_query('SELECT 1 FROM {test} WHERE name = :name', array(':name' => $name))->fetchField();
  3500. return $this->assertFalse($present, $message);
  3501. }
  3502. /**
  3503. * Test transaction stacking and commit / rollback.
  3504. */
  3505. function testTransactionStacking() {
  3506. // This test won't work right if transactions are not supported.
  3507. if (!Database::getConnection()->supportsTransactions()) {
  3508. return;
  3509. }
  3510. $database = Database::getConnection();
  3511. // Standard case: pop the inner transaction before the outer transaction.
  3512. $transaction = db_transaction();
  3513. $this->insertRow('outer');
  3514. $transaction2 = db_transaction();
  3515. $this->insertRow('inner');
  3516. // Pop the inner transaction.
  3517. unset($transaction2);
  3518. $this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the inner transaction');
  3519. // Pop the outer transaction.
  3520. unset($transaction);
  3521. $this->assertFalse($database->inTransaction(), 'Transaction closed after popping the outer transaction');
  3522. $this->assertRowPresent('outer');
  3523. $this->assertRowPresent('inner');
  3524. // Pop the transaction in a different order they have been pushed.
  3525. $this->cleanUp();
  3526. $transaction = db_transaction();
  3527. $this->insertRow('outer');
  3528. $transaction2 = db_transaction();
  3529. $this->insertRow('inner');
  3530. // Pop the outer transaction, nothing should happen.
  3531. unset($transaction);
  3532. $this->insertRow('inner-after-outer-commit');
  3533. $this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the outer transaction');
  3534. // Pop the inner transaction, the whole transaction should commit.
  3535. unset($transaction2);
  3536. $this->assertFalse($database->inTransaction(), 'Transaction closed after popping the inner transaction');
  3537. $this->assertRowPresent('outer');
  3538. $this->assertRowPresent('inner');
  3539. $this->assertRowPresent('inner-after-outer-commit');
  3540. // Rollback the inner transaction.
  3541. $this->cleanUp();
  3542. $transaction = db_transaction();
  3543. $this->insertRow('outer');
  3544. $transaction2 = db_transaction();
  3545. $this->insertRow('inner');
  3546. // Now rollback the inner transaction.
  3547. $transaction2->rollback();
  3548. unset($transaction2);
  3549. $this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the outer transaction');
  3550. // Pop the outer transaction, it should commit.
  3551. $this->insertRow('outer-after-inner-rollback');
  3552. unset($transaction);
  3553. $this->assertFalse($database->inTransaction(), 'Transaction closed after popping the inner transaction');
  3554. $this->assertRowPresent('outer');
  3555. $this->assertRowAbsent('inner');
  3556. $this->assertRowPresent('outer-after-inner-rollback');
  3557. // Rollback the inner transaction after committing the outer one.
  3558. $this->cleanUp();
  3559. $transaction = db_transaction();
  3560. $this->insertRow('outer');
  3561. $transaction2 = db_transaction();
  3562. $this->insertRow('inner');
  3563. // Pop the outer transaction, nothing should happen.
  3564. unset($transaction);
  3565. $this->assertTrue($database->inTransaction(), 'Still in a transaction after popping the outer transaction');
  3566. // Now rollback the inner transaction, it should rollback.
  3567. $transaction2->rollback();
  3568. unset($transaction2);
  3569. $this->assertFalse($database->inTransaction(), 'Transaction closed after popping the inner transaction');
  3570. $this->assertRowPresent('outer');
  3571. $this->assertRowAbsent('inner');
  3572. // Rollback the outer transaction while the inner transaction is active.
  3573. // In that case, an exception will be triggered because we cannot
  3574. // ensure that the final result will have any meaning.
  3575. $this->cleanUp();
  3576. $transaction = db_transaction();
  3577. $this->insertRow('outer');
  3578. $transaction2 = db_transaction();
  3579. $this->insertRow('inner');
  3580. $transaction3 = db_transaction();
  3581. $this->insertRow('inner2');
  3582. // Rollback the outer transaction.
  3583. try {
  3584. $transaction->rollback();
  3585. unset($transaction);
  3586. $this->fail('Rolling back the outer transaction while the inner transaction is active resulted in an exception.');
  3587. }
  3588. catch (DatabaseTransactionOutOfOrderException $e) {
  3589. $this->pass('Rolling back the outer transaction while the inner transaction is active resulted in an exception.');
  3590. }
  3591. $this->assertFalse($database->inTransaction(), 'No more in a transaction after rolling back the outer transaction');
  3592. // Try to commit one inner transaction.
  3593. unset($transaction3);
  3594. $this->pass('Trying to commit an inner transaction resulted in an exception.');
  3595. // Try to rollback one inner transaction.
  3596. try {
  3597. $transaction->rollback();
  3598. unset($transaction2);
  3599. $this->fail('Trying to commit an inner transaction resulted in an exception.');
  3600. }
  3601. catch (DatabaseTransactionNoActiveException $e) {
  3602. $this->pass('Trying to commit an inner transaction resulted in an exception.');
  3603. }
  3604. $this->assertRowAbsent('outer');
  3605. $this->assertRowAbsent('inner');
  3606. $this->assertRowAbsent('inner2');
  3607. }
  3608. }
  3609. /**
  3610. * Check the sequences API.
  3611. */
  3612. class DatabaseNextIdCase extends DrupalWebTestCase {
  3613. public static function getInfo() {
  3614. return array(
  3615. 'name' => 'Sequences API',
  3616. 'description' => 'Test the secondary sequences API.',
  3617. 'group' => 'Database',
  3618. );
  3619. }
  3620. /**
  3621. * Test that the sequences API work.
  3622. */
  3623. function testDbNextId() {
  3624. $first = db_next_id();
  3625. $second = db_next_id();
  3626. // We can test for exact increase in here because we know there is no
  3627. // other process operating on these tables -- normally we could only
  3628. // expect $second > $first.
  3629. $this->assertEqual($first + 1, $second, 'The second call from a sequence provides a number increased by one.');
  3630. $result = db_next_id(1000);
  3631. $this->assertEqual($result, 1001, 'Sequence provides a larger number than the existing ID.');
  3632. }
  3633. }
  3634. /**
  3635. * Tests the empty pseudo-statement class.
  3636. */
  3637. class DatabaseEmptyStatementTestCase extends DrupalWebTestCase {
  3638. public static function getInfo() {
  3639. return array(
  3640. 'name' => 'Empty statement',
  3641. 'description' => 'Test the empty pseudo-statement class.',
  3642. 'group' => 'Database',
  3643. );
  3644. }
  3645. /**
  3646. * Test that the empty result set behaves as empty.
  3647. */
  3648. function testEmpty() {
  3649. $result = new DatabaseStatementEmpty();
  3650. $this->assertTrue($result instanceof DatabaseStatementInterface, 'Class implements expected interface');
  3651. $this->assertNull($result->fetchObject(), 'Null result returned.');
  3652. }
  3653. /**
  3654. * Test that the empty result set iterates safely.
  3655. */
  3656. function testEmptyIteration() {
  3657. $result = new DatabaseStatementEmpty();
  3658. foreach ($result as $record) {
  3659. $this->fail('Iterating empty result set should not iterate.');
  3660. return;
  3661. }
  3662. $this->pass('Iterating empty result set skipped iteration.');
  3663. }
  3664. /**
  3665. * Test that the empty result set mass-fetches in an expected way.
  3666. */
  3667. function testEmptyFetchAll() {
  3668. $result = new DatabaseStatementEmpty();
  3669. $this->assertEqual($result->fetchAll(), array(), 'Empty array returned from empty result set.');
  3670. }
  3671. }
  3672. /**
  3673. * Tests management of database connections.
  3674. */
  3675. class ConnectionUnitTest extends DrupalUnitTestCase {
  3676. protected $key;
  3677. protected $target;
  3678. protected $originalTarget;
  3679. protected $monitor;
  3680. protected $originalCount;
  3681. protected $skipTest;
  3682. public static function getInfo() {
  3683. return array(
  3684. 'name' => 'Connection unit tests',
  3685. 'description' => 'Tests management of database connections.',
  3686. 'group' => 'Database',
  3687. );
  3688. }
  3689. function setUp() {
  3690. parent::setUp();
  3691. $this->key = 'default';
  3692. $this->originalTarget = 'default';
  3693. $this->target = 'DatabaseConnectionUnitTest';
  3694. // Determine whether the database driver is MySQL. If it is not, the test
  3695. // methods will not be executed.
  3696. // @todo Make this test driver-agnostic, or find a proper way to skip it.
  3697. // @see http://drupal.org/node/1273478
  3698. $connection_info = Database::getConnectionInfo('default');
  3699. $this->skipTest = (bool) ($connection_info['default']['driver'] != 'mysql');
  3700. if ($this->skipTest) {
  3701. // Insert an assertion to prevent Simpletest from interpreting the test
  3702. // as failure.
  3703. $this->pass('This test is only compatible with MySQL.');
  3704. }
  3705. // Create an additional connection to monitor the connections being opened
  3706. // and closed in this test.
  3707. // @see TestBase::changeDatabasePrefix()
  3708. $connection_info = Database::getConnectionInfo('default');
  3709. Database::addConnectionInfo('default', 'monitor', $connection_info['default']);
  3710. global $databases;
  3711. $databases['default']['monitor'] = $connection_info['default'];
  3712. $this->monitor = Database::getConnection('monitor');
  3713. }
  3714. /**
  3715. * Adds a new database connection info to Database.
  3716. */
  3717. protected function addConnection() {
  3718. // Add a new target to the connection, by cloning the current connection.
  3719. $connection_info = Database::getConnectionInfo($this->key);
  3720. Database::addConnectionInfo($this->key, $this->target, $connection_info[$this->originalTarget]);
  3721. // Verify that the new target exists.
  3722. $info = Database::getConnectionInfo($this->key);
  3723. // Note: Custom assertion message to not expose database credentials.
  3724. $this->assertIdentical($info[$this->target], $connection_info[$this->key], 'New connection info found.');
  3725. }
  3726. /**
  3727. * Returns the connection ID of the current test connection.
  3728. *
  3729. * @return integer
  3730. */
  3731. protected function getConnectionID() {
  3732. return (int) Database::getConnection($this->target, $this->key)->query('SELECT CONNECTION_ID()')->fetchField();
  3733. }
  3734. /**
  3735. * Asserts that a connection ID exists.
  3736. *
  3737. * @param integer $id
  3738. * The connection ID to verify.
  3739. */
  3740. protected function assertConnection($id) {
  3741. $list = $this->monitor->query('SHOW PROCESSLIST')->fetchAllKeyed(0, 0);
  3742. return $this->assertTrue(isset($list[$id]), format_string('Connection ID @id found.', array('@id' => $id)));
  3743. }
  3744. /**
  3745. * Asserts that a connection ID does not exist.
  3746. *
  3747. * @param integer $id
  3748. * The connection ID to verify.
  3749. */
  3750. protected function assertNoConnection($id) {
  3751. $list = $this->monitor->query('SHOW PROCESSLIST')->fetchAllKeyed(0, 0);
  3752. return $this->assertFalse(isset($list[$id]), format_string('Connection ID @id not found.', array('@id' => $id)));
  3753. }
  3754. /**
  3755. * Tests Database::closeConnection() without query.
  3756. *
  3757. * @todo getConnectionID() executes a query.
  3758. */
  3759. function testOpenClose() {
  3760. if ($this->skipTest) {
  3761. return;
  3762. }
  3763. // Add and open a new connection.
  3764. $this->addConnection();
  3765. $id = $this->getConnectionID();
  3766. Database::getConnection($this->target, $this->key);
  3767. // Verify that there is a new connection.
  3768. $this->assertConnection($id);
  3769. // Close the connection.
  3770. Database::closeConnection($this->target, $this->key);
  3771. // Wait 20ms to give the database engine sufficient time to react.
  3772. usleep(20000);
  3773. // Verify that we are back to the original connection count.
  3774. $this->assertNoConnection($id);
  3775. }
  3776. /**
  3777. * Tests Database::closeConnection() with a query.
  3778. */
  3779. function testOpenQueryClose() {
  3780. if ($this->skipTest) {
  3781. return;
  3782. }
  3783. // Add and open a new connection.
  3784. $this->addConnection();
  3785. $id = $this->getConnectionID();
  3786. Database::getConnection($this->target, $this->key);
  3787. // Verify that there is a new connection.
  3788. $this->assertConnection($id);
  3789. // Execute a query.
  3790. Database::getConnection($this->target, $this->key)->query('SHOW TABLES');
  3791. // Close the connection.
  3792. Database::closeConnection($this->target, $this->key);
  3793. // Wait 20ms to give the database engine sufficient time to react.
  3794. usleep(20000);
  3795. // Verify that we are back to the original connection count.
  3796. $this->assertNoConnection($id);
  3797. }
  3798. /**
  3799. * Tests Database::closeConnection() with a query and custom prefetch method.
  3800. */
  3801. function testOpenQueryPrefetchClose() {
  3802. if ($this->skipTest) {
  3803. return;
  3804. }
  3805. // Add and open a new connection.
  3806. $this->addConnection();
  3807. $id = $this->getConnectionID();
  3808. Database::getConnection($this->target, $this->key);
  3809. // Verify that there is a new connection.
  3810. $this->assertConnection($id);
  3811. // Execute a query.
  3812. Database::getConnection($this->target, $this->key)->query('SHOW TABLES')->fetchCol();
  3813. // Close the connection.
  3814. Database::closeConnection($this->target, $this->key);
  3815. // Wait 20ms to give the database engine sufficient time to react.
  3816. usleep(20000);
  3817. // Verify that we are back to the original connection count.
  3818. $this->assertNoConnection($id);
  3819. }
  3820. /**
  3821. * Tests Database::closeConnection() with a select query.
  3822. */
  3823. function testOpenSelectQueryClose() {
  3824. if ($this->skipTest) {
  3825. return;
  3826. }
  3827. // Add and open a new connection.
  3828. $this->addConnection();
  3829. $id = $this->getConnectionID();
  3830. Database::getConnection($this->target, $this->key);
  3831. // Verify that there is a new connection.
  3832. $this->assertConnection($id);
  3833. // Create a table.
  3834. $name = 'foo';
  3835. Database::getConnection($this->target, $this->key)->schema()->createTable($name, array(
  3836. 'fields' => array(
  3837. 'name' => array(
  3838. 'type' => 'varchar',
  3839. 'length' => 255,
  3840. ),
  3841. ),
  3842. ));
  3843. // Execute a query.
  3844. Database::getConnection($this->target, $this->key)->select('foo', 'f')
  3845. ->fields('f', array('name'))
  3846. ->execute()
  3847. ->fetchAll();
  3848. // Drop the table.
  3849. Database::getConnection($this->target, $this->key)->schema()->dropTable($name);
  3850. // Close the connection.
  3851. Database::closeConnection($this->target, $this->key);
  3852. // Wait 20ms to give the database engine sufficient time to react.
  3853. usleep(20000);
  3854. // Verify that we are back to the original connection count.
  3855. $this->assertNoConnection($id);
  3856. }
  3857. }
  3858. /**
  3859. * Test reserved keyword handling (introduced for MySQL 8+)
  3860. */
  3861. class DatabaseReservedKeywordTestCase extends DatabaseTestCase {
  3862. public static function getInfo() {
  3863. return array(
  3864. 'name' => 'Reserved Keywords',
  3865. 'description' => 'Test handling of reserved keywords.',
  3866. 'group' => 'Database',
  3867. );
  3868. }
  3869. function setUp() {
  3870. parent::setUp('database_test');
  3871. }
  3872. public function testTableNameQuoting() {
  3873. // Test db_query with {table} pattern.
  3874. $record = db_query('SELECT * FROM {system} LIMIT 1')->fetchObject();
  3875. $this->assertTrue(isset($record->filename), 'Successfully queried the {system} table.');
  3876. $connection = Database::getConnection()->getConnectionOptions();
  3877. if ($connection['driver'] === 'sqlite') {
  3878. // In SQLite simpletest's prefixed db tables exist in their own schema
  3879. // (e.g. simpletest124904.system), so we cannot test the schema.{table}
  3880. // syntax here as the table name will have the schema name prepended to it
  3881. // when prefixes are processed.
  3882. $this->assert(TRUE, 'Skipping schema.{system} test for SQLite.');
  3883. }
  3884. else {
  3885. $database = $connection['database'];
  3886. if ($connection['driver'] === 'pgsql') {
  3887. // database.scheme.table for PostgreSQL
  3888. // @see https://www.postgresql.org/docs/14/ddl-schemas.html
  3889. $database .= '.public';
  3890. }
  3891. // Test db_query with schema.{table} pattern
  3892. db_query('SELECT * FROM ' . $database . '.{system} LIMIT 1')->fetchObject();
  3893. $this->assertTrue(isset($record->filename), 'Successfully queried the schema.{system} table.');
  3894. }
  3895. }
  3896. public function testSelectReservedWordTableCount() {
  3897. $rows = db_select('virtual')
  3898. ->countQuery()
  3899. ->execute()
  3900. ->fetchField();
  3901. $this->assertEqual($rows, 1, 'Successful count query on a table with a reserved name.');
  3902. }
  3903. public function testSelectReservedWordTableSpecificField() {
  3904. $record = db_select('virtual')
  3905. ->fields('virtual', array('function'))
  3906. ->execute()
  3907. ->fetchAssoc();
  3908. $this->assertEqual($record['function'], 'Function value 1', 'Successfully read a field from a table with a name and column which are reserved words.');
  3909. }
  3910. public function testSelectReservedWordTableAllFields() {
  3911. $record = db_select('virtual')
  3912. ->fields('virtual')
  3913. ->execute()
  3914. ->fetchAssoc();
  3915. $this->assertEqual($record['function'], 'Function value 1', 'Successful all_fields query from a table with a name and column which are reserved words.');
  3916. }
  3917. public function testSelectReservedWordAliasCount() {
  3918. $rows = db_select('test', 'character')
  3919. ->countQuery()
  3920. ->execute()
  3921. ->fetchField();
  3922. $this->assertEqual($rows, 4, 'Successful count query using an alias which is a reserved word.');
  3923. }
  3924. public function testSelectReservedWordAliasSpecificFields() {
  3925. $record = db_select('test', 'high_priority')
  3926. ->fields('high_priority', array('name'))
  3927. ->condition('age', 27)
  3928. ->execute()->fetchAssoc();
  3929. $this->assertEqual($record['name'], 'George', 'Successful query using an alias which is a reserved word.');
  3930. }
  3931. public function testSelectReservedWordAliasAllFields() {
  3932. $record = db_select('test', 'high_priority')
  3933. ->fields('high_priority')
  3934. ->condition('age', 27)
  3935. ->execute()->fetchAssoc();
  3936. $this->assertEqual($record['name'], 'George', 'Successful all_fields query using an alias which is a reserved word.');
  3937. }
  3938. public function testInsertReservedWordTable() {
  3939. $num_records_before = db_query('SELECT COUNT(*) FROM {virtual}')->fetchField();
  3940. db_insert('virtual')
  3941. ->fields(array(
  3942. 'function' => 'Inserted function',
  3943. ))
  3944. ->execute();
  3945. $num_records_after = db_query('SELECT COUNT(*) FROM {virtual}')->fetchField();
  3946. $this->assertIdentical($num_records_before + 1, (int) $num_records_after, 'Successful insert into a table with a name and column which are reserved words.');
  3947. }
  3948. public function testDeleteReservedWordTable() {
  3949. $delete = db_delete('virtual')
  3950. ->condition('function', 'Function value 1');
  3951. $num_deleted = $delete->execute();
  3952. $this->assertEqual($num_deleted, 1, "Deleted 1 record from a table with a name and column which are reserved words..");
  3953. }
  3954. function testTruncateReservedWordTable() {
  3955. db_truncate('virtual')->execute();
  3956. $num_records_after = db_query("SELECT COUNT(*) FROM {virtual}")->fetchField();
  3957. $this->assertEqual(0, $num_records_after, 'Truncated a table with a reserved name.');
  3958. }
  3959. function testUpdateReservedWordTable() {
  3960. $num_updated = db_update('virtual')
  3961. ->fields(array('function' => 'Updated function'))
  3962. ->execute();
  3963. $this->assertIdentical($num_updated, 1, 'Updated 1 record in a table with a name and column which are reserved words.');
  3964. }
  3965. function testMergeReservedWordTable() {
  3966. $key = db_query('SELECT id FROM {virtual} LIMIT 1')->fetchField();
  3967. $num_records_before = db_query('SELECT COUNT(*) FROM {virtual}')->fetchField();
  3968. db_merge('virtual')
  3969. ->key(array('id' => $key))
  3970. ->fields(array('function' => 'Merged function'))
  3971. ->execute();
  3972. $num_records_after = db_query('SELECT COUNT(*) FROM {virtual}')->fetchField();
  3973. $this->assertIdentical($num_records_before, $num_records_after, 'Successful merge query on a table with a name and column which are reserved words.');
  3974. }
  3975. }
  3976. /**
  3977. * Test table prefix handling.
  3978. */
  3979. class DatabaseTablePrefixTestCase extends DatabaseTestCase {
  3980. public static function getInfo() {
  3981. return array(
  3982. 'name' => 'Table prefixes',
  3983. 'description' => 'Test handling of table prefixes.',
  3984. 'group' => 'Database',
  3985. );
  3986. }
  3987. public function testSchemaDotTablePrefixes() {
  3988. // Get a copy of the default connection options.
  3989. $db = Database::getConnection('default', 'default');
  3990. $connection_options = $db->getConnectionOptions();
  3991. if ($connection_options['driver'] === 'sqlite') {
  3992. // In SQLite simpletest's prefixed db tables exist in their own schema
  3993. // (e.g. simpletest124904.system), so we cannot test the schema.table
  3994. // prefix syntax here.
  3995. $this->assert(TRUE, 'Skipping schema.table prefixed tables test for SQLite.');
  3996. return;
  3997. }
  3998. $db_name = $connection_options['database'];
  3999. if ($connection_options['driver'] === 'pgsql') {
  4000. // database.scheme.table for PostgreSQL
  4001. // @see https://www.postgresql.org/docs/14/ddl-schemas.html
  4002. $db_name .= '.public';
  4003. }
  4004. // This prefix is usually something like simpletest12345
  4005. $test_prefix = $connection_options['prefix']['default'];
  4006. // Set up a new connection with table prefixes in the form "schema.table"
  4007. $prefixed = $connection_options;
  4008. $prefixed['prefix'] = array(
  4009. 'default' => $test_prefix,
  4010. 'users' => $db_name . '.' . $test_prefix,
  4011. 'role' => $db_name . '.' . $test_prefix,
  4012. );
  4013. Database::addConnectionInfo('default', 'prefixed', $prefixed);
  4014. // Test that the prefixed database connection can query the prefixed tables.
  4015. $num_users_prefixed = Database::getConnection('prefixed', 'default')->query('SELECT COUNT(1) FROM {users}')->fetchField();
  4016. $this->assertTrue((int) $num_users_prefixed > 0, 'Successfully queried the users table using a schema.table prefix');
  4017. $num_users_default = Database::getConnection('default', 'default')->query('SELECT COUNT(1) FROM {users}')->fetchField();
  4018. $this->assertEqual($num_users_default, $num_users_prefixed, 'Verified results of query using a connection with schema.table prefixed tables');
  4019. }
  4020. }