|
5 | 5 | use Exception; |
6 | 6 | use PDO; |
7 | 7 | use PDOException; |
8 | | -use Utopia\Database\Change; |
9 | 8 | use Utopia\Database\Database; |
10 | 9 | use Utopia\Database\Document; |
11 | 10 | use Utopia\Database\Exception as DatabaseException; |
@@ -1151,206 +1150,67 @@ public function updateDocument(string $collection, string $id, Document $documen |
1151 | 1150 | } |
1152 | 1151 |
|
1153 | 1152 | /** |
1154 | | - * @param string $collection |
| 1153 | + * @param string $tableName |
| 1154 | + * @param string $columns |
| 1155 | + * @param array<string> $batchKeys |
| 1156 | + * @param array<string> $attributes |
| 1157 | + * @param array<mixed> $bindValues |
1155 | 1158 | * @param string $attribute |
1156 | | - * @param array<Change> $changes |
1157 | | - * @return array<Document> |
1158 | | - * @throws DatabaseException |
| 1159 | + * @return mixed |
1159 | 1160 | */ |
1160 | | - public function createOrUpdateDocuments( |
1161 | | - string $collection, |
1162 | | - string $attribute, |
1163 | | - array $changes |
1164 | | - ): array { |
1165 | | - if (empty($changes)) { |
1166 | | - return $changes; |
1167 | | - } |
1168 | | - |
1169 | | - try { |
1170 | | - $name = $this->filter($collection); |
1171 | | - $attribute = $this->filter($attribute); |
1172 | | - |
1173 | | - $attributes = []; |
1174 | | - $bindIndex = 0; |
1175 | | - $batchKeys = []; |
1176 | | - $bindValues = []; |
1177 | | - |
1178 | | - foreach ($changes as $change) { |
1179 | | - $document = $change->getNew(); |
1180 | | - $attributes = $document->getAttributes(); |
1181 | | - $attributes['_uid'] = $document->getId(); |
1182 | | - $attributes['_createdAt'] = $document->getCreatedAt(); |
1183 | | - $attributes['_updatedAt'] = $document->getUpdatedAt(); |
1184 | | - $attributes['_permissions'] = \json_encode($document->getPermissions()); |
1185 | | - |
1186 | | - if (!empty($document->getSequence())) { |
1187 | | - $attributes['_id'] = $document->getSequence(); |
1188 | | - } |
1189 | | - |
1190 | | - if ($this->sharedTables) { |
1191 | | - $attributes['_tenant'] = $document->getTenant(); |
1192 | | - } |
1193 | | - |
1194 | | - \ksort($attributes); |
1195 | | - |
1196 | | - $columns = []; |
1197 | | - foreach (\array_keys($attributes) as $key => $attr) { |
1198 | | - /** |
1199 | | - * @var string $attr |
1200 | | - */ |
1201 | | - $columns[$key] = "{$this->quote($this->filter($attr))}"; |
1202 | | - } |
1203 | | - $columns = '(' . \implode(', ', $columns) . ')'; |
1204 | | - |
1205 | | - $bindKeys = []; |
1206 | | - |
1207 | | - foreach ($attributes as $attrValue) { |
1208 | | - if (\is_array($attrValue)) { |
1209 | | - $attrValue = \json_encode($attrValue); |
1210 | | - } |
1211 | | - $attrValue = (\is_bool($attrValue)) ? (int)$attrValue : $attrValue; |
1212 | | - $bindKey = 'key_' . $bindIndex; |
1213 | | - $bindKeys[] = ':' . $bindKey; |
1214 | | - $bindValues[$bindKey] = $attrValue; |
1215 | | - $bindIndex++; |
1216 | | - } |
1217 | | - |
1218 | | - $batchKeys[] = '(' . \implode(', ', $bindKeys) . ')'; |
1219 | | - } |
1220 | | - |
1221 | | - $getUpdateClause = function (string $attribute, bool $increment = false): string { |
1222 | | - $attribute = $this->quote($this->filter($attribute)); |
1223 | | - |
1224 | | - if ($increment) { |
1225 | | - $new = "{$attribute} + VALUES({$attribute})"; |
1226 | | - } else { |
1227 | | - $new = "VALUES({$attribute})"; |
1228 | | - } |
1229 | | - |
1230 | | - if ($this->sharedTables) { |
1231 | | - return "{$attribute} = IF(_tenant = VALUES(_tenant), {$new}, {$attribute})"; |
1232 | | - } |
1233 | | - |
1234 | | - return "{$attribute} = {$new}"; |
1235 | | - }; |
1236 | | - |
1237 | | - if (!empty($attribute)) { |
1238 | | - // Increment specific column by its new value in place |
1239 | | - $updateColumns = [ |
1240 | | - $getUpdateClause($attribute, increment: true), |
1241 | | - $getUpdateClause('_updatedAt'), |
1242 | | - ]; |
| 1161 | + public function getUpsertStatement( |
| 1162 | + string $tableName, |
| 1163 | + string $columns, |
| 1164 | + array $batchKeys, |
| 1165 | + array $attributes, |
| 1166 | + array $bindValues, |
| 1167 | + string $attribute = '', |
| 1168 | + ): mixed { |
| 1169 | + $getUpdateClause = function (string $attribute, bool $increment = false): string { |
| 1170 | + $attribute = $this->quote($this->filter($attribute)); |
| 1171 | + |
| 1172 | + if ($increment) { |
| 1173 | + $new = "{$attribute} + VALUES({$attribute})"; |
1243 | 1174 | } else { |
1244 | | - // Update all columns |
1245 | | - $updateColumns = []; |
1246 | | - foreach (\array_keys($attributes) as $attr) { |
1247 | | - /** |
1248 | | - * @var string $attr |
1249 | | - */ |
1250 | | - $updateColumns[] = $getUpdateClause($this->filter($attr)); |
1251 | | - } |
| 1175 | + $new = "VALUES({$attribute})"; |
1252 | 1176 | } |
1253 | 1177 |
|
1254 | | - $stmt = $this->getPDO()->prepare( |
1255 | | - " |
1256 | | - INSERT INTO {$this->getSQLTable($name)} {$columns} |
1257 | | - VALUES " . \implode(', ', $batchKeys) . " |
1258 | | - ON DUPLICATE KEY UPDATE |
1259 | | - " . \implode(', ', $updateColumns) |
1260 | | - ); |
1261 | | - |
1262 | | - foreach ($bindValues as $key => $binding) { |
1263 | | - $stmt->bindValue($key, $binding, $this->getPDOType($binding)); |
| 1178 | + if ($this->sharedTables) { |
| 1179 | + return "{$attribute} = IF(_tenant = VALUES(_tenant), {$new}, {$attribute})"; |
1264 | 1180 | } |
1265 | 1181 |
|
1266 | | - $stmt->execute(); |
1267 | | - $stmt->closeCursor(); |
1268 | | - |
1269 | | - $removeQueries = []; |
1270 | | - $removeBindValues = []; |
1271 | | - $addQueries = []; |
1272 | | - $addBindValues = []; |
1273 | | - |
1274 | | - foreach ($changes as $index => $change) { |
1275 | | - $old = $change->getOld(); |
1276 | | - $document = $change->getNew(); |
1277 | | - |
1278 | | - $current = []; |
1279 | | - foreach (Database::PERMISSIONS as $type) { |
1280 | | - $current[$type] = $old->getPermissionsByType($type); |
1281 | | - } |
1282 | | - |
1283 | | - // Calculate removals |
1284 | | - foreach (Database::PERMISSIONS as $type) { |
1285 | | - $toRemove = \array_diff($current[$type], $document->getPermissionsByType($type)); |
1286 | | - if (!empty($toRemove)) { |
1287 | | - $removeQueries[] = "( |
1288 | | - _document = :_uid_{$index} |
1289 | | - " . ($this->sharedTables ? " AND _tenant = :_tenant_{$index}" : '') . " |
1290 | | - AND _type = '{$type}' |
1291 | | - AND _permission IN (" . \implode(',', \array_map(fn ($i) => ":remove_{$type}_{$index}_{$i}", \array_keys($toRemove))) . ") |
1292 | | - )"; |
1293 | | - $removeBindValues[":_uid_{$index}"] = $document->getId(); |
1294 | | - if ($this->sharedTables) { |
1295 | | - $removeBindValues[":_tenant_{$index}"] = $document->getTenant(); |
1296 | | - } |
1297 | | - foreach ($toRemove as $i => $perm) { |
1298 | | - $removeBindValues[":remove_{$type}_{$index}_{$i}"] = $perm; |
1299 | | - } |
1300 | | - } |
1301 | | - } |
1302 | | - |
1303 | | - // Calculate additions |
1304 | | - foreach (Database::PERMISSIONS as $type) { |
1305 | | - $toAdd = \array_diff($document->getPermissionsByType($type), $current[$type]); |
1306 | | - |
1307 | | - foreach ($toAdd as $i => $permission) { |
1308 | | - $addQuery = "(:_uid_{$index}, '{$type}', :add_{$type}_{$index}_{$i}"; |
1309 | | - |
1310 | | - if ($this->sharedTables) { |
1311 | | - $addQuery .= ", :_tenant_{$index}"; |
1312 | | - } |
1313 | | - |
1314 | | - $addQuery .= ")"; |
1315 | | - $addQueries[] = $addQuery; |
1316 | | - $addBindValues[":_uid_{$index}"] = $document->getId(); |
1317 | | - $addBindValues[":add_{$type}_{$index}_{$i}"] = $permission; |
| 1182 | + return "{$attribute} = {$new}"; |
| 1183 | + }; |
1318 | 1184 |
|
1319 | | - if ($this->sharedTables) { |
1320 | | - $addBindValues[":_tenant_{$index}"] = $document->getTenant(); |
1321 | | - } |
1322 | | - } |
1323 | | - } |
| 1185 | + if (!empty($attribute)) { |
| 1186 | + // Increment specific column by its new value in place |
| 1187 | + $updateColumns = [ |
| 1188 | + $getUpdateClause($attribute, increment: true), |
| 1189 | + $getUpdateClause('_updatedAt'), |
| 1190 | + ]; |
| 1191 | + } else { |
| 1192 | + // Update all columns |
| 1193 | + $updateColumns = []; |
| 1194 | + foreach (\array_keys($attributes) as $attr) { |
| 1195 | + /** |
| 1196 | + * @var string $attr |
| 1197 | + */ |
| 1198 | + $updateColumns[] = $getUpdateClause($this->filter($attr)); |
1324 | 1199 | } |
| 1200 | + } |
1325 | 1201 |
|
1326 | | - // Execute permission removals |
1327 | | - if (!empty($removeQueries)) { |
1328 | | - $removeQuery = \implode(' OR ', $removeQueries); |
1329 | | - $stmtRemovePermissions = $this->getPDO()->prepare("DELETE FROM {$this->getSQLTable($name . '_perms')} WHERE {$removeQuery}"); |
1330 | | - foreach ($removeBindValues as $key => $value) { |
1331 | | - $stmtRemovePermissions->bindValue($key, $value, $this->getPDOType($value)); |
1332 | | - } |
1333 | | - $stmtRemovePermissions->execute(); |
1334 | | - } |
| 1202 | + $stmt = $this->getPDO()->prepare( |
| 1203 | + " |
| 1204 | + INSERT INTO {$this->getSQLTable($tableName)} {$columns} |
| 1205 | + VALUES " . \implode(', ', $batchKeys) . " |
| 1206 | + ON DUPLICATE KEY UPDATE |
| 1207 | + " . \implode(', ', $updateColumns) |
| 1208 | + ); |
1335 | 1209 |
|
1336 | | - // Execute permission additions |
1337 | | - if (!empty($addQueries)) { |
1338 | | - $sqlAddPermissions = "INSERT INTO {$this->getSQLTable($name . '_perms')} (_document, _type, _permission"; |
1339 | | - if ($this->sharedTables) { |
1340 | | - $sqlAddPermissions .= ", _tenant"; |
1341 | | - } |
1342 | | - $sqlAddPermissions .= ") VALUES " . \implode(', ', $addQueries); |
1343 | | - $stmtAddPermissions = $this->getPDO()->prepare($sqlAddPermissions); |
1344 | | - foreach ($addBindValues as $key => $value) { |
1345 | | - $stmtAddPermissions->bindValue($key, $value, $this->getPDOType($value)); |
1346 | | - } |
1347 | | - $stmtAddPermissions->execute(); |
1348 | | - } |
1349 | | - } catch (PDOException $e) { |
1350 | | - throw $this->processException($e); |
| 1210 | + foreach ($bindValues as $key => $binding) { |
| 1211 | + $stmt->bindValue($key, $binding, $this->getPDOType($binding)); |
1351 | 1212 | } |
1352 | | - |
1353 | | - return \array_map(fn ($change) => $change->getNew(), $changes); |
| 1213 | + return $stmt; |
1354 | 1214 | } |
1355 | 1215 |
|
1356 | 1216 | /** |
|
0 commit comments