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