Represents an abstract Database to RedBean To write a driver for a different database for RedBean Contains a number of functions all implementors can inherit or override.
Author: Gabor de Mooij and the RedBeanPHP Community
コード例 #1
0
ファイル: Update.php プロジェクト: gabordemooij/redbean
 /**
  * Test whether we can use SQL filters and
  * whether they are being applied properly for
  * different types of SELECT queries in the QueryWriter.
  */
 public function testSQLFilters()
 {
     R::nuke();
     AQueryWriter::setSQLFilters(array(QueryWriter::C_SQLFILTER_READ => array('book' => array('title' => ' LOWER(book.title) ')), QueryWriter::C_SQLFILTER_WRITE => array('book' => array('title' => ' UPPER(?) '))));
     $book = R::dispense('book');
     $book->title = 'story';
     R::store($book);
     asrt(R::getCell('SELECT title FROM book WHERE id = ?', array($book->id)), 'STORY');
     $book = $book->fresh();
     asrt($book->title, 'story');
     $library = R::dispense('library');
     $library->sharedBookList[] = $book;
     R::store($library);
     $library = $library->fresh();
     $books = $library->sharedBookList;
     $book = reset($books);
     asrt($book->title, 'story');
     $otherBook = R::dispense('book');
     $otherBook->sharedBook[] = $book;
     R::store($otherBook);
     $otherBook = $otherBook->fresh();
     $books = $otherBook->sharedBookList;
     $book = reset($books);
     asrt($book->title, 'story');
     $links = $book->ownBookBookList;
     $link = reset($links);
     $link->shelf = 'x13';
     AQueryWriter::setSQLFilters(array(QueryWriter::C_SQLFILTER_READ => array('book' => array('title' => ' LOWER(book.title) '), 'book_book' => array('shelf' => ' LOWER(book_book.shelf) ')), QueryWriter::C_SQLFILTER_WRITE => array('book' => array('title' => ' UPPER(?) '), 'book_book' => array('shelf' => ' UPPER(?) '))));
     R::store($link);
     asrt(R::getCell('SELECT shelf FROM book_book WHERE id = ?', array($link->id)), 'X13');
     $otherBook = $otherBook->fresh();
     unset($book->sharedBookList[$otherBook->id]);
     R::store($book);
     AQueryWriter::setSQLFilters(array());
 }
コード例 #2
0
ファイル: Debug.php プロジェクト: gabordemooij/redbean
 /**
  * Fills in a value of a binding and truncates the
  * resulting string if necessary.
  *
  * @param mixed $value bound value
  *
  * @return string
  */
 protected function fillInValue($value)
 {
     if (is_null($value)) {
         $value = 'NULL';
     }
     $value = strval($value);
     if (strlen($value) > $this->strLen) {
         $value = substr($value, 0, $this->strLen) . '... ';
     }
     if (!\RedBeanPHP\QueryWriter\AQueryWriter::canBeTreatedAsInt($value) && $value !== 'NULL') {
         $value = '\'' . $value . '\'';
     }
     return $value;
 }
