database_test.test 132 KB

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