Skip to content

Commit a29d70d

Browse files
authored
Merge pull request #613 from ArnabChatterjee20k/dat-557
Upsert fix for postgres
2 parents d5e57c2 + f9805a2 commit a29d70d

File tree

3 files changed

+299
-211
lines changed

3 files changed

+299
-211
lines changed

src/Database/Adapter/MariaDB.php

Lines changed: 49 additions & 189 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
use Exception;
66
use PDO;
77
use PDOException;
8-
use Utopia\Database\Change;
98
use Utopia\Database\Database;
109
use Utopia\Database\Document;
1110
use Utopia\Database\Exception as DatabaseException;
@@ -1148,206 +1147,67 @@ public function updateDocument(string $collection, string $id, Document $documen
11481147
}
11491148

11501149
/**
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
11521155
* @param string $attribute
1153-
* @param array<Change> $changes
1154-
* @return array<Document>
1155-
* @throws DatabaseException
1156+
* @return mixed
11561157
*/
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})";
12401171
} 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})";
12491173
}
12501174

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})";
12611177
}
12621178

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+
};
13151181

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));
13211196
}
1197+
}
13221198

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+
);
13321206

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));
13481209
}
1349-
1350-
return \array_map(fn ($change) => $change->getNew(), $changes);
1210+
return $stmt;
13511211
}
13521212

13531213
/**

0 commit comments

Comments
 (0)