コード例 #3
0
ファイル: Threeway.php プロジェクト: skullyframework/skully
 /**
  * Test whether a duplicate bean in the list isnt saved.
  * This was an issue with Postgres while testing the threeway tables.
  * Postgres returned the ID as a string while other drivers returned
  * a numeric value causing different outcome in array_diff when
  * calculating the shared additions.
  * 
  * @return void
  */
 public function testIssueWithDriverReturnID()
 {
     AQueryWriter::clearRenames();
     R::nuke();
     $book = R::dispense('book');
     $page = R::dispense('page');
     $book->sharedPageList[] = $page;
     R::store($book);
     asrt(R::count('page'), 1);
     $book = $book->fresh();
     $book->sharedPageList[] = $page;
     R::store($book);
     //don't save the duplicate bean!
     asrt(R::count('page'), 1);
     $book = $book->fresh();
     $page->item = 2;
     //even if we change a property ?
     $book->sharedPageList[] = $page;
     R::store($book);
     foreach ($book->sharedPageList as $listItem) {
         asrt(is_string($listItem->id), TRUE);
     }
     //same test but for own-list
     R::nuke();
     $book = R::dispense('book');
     $page = R::dispense('page');
     $book->ownPageList[] = $page;
     R::store($book);
     asrt(R::count('page'), 1);
     $book = $book->fresh();
     $book->ownPageList[] = $page;
     R::store($book);
     //don't save the duplicate bean!
     asrt(R::count('page'), 1);
     $book = $book->fresh();
     $book->ownPageList[] = $page;
     $page->item = 3;
     R::store($book);
     //don't save the duplicate bean!
     asrt(R::count('page'), 1);
     foreach ($book->ownPageList as $listItem) {
         asrt(is_string($listItem->id), TRUE);
     }
     AQueryWriter::clearRenames();
 }
コード例 #4
0
ファイル: OODBBean.php プロジェクト: skullyframework/skully
 /**
  * Creates a N-M relation by linking an intermediate bean.
  * This method can be used to quickly connect beans using indirect
  * relations. For instance, given an album and a song you can connect the two
  * using a track with a number like this:
  *
  * Usage:
  *
  * $album->link('track', array('number'=>1))->song = $song;
  *
  * or:
  *
  * $album->link($trackBean)->song = $song;
  *
  * What this method does is adding the link bean to the own-list, in this case
  * ownTrack. If the first argument is a string and the second is an array or
  * a JSON string then the linking bean gets dispensed on-the-fly as seen in
  * example #1. After preparing the linking bean, the bean is returned thus
  * allowing the chained setter: ->song = $song.
  *
  * @param string|OODBBean $type          type of bean to dispense or the full bean
  * @param string|array            $qualification JSON string or array (optional)
  *
  * @return OODBBean
  */
 public function link($typeOrBean, $qualification = array())
 {
     if (is_string($typeOrBean)) {
         $typeOrBean = AQueryWriter::camelsSnake($typeOrBean);
         $bean = $this->beanHelper->getToolBox()->getRedBean()->dispense($typeOrBean);
         if (is_string($qualification)) {
             $data = json_decode($qualification, TRUE);
         } else {
             $data = $qualification;
         }
         foreach ($data as $key => $value) {
             $bean->{$key} = $value;
         }
     } else {
         $bean = $typeOrBean;
     }
     $list = 'own' . ucfirst($bean->getMeta('type'));
     array_push($this->{$list}, $bean);
     return $bean;
 }
