PageRenderTime 45ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/testing/RedUNIT/Base/Database.php

http://github.com/gabordemooij/redbean
PHP | 509 lines | 332 code | 67 blank | 110 comment | 18 complexity | adbd49bd6469cff4f81020b60d198758 MD5 | raw file
  1. <?php
  2. namespace RedUNIT\Base;
  3. use RedUNIT\Base as Base;
  4. use RedBeanPHP\Facade as R;
  5. use RedBeanPHP\QueryWriter\SQLiteT as SQLiteT;
  6. use RedBeanPHP\OODB as OODB;
  7. use RedBeanPHP\OODBBean as OODBBean;
  8. use RedBeanPHP\ToolBox as ToolBox;
  9. use RedBeanPHP\AssociationManager as AssociationManager;
  10. use RedBeanPHP\RedException as RedException;
  11. use RedBeanPHP\RedException\SQL as SQL;
  12. use RedBeanPHP\QueryWriter\MySQL as MySQL;
  13. use RedBeanPHP\QueryWriter\PostgreSQL as PostgreSQL;
  14. use RedBeanPHP\QueryWriter\CUBRID as CUBRID;
  15. use RedBeanPHP\Adapter\DBAdapter as DBAdapter;
  16. /**
  17. * Database
  18. *
  19. * Tests basic RedBeanPHP database functionality.
  20. *
  21. * @file RedUNIT/Base/Database.php
  22. * @desc Tests basic database behaviors
  23. * @author Gabor de Mooij and the RedBeanPHP Community
  24. * @license New BSD/GPLv2
  25. *
  26. * (c) G.J.G.T. (Gabor) de Mooij and the RedBeanPHP Community.
  27. * This source file is subject to the New BSD/GPLv2 License that is bundled
  28. * with this source code in the file license.txt.
  29. */
  30. class Database extends Base
  31. {
  32. /**
  33. * What drivers should be loaded for this test pack?
  34. */
  35. public function getTargetDrivers()
  36. {
  37. return array( 'mysql', 'pgsql', 'sqlite', 'CUBRID' );
  38. }
  39. /**
  40. * Can we use meta mask with find() ?
  41. *
  42. * @return void
  43. */
  44. public function testSelectFindOne()
  45. {
  46. R::store(R::dispense('book'));
  47. R::store(R::dispense('book'));
  48. if ($this->currentlyActiveDriverID == 'pgsql') {
  49. R::getWriter()->setSQLFilters(array('r'=>array('book'=>array('__meta_total'=>'COUNT(*) OVER()'))), FALSE);
  50. } else {
  51. R::getWriter()->setSQLFilters(array('r'=>array('book'=>array('__meta_total'=>'2'))), FALSE);
  52. }
  53. $books = R::find('book', 'LIMIT 1');
  54. $book = reset($books);
  55. $bundle = $book->getMeta('data.bundle');
  56. asrt(intval($bundle['__meta_total']),2);
  57. R::getWriter()->setSQLFilters(array(), FALSE);
  58. }
  59. /**
  60. * Test whether we cannot just bind function names but
  61. * also function templates, i.e. little SQL snippets.
  62. *
  63. * @return void
  64. */
  65. public function testBindFuncFunctionTemplates()
  66. {
  67. R::bindFunc('read', 'xbean.lucky', '111 * %s', TRUE);
  68. $bean = R::dispense('xbean');
  69. $bean->lucky = 7;
  70. $id = R::store( $bean );
  71. $bean = R::load( 'xbean', $id );
  72. asrt( intval($bean->lucky), 777 );
  73. R::bindFunc('write', 'xbean.triple', '3 * %s', TRUE);
  74. $bean->triple = 3;
  75. R::store($bean);
  76. $bean = $bean->fresh();
  77. asrt( intval($bean->triple), 9);
  78. R::bindFunc('read', 'xbean.lucky', NULL);
  79. R::bindFunc('write', 'xbean.triple', NULL);
  80. R::getRedBean()->clearAllFuncBindings();
  81. }
  82. /**
  83. * Make ConvertToBean work together with getRow #759.
  84. * When no results are found for getRow it returns []
  85. * Then when you give that to convertToBean it wraps your
  86. * single row into an array of multiple rows, so you get [[]].
  87. * Then this loop has something to
  88. * iterate on foreach ( $rows as $row ) { ...
  89. * And then it crashes on: $id = $row['id'];
  90. */
  91. public function testHarmonizeConvertToBeanAndGetRow()
  92. {
  93. R::nuke();
  94. $book = R::convertToBean( 'book', R::getRow( 'SELECT * FROM book' ) );
  95. asrt( is_null( $book ), TRUE );
  96. $book = R::convertToBean( 'book', array() );
  97. asrt( is_null( $book ), TRUE );
  98. }
  99. /**
  100. * Test for bugfix:
  101. * adhere to return type specification for R::getRow #728
  102. * public static function getRow is documented as a function
  103. * that returns an array. However, in a situation
  104. * where the resultset is empty, this returns a boolean
  105. * and causes an unexpected failure in
  106. * code like this because it is expecting an array.
  107. */
  108. public function testReturnTypeGetRow()
  109. {
  110. R::nuke();
  111. $book = R::dispense( 'book' );
  112. R::store( $book );
  113. $row = R::getRow('SELECT * FROM book');
  114. asrt( is_array( $row ), TRUE );
  115. R::trash( $book );
  116. $row = R::getRow('SELECT * FROM book');
  117. asrt( is_array( $row ), TRUE );
  118. R::nuke();
  119. $row = R::getRow('SELECT * FROM book');
  120. asrt( is_array( $row ), TRUE );
  121. }
  122. /**
  123. * Test the (protected) database capability checker method
  124. * of the RedBeanPHP PDO driver (RPDO).
  125. */
  126. public function testDatabaseCapabilityChecker()
  127. {
  128. $capChecker = new \DatabaseCapabilityChecker( R::getDatabaseAdapter()->getDatabase()->getPDO() );
  129. $result = $capChecker->checkCapability('creativity');
  130. asrt( $result, FALSE ); /* nope, no strong AI yet.. */
  131. }
  132. /**
  133. * Test whether we can obtain the PDO object from the
  134. * database driver for custom database operations.
  135. *
  136. * @return void
  137. */
  138. public function testGetPDO()
  139. {
  140. $driver = R::getDatabaseAdapter();
  141. asrt( ( $driver instanceof DBAdapter), TRUE );
  142. $pdo = $driver->getDatabase()->getPDO();
  143. asrt( ( $pdo instanceof \PDO ), TRUE );
  144. $pdo2 = R::getPDO();
  145. asrt( ( $pdo2 instanceof \PDO ), TRUE );
  146. asrt( ( $pdo === $pdo2 ), TRUE );
  147. }
  148. /**
  149. * Test setter maximum integer bindings.
  150. *
  151. * @return void
  152. */
  153. public function testSetMaxBind()
  154. {
  155. $driver = R::getDatabaseAdapter()->getDatabase();
  156. $old = $driver->setMaxIntBind( 10 );
  157. //use SQLite to confirm...
  158. if ( $this->currentlyActiveDriverID === 'sqlite' ) {
  159. $type = R::getCell( 'SELECT typeof( ? ) ', array( 11 ) );
  160. asrt( $type, 'text' );
  161. $type = R::getCell( 'SELECT typeof( ? ) ', array( 10 ) );
  162. asrt( $type, 'integer' );
  163. $type = R::getCell( 'SELECT typeof( ? ) ', array( 9 ) );
  164. asrt( $type, 'integer' );
  165. }
  166. $new = $driver->setMaxIntBind( $old );
  167. asrt( $new, 10 );
  168. try {
  169. $driver->setMaxIntBind( '10' );
  170. fail();
  171. } catch( RedException $e ) {
  172. pass();
  173. }
  174. $new = $driver->setMaxIntBind( $old );
  175. asrt( $new, $old );
  176. $new = $driver->setMaxIntBind( $old );
  177. asrt( $new, $old );
  178. }
  179. /**
  180. * Can we use colons in SQL?
  181. *
  182. * @return void
  183. */
  184. public function testColonsInSQL()
  185. {
  186. R::nuke();
  187. $book = R::dispense( 'book' );
  188. $book->title = 'About :';
  189. R::store( $book );
  190. pass();
  191. $book = R::findOne( 'book', ' title LIKE :this ', array(
  192. ':this' => 'About :'
  193. ) );
  194. asrt( ( $book instanceof OODBBean ), TRUE );
  195. //without the colon?
  196. $book = R::findOne( 'book', ' title LIKE :this ', array(
  197. 'this' => 'About :'
  198. ) );
  199. asrt( ( $book instanceof OODBBean ), TRUE );
  200. $book = R::findOne( 'book', ' title LIKE :this ', array(
  201. ':this' => '%:%'
  202. ) );
  203. asrt( ( $book instanceof OODBBean ), TRUE );
  204. $book = R::findOne( 'book', ' title LIKE :this OR title LIKE :that', array(
  205. 'this' => '%:%', ':that' => 'That'
  206. ) );
  207. asrt( ( $book instanceof OODBBean ), TRUE );
  208. $records = R::getAll('SELECT * FROM book WHERE title LIKE :this', array( ':this' => 'About :' ) );
  209. asrt( count( $records ), 1 );
  210. $records = R::getAll('SELECT * FROM book WHERE title LIKE :this', array( 'this' => 'About :' ) );
  211. asrt( count( $records ), 1 );
  212. $records = R::getAll('SELECT * FROM book WHERE title LIKE :this OR title LIKE :that', array( ':this' => 'About :', ':that' => 'That' ) );
  213. asrt( count( $records ), 1 );
  214. $records = R::getRow('SELECT * FROM book WHERE title LIKE :this', array( ':this' => 'About :' ) );
  215. asrt( count( $records ), 2 );
  216. $records = R::getRow('SELECT * FROM book WHERE title LIKE :this', array( 'this' => 'About :' ) );
  217. asrt( count( $records ), 2 );
  218. $records = R::getRow('SELECT * FROM book WHERE title LIKE :this OR title LIKE :that', array( ':this' => 'About :', ':that' => 'That' ) );
  219. asrt( count( $records ), 2 );
  220. }
  221. /**
  222. * Test setting direct PDO.
  223. * Not much to test actually.
  224. *
  225. * @return void
  226. */
  227. public function testDirectPDO()
  228. {
  229. $pdo = R::getDatabaseAdapter()->getDatabase()->getPDO();
  230. R::getDatabaseAdapter()->getDatabase()->setPDO( $pdo );
  231. pass();
  232. }
  233. /**
  234. * Test for testConnection() method.
  235. *
  236. * @return void
  237. */
  238. public function testConnectionTester()
  239. {
  240. asrt( R::testConnection(), TRUE );
  241. }
  242. /**
  243. * Tests the various ways to fetch (select queries)
  244. * data using adapter methods in the facade.
  245. * Also tests the new R::getAssocRow() method,
  246. * as requested in issue #324.
  247. */
  248. public function testFetchTypes()
  249. {
  250. R::nuke();
  251. $page = R::dispense( 'page' );
  252. $page->a = 'a';
  253. $page->b = 'b';
  254. R::store( $page );
  255. $page = R::dispense( 'page' );
  256. $page->a = 'c';
  257. $page->b = 'd';
  258. R::store( $page );
  259. $expect = '[{"id":"1","a":"a","b":"b"},{"id":"2","a":"c","b":"d"}]';
  260. asrt( json_encode( R::getAll( 'SELECT * FROM page' ) ), $expect );
  261. $expect = '{"1":"a","2":"c"}';
  262. asrt( json_encode( R::getAssoc( 'SELECT id, a FROM page' ) ), $expect );
  263. $expect = '{"1":{"a":"a","b":"b"},"2":{"a":"c","b":"d"}}';
  264. asrt( json_encode( R::getAssoc( 'SELECT id, a, b FROM page' ) ), $expect );
  265. $expect = '[{"id":"1","a":"a"},{"id":"2","a":"c"}]';
  266. asrt( json_encode( R::getAssocRow( 'SELECT id, a FROM page' ) ), $expect );
  267. $expect = '[{"id":"1","a":"a","b":"b"},{"id":"2","a":"c","b":"d"}]';
  268. asrt( json_encode( R::getAssocRow( 'SELECT id, a, b FROM page' ) ), $expect );
  269. $expect = '{"id":"1","a":"a","b":"b"}';
  270. asrt( json_encode( R::getRow( 'SELECT * FROM page WHERE id = 1' ) ), $expect );
  271. $expect = '"a"';
  272. asrt( json_encode( R::getCell( 'SELECT a FROM page WHERE id = 1' ) ), $expect );
  273. $expect = '"b"';
  274. asrt( json_encode( R::getCell( 'SELECT b FROM page WHERE id = 1') ), $expect );
  275. $expect = '"c"';
  276. asrt( json_encode( R::getCell('SELECT a FROM page WHERE id = 2') ), $expect );
  277. $expect = '["a","c"]';
  278. asrt( json_encode( R::getCol( 'SELECT a FROM page' ) ), $expect );
  279. $expect = '["b","d"]';
  280. asrt( json_encode( R::getCol('SELECT b FROM page') ), $expect );
  281. }
  282. /**
  283. * Tests whether we can store an empty bean.
  284. * An empty bean has no properties, only ID. Normally we would
  285. * skip the ID field in an INSERT, this test forces the driver
  286. * to specify a value for the ID field. Different writers have to
  287. * use different values: Mysql uses NULL to insert a new auto-generated ID,
  288. * while Postgres has to use DEFAULT.
  289. */
  290. public function testEmptyBean()
  291. {
  292. testpack( 'Test Empty Bean Storage.' );
  293. R::nuke();
  294. $bean = R::dispense( 'emptybean' );
  295. $id = R::store( $bean );
  296. asrt( ( $id > 0 ), TRUE );
  297. asrt( R::count( 'emptybean' ), 1 );
  298. $bean = R::dispense( 'emptybean' );
  299. $id = R::store( $bean );
  300. asrt( ( $id > 0 ), TRUE );
  301. asrt( R::count( 'emptybean' ), 2 );
  302. //also test in frozen mode
  303. R::freeze( TRUE );
  304. $bean = R::dispense( 'emptybean' );
  305. $id = R::store( $bean );
  306. asrt( ( $id > 0 ), TRUE );
  307. asrt( R::count( 'emptybean' ), 3 );
  308. R::freeze( FALSE );
  309. }
  310. /**
  311. * Test the database driver and low level functions.
  312. *
  313. * @return void
  314. */
  315. public function testDriver()
  316. {
  317. $currentDriver = $this->currentlyActiveDriverID;
  318. R::store( R::dispense( 'justabean' ) );
  319. $adapter = new TroubleDapter( R::getToolBox()->getDatabaseAdapter()->getDatabase() );
  320. $adapter->setSQLState( 'HY000' );
  321. $writer = new SQLiteT( $adapter );
  322. $redbean = new OODB( $writer );
  323. $toolbox = new ToolBox( $redbean, $adapter, $writer );
  324. // We can only test this for a known driver...
  325. if ( $currentDriver === 'sqlite' ) {
  326. try {
  327. $redbean->find( 'bean' );
  328. pass();
  329. } catch (\Exception $e ) {
  330. var_dump( $e->getSQLState() );
  331. fail();
  332. }
  333. }
  334. $adapter->setSQLState( -999 );
  335. try {
  336. $redbean->find( 'bean' );
  337. fail();
  338. } catch (\Exception $e ) {
  339. pass();
  340. }
  341. try {
  342. $redbean->wipe( 'justabean' );
  343. fail();
  344. } catch (\Exception $e ) {
  345. pass();
  346. }
  347. $toolbox = R::getToolBox();
  348. $adapter = $toolbox->getDatabaseAdapter();
  349. $writer = $toolbox->getWriter();
  350. $redbean = $toolbox->getRedBean();
  351. $pdo = $adapter->getDatabase();
  352. $page = $redbean->dispense( "page" );
  353. try {
  354. $adapter->exec( "an invalid query" );
  355. fail();
  356. } catch ( SQL $e ) {
  357. pass();
  358. }
  359. // Special data type description should result in magic number 99 (specified)
  360. if ( $currentDriver == 'mysql' ) {
  361. asrt( $writer->code( MySQL::C_DATATYPE_SPECIAL_DATE ), 99 );
  362. }
  363. if ( $currentDriver == 'pgsql' ) {
  364. asrt( $writer->code( PostgreSQL::C_DATATYPE_SPECIAL_DATE ), 99 );
  365. }
  366. if ( $currentDriver == 'CUBRID' ) {
  367. asrt( $writer->code( CUBRID::C_DATATYPE_SPECIAL_DATE ), 99 );
  368. }
  369. asrt( (int) $adapter->getCell( "SELECT 123" ), 123 );
  370. $page->aname = "my page";
  371. $id = (int) $redbean->store( $page );
  372. asrt( (int) $page->id, 1 );
  373. asrt( (int) $pdo->GetCell( "SELECT count(*) FROM page" ), 1 );
  374. asrt( $pdo->GetCell( "SELECT aname FROM page LIMIT 1" ), "my page" );
  375. asrt( (int) $id, 1 );
  376. $page = $redbean->load( "page", 1 );
  377. asrt( $page->aname, "my page" );
  378. asrt( ( (bool) $page->getMeta( "type" ) ), TRUE );
  379. asrt( isset( $page->id ), TRUE );
  380. asrt( ( $page->getMeta( "type" ) ), "page" );
  381. asrt( (int) $page->id, $id );
  382. }
  383. /**
  384. * Test selecting.
  385. *
  386. * @return void
  387. */
  388. public function testSelects()
  389. {
  390. $rooms = R::dispense( 'room', 2 );
  391. $rooms[0]->kind = 'suite';
  392. $rooms[1]->kind = 'classic';
  393. $rooms[0]->number = 6;
  394. $rooms[1]->number = 7;
  395. R::store( $rooms[0] );
  396. R::store( $rooms[1] );
  397. $rooms = R::getAssoc('SELECT * FROM room WHERE id < -999');
  398. asrt(is_array($rooms), TRUE);
  399. asrt(count($rooms), 0);
  400. $rooms = R::getAssoc( 'SELECT ' . R::getWriter()->esc( 'number' ) . ', kind FROM room ORDER BY kind ASC' );
  401. foreach ( $rooms as $key => $room ) {
  402. asrt( ( $key === 6 || $key === 7 ), TRUE );
  403. asrt( ( $room == 'classic' || $room == 'suite' ), TRUE );
  404. }
  405. $rooms = R::getDatabaseAdapter()->getAssoc( 'SELECT kind FROM room' );
  406. foreach ( $rooms as $key => $room ) {
  407. asrt( ( $room == 'classic' || $room == 'suite' ), TRUE );
  408. asrt( $room, $key );
  409. }
  410. $rooms = R::getAssoc( 'SELECT `number`, kind FROM rooms2 ORDER BY kind ASC' );
  411. asrt( count( $rooms ), 0 );
  412. asrt( is_array( $rooms ), TRUE );
  413. // GetCell should return NULL in case of exception
  414. asrt( NULL, R::getCell( 'SELECT dream FROM fantasy' ) );
  415. }
  416. }
  417. /**
  418. * Malfunctioning database adapter to test exceptions.
  419. */
  420. class TroubleDapter extends DBAdapter
  421. {
  422. private $sqlState;
  423. public function setSQLState( $sqlState )
  424. {
  425. $this->sqlState = $sqlState;
  426. }
  427. public function get( $sql, $values = array() )
  428. {
  429. $exception = new SQL( 'Just a trouble maker' );
  430. $exception->setSQLState( $this->sqlState );
  431. $exception->setDriverDetails( array(0,1,0) );
  432. throw $exception;
  433. }
  434. public function getRow( $sql, $aValues = array() )
  435. {
  436. $this->get( $sql, $aValues );
  437. }
  438. public function exec( $sql, $aValues = array(), $noEvent = FALSE )
  439. {
  440. $this->get( $sql, $aValues );
  441. }
  442. }