public function saveItems(array $itemData, EarthIT_Schema_ResourceClass $rc, array $options = array()) { if (count($itemData) == 0) { // Save a few processor cycles return $options[EarthIT_Storage_ItemSaver::RETURN_SAVED] ? array() : null; } EarthIT_Storage_Util::defaultSaveItemsOptions($options); $rcName = $rc->getName(); $blankItem = EarthIT_Storage_Util::defaultItem($rc, true); $savedItems = array(); foreach ($itemData as $item) { $id = EarthIT_Storage_Util::itemId($item, $rc); if ($id === null) { $pk = $rc->getPrimaryKey(); if ($pk !== null) { foreach ($pk->getFieldNames() as $fn) { if (!isset($item[$fn])) { if ($rc->getField($fn)->getType()->getName() == 'entity ID') { $item[$fn] = call_user_func($this->idGenerator); } else { throw new Exception("Don't know how to generate ID component '{$fn}' of '" . $rc->getName() . "'"); } } } } $id = EarthIT_Storage_Util::itemId($item, $rc); if ($id === null) { // Note: In some cases we may want to allow ID-less records, // in which case remove the throw and just append the item to wherever lists. throw new Exception("Failed to generate an ID for a " . $rc->getName()); } } $item = EarthIT_Storage_Util::castItemFieldValues($item, $rc); if (isset($this->items[$rcName][$id])) { switch ($odk = $options[EarthIT_Storage_ItemSaver::ON_DUPLICATE_KEY]) { case EarthIT_Storage_ItemSaver::ODK_ERROR: case EarthIT_Storage_ItemSaver::ODK_UNDEFINED: throw new Exception("saveItem causes collision for " . $rc->getName() . " '{$id}'"); case EarthIT_Storage_ItemSaver::ODK_KEEP: $savedItems[$id] = $this->items[$rcName][$id]; break; case EarthIT_Storage_ItemSaver::ODK_REPLACE: $savedItems[$id] = $this->items[$rcName][$id] = $item + $blankItem; break; case EarthIT_Storage_ItemSaver::ODK_UPDATE: $savedItems[$id] = $this->items[$rcName][$id] = $item + $this->items[$rcName][$id]; break; default: throw new Exception("Unrecognized on-duplicate-key option: '{$odk}'"); } } else { $savedItems[$id] = $this->items[$rcName][$id] = $item + $blankItem; } } return $options[EarthIT_Storage_ItemSaver::RETURN_SAVED] ? $savedItems : null; }
protected function _bulkInsertQueries(array $itemData, EarthIT_Schema_ResourceClass $rc, $returnSaved) { $storableFields = EarthIT_Storage_Util::storableFields($rc); $defaultItem = EarthIT_Storage_Util::defaultItem($rc); foreach ($itemData as &$item) { $item += $defaultItem; } unset($item); $fieldsToStore = self::ensureSameFieldsGivenForAllItems($itemData, $storableFields); $params = array(); $params['table'] = EarthIT_DBC_SQLExpressionUtil::tableExpression($rc, $this->dbObjectNamer); $paramCounter = 0; $columnNameParams = array(); $columnNamePlaceholders = array(); $toStoreColumnNamePlaceholders = array(); // Only the ones we're specifying in our insert foreach ($storableFields as $fn => $f) { $columnNameParam = "c_" . $paramCounter++; $columnNameParams[$fn] = $columnNameParam; $params[$columnNameParam] = new EarthIT_DBC_SQLIdentifier($this->dbObjectNamer->getColumnName($rc, $f)); $columnNamePlaceholder = "{{$columnNameParam}}"; $columnNamePlaceholders[$fn] = $columnNamePlaceholder; } foreach ($fieldsToStore as $fn => $f) { $columnNameParam = $columnNameParams[$fn]; $toStoreColumnNamePlaceholders[] = $columnNamePlaceholders[$fn]; } $columnDbExternalValueSqls = array(); if ($returnSaved) { foreach ($storableFields as $fn => $f) { $t = $this->dbInternalToExternalValueSql($f, $rc, $columnNamePlaceholders[$fn]); if ($t != $columnNamePlaceholders[$fn]) { $t .= " AS " . $columnNamePlaceholders[$fn]; } $columnDbExternalValueSqls[$fn] = $t; } } if (count($fieldsToStore) == 0) { if ($returnSaved) { throw new Exception("Returning saved records not supported for MySQL."); } // INSERT INTO ... DEFAULT VALUES doesn't seem to have a bulk form, // so we'll have to make multiple queries. // Fortunately they're pretty simple queries (actually just the same one repeated). return array_fill(0, count($itemData), new EarthIT_Storage_StorageQuery("INSERT INTO {table} DEFAULT VALUES", $params, $returnSaved)); } $valueRows = array(); foreach ($itemData as $item) { $valueSqls = array(); foreach ($fieldsToStore as $fn => $f) { $paramName = "v_" . $paramCounter++; $value = isset($item[$fn]) ? $item[$fn] : null; if ($value instanceof EarthIT_DBC_SQLQueryComponent) { $isAlreadyInternal = true; } else { if ($value instanceof EarthIT_Storage_InternalValue) { $isAlreadyInternal = true; $value = $value->getValue(); } else { $isAlreadyInternal = false; } } if ($isAlreadyInternal) { $valueSqls[] = "{{$paramName}}"; $params[$paramName] = $value; } else { $valueSqls[] = $this->dbExternalToInternalValueSql($f, $rc, "{{$paramName}}"); $params[$paramName] = $this->schemaToDbExternalValue($value, $f, $rc); } } $valueRows[] = "(" . implode(', ', $valueSqls) . ")"; } $sql = "INSERT INTO {table}\n" . "(" . implode(", ", $toStoreColumnNamePlaceholders) . ") VALUES\n" . implode(",\n", $valueRows); if ($returnSaved) { $sql .= "\nRETURNING " . implode(', ', $columnDbExternalValueSqls); } return array(new EarthIT_Storage_StorageQuery($sql, $params, $returnSaved)); }