コード例 #5
0
ファイル: Via.php プロジェクト: gabordemooij/redbean
 /**
  * Tests fix for issue #378.
  * Via property does not get cleared properly.
  *
  * @return void
  */
 public function testIssue378()
 {
     R::nuke();
     $mediaBean = R::dispense('media');
     $fooPerson = R::dispense('person');
     $mediaBean->sharedPersonList[] = $fooPerson;
     R::store($mediaBean);
     asrt(count($mediaBean->sharedPersonList), 1);
     $person = R::findOne('person');
     $person->via('relation')->sharedMarriageList[] = R::dispense('marriage');
     //this second one caused the via property to not get cleared
     $person->via('relation')->sharedMarriageList;
     asrt(count($person->sharedMediaList), 1);
     //also found this scenario, non-existing property
     $book = R::dispense('book');
     $book->sharedPage[] = R::dispense('page');
     R::store($book);
     $book = $book->fresh();
     $book->via('garbage')->nothing;
     asrt(count($book->sharedPageList), 1);
     //yet another
     $book = R::dispense('magazine');
     $book->ownAdList[] = R::dispense('ad');
     $book->sharedPage[] = R::dispense('page');
     R::store($book);
     $book = $book->fresh();
     $book->via('garbage')->ownAdList;
     asrt(count($book->sharedPageList), 1);
     $book = R::dispense('folder');
     $book->sharedPage[] = R::dispense('page');
     R::store($book);
     $book = $book->fresh();
     $book->via('garbage')->sharedItemList[] = R::dispense('item');
     asrt(count($book->sharedPageList), 1);
     $book = R::dispense('folder2');
     $book->sharedPage[] = R::dispense('page');
     R::store($book);
     $book = $book->fresh();
     $book->via('garbage')->sharedItemList[] = R::dispense('item');
     $book->via('garbage')->sharedItemList[] = R::dispense('item');
     asrt(count($book->sharedPageList), 1);
     $book = R::dispense('folder3');
     $book->sharedPage[] = R::dispense('page');
     R::store($book);
     $book = $book->fresh();
     $book->via('garbage')->item = R::dispense('item');
     asrt(count($book->sharedPageList), 1);
     $book = R::dispense('folder3');
     $book->sharedPage[] = R::dispense('page');
     R::store($book);
     $book = $book->fresh();
     $book->via('garbage')->item = 'test';
     asrt(count($book->sharedPageList), 1);
     //yet another
     $book = R::dispense('leaflet');
     $book->title = 'leaflet';
     $book->sharedPage[] = R::dispense('page');
     R::store($book);
     $book = $book->fresh();
     $book->via('garbage')->title;
     asrt(count($book->sharedPageList), 1);
     //yet another
     $book = R::dispense('paper');
     $book->author = R::dispense('author');
     $book->sharedPage[] = R::dispense('page');
     R::store($book);
     $book = $book->fresh();
     $book->via('garbage')->author;
     asrt(count($book->sharedPageList), 1);
     $book = R::dispense('paper2');
     $book->sharedPage[] = R::dispense('page');
     R::store($book);
     $book = $book->fresh();
     $book->via('garbage')->author;
     asrt(count($book->sharedPageList), 1);
     //yet another one
     $book = R::dispense('archive');
     $book->sharedItem[] = R::dispense('item');
     $book->sharedPage[] = R::dispense('page');
     R::store($book);
     $book = $book->fresh();
     unset($book->via('garbage')->sharedItem);
     asrt(count($book->sharedPageList), 1);
     //theoretic cases
     $book = R::dispense('guide');
     $book->sharedItem[] = R::dispense('item');
     $book->sharedPage[] = R::dispense('page');
     R::store($book);
     $book = $book->fresh();
     $book->via('relation')->countShared('item');
     $book->via('relation')->countShared('item');
     asrt(count($book->sharedPageList), 1);
     $book = R::dispense('catalogue');
     $book->sharedPage[] = R::dispense('page');
     R::store($book);
     $book = $book->fresh();
     $book->via('relation')->countShared('item');
     $book->via('relation')->countShared('item');
     asrt(count($book->sharedPageList), 1);
     $book = R::dispense('tabloid');
     $book->ownItemList[] = R::dispense('item');
     $book->sharedPage[] = R::dispense('page');
     R::store($book);
     $book = $book->fresh();
     $book->via('relation')->countOwn('item');
     $book->via('relation')->countOwn('item');
     asrt(count($book->sharedPageList), 1);
     $book = R::dispense('booklet');
     $book->ownItemList[] = R::dispense('item');
     $book->sharedPage[] = R::dispense('page');
     R::store($book);
     $book = $book->fresh();
     $book->via('relation')->countOwn('item');
     $book->via('relation')->countOwn('item');
     asrt(count($book->sharedPageList), 1);
     AQueryWriter::clearRenames();
 }
コード例 #6
0
ファイル: Facade.php プロジェクト: up9cloud/redbean
 /**
  * Facade method for AQueryWriter::renameAssociation()
  *
  * @param string|array $from
  * @param string       $to
  *
  * @return void
  */
 public static function renameAssociation($from, $to = NULL)
 {
     AQueryWriter::renameAssociation($from, $to);
 }
