Skip to content

Commit 9961ba2

Browse files
committed
Merge branch 'main' of github.com:utopia-php/database into primary-attribute
# Conflicts: # tests/unit/Validator/StructureTest.php
2 parents 07ab3f3 + cd961f1 commit 9961ba2

22 files changed

+2523
-423
lines changed

docker-compose.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ services:
1717
- ./dev/xdebug.ini:/usr/local/etc/php/conf.d/xdebug.ini
1818
- /var/run/docker.sock:/var/run/docker.sock
1919
- ./docker-compose.yml:/usr/src/code/docker-compose.yml
20+
environment:
21+
PHP_IDE_CONFIG: serverName=tests
2022

2123
adminer:
2224
image: adminer

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;
@@ -1151,206 +1150,67 @@ public function updateDocument(string $collection, string $id, Document $documen
11511150
}
11521151

11531152
/**
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
11551158
* @param string $attribute
1156-
* @param array<Change> $changes
1157-
* @return array<Document>
1158-
* @throws DatabaseException
1159+
* @return mixed
11591160
*/
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})";
12431174
} 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})";
12521176
}
12531177

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})";
12641180
}
12651181

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

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));
13241199
}
1200+
}
13251201

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

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));
13511212
}
1352-
1353-
return \array_map(fn ($change) => $change->getNew(), $changes);
1213+
return $stmt;
13541214
}
13551215

13561216
/**

0 commit comments

Comments
 (0)