database_test.test 143 KB

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