コード例 #7
0
ファイル: Prefixes.php プロジェクト: diego-vieira/redbean
 /**
  * Test prettier tables using via().
  */
 public function testViaPrettification()
 {
     R::nuke();
     R::renameAssociation('tbl_author_tbl_friend', 'tbl_author_friend');
     $author = R::xdispense(AUTHOR);
     $author->name = 'Mr. Quill';
     $friend = R::xdispense(FRIEND);
     $friend->name = 'Muse';
     $author->{FRIENDLIST}[] = $friend;
     $id = R::store($author);
     //print_r(R::inspect()); exit;
     $author = R::load(AUTHOR, $id);
     $tables = array_flip(R::inspect());
     asrt(isset($tables['tbl_author_friend']), TRUE);
     asrt(isset($tables['tbl_author_tbl_friend']), FALSE);
     asrt(count($author->{FRIENDLIST}), 1);
     AQueryWriter::clearRenames();
 }
コード例 #8
0
ファイル: core_cls_db_orm.php プロジェクト: EGArian/sarkesh
 /**
  * Binds parameters. This method binds parameters to a\PDOStatement for
  * Query Execution. This method binds parameters as NULL, INTEGER or STRING
  * and supports both named keys and question mark keys.
  *
  * @param \PDOStatement $statement \PDO Statement instance
  * @param  array        $bindings   values that need to get bound to the statement
  *
  * @return void
  */
 protected function bindParams($statement, $bindings)
 {
     foreach ($bindings as $key => &$value) {
         if (is_integer($key)) {
             if (is_null($value)) {
                 $statement->bindValue($key + 1, NULL, \PDO::PARAM_NULL);
             } elseif (!$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt($value) && $value < 2147483648) {
                 $statement->bindParam($key + 1, $value, \PDO::PARAM_INT);
             } else {
                 $statement->bindParam($key + 1, $value, \PDO::PARAM_STR);
             }
         } else {
             if (is_null($value)) {
                 $statement->bindValue($key, NULL, \PDO::PARAM_NULL);
             } elseif (!$this->flagUseStringOnlyBinding && AQueryWriter::canBeTreatedAsInt($value) && $value < 2147483648) {
                 $statement->bindParam($key, $value, \PDO::PARAM_INT);
             } else {
                 $statement->bindParam($key, $value, \PDO::PARAM_STR);
             }
         }
     }
 }
コード例 #9
0
 /**
  * Determines whether the bea has a shared list based on
  * schema inspection from realtime schema or cache.
  *
  * @param string $type   bean type to get list for
  * @param string $target type of list you are looking for
  *
  * @return boolean
  */
 protected function hasSharedList($type, $target)
 {
     return in_array(AQueryWriter::getAssocTableFormat(array($type, $target)), $this->tables);
 }
