/** * Override {@link epDbPort::checkIndex()} * @param epClassMap $cm * @param epDb $db * @return false|array */ public function checkIndex($cm, $db) { // reset counter and return value $ret = array(array(), array()); // get all columns in the pg_attribute and pg_class table $sql = 'SELECT a.attname, a.attnum ' . ' FROM pg_attribute a, pg_class c ' . ' WHERE c.relname = ' . $db->quote($cm->getTable()) . ' AND a.attrelid = c.oid AND a.attnum > 0' . ' ORDER BY a.attnum'; // execute the query if (!$db->execute($sql)) { return false; } // array to collect all columns $columns = array(); // go through reach record $okay = $this->db->rsRestart(); while ($okay) { $num = $db->rsGetCol('attnum'); $name = $db->rsGetCol('attname'); $columns[$num] = $name; $okay = $this->db->rsNext(); } // get all the indexes in the table (indkey has a list, space separated) $sql = 'SELECT c2.relname AS indexname, i.indisprimary, i.indisunique, i.indkey AS indkey' . ' FROM pg_class c, pg_class c2, pg_index i' . ' WHERE c.relname = ' . $db->quote($cm->getTable()) . ' AND c.oid = i.indrelid AND i.indexrelid = c2.oid'; // execute above query if (!$db->execute($sql)) { return false; } // go through reach record $okay = $db->rsRestart(); while ($okay) { // skip the primary index if ($this->db->rsGetCol('indisprimary') == 't') { // next row $okay = $this->db->rsNext(); continue; } // get index name $name = $this->db->rsGetCol('indexname'); $unique = $this->db->rsGetCol('indisunique'); // $unique is t if unique // $unique is f if index $unique = $unique == 't' ? 0 : 1; $indexes = explode(' ', $this->db->rsGetCol('indkey')); foreach ($indexes as $index) { $ret[$unique][$name][] = $columns[$index]; } // next row $okay = $db->rsNext(); } return $ret; }
/** * Override {@link epDbPort::checkIndex()} * @param epClassMap $cm * @param epDb $db * @return false|array */ public function checkIndex($cm, $db) { // get index list $sql = 'PRAGMA index_list(' . $db->quoteId($cm->getTable()) . ')'; if (!$db->execute($sql)) { return false; } // reset counter and return value $ret = array(array(), array()); $indexes = array(); $uniques = array(); // go through reach record $okay = $db->rsRestart(); while ($okay) { // get index name $name = $db->rsGetCol('name'); $unique = $db->rsGetCol('unique'); // $unique is 1 if unique // $unique is 0 if index $unique = !$unique; // ??? // store the index name for further information $indexes[] = $name; $uniques[$name] = $unique; $ret[$unique][$name] = array(); // next row $okay = $db->rsNext(); } // go through each index foreach ($indexes as $index) { $sql = 'PRAGMA index_info(' . $db->quoteId($index) . ')'; if (!$db->execute($sql)) { return false; } // go through reach record $okay = $db->rsRestart(); while ($okay) { // get index name $column = $db->rsGetCol('name'); $ret[$uniques[$index]][$index][] = $column; // next row $okay = $db->rsNext(); } } return $ret; }
/** * Override {@link epDbPort::createTable()} * * Generate SQL code to create table * * @param epClassMap $cm * @param string $indent * @param epDb $db * @return string|array (of strings) */ public function createTable($cm, $db, $indent = ' ') { // start create table $sql = "CREATE TABLE " . $db->quoteId($cm->getTable()) . " ("; // the oid field $fstr = $this->_defineField($db->quoteId($cm->getOidColumn()), 'INTEGER', '12', false, true); $sql .= $fstr . ","; // write sql for each field foreach ($cm->getAllFields() as $fname => $fm) { if ($fm->isPrimitive()) { // get the field definition $fstr = $this->_defineField($db->quoteId($fm->getColumnName()), $fm->getType(), $fm->getTypeParams(), $fm->getDefaultValue(), false); $sql .= $fstr . ","; } } // remove the last ',' $sql = substr($sql, 0, strlen($sql) - 1); // end of table creation $sql .= ");"; return $sql; }
/** * Generate SQL code to create table * * Sometimes extra procedure may be needed for some fields to work. * For example, some database does not have a direct auto-incremental * keyword and, to make it work, you need extra procedure after * creating the table. Here is an example, for a table column in * an Oracle database to be auto-incremental, we need to insert * a sequence and a trigger (See this link for more info * {@link http://webxadmin.free.fr/article.php?i=134}). If this is * the case the subclass should override this method and return both * "create table" statement and the extra. * * @param epClassMap $cm * @param epDb $db * @param string $indent * @return string|array (of strings) */ public function createTable($cm, $db, $indent = ' ') { // start create table $sql = "CREATE TABLE " . $db->quoteId($cm->getTable()) . " (\n"; // the oid field $fstr = $this->_defineField($db->quoteId($cm->getOidColumn()), 'integer', '12', false, true); $sql .= $indent . $fstr . ",\n"; // write sql for each field foreach ($cm->getAllFields() as $fname => $fm) { if ($fm->isPrimitive()) { // get the field definition $fstr = $this->_defineField($db->quoteId($fm->getColumnName()), $fm->getType(), $fm->getTypeParams(), $fm->getDefaultValue(), false); $sql .= $indent . $fstr . ",\n"; } } // write primary key $sql .= $indent . "PRIMARY KEY (" . $db->quoteId($cm->getOidColumn()) . ")\n"; // end of table creation $sql .= ");\n"; return $sql; }
/** * Returns the base (root) field map * @return null|epFieldMap */ protected function &getBase() { // return cached one if ($this->base) { return $this->base; } // class map exists? if (!$this->class_map) { return self::$false; } // get the base if (!($this->base = $this->class_map->getBaseField($this->name))) { $this->base = $this; } return $this->base; }
/** * Override {@link epDbPort::createTable()} * * Generate SQL code to create table * * @param epClassMap $cm * @param string $indent * @param epDb $db * @return string|array (of strings) */ public function createTable($cm, $db, $indent = ' ') { // start create table $sql = "CREATE TABLE \"" . $cm->getTable() . "\" ("; $sql = $sql . "CONSTRAINT " . $cm->getTable() . "_" . $cm->getOidColumn() . " PRIMARY KEY (" . $cm->getOidColumn() . "),"; // the oid field $fstr = $this->_defineField($cm->getOidColumn(), 'Integer', '', false, true); $sql .= $fstr . ","; // write sql for each field foreach ($cm->getAllFields() as $fname => $fm) { if ($fm->isPrimitive()) { // get the field definition $fstr = $this->_defineField($db->quoteId($fm->getColumnName()), $fm->getType(), $fm->getTypeParams(), $fm->getDefaultValue(), false); $sql .= $fstr . ","; } } // remove the last ',' $sql = substr($sql, 0, strlen($sql) - 1); // end of table creation // WITH OIDS - see http://www.ezpdo.net/forum/viewtopic.php?pid=750#p750 $sql .= ") WITH OIDS;"; return $sql; }
/** * Return the '<table> as <alias>' part for the SQL FROM clause * @return string */ public function getTableAlias() { // quote ids $table = $this->quoteId($this->cm->getTable()); $alias = $this->quoteId($this->alias); // return sql return "{$table} AS {$alias}"; }
/** * Create indexes all at once * * This method may be useful if you want to create indexes all at once * or create indexes for already created tables. * * @return array (The array of classes and relationship tables with indexes created) * @access public */ public function createIndexes() { // initialize tables if (!$this->cmf || !$this->cm_obj_rel) { $this->initialize(); } // done if no classes at all if (!($cms = $this->cmf->allMade())) { return array(); } // array to hold classes/tables with indexes created $classes_done = array(); // go through all classes mapped foreach ($cms as $cm) { // get class name $class = $cm->getName(); // relation table dealt with for each class if ($class == $this->cm_obj_rel->getName()) { continue; } // don't try to create indexes twice if (!in_array($class, $classes_done)) { // create indexes on the class if ($db =& $this->_getDb($cm)) { $db->index($cm); $classes_done[] = $class; } } // create indexes on the object relations for the class foreach ($this->_getRelationPairs($class) as $base_a_b) { // don't try to create indexes twice if (in_array($base_a_b, $classes_done)) { continue; } // split base a and b list($base_a, $base_b) = split(' ', $base_a_b); // switch relation table $this->_setRelationTable($base_a, $base_b); // call db to create index if ($db =& $this->_getDb($this->cm_obj_rel)) { $db->index($this->cm_obj_rel); $classes_done[] = $base_a_b; } } } return $classes_done; }
/** * Parse the comment of the var (field) * @param string $var the name of the var * @param string $comment the comment associated to the var * @return epFieldMap */ protected function parseVarComment($var, $comment) { $class_var = $this->cm->getName() . '::' . $var; // parse var comment $c = new epComment($comment); if (!$c) { throw new epExceptionParser('Cannot parse comment for var [' . $class_var . ']'); return false; } // get the @orm tag value if (!($value = $c->getTagValue('orm'))) { //warn('No @orm tag for var [' . $class_var . ']. Ignored.'); return false; } // parse var tag if (!($t = new epVarTag())) { throw new epExceptionParser('Cannot parse @orm tag for var [' . $class_var . ']'); return false; } $error = $t->parse($value); if (is_string($error)) { throw new epExceptionParser('Error in parsing @orm tag for var [' . $class_var . ']: ' . $error); return false; } // call field map factory to create a field map if (!($fm = epFieldMapFactory::make($var, $t->get('type'), $t->get('params')))) { return false; } // always harvest 'raw' customer tags $fm->setTags($c->getTags()); // set column name if set if ($column_name = $t->get('name')) { $fm->setColumnName($column_name); } // get key type if ($key_type = $t->get('keytype')) { // get key name if (!($key_name = $t->get('keyname'))) { $key_name = $var; } switch ($key_type) { case 'unique': $this->cm->addUniqueKey($key_name, $var); break; case 'index': $this->cm->addIndexKey($key_name, $var); break; } } return $fm; }
/** * SQL to create unique keys * @param epClassMap $cm * @param epDb $db * @param string $indent * @return string */ protected function _uniqueKeys($cm, $db, $indent = ' ') { $sql = ''; foreach ($cm->getUniqueKeys() as $name => $key) { // quote keys foreach ($key as $k => $v) { $key[$k] = $db->quoteId($v); } // get stmt for this key $sql .= $indent . $this->_uniqueKey($db->quoteId($name), $key) . ",\n"; } return $sql; }
/** * Returns the class::var name for a var * @params string $var_name * @return string */ protected function _varName($var) { $class = ''; if ($this->ep_cm) { $class = $this->ep_cm->getName() . '::'; } return $class . $var; }
/** * Converts the last record set into uoids * @param epClassMap $cm the class map for the conversion * @param array (of integers) object ids to be excluded * @return false|array (of uoids) * @throws epExceptionDbObject */ protected function _rs2uoid($cm, $oids_ex = null) { // !!!important!!! with a large db, the list of oid to be excluded // $oids_ex can grown really large and can significantly slow down // queries. so it is suppressed in the select statement and moved // to this method to process. // get the class name $class = $cm->getName(); // reset counter and return value $ret = array(); // go through reach record $okay = $this->db->rsRestart(); while ($okay) { // get oid column $oid = $this->db->rsGetCol($cn = $cm->getOidColumn(), $class . '.' . $cn); // exclude it? if ($oids_ex && in_array($oid, $oids_ex)) { // next row $okay = $this->db->rsNext(); // exclude it continue; } // get class_b $class_b = $this->db->rsGetCol('class_b', $class . '.' . 'class_b'); // get oid_b $oid_b = $this->db->rsGetCol('oid_b', $class . '.' . 'oid_b'); // collect return result $ret[] = $class_b . ':' . $oid_b; // next row $okay = $this->db->rsNext(); } return $ret; }
/** * Change table name for object relations class * @param string $base_a * @param string $base_b * @return void */ protected function _setRelationTable($base_a, $base_b) { $this->cm_obj_rel->setTable($this->getRelationTable($base_a, $base_b)); }
/** * Converts the last record set into epObject object(s) with class map * @param epClassMap $cm the class map for the conversion * @param array (of integers) object ids to be excluded * @return false|array (of epObject) * @throws epExceptionDbObject */ protected function &_rs2obj($cm, $ex = null) { // !!!important!!! with a large db, the list of oid to be excluded // $ex can grown really large and can significantly slow down // queries. so it is suppressed in the select statement and moved // to this method to process. // get epManager instance and cache it if (!$this->ep_m) { $this->ep_m =& epManager::instance(); } // get the class name $class = $cm->getName(); // get all mapped vars if (!($fms = $cm->getAllFields())) { return self::$false; } // reset counter and return value $ret = array(); // go through reach record $okay = $this->db->rsRestart(); while ($okay) { // get oid column $oid = $this->db->rsGetCol($cn = $cm->getOidColumn(), $class . '.' . $cn); // exclude it? if ($ex && in_array($oid, $ex)) { // next row $okay = $this->db->rsNext(); // exclude it continue; } // call epManager to create an instance (false: no caching; false: no event dispatching) if (!($o =& $this->ep_m->_create($class, false, false))) { // next row $okay = $this->db->rsNext(); continue; } // go through each field foreach ($fms as $fname => $fm) { // skip non-primivite field if (!$fm->isPrimitive()) { continue; } // get var value and set to object $val = $this->db->rsGetCol($cn = $fm->getColumnName(), $class . '.' . $cn); // set value to var (true: no dirty flag change) $o->epSet($fm->getName(), $this->_castType($val, $fm->getType()), true); } // set oid $o->epSetObjectId($oid); // collect return result $ret[] = $o; // next row $okay = $this->db->rsNext(); } return $ret; }
/** * Returns the field map for the variable * Note that param $var_name will be replaced with the best matched var name * @param string $var_name * @return false|epFieldMap */ protected function &_getFieldMap($var_name) { // check if we have class map if (!$this->ep_cm || !$var_name) { return self::$false; } // check if field map is cached if (isset($this->ep_fms[$var_name])) { return $this->ep_fms[$var_name]; } // get field map $fm = $this->ep_cm->getField($var_name); // cache it $this->ep_fms[$var_name] = $fm; // return the field map for the var return $fm; }
/** * Parse the comment of the var (field) * @param string $var the name of the var * @param string $comment the comment associated to the var * @return epFieldMap */ protected function parseVarComment($var, $comment) { $class_var = $this->cm->getName() . '::' . $var; // parse var comment $c = new epComment($comment); if (!$c) { throw new epExceptionParser('Cannot parse comment for var [' . $class_var . ']'); return false; } // get the @orm tag value if (!($value = $c->getTagValue('orm'))) { //warn('No @orm tag for var [' . $class_var . ']. Ignored.'); return false; } // parse var tag if (!($t = new epVarTag($value))) { throw new epExceptionParser('Cannot parse @orm tag for var [' . $class_var . ']'); return false; } // call field map factory to create a field map if (!($fm = epFieldMapFactory::make($var, $t->get('type'), $t->get('params')))) { return false; } // set column name if set if ($column_name = $t->get('name')) { $fm->setColumnName($column_name); } return $fm; }