Merge pull request #11673 from appwrite/big-int

Big int
This commit is contained in:
Jake Barnby
2026-05-06 20:17:45 +12:00
committed by GitHub
28 changed files with 1101 additions and 31 deletions
+1
View File
@@ -52,6 +52,7 @@ const APP_DATABASE_ATTRIBUTE_IP = 'ip';
const APP_DATABASE_ATTRIBUTE_DATETIME = 'datetime';
const APP_DATABASE_ATTRIBUTE_URL = 'url';
const APP_DATABASE_ATTRIBUTE_INT_RANGE = 'intRange';
const APP_DATABASE_ATTRIBUTE_BIGINT_RANGE = 'bigintRange';
const APP_DATABASE_ATTRIBUTE_FLOAT_RANGE = 'floatRange';
const APP_DATABASE_ATTRIBUTE_POINT = 'point';
const APP_DATABASE_ATTRIBUTE_LINE = 'line';
+7
View File
@@ -36,6 +36,13 @@ Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function ($attribute) {
return new Range($min, $max, Range::TYPE_INTEGER);
}, Database::VAR_INTEGER);
// BigInt uses a dedicated bigintRange format name to avoid clobbering `intRange`.
Structure::addFormat(APP_DATABASE_ATTRIBUTE_BIGINT_RANGE, function ($attribute) {
$min = $attribute['formatOptions']['min'] ?? -INF;
$max = $attribute['formatOptions']['max'] ?? INF;
return new Range($min, $max, Range::TYPE_INTEGER);
}, Database::VAR_BIGINT);
Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) {
$min = $attribute['formatOptions']['min'] ?? -INF;
$max = $attribute['formatOptions']['max'] ?? INF;
+4
View File
@@ -11,6 +11,7 @@ use Appwrite\Utopia\Response\Model\AlgoScryptModified;
use Appwrite\Utopia\Response\Model\AlgoSha;
use Appwrite\Utopia\Response\Model\Any;
use Appwrite\Utopia\Response\Model\Attribute;
use Appwrite\Utopia\Response\Model\AttributeBigInt;
use Appwrite\Utopia\Response\Model\AttributeBoolean;
use Appwrite\Utopia\Response\Model\AttributeDatetime;
use Appwrite\Utopia\Response\Model\AttributeEmail;
@@ -37,6 +38,7 @@ use Appwrite\Utopia\Response\Model\Branch;
use Appwrite\Utopia\Response\Model\Bucket;
use Appwrite\Utopia\Response\Model\Collection;
use Appwrite\Utopia\Response\Model\Column;
use Appwrite\Utopia\Response\Model\ColumnBigInt;
use Appwrite\Utopia\Response\Model\ColumnBoolean;
use Appwrite\Utopia\Response\Model\ColumnDatetime;
use Appwrite\Utopia\Response\Model\ColumnEmail;
@@ -297,6 +299,7 @@ Response::setModel(new Attribute());
Response::setModel(new AttributeList());
Response::setModel(new AttributeString());
Response::setModel(new AttributeInteger());
Response::setModel(new AttributeBigInt());
Response::setModel(new AttributeFloat());
Response::setModel(new AttributeBoolean());
Response::setModel(new AttributeEmail());
@@ -330,6 +333,7 @@ Response::setModel(new Column());
Response::setModel(new ColumnList());
Response::setModel(new ColumnString());
Response::setModel(new ColumnInteger());
Response::setModel(new ColumnBigInt());
Response::setModel(new ColumnFloat());
Response::setModel(new ColumnBoolean());
Response::setModel(new ColumnEmail());
+1 -1
View File
@@ -74,7 +74,7 @@
"utopia-php/locale": "0.8.*",
"utopia-php/logger": "0.6.*",
"utopia-php/messaging": "0.22.*",
"utopia-php/migration": "1.9.*",
"utopia-php/migration": "1.*",
"utopia-php/platform": "0.13.*",
"utopia-php/pools": "1.*",
"utopia-php/span": "1.1.*",
Generated
+13 -13
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "17ccba478a5cace1251b2211e25021f2",
"content-hash": "ec2ad489c60f0102f0dfab223b6d1fe4",
"packages": [
{
"name": "adhocore/jwt",
@@ -3850,16 +3850,16 @@
},
{
"name": "utopia-php/database",
"version": "5.6.0",
"version": "5.7.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "609ebcd64be1ec6fab00c5f46fce54acb0031b3c"
"reference": "eb35e68f7f90932d5a60bd72e70158ae7a4e0511"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/609ebcd64be1ec6fab00c5f46fce54acb0031b3c",
"reference": "609ebcd64be1ec6fab00c5f46fce54acb0031b3c",
"url": "https://api.github.com/repos/utopia-php/database/zipball/eb35e68f7f90932d5a60bd72e70158ae7a4e0511",
"reference": "eb35e68f7f90932d5a60bd72e70158ae7a4e0511",
"shasum": ""
},
"require": {
@@ -3904,9 +3904,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/5.6.0"
"source": "https://github.com/utopia-php/database/tree/5.7.0"
},
"time": "2026-05-01T01:28:07+00:00"
"time": "2026-05-06T01:04:08+00:00"
},
{
"name": "utopia-php/detector",
@@ -4531,16 +4531,16 @@
},
{
"name": "utopia-php/migration",
"version": "1.9.7",
"version": "1.10.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/migration.git",
"reference": "81b608a6871f56b70496803d12010823300aab6e"
"reference": "55f4863d690e775f44fec3cae4bd1f4491fed5ea"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/81b608a6871f56b70496803d12010823300aab6e",
"reference": "81b608a6871f56b70496803d12010823300aab6e",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/55f4863d690e775f44fec3cae4bd1f4491fed5ea",
"reference": "55f4863d690e775f44fec3cae4bd1f4491fed5ea",
"shasum": ""
},
"require": {
@@ -4580,9 +4580,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/1.9.7"
"source": "https://github.com/utopia-php/migration/tree/1.10.0"
},
"time": "2026-05-05T07:18:48+00:00"
"time": "2026-05-06T04:35:32+00:00"
},
{
"name": "utopia-php/mongo",
@@ -0,0 +1 @@
Create a bigint attribute. Optionally, minimum and maximum values can be provided.
@@ -0,0 +1 @@
Update a bigint attribute. Changing the `default` value will not update already existing documents.
@@ -0,0 +1 @@
Create a bigint column. Optionally, minimum and maximum values can be provided.
@@ -0,0 +1 @@
Update a bigint column. Changing the `default` value will not update already existing rows.
@@ -241,6 +241,10 @@ abstract class Action extends UtopiaAction
? UtopiaResponse::MODEL_ATTRIBUTE_INTEGER
: UtopiaResponse::MODEL_COLUMN_INTEGER,
Database::VAR_BIGINT => $isCollections
? UtopiaResponse::MODEL_ATTRIBUTE_BIGINT
: UtopiaResponse::MODEL_COLUMN_BIGINT,
Database::VAR_FLOAT => $isCollections
? UtopiaResponse::MODEL_ATTRIBUTE_FLOAT
: UtopiaResponse::MODEL_COLUMN_FLOAT,
@@ -540,6 +544,7 @@ abstract class Action extends UtopiaAction
switch ($attribute->getAttribute('format')) {
case APP_DATABASE_ATTRIBUTE_INT_RANGE:
case APP_DATABASE_ATTRIBUTE_BIGINT_RANGE:
case APP_DATABASE_ATTRIBUTE_FLOAT_RANGE:
$min ??= $attribute->getAttribute('formatOptions')['min'];
$max ??= $attribute->getAttribute('formatOptions')['max'];
@@ -548,14 +553,15 @@ abstract class Action extends UtopiaAction
throw new Exception($this->getInvalidValueException(), 'Minimum value must be lesser than maximum value');
}
if ($attribute->getAttribute('format') === APP_DATABASE_ATTRIBUTE_INT_RANGE) {
$validator = new Range($min, $max, Database::VAR_INTEGER);
} else {
if ($attribute->getAttribute('format') === APP_DATABASE_ATTRIBUTE_FLOAT_RANGE) {
$validator = new Range($min, $max, Database::VAR_FLOAT);
if (!is_null($default)) {
$default = \floatval($default);
}
} else {
// intRange and bigintRange share the same integer range semantics
$validator = new Range($min, $max, Range::TYPE_INTEGER);
}
if (!is_null($default) && !$validator->isValid($default)) {
@@ -0,0 +1,117 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt;
use Appwrite\Event\Database as EventDatabase;
use Appwrite\Event\Event;
use Appwrite\Extend\Exception;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Action;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Deprecated;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\Integer;
use Utopia\Validator\Nullable;
use Utopia\Validator\Range;
class Create extends Action
{
public static function getName(): string
{
return 'createBigIntAttribute';
}
protected function getResponseModel(): string|array
{
return UtopiaResponse::MODEL_ATTRIBUTE_BIGINT;
}
public function __construct()
{
$this
->setHttpMethod(self::HTTP_REQUEST_METHOD_POST)
->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/bigint')
->desc('Create bigint attribute')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].create')
->label('audits.event', 'attribute.create')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: $this->getSDKNamespace(),
group: $this->getSDKGroup(),
name: self::getName(),
description: '/docs/references/databases/create-bigint-attribute.md',
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_ACCEPTED,
model: $this->getResponseModel(),
)
],
deprecated: new Deprecated(
since: '1.8.0',
replaceWith: 'tablesDB.createBigIntColumn',
),
))
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('min', null, new Nullable(new Integer(false, 64)), 'Minimum value', true)
->param('max', null, new Nullable(new Integer(false, 64)), 'Maximum value', true)
->param('default', null, new Nullable(new Integer(false, 64)), 'Default value. Cannot be set when attribute is required.', true)
->param('array', false, new Boolean(), 'Is attribute an array?', true)
->inject('response')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, UtopiaResponse $response, Database $dbForProject, EventDatabase $queueForDatabase, Event $queueForEvents, Authorization $authorization): void
{
$min ??= \PHP_INT_MIN;
$max ??= \PHP_INT_MAX;
if ($min > $max) {
throw new Exception($this->getInvalidValueException(), 'Minimum value must be lesser than maximum value');
}
$validator = new Range($min, $max, Range::TYPE_INTEGER);
if (!\is_null($default) && !$validator->isValid($default)) {
throw new Exception($this->getInvalidValueException(), $validator->getDescription());
}
$attribute = $this->createAttribute($databaseId, $collectionId, new Document([
'key' => $key,
'type' => Database::VAR_BIGINT,
'size' => 8,
'required' => $required,
'default' => $default,
'array' => $array,
'format' => APP_DATABASE_ATTRIBUTE_BIGINT_RANGE,
'formatOptions' => ['min' => $min, 'max' => $max],
]), $response, $dbForProject, $queueForDatabase, $queueForEvents, $authorization);
$formatOptions = $attribute->getAttribute('formatOptions', []);
if (!empty($formatOptions)) {
$attribute->setAttribute('min', \intval($formatOptions['min']));
$attribute->setAttribute('max', \intval($formatOptions['max']));
}
$response
->setStatusCode(SwooleResponse::STATUS_CODE_ACCEPTED)
->dynamic($attribute, $this->getResponseModel());
}
}
@@ -0,0 +1,106 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt;
use Appwrite\Event\Event;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Action;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Deprecated;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\Integer;
use Utopia\Validator\Nullable;
class Update extends Action
{
public static function getName(): string
{
return 'updateBigIntAttribute';
}
protected function getResponseModel(): string|array
{
return UtopiaResponse::MODEL_ATTRIBUTE_BIGINT;
}
public function __construct()
{
$this
->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH)
->setHttpPath('/v1/databases/:databaseId/collections/:collectionId/attributes/bigint/:key')
->desc('Update bigint attribute')
->groups(['api', 'database', 'schema'])
->label('scope', 'collections.write')
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].collections.[collectionId].attributes.[attributeId].update')
->label('audits.event', 'attribute.update')
->label('audits.resource', 'database/{request.databaseId}/collection/{request.collectionId}')
->label('sdk', new Method(
namespace: $this->getSDKNamespace(),
group: $this->getSDKGroup(),
name: self::getName(),
description: '/docs/references/databases/update-bigint-attribute.md',
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_OK,
model: $this->getResponseModel(),
)
],
contentType: ContentType::JSON,
deprecated: new Deprecated(
since: '1.8.0',
replaceWith: 'tablesDB.updateBigIntColumn',
),
))
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('collectionId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Collection ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Attribute Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is attribute required?')
->param('min', null, new Nullable(new Integer(false, 64)), 'Minimum value', true)
->param('max', null, new Nullable(new Integer(false, 64)), 'Maximum value', true)
->param('default', null, new Nullable(new Integer(false, 64)), 'Default value. Cannot be set when attribute is required.')
->param('newKey', null, fn (Database $dbForProject) => new Nullable(new Key(false, $dbForProject->getAdapter()->getMaxUIDLength())), 'New Attribute Key.', true, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
public function action(string $databaseId, string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, ?string $newKey, UtopiaResponse $response, Database $dbForProject, Event $queueForEvents, Authorization $authorization): void
{
$attribute = $this->updateAttribute(
databaseId: $databaseId,
collectionId: $collectionId,
key: $key,
dbForProject: $dbForProject,
queueForEvents: $queueForEvents,
authorization: $authorization,
type: Database::VAR_BIGINT,
default: $default,
required: $required,
min: $min,
max: $max,
newKey: $newKey
);
$formatOptions = $attribute->getAttribute('formatOptions', []);
if (!empty($formatOptions)) {
$attribute->setAttribute('min', \intval($formatOptions['min']));
$attribute->setAttribute('max', \intval($formatOptions['max']));
}
$response
->setStatusCode(SwooleResponse::STATUS_CODE_OK)
->dynamic($attribute, $this->getResponseModel());
}
}
@@ -290,13 +290,15 @@ class Create extends Action
}
if (isset($attribute['min']) || isset($attribute['max'])) {
$format = $type === Database::VAR_INTEGER
? APP_DATABASE_ATTRIBUTE_INT_RANGE
: APP_DATABASE_ATTRIBUTE_FLOAT_RANGE;
$format = match($type) {
Database::VAR_INTEGER => APP_DATABASE_ATTRIBUTE_INT_RANGE,
Database::VAR_BIGINT => APP_DATABASE_ATTRIBUTE_BIGINT_RANGE,
default => APP_DATABASE_ATTRIBUTE_FLOAT_RANGE,
};
$formatOptions = [
'min' => $attribute['min'] ?? ($type === Database::VAR_INTEGER ? \PHP_INT_MIN : -\PHP_FLOAT_MAX),
'max' => $attribute['max'] ?? ($type === Database::VAR_INTEGER ? \PHP_INT_MAX : \PHP_FLOAT_MAX),
'min' => $attribute['min'] ?? ($type === Database::VAR_INTEGER || $type === Database::VAR_BIGINT ? \PHP_INT_MIN : -\PHP_FLOAT_MAX),
'max' => $attribute['max'] ?? ($type === Database::VAR_INTEGER || $type === Database::VAR_BIGINT ? \PHP_INT_MAX : \PHP_FLOAT_MAX),
];
}
@@ -0,0 +1,70 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\BigInt;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt\Create as BigIntCreate;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\Integer;
use Utopia\Validator\Nullable;
class Create extends BigIntCreate
{
public static function getName(): string
{
return 'createBigIntColumn';
}
protected function getResponseModel(): string|array
{
return UtopiaResponse::MODEL_COLUMN_BIGINT;
}
public function __construct()
{
$this
->setHttpMethod(self::HTTP_REQUEST_METHOD_POST)
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/bigint')
->desc('Create bigint column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].create')
->label('audits.event', 'column.create')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: $this->getSDKNamespace(),
group: $this->getSDKGroup(),
name: self::getName(),
description: '/docs/references/tablesdb/create-bigint-column.md',
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_ACCEPTED,
model: $this->getResponseModel(),
)
]
))
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('tableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Table ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Column Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is column required?')
->param('min', null, new Nullable(new Integer(false, 64)), 'Minimum value', true)
->param('max', null, new Nullable(new Integer(false, 64)), 'Maximum value', true)
->param('default', null, new Nullable(new Integer(false, 64)), 'Default value. Cannot be set when column is required.', true)
->param('array', false, new Boolean(), 'Is column an array?', true)
->inject('response')
->inject('dbForProject')
->inject('queueForDatabase')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
}
@@ -0,0 +1,71 @@
<?php
namespace Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\BigInt;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt\Update as BigIntUpdate;
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
use Appwrite\Utopia\Response as UtopiaResponse;
use Utopia\Database\Database;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\UID;
use Utopia\Http\Adapter\Swoole\Response as SwooleResponse;
use Utopia\Validator\Boolean;
use Utopia\Validator\Integer;
use Utopia\Validator\Nullable;
class Update extends BigIntUpdate
{
public static function getName(): string
{
return 'updateBigIntColumn';
}
protected function getResponseModel(): string|array
{
return UtopiaResponse::MODEL_COLUMN_BIGINT;
}
public function __construct()
{
$this
->setHttpMethod(self::HTTP_REQUEST_METHOD_PATCH)
->setHttpPath('/v1/tablesdb/:databaseId/tables/:tableId/columns/bigint/:key')
->desc('Update bigint column')
->groups(['api', 'database', 'schema'])
->label('scope', ['tables.write', 'collections.write', 'columns.write', 'attributes.write'])
->label('resourceType', RESOURCE_TYPE_DATABASES)
->label('event', 'databases.[databaseId].tables.[tableId].columns.[columnId].update')
->label('audits.event', 'column.update')
->label('audits.resource', 'database/{request.databaseId}/table/{request.tableId}')
->label('sdk', new Method(
namespace: $this->getSDKNamespace(),
group: $this->getSDKGroup(),
name: self::getName(),
description: '/docs/references/tablesdb/update-bigint-column.md',
auth: [AuthType::ADMIN, AuthType::KEY],
responses: [
new SDKResponse(
code: SwooleResponse::STATUS_CODE_OK,
model: $this->getResponseModel(),
)
],
contentType: ContentType::JSON
))
->param('databaseId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Database ID.', false, ['dbForProject'])
->param('tableId', '', fn (Database $dbForProject) => new UID($dbForProject->getAdapter()->getMaxUIDLength()), 'Table ID.', false, ['dbForProject'])
->param('key', '', fn (Database $dbForProject) => new Key(false, $dbForProject->getAdapter()->getMaxUIDLength()), 'Column Key.', false, ['dbForProject'])
->param('required', null, new Boolean(), 'Is column required?')
->param('min', null, new Nullable(new Integer(false, 64)), 'Minimum value', true)
->param('max', null, new Nullable(new Integer(false, 64)), 'Maximum value', true)
->param('default', null, new Nullable(new Integer(false, 64)), 'Default value. Cannot be set when column is required.')
->param('newKey', null, fn (Database $dbForProject) => new Nullable(new Key(false, $dbForProject->getAdapter()->getMaxUIDLength())), 'New Column Key.', true, ['dbForProject'])
->inject('response')
->inject('dbForProject')
->inject('queueForEvents')
->inject('authorization')
->callback($this->action(...));
}
}
@@ -2,6 +2,8 @@
namespace Appwrite\Platform\Modules\Databases\Services\Registry;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt\Create as CreateBigIntAttribute;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\BigInt\Update as UpdateBigIntAttribute;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Boolean\Create as CreateBooleanAttribute;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Boolean\Update as UpdateBooleanAttribute;
use Appwrite\Platform\Modules\Databases\Http\Databases\Collections\Attributes\Datetime\Create as CreateDatetimeAttribute;
@@ -171,6 +173,10 @@ class Legacy extends Base
$service->addAction(CreateIntegerAttribute::getName(), new CreateIntegerAttribute());
$service->addAction(UpdateIntegerAttribute::getName(), new UpdateIntegerAttribute());
// Attribute: BigInt
$service->addAction(CreateBigIntAttribute::getName(), new CreateBigIntAttribute());
$service->addAction(UpdateBigIntAttribute::getName(), new UpdateBigIntAttribute());
// Attribute: IP
$service->addAction(CreateIPAttribute::getName(), new CreateIPAttribute());
$service->addAction(UpdateIPAttribute::getName(), new UpdateIPAttribute());
@@ -5,6 +5,8 @@ namespace Appwrite\Platform\Modules\Databases\Services\Registry;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Create as CreateTablesDatabase;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Delete as DeleteTablesDatabase;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Get as GetTablesDatabase;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\BigInt\Create as CreateBigInt;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\BigInt\Update as UpdateBigInt;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Boolean\Create as CreateBoolean;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Boolean\Update as UpdateBoolean;
use Appwrite\Platform\Modules\Databases\Http\TablesDB\Tables\Columns\Datetime\Create as CreateDatetime;
@@ -151,6 +153,10 @@ class TablesDB extends Base
$service->addAction(CreateInteger::getName(), new CreateInteger());
$service->addAction(UpdateInteger::getName(), new UpdateInteger());
// Column: BigInt
$service->addAction(CreateBigInt::getName(), new CreateBigInt());
$service->addAction(UpdateBigInt::getName(), new UpdateBigInt());
// Column: IP
$service->addAction(CreateIP::getName(), new CreateIP());
$service->addAction(UpdateIP::getName(), new UpdateIP());
@@ -437,6 +437,15 @@ class OpenAPI3 extends Format
$node['schema']['type'] = $validator->getType();
$node['schema']['x-example'] = ($param['example'] ?? '') ?: '<' . \strtoupper(Template::fromCamelCaseToSnake($node['name'])) . '>';
break;
case \Utopia\Database\Validator\BigInt::class:
// BigInt validator reports Database::VAR_BIGINT, but OpenAPI expects scalar types.
// We expose it as int64 to keep schema consistent with Column/Attribute models.
$node['schema']['type'] = 'integer';
$node['schema']['format'] = 'int64';
if (!empty($param['example'])) {
$node['schema']['x-example'] = $param['example'];
}
break;
case \Utopia\Validator\Boolean::class:
$node['schema']['type'] = $validator->getType();
$node['schema']['x-example'] = ($param['example'] ?? '') ?: false;
@@ -23,6 +23,7 @@ class Attributes extends Validator
protected array $supportedTypes = [
Database::VAR_STRING,
Database::VAR_INTEGER,
Database::VAR_BIGINT,
Database::VAR_FLOAT,
Database::VAR_BOOLEAN,
Database::VAR_DATETIME,
@@ -181,9 +182,9 @@ class Attributes extends Validator
return false;
}
// Validate signed only for integer/float types
if (isset($attribute['signed']) && !in_array($attribute['type'], [Database::VAR_INTEGER, Database::VAR_FLOAT])) {
$this->message = "Attribute '" . $attribute['key'] . "': 'signed' can only be used with integer or float types";
// Validate signed only for integer/bigint/float types
if (isset($attribute['signed']) && !in_array($attribute['type'], [Database::VAR_INTEGER, Database::VAR_BIGINT, Database::VAR_FLOAT])) {
$this->message = "Attribute '" . $attribute['key'] . "': 'signed' can only be used with integer, bigint or float types";
return false;
}
@@ -199,10 +200,10 @@ class Attributes extends Validator
return false;
}
// Validate min/max range for integer/float
// Validate min/max range for integer/bigint/float
if (isset($attribute['min']) || isset($attribute['max'])) {
if (!in_array($attribute['type'], [Database::VAR_INTEGER, Database::VAR_FLOAT])) {
$this->message = "Attribute '" . $attribute['key'] . "': min/max can only be used with integer or float types";
if (!in_array($attribute['type'], [Database::VAR_INTEGER, Database::VAR_BIGINT, Database::VAR_FLOAT])) {
$this->message = "Attribute '" . $attribute['key'] . "': min/max can only be used with integer, bigint or float types";
return false;
}
@@ -264,7 +265,7 @@ class Attributes extends Validator
if (isset($attribute['min']) || isset($attribute['max'])) {
$min = $attribute['min'] ?? \PHP_INT_MIN;
$max = $attribute['max'] ?? \PHP_INT_MAX;
$rangeValidator = new Range($min, $max, Database::VAR_INTEGER);
$rangeValidator = new Range($min, $max, Range::TYPE_INTEGER);
if (!$rangeValidator->isValid($attribute['default'])) {
$this->message = "Default value for integer attribute '" . $attribute['key'] . "' must be between $min and $max";
return false;
@@ -272,6 +273,23 @@ class Attributes extends Validator
}
break;
case Database::VAR_BIGINT:
if (!is_int($attribute['default'])) {
$this->message = "Default value for bigint attribute '" . $attribute['key'] . "' must be an integer";
return false;
}
// Validate within range if min/max specified
if (isset($attribute['min']) || isset($attribute['max'])) {
$min = $attribute['min'] ?? \PHP_INT_MIN;
$max = $attribute['max'] ?? \PHP_INT_MAX;
$rangeValidator = new Range($min, $max, Range::TYPE_INTEGER);
if (!$rangeValidator->isValid($attribute['default'])) {
$this->message = "Default value for bigint attribute '" . $attribute['key'] . "' must be between $min and $max";
return false;
}
}
break;
case Database::VAR_FLOAT:
if (!is_float($attribute['default']) && !is_int($attribute['default'])) {
$this->message = "Default value for float attribute '" . $attribute['key'] . "' must be a number";
@@ -281,7 +299,7 @@ class Attributes extends Validator
if (isset($attribute['min']) || isset($attribute['max'])) {
$min = $attribute['min'] ?? -\PHP_FLOAT_MAX;
$max = $attribute['max'] ?? \PHP_FLOAT_MAX;
$rangeValidator = new Range($min, $max, Database::VAR_FLOAT);
$rangeValidator = new Range($min, $max, Range::TYPE_FLOAT);
if (!$rangeValidator->isValid((float)$attribute['default'])) {
$this->message = "Default value for float attribute '" . $attribute['key'] . "' must be between $min and $max";
return false;
+2
View File
@@ -72,6 +72,7 @@ class Response extends SwooleResponse
public const MODEL_ATTRIBUTE_LIST = 'attributeList';
public const MODEL_ATTRIBUTE_STRING = 'attributeString';
public const MODEL_ATTRIBUTE_INTEGER = 'attributeInteger';
public const MODEL_ATTRIBUTE_BIGINT = 'attributeBigint';
public const MODEL_ATTRIBUTE_FLOAT = 'attributeFloat';
public const MODEL_ATTRIBUTE_BOOLEAN = 'attributeBoolean';
public const MODEL_ATTRIBUTE_EMAIL = 'attributeEmail';
@@ -95,6 +96,7 @@ class Response extends SwooleResponse
public const MODEL_COLUMN_LIST = 'columnList';
public const MODEL_COLUMN_STRING = 'columnString';
public const MODEL_COLUMN_INTEGER = 'columnInteger';
public const MODEL_COLUMN_BIGINT = 'columnBigint';
public const MODEL_COLUMN_FLOAT = 'columnFloat';
public const MODEL_COLUMN_BOOLEAN = 'columnBoolean';
public const MODEL_COLUMN_EMAIL = 'columnEmail';
@@ -0,0 +1,66 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
class AttributeBigInt extends Attribute
{
public function __construct()
{
parent::__construct();
$this
->addRule('key', [
'type' => self::TYPE_STRING,
'description' => 'Attribute Key.',
'default' => '',
'example' => 'count',
])
->addRule('type', [
'type' => self::TYPE_STRING,
'description' => 'Attribute type.',
'default' => '',
'example' => 'bigint',
])
->addRule('min', [
'type' => self::TYPE_INTEGER,
'format' => 'int64',
'description' => 'Minimum value to enforce for new documents.',
'default' => null,
'required' => false,
'example' => 1,
])
->addRule('max', [
'type' => self::TYPE_INTEGER,
'format' => 'int64',
'description' => 'Maximum value to enforce for new documents.',
'default' => null,
'required' => false,
'example' => 10,
])
->addRule('default', [
'type' => self::TYPE_INTEGER,
'format' => 'int64',
'description' => 'Default value for attribute when not provided. Cannot be set when attribute is required.',
'default' => null,
'required' => false,
'example' => 10,
])
;
}
public array $conditions = [
'type' => 'bigint'
];
public function getName(): string
{
return 'AttributeBigInt';
}
public function getType(): string
{
return Response::MODEL_ATTRIBUTE_BIGINT;
}
}
@@ -19,6 +19,9 @@ class AttributeList extends Model
->addRule('attributes', [
'type' => [
Response::MODEL_ATTRIBUTE_BOOLEAN,
// BigInt must come before Integer: response model dispatch is "first match wins",
// and Integer matches all int types (including bigint), while BigInt is more specific (size=8).
Response::MODEL_ATTRIBUTE_BIGINT,
Response::MODEL_ATTRIBUTE_INTEGER,
Response::MODEL_ATTRIBUTE_FLOAT,
Response::MODEL_ATTRIBUTE_EMAIL,
@@ -62,6 +62,9 @@ class Collection extends Model
->addRule('attributes', [
'type' => [
Response::MODEL_ATTRIBUTE_BOOLEAN,
// BigInt must come before Integer: response model dispatch is "first match wins",
// and Integer matches all int types (including bigint), while BigInt is more specific (size=8).
Response::MODEL_ATTRIBUTE_BIGINT,
Response::MODEL_ATTRIBUTE_INTEGER,
Response::MODEL_ATTRIBUTE_FLOAT,
Response::MODEL_ATTRIBUTE_EMAIL,
@@ -0,0 +1,66 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
class ColumnBigInt extends Column
{
public function __construct()
{
parent::__construct();
$this
->addRule('key', [
'type' => self::TYPE_STRING,
'description' => 'Column Key.',
'default' => '',
'example' => 'count',
])
->addRule('type', [
'type' => self::TYPE_STRING,
'description' => 'Column type.',
'default' => '',
'example' => 'bigint',
])
->addRule('min', [
'type' => self::TYPE_INTEGER,
'format' => 'int64',
'description' => 'Minimum value to enforce for new documents.',
'default' => null,
'required' => false,
'example' => 1,
])
->addRule('max', [
'type' => self::TYPE_INTEGER,
'format' => 'int64',
'description' => 'Maximum value to enforce for new documents.',
'default' => null,
'required' => false,
'example' => 10,
])
->addRule('default', [
'type' => self::TYPE_INTEGER,
'format' => 'int64',
'description' => 'Default value for column when not provided. Cannot be set when column is required.',
'default' => null,
'required' => false,
'example' => 10,
])
;
}
public array $conditions = [
'type' => 'bigint'
];
public function getName(): string
{
return 'ColumnBigInt';
}
public function getType(): string
{
return Response::MODEL_COLUMN_BIGINT;
}
}
@@ -19,6 +19,9 @@ class ColumnList extends Model
->addRule('columns', [
'type' => [
Response::MODEL_COLUMN_BOOLEAN,
// BigInt must come before Integer: response model dispatch is "first match wins",
// and Integer matches all int types (including bigint), while BigInt is more specific (size=8).
Response::MODEL_COLUMN_BIGINT,
Response::MODEL_COLUMN_INTEGER,
Response::MODEL_COLUMN_FLOAT,
Response::MODEL_COLUMN_EMAIL,
@@ -63,6 +63,9 @@ class Table extends Model
->addRule('columns', [
'type' => [
Response::MODEL_COLUMN_BOOLEAN,
// BigInt must come before Integer: response model dispatch is "first match wins",
// and Integer matches all int types (including bigint), while BigInt is more specific (size=8).
Response::MODEL_COLUMN_BIGINT,
Response::MODEL_COLUMN_INTEGER,
Response::MODEL_COLUMN_FLOAT,
Response::MODEL_COLUMN_EMAIL,
@@ -1256,7 +1256,6 @@ trait MigrationsBase
'max' => 65,
'required' => true,
]);
$this->assertEquals(202, $response['headers']['status-code']);
$this->assertEquals($response['body']['key'], 'age');
$this->assertEquals($response['body']['type'], 'integer');
@@ -1573,6 +1572,19 @@ trait MigrationsBase
$this->assertEquals(202, $varchar['headers']['status-code']);
$bigint = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/bigint', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey']
], [
'key' => 'bigint',
'min' => 2147483648,
'max' => 9223372036854775807,
'required' => false,
]);
$this->assertEquals(202, $bigint['headers']['status-code']);
$mediumtext = $this->client->call(Client::METHOD_POST, '/databases/' . $databaseId . '/collections/' . $collectionId . '/attributes/mediumtext', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
@@ -1623,6 +1635,7 @@ trait MigrationsBase
'mediumtext' => 'mediumText',
'longtext' => 'longText',
'varchar' => 'varchar',
'bigint' => 2147483648 + $i,
]
]);
@@ -1711,6 +1724,8 @@ trait MigrationsBase
$this->assertStringContainsString('mediumText', $csvData, 'CSV should contain the medium column header');
$this->assertStringContainsString('longText', $csvData, 'CSV should contain the long text column header');
$this->assertStringContainsString('varchar', $csvData, 'CSV should contain the varchar column header');
$this->assertStringContainsString('bigint', $csvData, 'CSV should contain the bigint column header');
$this->assertStringContainsString('2147483649', $csvData, 'CSV should contain bigint test data');
// Cleanup
$this->client->call(Client::METHOD_DELETE, '/databases/' . $databaseId, [
@@ -0,0 +1,482 @@
<?php
namespace Tests\E2E\Services\TablesDB;
use Tests\E2E\Client;
use Tests\E2E\Scopes\ApiTablesDB;
use Tests\E2E\Scopes\ProjectCustom;
use Tests\E2E\Scopes\SchemaPolling;
use Tests\E2E\Scopes\Scope;
use Tests\E2E\Scopes\SideServer;
use Tests\E2E\Traits\DatabasesUrlHelpers;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
class DatabasesNumericTypesTest extends Scope
{
use ProjectCustom;
use SideServer;
use ApiTablesDB;
use DatabasesUrlHelpers;
use SchemaPolling;
private static array $setupCache = [];
/**
* Setup database, table, and numeric columns for parallel-safe tests.
*/
protected function setupDatabaseAndTable(): array
{
$cacheKey = $this->getProject()['$id'] ?? 'default';
if (!empty(self::$setupCache[$cacheKey])) {
return self::$setupCache[$cacheKey];
}
$projectId = $this->getProject()['$id'];
$apiKey = $this->getProject()['apiKey'];
$headers = [
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apiKey,
];
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', $headers, [
'databaseId' => ID::unique(),
'name' => 'Numeric Types Test Database',
]);
$this->assertEquals(201, $database['headers']['status-code']);
$databaseId = $database['body']['$id'];
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', $headers, [
'tableId' => ID::unique(),
'name' => 'Numeric Types Table',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::any()),
Permission::read(Role::any()),
],
]);
$this->assertEquals(201, $table['headers']['status-code']);
$tableId = $table['body']['$id'];
// Create integer column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', $headers, [
'key' => 'integer_field',
'required' => false,
'min' => -10,
'max' => 10,
'default' => 0,
]);
// Create bigint column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/bigint', $headers, [
'key' => 'bigint_field',
'required' => false,
'min' => -9007199254740991,
'max' => 9007199254740991,
'default' => 9007199254740000,
]);
// Create unsigned integer column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', $headers, [
'key' => 'unsigned_int_field',
'required' => false,
'min' => 0,
'max' => 100,
'default' => 0,
'signed' => false,
]);
// Create unsigned bigint column
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/bigint', $headers, [
'key' => 'unsigned_bigint_field',
'required' => false,
'min' => 0,
'max' => 9223372036854775807,
'default' => 0,
'signed' => false,
]);
// Cache before waiting so that if waitForAllAttributes times out,
// subsequent calls don't try to re-create the same columns (causing 409)
self::$setupCache[$cacheKey] = [
'databaseId' => $databaseId,
'tableId' => $tableId,
];
// Wait for all columns to be available
$this->waitForAllAttributes($databaseId, $tableId);
return self::$setupCache[$cacheKey];
}
/**
* Setup database/table without caching so mutations (update/delete) don't
* affect other tests that might be executed in a different order.
*/
protected function setupFreshDatabaseAndTable(): array
{
$projectId = $this->getProject()['$id'];
$apiKey = $this->getProject()['apiKey'];
$headers = [
'content-type' => 'application/json',
'x-appwrite-project' => $projectId,
'x-appwrite-key' => $apiKey,
];
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', $headers, [
'databaseId' => ID::unique(),
'name' => 'Numeric Types Test Database',
]);
$this->assertEquals(201, $database['headers']['status-code']);
$databaseId = $database['body']['$id'];
$table = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables', $headers, [
'tableId' => ID::unique(),
'name' => 'Numeric Types Table',
'rowSecurity' => true,
'permissions' => [
Permission::create(Role::any()),
Permission::read(Role::any()),
],
]);
$this->assertEquals(201, $table['headers']['status-code']);
$tableId = $table['body']['$id'];
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', $headers, [
'key' => 'integer_field',
'required' => false,
'min' => -10,
'max' => 10,
'default' => 0,
]);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/bigint', $headers, [
'key' => 'bigint_field',
'required' => false,
'min' => -9007199254740991,
'max' => 9007199254740991,
'default' => 9007199254740000,
]);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer', $headers, [
'key' => 'unsigned_int_field',
'required' => false,
'max' => 100,
'default' => 0,
'signed' => false,
]);
$this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/bigint', $headers, [
'key' => 'unsigned_bigint_field',
'required' => false,
'max' => 9223372036854775807,
'default' => 0,
'signed' => false,
]);
$this->waitForAllAttributes($databaseId, $tableId);
return [
'databaseId' => $databaseId,
'tableId' => $tableId,
];
}
public function testCreateDatabase(): void
{
$database = $this->client->call(Client::METHOD_POST, '/tablesdb', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'databaseId' => ID::unique(),
'name' => 'Numeric Types Test Database',
]);
$this->assertEquals(201, $database['headers']['status-code']);
}
public function testCreateTable(): void
{
$data = $this->setupDatabaseAndTable();
$table = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $data['databaseId'] . '/tables/' . $data['tableId'], [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $table['headers']['status-code']);
$this->assertEquals($data['tableId'], $table['body']['$id']);
}
public function testGetIntegerAndBigIntColumns(): void
{
$data = $this->setupDatabaseAndTable();
$databaseId = $data['databaseId'];
$tableId = $data['tableId'];
$integerColumn = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer_field', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $integerColumn['headers']['status-code']);
$this->assertEquals('integer_field', $integerColumn['body']['key']);
$this->assertEquals('integer', $integerColumn['body']['type']);
$this->assertEquals(false, $integerColumn['body']['required']);
$this->assertEquals(false, $integerColumn['body']['array']);
$this->assertEquals(-10, $integerColumn['body']['min']);
$this->assertEquals(10, $integerColumn['body']['max']);
$this->assertEquals(0, $integerColumn['body']['default']);
$bigintColumn = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/bigint_field', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $bigintColumn['headers']['status-code']);
$this->assertEquals('bigint_field', $bigintColumn['body']['key']);
$this->assertEquals('bigint', $bigintColumn['body']['type']);
$this->assertEquals(false, $bigintColumn['body']['required']);
$this->assertEquals(false, $bigintColumn['body']['array']);
$this->assertEquals(-9007199254740991, $bigintColumn['body']['min']);
$this->assertEquals(9007199254740991, $bigintColumn['body']['max']);
$this->assertEquals(9007199254740000, $bigintColumn['body']['default']);
}
public function testGetUnsignedIntegerAndBigIntColumns(): void
{
$data = $this->setupDatabaseAndTable();
$databaseId = $data['databaseId'];
$tableId = $data['tableId'];
$unsignedIntColumn = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/unsigned_int_field', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $unsignedIntColumn['headers']['status-code']);
$this->assertEquals('unsigned_int_field', $unsignedIntColumn['body']['key']);
$this->assertEquals('integer', $unsignedIntColumn['body']['type']);
$this->assertEquals(false, $unsignedIntColumn['body']['required']);
$this->assertEquals(false, $unsignedIntColumn['body']['array']);
$this->assertEquals(false, $unsignedIntColumn['body']['signed']);
$this->assertEquals(0, $unsignedIntColumn['body']['min']);
$this->assertEquals(100, $unsignedIntColumn['body']['max']);
$this->assertEquals(0, $unsignedIntColumn['body']['default']);
$unsignedBigintColumn = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/unsigned_bigint_field', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $unsignedBigintColumn['headers']['status-code']);
$this->assertEquals('unsigned_bigint_field', $unsignedBigintColumn['body']['key']);
$this->assertEquals('bigint', $unsignedBigintColumn['body']['type']);
$this->assertEquals(false, $unsignedBigintColumn['body']['required']);
$this->assertEquals(false, $unsignedBigintColumn['body']['array']);
$this->assertEquals(false, $unsignedBigintColumn['body']['signed']);
$this->assertEquals(0, $unsignedBigintColumn['body']['min']);
$this->assertEquals(9223372036854775807, $unsignedBigintColumn['body']['max']);
$this->assertEquals(0, $unsignedBigintColumn['body']['default']);
}
public function testListColumnsWithNumericTypes(): void
{
$data = $this->setupDatabaseAndTable();
$databaseId = $data['databaseId'];
$tableId = $data['tableId'];
$columns = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $columns['headers']['status-code']);
$this->assertIsArray($columns['body']['columns']);
$this->assertGreaterThan(0, $columns['body']['total']);
$columnKeys = array_map(fn ($col) => $col['key'], $columns['body']['columns']);
$this->assertContains('integer_field', $columnKeys);
$this->assertContains('bigint_field', $columnKeys);
$this->assertContains('unsigned_int_field', $columnKeys);
$this->assertContains('unsigned_bigint_field', $columnKeys);
$columnTypeByKey = [];
foreach ($columns['body']['columns'] as $col) {
$columnTypeByKey[$col['key']] = $col['type'];
}
$this->assertEquals('integer', $columnTypeByKey['integer_field']);
$this->assertEquals('bigint', $columnTypeByKey['bigint_field']);
$this->assertEquals('integer', $columnTypeByKey['unsigned_int_field']);
$this->assertEquals('bigint', $columnTypeByKey['unsigned_bigint_field']);
}
public function testCreateRowWithIntegerAndBigIntTypes(): void
{
$data = $this->setupDatabaseAndTable();
$databaseId = $data['databaseId'];
$tableId = $data['tableId'];
$row = $this->client->call(Client::METHOD_POST, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/rows', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
], [
'rowId' => ID::unique(),
'data' => [
'integer_field' => 5,
'bigint_field' => 456,
'unsigned_int_field' => 50,
'unsigned_bigint_field' => 9007199254740000,
],
'permissions' => [
Permission::read(Role::any()),
],
]);
$this->assertEquals(201, $row['headers']['status-code']);
$this->assertEquals(5, $row['body']['integer_field']);
$this->assertEquals(456, $row['body']['bigint_field']);
$this->assertEquals(50, $row['body']['unsigned_int_field']);
$this->assertEquals(9007199254740000, $row['body']['unsigned_bigint_field']);
}
public function testUpdateIntegerAndBigIntColumns(): void
{
$data = $this->setupFreshDatabaseAndTable();
$databaseId = $data['databaseId'];
$tableId = $data['tableId'];
// Update integer column
$updateInteger = $this->client->call(
Client::METHOD_PATCH,
'/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer/integer_field',
[
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
],
[
'required' => false,
'min' => -20,
'max' => 20,
'default' => 3,
]
);
$this->assertEquals(200, $updateInteger['headers']['status-code']);
$this->assertEventually(function () use ($databaseId, $tableId) {
$column = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer_field', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $column['headers']['status-code']);
$this->assertEquals(-20, $column['body']['min']);
$this->assertEquals(20, $column['body']['max']);
$this->assertEquals(3, $column['body']['default']);
}, 30000, 250);
// Update bigint column
$updateBigint = $this->client->call(
Client::METHOD_PATCH,
'/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/bigint/bigint_field',
[
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
],
[
'required' => false,
'min' => -999,
'max' => 999,
'default' => 10,
]
);
$this->assertEquals(200, $updateBigint['headers']['status-code']);
$this->assertEventually(function () use ($databaseId, $tableId) {
$column = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/bigint_field', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(200, $column['headers']['status-code']);
$this->assertEquals(-999, $column['body']['min']);
$this->assertEquals(999, $column['body']['max']);
$this->assertEquals(10, $column['body']['default']);
}, 30000, 250);
}
public function testDeleteIntegerAndBigIntColumns(): void
{
$data = $this->setupFreshDatabaseAndTable();
$databaseId = $data['databaseId'];
$tableId = $data['tableId'];
// Delete integer column
$deleteInteger = $this->client->call(
Client::METHOD_DELETE,
'/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer_field',
[
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]
);
$this->assertEquals(204, $deleteInteger['headers']['status-code']);
$this->assertEventually(function () use ($databaseId, $tableId) {
$column = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/integer_field', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(404, $column['headers']['status-code']);
}, 30000, 250);
// Delete bigint column
$deleteBigint = $this->client->call(
Client::METHOD_DELETE,
'/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/bigint_field',
[
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]
);
$this->assertEquals(204, $deleteBigint['headers']['status-code']);
$this->assertEventually(function () use ($databaseId, $tableId) {
$column = $this->client->call(Client::METHOD_GET, '/tablesdb/' . $databaseId . '/tables/' . $tableId . '/columns/bigint_field', [
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
'x-appwrite-key' => $this->getProject()['apiKey'],
]);
$this->assertEquals(404, $column['headers']['status-code']);
}, 30000, 250);
}
}