コード例 #10
0
ファイル: Writer.php プロジェクト: AntonyAntonio/phpback
 /**
  * Test Facade bind function method.
  * Test for MySQL WKT spatial format.
  */
 public function testFunctionFilters()
 {
     R::nuke();
     R::bindFunc('read', 'location.point', 'asText');
     R::bindFunc('write', 'location.point', 'GeomFromText');
     R::store(R::dispense('location'));
     R::freeze(true);
     try {
         R::find('location');
         fail();
     } catch (SQL $exception) {
         pass();
     }
     R::freeze(false);
     try {
         R::find('location');
         pass();
     } catch (SQL $exception) {
         fail();
     }
     $location = R::dispense('location');
     $location->point = 'POINT(14 6)';
     R::store($location);
     $columns = R::inspect('location');
     asrt($columns['point'], 'point');
     $location = $location->fresh();
     asrt($location->point, 'POINT(14 6)');
     R::nuke();
     $location = R::dispense('location');
     $location->point = 'LINESTRING(0 0,1 1,2 2)';
     R::store($location);
     $columns = R::inspect('location');
     asrt($columns['point'], 'linestring');
     $location->bustcache = 2;
     R::store($location);
     $location = $location->fresh();
     asrt($location->point, 'LINESTRING(0 0,1 1,2 2)');
     R::nuke();
     $location = R::dispense('location');
     $location->point = 'POLYGON((0 0,10 0,10 10,0 10,0 0),(5 5,7 5,7 7,5 7,5 5))';
     R::store($location);
     $columns = R::inspect('location');
     asrt($columns['point'], 'polygon');
     $location->bustcache = 4;
     R::store($location);
     $location = $location->fresh();
     asrt($location->point, 'POLYGON((0 0,10 0,10 10,0 10,0 0),(5 5,7 5,7 7,5 7,5 5))');
     R::bindFunc('read', 'location.point', NULL);
     $location->bustcache = 1;
     R::store($location);
     $location = $location->fresh();
     asrt($location->point === 'POLYGON((0 0,10 0,10 10,0 10,0 0),(5 5,7 5,7 7,5 7,5 5))', FALSE);
     $filters = AQueryWriter::getSQLFilters();
     asrt(is_array($filters), TRUE);
     asrt(count($filters), 2);
     asrt(isset($filters[QueryWriter::C_SQLFILTER_READ]), TRUE);
     asrt(isset($filters[QueryWriter::C_SQLFILTER_WRITE]), TRUE);
     R::bindFunc('read', 'place.point', 'asText');
     R::bindFunc('write', 'place.point', 'GeomFromText');
     R::bindFunc('read', 'place.line', 'asText');
     R::bindFunc('write', 'place.line', 'GeomFromText');
     R::nuke();
     $place = R::dispense('place');
     $place->point = 'POINT(13.2 666.6)';
     $place->line = 'LINESTRING(9.2 0,3 1.33)';
     R::store($place);
     $columns = R::inspect('place');
     asrt($columns['point'], 'point');
     asrt($columns['line'], 'linestring');
     $place = R::findOne('place');
     asrt($place->point, 'POINT(13.2 666.6)');
     asrt($place->line, 'LINESTRING(9.2 0,3 1.33)');
     R::bindFunc('read', 'place.point', NULL);
     R::bindFunc('write', 'place.point', NULL);
     R::bindFunc('read', 'place.line', NULL);
     R::bindFunc('write', 'place.line', NULL);
 }
コード例 #11
0
ファイル: OODB.php プロジェクト: nekrasovdmytro/redbean
 /**
  * Binds an SQL function to a column.
  * This method can be used to setup a decode/encode scheme or
  * perform UUID insertion. This method is especially useful for handling
  * MySQL spatial columns, because they need to be processed first using
  * the asText/GeomFromText functions.
  *
  * @param string $mode     mode to set function for, i.e. read or write
  * @param string $field    field (table.column) to bind SQL function to
  * @param string $function SQL function to bind to field
  *
  * @return void
  */
 public function bindFunc($mode, $field, $function)
 {
     list($type, $property) = explode('.', $field);
     $mode = $mode === 'write' ? QueryWriter::C_SQLFILTER_WRITE : QueryWriter::C_SQLFILTER_READ;
     if (!isset(self::$sqlFilters[$mode])) {
         self::$sqlFilters[$mode] = array();
     }
     if (!isset(self::$sqlFilters[$mode][$type])) {
         self::$sqlFilters[$mode][$type] = array();
     }
     if (is_null($function)) {
         unset(self::$sqlFilters[$mode][$type][$property]);
     } else {
         if ($mode === QueryWriter::C_SQLFILTER_WRITE) {
             self::$sqlFilters[$mode][$type][$property] = $function . '(?)';
         } else {
             self::$sqlFilters[$mode][$type][$property] = $function . "({$field})";
         }
     }
     AQueryWriter::setSQLFilters(self::$sqlFilters, !$this->isFrozen);
 }
