diff --git a/sources/Data/Database/Database.uc b/sources/Data/Database/Database.uc index c51cdc5..bf9afdd 100644 --- a/sources/Data/Database/Database.uc +++ b/sources/Data/Database/Database.uc @@ -104,6 +104,8 @@ public function DBReadTask ReadData( * * @param pointer JSON pointer to the location in the database, where `data` * should be written (as a JSON value). + * This JSON pointer can make use of "-" index for JSON arrays that allows + * appending data at their end. * `none` is always treated as an invalid JSON pointer. * @param data Data that needs to be written at the specified location * inside the database. For method to succeed this object needs to have @@ -273,6 +275,9 @@ public function DBKeysTask GetDataKeys(JSONPointer pointer) * @param pointer JSON pointer to the location in the database, where * data should be incremented (by `increment`). * `none` is always treated as an invalid JSON pointer. + * This JSON pointer can make use of "-" index for JSON arrays that allows + * to add `none` value at the end of that array and then "increment" it + * with `increment` parameter. * @param increment JSON-compatible value to be used as an increment for * the data at the specified location inside the database. * @return Task object that corresponds to this `IncrementData()` call. diff --git a/sources/Data/Database/Local/DBRecord.uc b/sources/Data/Database/Local/DBRecord.uc index 00e8bae..4aad844 100644 --- a/sources/Data/Database/Local/DBRecord.uc +++ b/sources/Data/Database/Local/DBRecord.uc @@ -133,8 +133,9 @@ struct StorageItem }; var private config array storage; -var private const int LATIN_LETTERS_AMOUNT; -var private const int LOWER_A_CODEPOINT, UPPER_A_CODEPOINT; +var private const int LATIN_LETTERS_AMOUNT; +var private const int LOWER_A_CODEPOINT, UPPER_A_CODEPOINT; +var private const string JSONPOINTER_NEW_ARRAY_ELEMENT; /** * Since `DBRecord` represents JSON array or object, we can use @@ -422,16 +423,18 @@ public final function bool SaveObject( return false; } directContainer = pointer.record; + itemKey = __().text.ToString(jsonPointer.Pop(true)); if (directContainer.isJSONArray) { index = jsonPointer.PopNumeric(true); + if (index < 0 && itemKey == JSONPOINTER_NEW_ARRAY_ELEMENT) { + index = directContainer.GetStorageLength(); + } if (index < 0) { return false; } } - else - { - itemKey = __().text.ToString(jsonPointer.Pop(true)); + else { index = directContainer.FindItem(itemKey); } directContainer.SetItem(index, ConvertObjectToItem(newItem), itemKey); @@ -624,16 +627,18 @@ public final function Database.DBQueryResult IncrementObject( return DBR_InvalidPointer; } directContainer = pointer.record; + itemKey = __().text.ToString(jsonPointer.Pop(true)); if (directContainer.isJSONArray) { index = jsonPointer.PopNumeric(true); + if (index < 0 && itemKey == JSONPOINTER_NEW_ARRAY_ELEMENT) { + index = directContainer.GetStorageLength(); + } if (index < 0) { return DBR_InvalidPointer; } } - else - { - itemKey = __().text.ToString(jsonPointer.Pop(true)); + else { index = directContainer.FindItem(itemKey); } if (directContainer.IncrementItem(index, object, itemKey)) { @@ -752,6 +757,8 @@ private final function bool IncrementItem( } if (IncrementItemByObject(itemToIncrement, object)) { + // Increment object cannot overwrite existing `DBRecord` with + // other value, so it's safe to skip cleaning check storage[index] = itemToIncrement; storage[index].k = itemName; return true; @@ -1154,7 +1161,10 @@ private final function bool ReadNumericObjectInto( // Add storing bytes defaultproperties { - LATIN_LETTERS_AMOUNT = 26 - LOWER_A_CODEPOINT = 97 - UPPER_A_CODEPOINT = 65 + LATIN_LETTERS_AMOUNT = 26 + LOWER_A_CODEPOINT = 97 + UPPER_A_CODEPOINT = 65 + // JSON Pointers allow using "-" as an indicator that element must be + // added at the end of the array + JSONPOINTER_NEW_ARRAY_ELEMENT = "-" } \ No newline at end of file diff --git a/sources/Data/Database/Tests/TEST_LocalDatabase.uc b/sources/Data/Database/Tests/TEST_LocalDatabase.uc index 1bd1679..710150d 100644 --- a/sources/Data/Database/Tests/TEST_LocalDatabase.uc +++ b/sources/Data/Database/Tests/TEST_LocalDatabase.uc @@ -710,6 +710,7 @@ protected static function SubTest_WritingArrayIndicies(LocalDatabaseInstance db) db.WriteData(__().json.Pointer(P("")), templateObject); db.WriteData(__().json.Pointer(P("/A")), templateArray); db.WriteData(__().json.Pointer(P("/A/100")), __().box.int(-342)); + db.WriteData(__().json.Pointer(P("/A/-")), __().box.int(95)); Issue("Database allows writing data into negative JSON array indices."); writeTask = db.WriteData(__().json.Pointer(P("/A/-5")), __().box.int(1202)); @@ -717,12 +718,13 @@ protected static function SubTest_WritingArrayIndicies(LocalDatabaseInstance db) writeTask.TryCompleting(); Issue("Database cannot extend stored JSON array's length by assigning to" - @ "the out-of-bounds index."); + @ "the out-of-bounds index or \"-\"."); ReadFromDB(db, "/A"); resultArray = DynamicArray(default.resultObject); - TEST_ExpectTrue(resultArray.GetLength() == 101); + TEST_ExpectTrue(resultArray.GetLength() == 102); TEST_ExpectNone(resultArray.GetItem(99)); TEST_ExpectTrue(resultArray.GetInt(100) == -342); + TEST_ExpectTrue(resultArray.GetInt(101) == 95); TEST_ExpectTrue(resultArray.GetBool(0)); } @@ -1221,14 +1223,17 @@ protected static function SubTest_IncrementMissing(LocalDatabaseInstance db) task.connect = DBIncrementHandler; task.TryCompleting(); TEST_ExpectTrue(default.resultType == DBR_Success); - task = db.IncrementData(__().json.Pointer(P("/B/A/1//10")), none); + db.IncrementData(__().json.Pointer(P("/B/A/1//10")), none); + task = db.IncrementData(__().json.Pointer(P("/B/A/1//-")), + __().box.int(85)); task.connect = DBIncrementHandler; task.TryCompleting(); TEST_ExpectTrue(default.resultType == DBR_Success); db.CheckDataType(__().json.Pointer(P("/L"))).connect = DBCheckHandler; ReadFromDB(db, "/B/A/1/"); TEST_ExpectTrue(default.resultDataType == JSON_Number); - TEST_ExpectTrue(DynamicArray(default.resultObject).GetLength() == 11); + TEST_ExpectTrue(DynamicArray(default.resultObject).GetLength() == 12); + TEST_ExpectTrue(DynamicArray(default.resultObject).GetInt(11) == 85); } defaultproperties