コード例 #12
0
ファイル: Repository.php プロジェクト: AntonyAntonio/phpback
 /**
  * Counts the number of beans of type $type.
  * This method accepts a second argument to modify the count-query.
  * A third argument can be used to provide bindings for the SQL snippet.
  *
  * @param string $type     type of bean we are looking for
  * @param string $addSQL   additional SQL snippet
  * @param array  $bindings parameters to bind to SQL
  *
  * @return integer
  *
  * @throws SQLException
  */
 public function count($type, $addSQL = '', $bindings = array())
 {
     $type = AQueryWriter::camelsSnake($type);
     if (count(explode('_', $type)) > 2) {
         throw new RedException('Invalid type for count.');
     }
     try {
         return (int) $this->writer->queryRecordCount($type, array(), $addSQL, $bindings);
     } catch (SQLException $exception) {
         if (!$this->writer->sqlStateIn($exception->getSQLState(), array(QueryWriter::C_SQLSTATE_NO_SUCH_TABLE, QueryWriter::C_SQLSTATE_NO_SUCH_COLUMN))) {
             throw $exception;
         }
     }
     return 0;
 }
コード例 #13
0
ファイル: CUBRID.php プロジェクト: AntonyAntonio/phpback
 /**
  * @see QueryWriter::esc
  */
 public function esc($dbStructure, $noQuotes = FALSE)
 {
     return parent::esc(strtolower($dbStructure), $noQuotes);
 }
コード例 #14
0
ファイル: Misc.php プロジェクト: skullyframework/skully
 /**
  * Test camelCase to snake_case conversions.
  * 
  * @return void
  */
 public function testCamel2Snake()
 {
     asrt(AQueryWriter::camelsSnake('bookPage'), 'book_page');
     asrt(AQueryWriter::camelsSnake('FTP'), 'ftp');
     asrt(AQueryWriter::camelsSnake('ACLRules'), 'acl_rules');
     asrt(AQueryWriter::camelsSnake('SSHConnectionProxy'), 'ssh_connection_proxy');
     asrt(AQueryWriter::camelsSnake('proxyServerFacade'), 'proxy_server_facade');
     asrt(AQueryWriter::camelsSnake('proxySSHClient'), 'proxy_ssh_client');
     asrt(AQueryWriter::camelsSnake('objectACL2Factory'), 'object_acl2_factory');
     asrt(AQueryWriter::camelsSnake('bookItems4Page'), 'book_items4_page');
     asrt(AQueryWriter::camelsSnake('book☀Items4Page'), 'book☀_items4_page');
 }
コード例 #15
0
ファイル: rb.php プロジェクト: WTer/NJB
 /**
  * @see QueryWriter::scanType
  */
 public function scanType($value, $flagSpecial = FALSE)
 {
     $this->svalue = $value;
     if ($value === INF) {
         return self::C_DATATYPE_TEXT;
     }
     if ($flagSpecial && $value) {
         if (preg_match('/^\\d{4}\\-\\d\\d-\\d\\d$/', $value)) {
             return PostgreSQL::C_DATATYPE_SPECIAL_DATE;
         }
         if (preg_match('/^\\d{4}\\-\\d\\d-\\d\\d\\s\\d\\d:\\d\\d:\\d\\d(\\.\\d{1,6})?$/', $value)) {
             return PostgreSQL::C_DATATYPE_SPECIAL_DATETIME;
         }
         if (preg_match('/^\\([\\d\\.]+,[\\d\\.]+\\)$/', $value)) {
             return PostgreSQL::C_DATATYPE_SPECIAL_POINT;
         }
         if (preg_match('/^\\[\\([\\d\\.]+,[\\d\\.]+\\),\\([\\d\\.]+,[\\d\\.]+\\)\\]$/', $value)) {
             return PostgreSQL::C_DATATYPE_SPECIAL_LSEG;
         }
         if (preg_match('/^\\<\\([\\d\\.]+,[\\d\\.]+\\),[\\d\\.]+\\>$/', $value)) {
             return PostgreSQL::C_DATATYPE_SPECIAL_CIRCLE;
         }
         if (preg_match('/^\\((\\([\\d\\.]+,[\\d\\.]+\\),?)+\\)$/', $value)) {
             return PostgreSQL::C_DATATYPE_SPECIAL_POLYGON;
         }
         if (preg_match('/^\\-?(\\$|€|¥|£)[\\d,\\.]+$/', $value)) {
             return PostgreSQL::C_DATATYPE_SPECIAL_MONEY;
         }
     }
     if (is_float($value)) {
         return self::C_DATATYPE_DOUBLE;
     }
     if ($this->startsWithZeros($value)) {
         return self::C_DATATYPE_TEXT;
     }
     if ($value === FALSE || $value === TRUE || $value === NULL || is_numeric($value) && AQueryWriter::canBeTreatedAsInt($value) && $value < 2147483648.0 && $value > -2147483648.0) {
         return self::C_DATATYPE_INTEGER;
     } elseif (is_numeric($value)) {
         return self::C_DATATYPE_DOUBLE;
     } else {
         return self::C_DATATYPE_TEXT;
     }
 }
コード例 #16
0
ファイル: OODBBean.php プロジェクト: wozzup/redbean
 /**
  * Registers a association renaming globally.
  *
  * @param string $via type you wish to use for shared lists
  *
  * @return OODBBean
  */
 public function via($via)
 {
     $this->via = AQueryWriter::camelsSnake($via);
     return $this;
 }
コード例 #17
0
ファイル: Foreignkeys.php プロジェクト: gabordemooij/redbean
 /**
  * Test whether unique constraints are properly created using
  * reflection.
  *
  * @return void
  */
 public function testUniqueInspect()
 {
     $writer = R::getWriter();
     R::nuke();
     $book = R::dispense('book');
     $category = R::dispense('category');
     $book->sharedCategory[] = $category;
     R::store($book);
     asrt(count(get_uniques_for_type('book_category')), 1);
     asrt(are_cols_in_unique('book_category', array('book_id', 'category_id')), TRUE);
     R::nuke();
     $book = R::dispense('book');
     $category = R::dispense('category');
     $book->via('library')->sharedCategory[] = $category;
     R::store($book);
     asrt(count(get_uniques_for_type('book_category')), 0);
     asrt(are_cols_in_unique('book_category', array('book_id', 'category_id')), FALSE);
     asrt(count(get_uniques_for_type('library')), 1);
     asrt(are_cols_in_unique('library', array('book_id', 'category_id')), TRUE);
     AQueryWriter::clearRenames();
     R::nuke();
     $book = R::dispense('book');
     $category = R::dispense('category');
     $book->sharedCategory[] = $category;
     R::store($book);
     asrt(count(get_uniques_for_type('book_category')), 1);
     asrt(are_cols_in_unique('book_category', array('book_id', 'category_id')), TRUE);
     asrt(count(get_uniques_for_type('library')), 0);
     asrt(are_cols_in_unique('library', array('book_id', 'category_id')), FALSE);
     R::nuke();
     $book = R::dispense('book');
     $book2 = R::dispense('book');
     $book->sharedBook[] = $book2;
     R::store($book);
     asrt(count(get_uniques_for_type('book_book')), 1);
     asrt(are_cols_in_unique('book_book', array('book_id', 'book2_id')), TRUE);
     try {
         $result = R::getWriter()->addUniqueConstraint('nonexistant', array('a', 'b'));
     } catch (\Exception $e) {
         print_r($e);
         exit;
     }
     pass();
     //dont crash!
     asrt($result, FALSE);
 }