├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── feature_request.md │ └── question.md ├── .gitignore ├── ConsoleApp1 ├── ConsoleApp1.csproj ├── Program.cs └── Tools │ ├── Faker.Names.cs │ └── Faker.cs ├── LICENSE ├── LiteDB.Benchmarks ├── Benchmarks │ ├── BenchmarkBase.cs │ ├── Constants.cs │ ├── Deletion │ │ └── DeletionBenchmark.cs │ ├── Generator │ │ └── FileMetaDataGenerationDatabaseBenchmark.cs │ ├── Insertion │ │ ├── InsertionBasicBenchmark.cs │ │ ├── InsertionIgnoreExpressionPropertyBenchmark.cs │ │ └── InsertionInMemoryBenchmark.cs │ └── Queries │ │ ├── QueryAllBenchmark.cs │ │ ├── QueryCompoundIndexBenchmark.cs │ │ ├── QueryCountBenchmark.cs │ │ ├── QueryIgnoreExpressionPropertiesBenchmark.cs │ │ ├── QueryMultipleParametersBenchmark.cs │ │ ├── QuerySimpleIndexBenchmarks.cs │ │ └── QueryWithDateTimeOffsetBenchmark.cs ├── LiteDB.Benchmarks.csproj ├── Models │ ├── FileMetaBase.cs │ ├── FileMetaWithExclusion.cs │ └── Generators │ │ └── FileMetaGenerator.cs └── Program.cs ├── LiteDB.Shell ├── Commands │ ├── Close.cs │ ├── Ed.cs │ ├── Help.cs │ ├── IShellCommand.cs │ ├── Open.cs │ ├── Pretty.cs │ ├── Quit.cs │ ├── Run.cs │ ├── ShowCollections.cs │ └── Version.cs ├── LiteDB.Shell.csproj ├── Program.cs ├── Properties │ └── launchSettings.json ├── Shell │ ├── Display.cs │ ├── Env.cs │ ├── InputCommand.cs │ └── ShellProgram.cs └── Utils │ ├── OptionSet.cs │ ├── StringExtensions.cs │ └── StringScanner.cs ├── LiteDB.Stress ├── Examples │ ├── test-01.xml │ └── test-02.xml ├── LiteDB.Stress.csproj ├── Program.cs ├── Properties │ └── launchSettings.json └── Test │ ├── ITaskItem.cs │ ├── InsertField.cs │ ├── InsertTaskItem.cs │ ├── SqlTaskItem.cs │ ├── TestExecution.cs │ ├── TestFile.cs │ ├── ThreadInfo.cs │ └── TimeSpanEx.cs ├── LiteDB.Tests ├── Database │ ├── AutoId_Tests.cs │ ├── ConnectionString_Tests.cs │ ├── Contains_Tests.cs │ ├── Create_Database_Tests.cs │ ├── Crud_Tests.cs │ ├── Database_Pragmas_Tests.cs │ ├── DbRef_Include_Tests.cs │ ├── DbRef_Index_Tests.cs │ ├── DbRef_Interface_Tests.cs │ ├── DeleteMany_Tests.cs │ ├── Delete_By_Name_Tests.cs │ ├── DocumentUpgrade_Tests.cs │ ├── Document_Size_Tests.cs │ ├── FindAll_Tests.cs │ ├── IndexMultiKeyLinq_Tests.cs │ ├── IndexSortAndFilter_Tests.cs │ ├── MultiKey_Mapper_Tests.cs │ ├── NonIdPoco_Tests.cs │ ├── PredicateBuilder_Tests.cs │ ├── Query_Min_Max_Tests.cs │ ├── Site_Tests.cs │ ├── Snapshot_Upgrade_Tests.cs │ ├── Storage_Tests.cs │ ├── Upgrade_Tests.cs │ └── Writing_While_Reading_Test.cs ├── Document │ ├── Bson_Tests.cs │ ├── Case_Insensitive_Tests.cs │ ├── Decimal_Tests.cs │ ├── Implicit_Tests.cs │ ├── Json_Tests.cs │ └── ObjectId_Tests.cs ├── Engine │ ├── Collation_Tests.cs │ ├── Create_Database_Tests.cs │ ├── Crypto_Tests.cs │ ├── DropCollection_Tests.cs │ ├── Index_Tests.cs │ ├── ParallelQuery_Tests.cs │ ├── Rebuild_Crash_Tests.cs │ ├── Rebuild_Tests.cs │ ├── Recursion_Tests.cs │ ├── Transactions_Tests.cs │ ├── Update_Tests.cs │ └── UserVersion_Tests.cs ├── Expressions │ ├── Expressions_Exec_Tests.cs │ └── Expressions_Tests.cs ├── Internals │ ├── Aes_Tests.cs │ ├── BasePage_Tests.cs │ ├── BufferWriter_Tests.cs │ ├── CacheAsync_Tests.cs │ ├── Cache_Tests.cs │ ├── Disk_Tests.cs │ ├── Document_Tests.cs │ ├── ExtendedLength_Tests.cs │ ├── Extensions_Test.cs │ ├── FreePage_Tests.cs │ ├── FreeSlot_Tests.cs │ ├── HeaderPage_Tests.cs │ ├── Pragma_Tests.cs │ └── Sort_Tests.cs ├── Issues │ ├── Issue1585_Tests.cs │ ├── Issue1651_Tests.cs │ ├── Issue1695_Tests.cs │ ├── Issue1701_Tests.cs │ ├── Issue1838_Tests.cs │ ├── Issue1860_Tests.cs │ ├── Issue1865_Tests.cs │ ├── Issue2112_Tests.cs │ ├── Issue2127_Tests.cs │ ├── Issue2129_Tests.cs │ ├── Issue2265_Tests.cs │ ├── Issue2298_Tests.cs │ ├── Issue2417_Tests.cs │ ├── Issue2458_Tests.cs │ ├── Issue2471_Test.cs │ ├── Issue2487_Tests.cs │ ├── Issue2494_Tests.cs │ ├── Issue2506_Tests.cs │ ├── Issue2534_Tests.cs │ └── Pull2468_Tests.cs ├── LiteDB.Tests.csproj ├── Mapper │ ├── CustomInterface_Tests.cs │ ├── CustomMappingCtor_Tests.cs │ ├── CustomMapping_Tests.cs │ ├── DbRefAbstract_Tests.cs │ ├── Dictionary_Tests.cs │ ├── Enum_Tests.cs │ ├── GenericMap_Tests.cs │ ├── LinqBsonExpression_Tests.cs │ ├── LinqEval_Tests.cs │ ├── Mapper_Tests.cs │ ├── Records_Tests.cs │ └── StructField_Tests.cs ├── Query │ ├── Aggregate_Tests.cs │ ├── Data │ │ ├── PersonGroupByData.cs │ │ └── PersonQueryData.cs │ ├── GroupBy_Tests.cs │ ├── Include_Tests.cs │ ├── OrderBy_Tests.cs │ ├── QueryApi_Tests.cs │ ├── Select_Tests.cs │ └── Where_Tests.cs ├── Resources │ ├── Issue2417_MyData.db │ ├── Issue2417_TestCacheDb.db │ ├── Issue_2494_EncryptedV4.db │ ├── person.json │ ├── v4.db │ └── zip.json ├── Utils │ ├── AssertEx.cs │ ├── DataGen.cs │ ├── Faker.Names.cs │ ├── Faker.cs │ ├── LiteEngineExtensions.cs │ ├── Models │ │ ├── Person.cs │ │ └── Zip.cs │ └── TempFile.cs └── xunit.runner.json ├── LiteDB.sln ├── LiteDB ├── Client │ ├── Database │ │ ├── Collections │ │ │ ├── Aggregate.cs │ │ │ ├── Delete.cs │ │ │ ├── Find.cs │ │ │ ├── Include.cs │ │ │ ├── Index.cs │ │ │ ├── Insert.cs │ │ │ ├── Update.cs │ │ │ └── Upsert.cs │ │ ├── ILiteCollection.cs │ │ ├── ILiteDatabase.cs │ │ ├── ILiteQueryable.cs │ │ ├── ILiteRepository.cs │ │ ├── LiteCollection.cs │ │ ├── LiteDatabase.cs │ │ ├── LiteQueryable.cs │ │ └── LiteRepository.cs │ ├── Mapper │ │ ├── Attributes │ │ │ ├── BsonCtorAttribute.cs │ │ │ ├── BsonFieldAttribute.cs │ │ │ ├── BsonIdAttribute.cs │ │ │ ├── BsonIgnoreAttribute.cs │ │ │ └── BsonRefAttribute.cs │ │ ├── BsonMapper.Deserialize.cs │ │ ├── BsonMapper.GetEntityMapper.cs │ │ ├── BsonMapper.Serialize.cs │ │ ├── BsonMapper.cs │ │ ├── EntityBuilder.cs │ │ ├── EntityMapper.cs │ │ ├── Linq │ │ │ ├── LinqExpressionVisitor.cs │ │ │ ├── ParameterExpressionVisitor.cs │ │ │ └── TypeResolver │ │ │ │ ├── BsonValueResolver.cs │ │ │ │ ├── ConvertResolver.cs │ │ │ │ ├── DateTimeResolver.cs │ │ │ │ ├── EnumerableResolver.cs │ │ │ │ ├── GuidResolver.cs │ │ │ │ ├── ICollectionResolver.cs │ │ │ │ ├── ITypeResolver.cs │ │ │ │ ├── MathResolver.cs │ │ │ │ ├── NullableResolver.cs │ │ │ │ ├── NumberResolver.cs │ │ │ │ ├── ObjectIdResolver.cs │ │ │ │ ├── RegexResolver.cs │ │ │ │ └── StringResolver.cs │ │ ├── MemberMapper.cs │ │ ├── Reflection │ │ │ ├── Reflection.Expression.cs │ │ │ └── Reflection.cs │ │ └── TypeNameBinder │ │ │ ├── DefaultTypeNameBinder.cs │ │ │ └── ITypeNameBinder.cs │ ├── Shared │ │ ├── SharedDataReader.cs │ │ └── SharedEngine.cs │ ├── SqlParser │ │ ├── Commands │ │ │ ├── Begin.cs │ │ │ ├── Checkpoint.cs │ │ │ ├── Commit.cs │ │ │ ├── Create.cs │ │ │ ├── Delete.cs │ │ │ ├── Drop.cs │ │ │ ├── Insert.cs │ │ │ ├── ParseLists.cs │ │ │ ├── Pragma.cs │ │ │ ├── Rebuild.cs │ │ │ ├── Rename.cs │ │ │ ├── Rollback.cs │ │ │ ├── Select.cs │ │ │ └── Update.cs │ │ └── SqlParser.cs │ ├── Storage │ │ ├── ILiteStorage.cs │ │ ├── LiteFileInfo.cs │ │ ├── LiteFileStream.Read.cs │ │ ├── LiteFileStream.Write.cs │ │ ├── LiteFileStream.cs │ │ └── LiteStorage.cs │ └── Structures │ │ ├── ConnectionString.cs │ │ ├── ConnectionType.cs │ │ ├── Query.cs │ │ └── QueryAny.cs ├── Document │ ├── Bson │ │ └── BsonSerializer.cs │ ├── BsonArray.cs │ ├── BsonAutoId.cs │ ├── BsonDocument.cs │ ├── BsonType.cs │ ├── BsonValue.cs │ ├── DataReader │ │ ├── BsonDataReader.cs │ │ ├── BsonDataReaderExtensions.cs │ │ └── IBsonDataReader.cs │ ├── Expression │ │ ├── BsonExpression.cs │ │ ├── Methods │ │ │ ├── Aggregate.cs │ │ │ ├── DataTypes.cs │ │ │ ├── Date.cs │ │ │ ├── Math.cs │ │ │ ├── Misc.cs │ │ │ └── String.cs │ │ └── Parser │ │ │ ├── BsonExpressionAttributes.cs │ │ │ ├── BsonExpressionFunctions.cs │ │ │ ├── BsonExpressionOperators.cs │ │ │ ├── BsonExpressionParser.cs │ │ │ ├── BsonExpressionType.cs │ │ │ ├── DocumentScope.cs │ │ │ └── ExpressionContext.cs │ ├── Json │ │ ├── JsonReader.cs │ │ ├── JsonSerializer.cs │ │ └── JsonWriter.cs │ └── ObjectId.cs ├── Engine │ ├── Disk │ │ ├── DiskReader.cs │ │ ├── DiskService.cs │ │ ├── MemoryCache.cs │ │ ├── Serializer │ │ │ ├── BufferReader.cs │ │ │ └── BufferWriter.cs │ │ ├── StreamFactory │ │ │ ├── FileStreamFactory.cs │ │ │ ├── IStreamFactory.cs │ │ │ ├── StreamFactory.cs │ │ │ └── StreamPool.cs │ │ └── Streams │ │ │ ├── AesStream.cs │ │ │ ├── ConcurrentStream.cs │ │ │ └── TempStream.cs │ ├── Engine │ │ ├── Collection.cs │ │ ├── Delete.cs │ │ ├── Index.cs │ │ ├── Insert.cs │ │ ├── Pragma.cs │ │ ├── Query.cs │ │ ├── Rebuild.cs │ │ ├── Recovery.cs │ │ ├── Sequence.cs │ │ ├── SystemCollections.cs │ │ ├── Transaction.cs │ │ ├── Update.cs │ │ ├── Upgrade.cs │ │ └── Upsert.cs │ ├── EnginePragmas.cs │ ├── EngineSettings.cs │ ├── EngineState.cs │ ├── FileReader │ │ ├── FileReaderError.cs │ │ ├── FileReaderV7.cs │ │ ├── FileReaderV8.cs │ │ ├── IFileReader.cs │ │ ├── IndexInfo.cs │ │ └── Legacy │ │ │ ├── AesEncryption.cs │ │ │ ├── BsonReader.cs │ │ │ └── ByteReader.cs │ ├── ILiteEngine.cs │ ├── LiteEngine.cs │ ├── Pages │ │ ├── BasePage.cs │ │ ├── CollectionPage.cs │ │ ├── DataPage.cs │ │ ├── HeaderPage.cs │ │ └── IndexPage.cs │ ├── Pragmas.cs │ ├── Query │ │ ├── IndexQuery │ │ │ ├── Index.cs │ │ │ ├── IndexAll.cs │ │ │ ├── IndexEquals.cs │ │ │ ├── IndexIn.cs │ │ │ ├── IndexLike.cs │ │ │ ├── IndexRange.cs │ │ │ ├── IndexScan.cs │ │ │ └── IndexVirtual.cs │ │ ├── Lookup │ │ │ ├── DatafileLookup.cs │ │ │ ├── IDocumentLookup.cs │ │ │ └── IndexKeyLoader.cs │ │ ├── Pipeline │ │ │ ├── BasePipe.cs │ │ │ ├── DocumentCacheEnumerable.cs │ │ │ ├── GroupByPipe.cs │ │ │ └── QueryPipe.cs │ │ ├── Query.cs │ │ ├── QueryExecutor.cs │ │ ├── QueryOptimization.cs │ │ └── Structures │ │ │ ├── GroupBy.cs │ │ │ ├── IndexCost.cs │ │ │ ├── OrderBy.cs │ │ │ ├── QueryPlan.cs │ │ │ └── Select.cs │ ├── Services │ │ ├── CollectionService.cs │ │ ├── DataService.cs │ │ ├── IndexService.cs │ │ ├── LockService.cs │ │ ├── RebuildService.cs │ │ ├── SnapShot.cs │ │ ├── TransactionMonitor.cs │ │ ├── TransactionService.cs │ │ └── WalIndexService.cs │ ├── Sort │ │ ├── SortContainer.cs │ │ ├── SortDisk.cs │ │ └── SortService.cs │ ├── Structures │ │ ├── CollectionIndex.cs │ │ ├── CursorInfo.cs │ │ ├── DataBlock.cs │ │ ├── Done.cs │ │ ├── FileOrigin.cs │ │ ├── IndexNode.cs │ │ ├── LockMode.cs │ │ ├── PageAddress.cs │ │ ├── PageBuffer.cs │ │ ├── PagePosition.cs │ │ ├── Pragma.cs │ │ ├── RebuildOptions.cs │ │ ├── TransactionPages.cs │ │ └── TransactionState.cs │ └── SystemCollections │ │ ├── Register.cs │ │ ├── SysCols.cs │ │ ├── SysDatabase.cs │ │ ├── SysDump.cs │ │ ├── SysFile.cs │ │ ├── SysFileCsv.cs │ │ ├── SysFileJson.cs │ │ ├── SysIndexes.cs │ │ ├── SysOpenCursors.cs │ │ ├── SysPageList.cs │ │ ├── SysQuery.cs │ │ ├── SysSequences.cs │ │ ├── SysSnapshots.cs │ │ ├── SysTransactions.cs │ │ └── SystemCollection.cs ├── LiteDB.csproj ├── LiteDB.snk └── Utils │ ├── AsyncManualResetEvent.cs │ ├── BufferSlice.cs │ ├── Collation.cs │ ├── Constants.cs │ ├── Encoding.cs │ ├── ExtendedLengthHelper.cs │ ├── Extensions │ ├── BufferExtensions.cs │ ├── BufferSliceExtensions.cs │ ├── DateExtensions.cs │ ├── DictionaryExtensions.cs │ ├── EnumerableExtensions.cs │ ├── ExpressionExtensions.cs │ ├── HashSetExtensions.cs │ ├── IOExceptionExtensions.cs │ ├── LinqExtensions.cs │ ├── StopWatchExtensions.cs │ ├── StreamExtensions.cs │ ├── StringExtensions.cs │ └── TypeInfoExtensions.cs │ ├── FileHelper.cs │ ├── LCID.cs │ ├── LiteException.cs │ ├── MimeTypeConverter.cs │ ├── Pool │ └── BucketHelper.cs │ ├── Randomizer.cs │ ├── Result.cs │ ├── Tokenizer.cs │ └── TryCatch.cs ├── README.md ├── appveyor.yml └── icon_64x64.png /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Version** 11 | Which LiteDB version/OS/.NET framework version are you using. **(REQUIRED)** 12 | 13 | **Describe the bug** 14 | A clear and concise description of what the bug is. 15 | 16 | **Code to Reproduce** 17 | Write a small snippet to isolate your bug and could be possible to our team test. **(REQUIRED)** 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots/Stacktrace** 23 | If applicable, add screenshots/stacktrace 24 | 25 | **Additional context** 26 | Add any other context about the problem here. 27 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[SUGGESTION]" 5 | labels: suggestion 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Question 3 | about: Write your question about LiteDB 4 | title: "[QUESTION]" 5 | labels: question 6 | assignees: '' 7 | 8 | --- 9 | 10 | 11 | -------------------------------------------------------------------------------- /ConsoleApp1/ConsoleApp1.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ConsoleApp1/Tools/Faker.Names.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litedb-org/LiteDB/2cc00c7094a966b7912c321c1ff7a754433c6d14/ConsoleApp1/Tools/Faker.Names.cs -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2022 Mauricio David 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LiteDB.Benchmarks/Benchmarks/BenchmarkBase.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Attributes; 2 | 3 | namespace LiteDB.Benchmarks.Benchmarks 4 | { 5 | public abstract class BenchmarkBase 6 | { 7 | // Insertion data size 8 | [Params(10, 50, 100, 500, 1000, 5000, 10000)] 9 | public int DatasetSize; 10 | 11 | public virtual string DatabasePath 12 | { 13 | get => Constants.DATABASE_NAME; 14 | set => throw new System.NotImplementedException(); 15 | } 16 | 17 | [Params(ConnectionType.Direct)] 18 | public ConnectionType ConnectionType; 19 | 20 | [Params(null, "SecurePassword")] 21 | public string Password; 22 | 23 | public ConnectionString ConnectionString() => new ConnectionString(DatabasePath) 24 | { 25 | Connection = ConnectionType, 26 | Password = Password 27 | }; 28 | 29 | protected ILiteDatabase DatabaseInstance { get; set; } 30 | } 31 | } -------------------------------------------------------------------------------- /LiteDB.Benchmarks/Benchmarks/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace LiteDB.Benchmarks.Benchmarks 2 | { 3 | internal static class Constants 4 | { 5 | internal const string DATABASE_NAME = "Lite.db"; 6 | 7 | internal static class Categories 8 | { 9 | internal const string DATA_GEN = nameof(DATA_GEN); 10 | internal const string QUERIES = nameof(QUERIES); 11 | internal const string INSERTION = nameof(INSERTION); 12 | internal const string DELETION = nameof(DELETION); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /LiteDB.Benchmarks/Benchmarks/Generator/FileMetaDataGenerationDatabaseBenchmark.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using BenchmarkDotNet.Attributes; 3 | using LiteDB.Benchmarks.Models; 4 | using LiteDB.Benchmarks.Models.Generators; 5 | 6 | namespace LiteDB.Benchmarks.Benchmarks.Generator 7 | { 8 | /// 9 | /// This benchmark is used purely for the sake of providing information 10 | /// about how long and how many resources it takes to generate the test data. 11 | /// 12 | [BenchmarkCategory(Constants.Categories.DATA_GEN)] 13 | public class FileMetaDataGenerationDatabaseBenchmark 14 | { 15 | // Benchmark params 16 | [Params(10, 50, 100, 500, 1000, 5000, 10000)] 17 | public int N; 18 | 19 | [Benchmark] 20 | public List DataGeneration() 21 | { 22 | return FileMetaGenerator.GenerateList(N); 23 | } 24 | 25 | [Benchmark] 26 | public List DataWithExclusionsGeneration() 27 | { 28 | return FileMetaGenerator.GenerateList(N); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /LiteDB.Benchmarks/Benchmarks/Queries/QueryAllBenchmark.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using BenchmarkDotNet.Attributes; 5 | using LiteDB.Benchmarks.Models; 6 | using LiteDB.Benchmarks.Models.Generators; 7 | 8 | namespace LiteDB.Benchmarks.Benchmarks.Queries 9 | { 10 | [BenchmarkCategory(Constants.Categories.QUERIES)] 11 | public class QueryAllBenchmark : BenchmarkBase 12 | { 13 | private ILiteCollection _fileMetaCollection; 14 | 15 | [GlobalSetup] 16 | public void GlobalSetup() 17 | { 18 | File.Delete(DatabasePath); 19 | 20 | DatabaseInstance = new LiteDatabase(ConnectionString()); 21 | _fileMetaCollection = DatabaseInstance.GetCollection(); 22 | 23 | _fileMetaCollection.Insert(FileMetaGenerator.GenerateList(DatasetSize)); // executed once per each N value 24 | DatabaseInstance.Checkpoint(); 25 | } 26 | 27 | [Benchmark(Baseline = true)] 28 | public List FindAll() 29 | { 30 | return _fileMetaCollection.FindAll().ToList(); 31 | } 32 | 33 | [Benchmark] 34 | public List FindAllWithExpression() 35 | { 36 | return _fileMetaCollection.Find(_ => true).ToList(); 37 | } 38 | 39 | [Benchmark] 40 | public List FindAllWithQuery() 41 | { 42 | return _fileMetaCollection.Find(Query.All()).ToList(); 43 | } 44 | 45 | [GlobalCleanup] 46 | public void GlobalCleanup() 47 | { 48 | // Disposing logic 49 | DatabaseInstance?.Checkpoint(); 50 | DatabaseInstance?.Dispose(); 51 | DatabaseInstance = null; 52 | 53 | File.Delete(DatabasePath); 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /LiteDB.Benchmarks/Benchmarks/Queries/QueryCountBenchmark.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Linq; 3 | using BenchmarkDotNet.Attributes; 4 | using LiteDB.Benchmarks.Models; 5 | using LiteDB.Benchmarks.Models.Generators; 6 | 7 | namespace LiteDB.Benchmarks.Benchmarks.Queries 8 | { 9 | [BenchmarkCategory(Constants.Categories.QUERIES)] 10 | public class QueryCountBenchmark : BenchmarkBase 11 | { 12 | private ILiteCollection _fileMetaCollection; 13 | 14 | [GlobalSetup] 15 | public void GlobalSetup() 16 | { 17 | File.Delete(DatabasePath); 18 | 19 | DatabaseInstance = new LiteDatabase(ConnectionString()); 20 | _fileMetaCollection = DatabaseInstance.GetCollection(); 21 | _fileMetaCollection.EnsureIndex(fileMeta => fileMeta.ShouldBeShown); 22 | 23 | _fileMetaCollection.Insert(FileMetaGenerator.GenerateList(DatasetSize)); // executed once per each N value 24 | 25 | DatabaseInstance.Checkpoint(); 26 | } 27 | 28 | [Benchmark(Baseline = true)] 29 | public int CountWithLinq() 30 | { 31 | return _fileMetaCollection.Find(Query.EQ(nameof(FileMetaBase.ShouldBeShown), true)).Count(); 32 | } 33 | 34 | [Benchmark] 35 | public int CountWithExpression() 36 | { 37 | return _fileMetaCollection.Count(fileMeta => fileMeta.ShouldBeShown); 38 | } 39 | 40 | [Benchmark] 41 | public int CountWithQuery() 42 | { 43 | return _fileMetaCollection.Count(Query.EQ(nameof(FileMetaBase.ShouldBeShown), true)); 44 | } 45 | 46 | [GlobalCleanup] 47 | public void GlobalCleanup() 48 | { 49 | // Disposing logic 50 | DatabaseInstance.DropCollection(nameof(FileMetaBase)); 51 | DatabaseInstance?.Checkpoint(); 52 | DatabaseInstance?.Dispose(); 53 | DatabaseInstance = null; 54 | 55 | File.Delete(DatabasePath); 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /LiteDB.Benchmarks/LiteDB.Benchmarks.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net472;net6 6 | 8 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /LiteDB.Benchmarks/Models/FileMetaBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LiteDB.Benchmarks.Models 4 | { 5 | public class FileMetaBase 6 | { 7 | [BsonIgnore] 8 | public const string BsonIdPropertyKey = "_id"; 9 | 10 | [BsonId] 11 | public virtual string Id => $"{FileId}_{Version}"; 12 | 13 | public Guid FileId { get; set; } 14 | 15 | public string ParentId { get; set; } 16 | 17 | public string Title { get; set; } 18 | 19 | public string MimeType { get; set; } 20 | 21 | public int Version { get; set; } 22 | 23 | public DateTimeOffset? ValidFrom { get; set; } 24 | 25 | public DateTimeOffset? ValidTo { get; set; } 26 | 27 | public bool IsFavorite { get; set; } 28 | 29 | public bool ShouldBeShown { get; set; } 30 | 31 | public virtual bool IsValid => ValidFrom == null || ValidFrom <= DateTimeOffset.UtcNow && ValidTo == null || ValidTo > DateTimeOffset.UtcNow; 32 | } 33 | } -------------------------------------------------------------------------------- /LiteDB.Benchmarks/Models/FileMetaWithExclusion.cs: -------------------------------------------------------------------------------- 1 | namespace LiteDB.Benchmarks.Models 2 | { 3 | public class FileMetaWithExclusion : FileMetaBase 4 | { 5 | public FileMetaWithExclusion() 6 | { 7 | } 8 | 9 | public FileMetaWithExclusion(FileMetaBase fileMetaBase) 10 | { 11 | FileId = fileMetaBase.FileId; 12 | ParentId = fileMetaBase.ParentId; 13 | Title = fileMetaBase.Title; 14 | MimeType = fileMetaBase.MimeType; 15 | Version = fileMetaBase.Version; 16 | ValidFrom = fileMetaBase.ValidFrom; 17 | ValidTo = fileMetaBase.ValidTo; 18 | IsFavorite = fileMetaBase.IsFavorite; 19 | ShouldBeShown = fileMetaBase.ShouldBeShown; 20 | } 21 | 22 | [BsonIgnore] 23 | public override bool IsValid => base.IsValid; 24 | } 25 | } -------------------------------------------------------------------------------- /LiteDB.Benchmarks/Models/Generators/FileMetaGenerator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace LiteDB.Benchmarks.Models.Generators 5 | { 6 | public static class FileMetaGenerator where T : FileMetaBase, new() 7 | { 8 | private static Random _random; 9 | 10 | private static T Generate() 11 | { 12 | var docGuid = Guid.NewGuid(); 13 | 14 | var generatedFileMeta = new T 15 | { 16 | FileId = docGuid, 17 | Version = _random.Next(5), 18 | Title = $"Document-{docGuid}", 19 | MimeType = "application/pdf", 20 | IsFavorite = _random.Next(10) >= 9, 21 | ShouldBeShown = _random.Next(10) >= 7 22 | }; 23 | 24 | if (_random.Next(10) >= 5) 25 | { 26 | generatedFileMeta.ValidFrom = DateTimeOffset.Now.AddDays(-20 + _random.Next(40)); 27 | generatedFileMeta.ValidFrom = DateTimeOffset.UtcNow.AddDays(-10 + _random.Next(40)); 28 | } 29 | 30 | return generatedFileMeta; 31 | } 32 | 33 | public static List GenerateList(int amountToGenerate) 34 | { 35 | _random = new Random(0); 36 | 37 | var generatedList = new List(); 38 | for (var i = 0; i < amountToGenerate; i++) generatedList.Add(Generate()); 39 | 40 | foreach (var fileMeta in generatedList) 41 | { 42 | if (_random.Next(100) <= 1) 43 | { 44 | continue; 45 | } 46 | 47 | fileMeta.ParentId = generatedList[_random.Next(amountToGenerate)].Id; 48 | } 49 | 50 | return generatedList; 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /LiteDB.Benchmarks/Program.cs: -------------------------------------------------------------------------------- 1 | using BenchmarkDotNet.Configs; 2 | using BenchmarkDotNet.Diagnosers; 3 | using BenchmarkDotNet.Environments; 4 | using BenchmarkDotNet.Exporters; 5 | using BenchmarkDotNet.Jobs; 6 | using BenchmarkDotNet.Running; 7 | using BenchmarkDotNet.Toolchains.CsProj; 8 | 9 | namespace LiteDB.Benchmarks 10 | { 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | BenchmarkRunner.Run(typeof(Program).Assembly, DefaultConfig.Instance 16 | //.With(new BenchmarkDotNet.Filters.AnyCategoriesFilter(new[] { Benchmarks.Constants.Categories.GENERAL })) 17 | //.AddFilter(new BenchmarkDotNet.Filters.AnyCategoriesFilter([Benchmarks.Constants.Categories.GENERAL])) 18 | .AddJob(Job.Default.WithRuntime(CoreRuntime.Core80) 19 | .WithJit(Jit.RyuJit) 20 | .WithToolchain(CsProjCoreToolchain.NetCoreApp80) 21 | .WithGcForce(true)) 22 | /*.With(Job.Default.With(MonoRuntime.Default) 23 | .With(Jit.Llvm) 24 | .With(new[] {new MonoArgument("--optimize=inline")}) 25 | .WithGcForce(true))*/ 26 | .AddDiagnoser(MemoryDiagnoser.Default) 27 | .AddExporter(BenchmarkReportExporter.Default, HtmlExporter.Default, MarkdownExporter.GitHub) 28 | .KeepBenchmarkFiles()); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /LiteDB.Shell/Commands/Close.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | 6 | namespace LiteDB.Shell.Commands 7 | { 8 | [Help( 9 | Name = "close", 10 | Syntax = "close", 11 | Description = "Close current datafile" 12 | )] 13 | internal class Close : IShellCommand 14 | { 15 | public bool IsCommand(StringScanner s) 16 | { 17 | return s.Scan(@"close$").Length > 0; 18 | } 19 | 20 | public void Execute(StringScanner s, Env env) 21 | { 22 | if (env.Database != null) 23 | { 24 | env.Database.Dispose(); 25 | env.Database = null; 26 | } 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /LiteDB.Shell/Commands/Ed.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | 5 | namespace LiteDB.Shell.Commands 6 | { 7 | [Help( 8 | Name = "ed", 9 | Syntax = "ed", 10 | Description = "Open your last command in notepad." 11 | )] 12 | internal class Ed : IShellCommand 13 | { 14 | public bool IsCommand(StringScanner s) 15 | { 16 | return s.Match(@"ed$"); 17 | } 18 | 19 | public void Execute(StringScanner s, Env env) 20 | { 21 | var temp = Path.GetTempPath() + "LiteDB.Shell.txt"; 22 | 23 | // remove "ed" command from history 24 | env.Input.History.RemoveAt(env.Input.History.Count - 1); 25 | 26 | var last = env.Input.History.Count > 0 ? env.Input.History[env.Input.History.Count - 1] : ""; 27 | 28 | File.WriteAllText(temp, last.Replace("\n", Environment.NewLine)); 29 | 30 | Process.Start("notepad.exe", temp).WaitForExit(); 31 | 32 | var text = File.ReadAllText(temp); 33 | 34 | if (text == last) return; 35 | 36 | env.Input.Queue.Enqueue(text); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /LiteDB.Shell/Commands/Help.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Reflection; 4 | using System.Text; 5 | 6 | namespace LiteDB.Shell.Commands 7 | { 8 | internal class HelpAttribute : Attribute 9 | { 10 | public string Name { get; set; } 11 | public string Syntax { get; set; } 12 | public string Description { get; set; } 13 | public string[] Examples { get; set; } = new string[0]; 14 | } 15 | 16 | internal class Help : IShellCommand 17 | { 18 | public bool IsCommand(StringScanner s) 19 | { 20 | return s.Scan(@"help\s*").Length > 0; 21 | } 22 | 23 | public void Execute(StringScanner s, Env env) 24 | { 25 | var param = s.Scan(".*"); 26 | var d = env.Display; 27 | 28 | // getting all HelpAttributes inside assemblies 29 | var helps = AppDomain.CurrentDomain.GetAssemblies() 30 | .SelectMany(x => x.GetTypes()) 31 | .Select(x => CustomAttributeExtensions.GetCustomAttributes(x, typeof(HelpAttribute), true).FirstOrDefault()) 32 | .Where(x => x != null) 33 | .Select(x => x as HelpAttribute) 34 | .ToArray(); 35 | 36 | d.WriteLine(ConsoleColor.White, "# LiteDB Shell Command Reference"); 37 | 38 | foreach (var help in helps) 39 | { 40 | d.WriteLine(""); 41 | d.WriteLine(ConsoleColor.Cyan, "> " + help.Syntax); 42 | d.WriteLine(ConsoleColor.DarkCyan, " " + help.Description); 43 | 44 | // show examples only when named help command 45 | foreach (var example in help.Examples) 46 | { 47 | d.WriteLine(ConsoleColor.Gray, " > " + example); 48 | } 49 | } 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /LiteDB.Shell/Commands/IShellCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace LiteDB.Shell 6 | { 7 | internal interface IShellCommand 8 | { 9 | bool IsCommand(StringScanner s); 10 | 11 | void Execute(StringScanner s, Env env); 12 | } 13 | } -------------------------------------------------------------------------------- /LiteDB.Shell/Commands/Open.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | 6 | namespace LiteDB.Shell.Commands 7 | { 8 | [Help( 9 | Name = "open", 10 | Syntax = "open ", 11 | Description = "Open (or create) a new datafile. Can be used a single filename or a connection string with all supported parameters.", 12 | Examples = new string[] { 13 | "open mydb.db", 14 | "open filename=mydb.db; password=johndoe; initial=100Mb" 15 | } 16 | )] 17 | internal class Open : IShellCommand 18 | { 19 | public bool IsCommand(StringScanner s) 20 | { 21 | return s.Scan(@"open\s+").Length > 0; 22 | } 23 | 24 | public void Execute(StringScanner s, Env env) 25 | { 26 | var connectionString = new ConnectionString(s.Scan(@".+").TrimToNull()); 27 | 28 | if (env.Database != null) 29 | { 30 | env.Database.Dispose(); 31 | env.Database = null; 32 | } 33 | 34 | env.Database = new LiteDatabase(connectionString); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /LiteDB.Shell/Commands/Pretty.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LiteDB.Shell.Commands 4 | { 5 | [Help( 6 | Name = "pretty", 7 | Syntax = "pretty [on|off]", 8 | Description = "Print all json results with identation/break lines", 9 | Examples = new string[] { 10 | "pretty" 11 | } 12 | )] 13 | internal class Pretty : IShellCommand 14 | { 15 | public bool IsCommand(StringScanner s) 16 | { 17 | return s.Scan(@"pretty\s*").Length > 0; 18 | } 19 | 20 | public void Execute(StringScanner s, Env env) 21 | { 22 | env.Display.Pretty = !(s.Scan(@"off\s*").Length > 0); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /LiteDB.Shell/Commands/Quit.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LiteDB.Shell.Commands 4 | { 5 | [Help( 6 | Name = "quit", 7 | Syntax = "quit|exit", 8 | Description = "Close shell application" 9 | )] 10 | internal class Quit : IShellCommand 11 | { 12 | public bool IsCommand(StringScanner s) 13 | { 14 | return s.Match(@"(quit|exit)$"); 15 | } 16 | 17 | public void Execute(StringScanner s, Env env) 18 | { 19 | env.Database?.Dispose(); 20 | env.Input.Running = false; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /LiteDB.Shell/Commands/Run.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace LiteDB.Shell.Commands 5 | { 6 | [Help( 7 | Name = "run", 8 | Syntax = "run ", 9 | Description = "Queue shell commands inside filename to be run in order.", 10 | Examples = new string[] { 11 | "run scripts.txt" 12 | } 13 | )] 14 | internal class Run : IShellCommand 15 | { 16 | public bool IsCommand(StringScanner s) 17 | { 18 | return s.Scan(@"run\s+").Length > 0; 19 | } 20 | 21 | public void Execute(StringScanner s, Env env) 22 | { 23 | if (env.Database == null) throw new Exception("Database not connected"); 24 | 25 | var filename = s.Scan(@".+").Trim(); 26 | 27 | foreach (var line in File.ReadAllLines(filename)) 28 | { 29 | env.Input.Queue.Enqueue(line); 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /LiteDB.Shell/Commands/ShowCollections.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace LiteDB.Shell.Commands 5 | { 6 | [Help( 7 | Name = "show collections", 8 | Syntax = "show collections", 9 | Description = "List all collections inside datafile." 10 | )] 11 | internal class ShowCollections : IShellCommand 12 | { 13 | public bool IsCommand(StringScanner s) 14 | { 15 | return s.Match(@"show\scollections$"); 16 | } 17 | 18 | public void Execute(StringScanner s, Env env) 19 | { 20 | if (env.Database == null) throw new Exception("Database not connected"); 21 | 22 | var cols = env.Database.GetCollectionNames().OrderBy(x => x).ToArray(); 23 | 24 | if (cols.Length > 0) 25 | { 26 | env.Display.WriteLine(ConsoleColor.Cyan, string.Join(Environment.NewLine, cols)); 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /LiteDB.Shell/Commands/Version.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LiteDB.Shell.Commands 4 | { 5 | [Help( 6 | Name = "version", 7 | Syntax = "ver", 8 | Description = "Show LiteDB version" 9 | )] 10 | internal class Version : IShellCommand 11 | { 12 | public bool IsCommand(StringScanner s) 13 | { 14 | return s.Scan(@"ver(sion)?$").Length > 0; 15 | } 16 | 17 | public void Execute(StringScanner s, Env env) 18 | { 19 | var assembly = typeof(ILiteDatabase).Assembly.GetName(); 20 | 21 | env.Display.WriteLine(assembly.FullName); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /LiteDB.Shell/LiteDB.Shell.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6 5 | LiteDB.Shell 6 | LiteDB.Shell 7 | Exe 8 | LiteDB.Shell 9 | 5.0.6.0 10 | 5.0.6 11 | 5.0.6 12 | Maurício David 13 | MIT 14 | en-US 15 | false 16 | 1701;1702;1705;1591 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /LiteDB.Shell/Program.cs: -------------------------------------------------------------------------------- 1 | namespace LiteDB.Shell 2 | { 3 | internal class Program 4 | { 5 | /// 6 | /// Opens console shell app. Usage: 7 | /// LiteDB.Shell [myfile.db] --param1 value1 --params2 "value 2" 8 | /// Parameters: 9 | /// --exec "command" : Execute an shell command (can be multiples --exec) 10 | /// --run script.txt : Run script commands file 11 | /// --pretty : Show JSON in multiline + idented 12 | /// --exit : Exit after last command 13 | /// 14 | private static void Main(string[] args) 15 | { 16 | var input = new InputCommand(); 17 | var display = new Display(); 18 | var o = new OptionSet(); 19 | 20 | // default arg 21 | o.Register((v) => input.Queue.Enqueue("open " + v)); 22 | o.Register("pretty", () => display.Pretty = true); 23 | o.Register("exit", () => input.AutoExit = true); 24 | o.Register("run", (v) => input.Queue.Enqueue("run " + v)); 25 | o.Register("exec", (v) => input.Queue.Enqueue(v)); 26 | 27 | // parse command line calling register parameters 28 | o.Parse(args); 29 | 30 | ShellProgram.Start(input, display); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /LiteDB.Shell/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "LiteDB.Shell": { 4 | "commandName": "Project", 5 | "commandLineArgs": "app.db" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /LiteDB.Shell/Shell/Env.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using LiteDB; 5 | 6 | namespace LiteDB.Shell 7 | { 8 | internal class Env 9 | { 10 | public Display Display { get; set; } 11 | public InputCommand Input { get; set; } 12 | public ILiteDatabase Database { get; set; } 13 | public bool Running { get; set; } = false; 14 | } 15 | } -------------------------------------------------------------------------------- /LiteDB.Shell/Utils/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LiteDB.Shell 4 | { 5 | internal static class StringExtensions 6 | { 7 | public static string ThrowIfEmpty(this string str, string message) 8 | { 9 | if (string.IsNullOrEmpty(str) || str.Trim().Length == 0) 10 | { 11 | throw new ArgumentException(message); 12 | } 13 | 14 | return str; 15 | } 16 | 17 | public static string TrimToNull(this string str) 18 | { 19 | var v = str.Trim(); 20 | 21 | return v.Length == 0 ? null : v; 22 | } 23 | 24 | public static string MaxLength(this string str, int len) 25 | { 26 | return len == 0 || str.Length < len ? 27 | str : 28 | str.Substring(0, len - 3) + "..."; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /LiteDB.Stress/Examples/test-01.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | { myItem: 'abc', array: [1, 2, 3] } 11 | 12 | UPDATE col1 SET name = UPPER(name), large=LPAD(name, RANDOM(50, 3000), 'x') 13 | DELETE col1 WHERE 1 = 1 14 | CHECKPOINT 15 | SELECT COUNT(*) FROM col1 16 | SELECT { data: FORMAT(dataFileSize, 'n0'), log: FORMAT(logFileSize, 'n0') } FROM $database 17 | -------------------------------------------------------------------------------- /LiteDB.Stress/Examples/test-02.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /LiteDB.Stress/LiteDB.Stress.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Exe 5 | net8 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /LiteDB.Stress/Program.cs: -------------------------------------------------------------------------------- 1 | using LiteDB; 2 | using LiteDB.Engine; 3 | using System; 4 | using System.Collections.Concurrent; 5 | using System.Collections.Generic; 6 | using System.Diagnostics; 7 | using System.IO; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | 11 | namespace LiteDB.Stress 12 | { 13 | public class Program 14 | { 15 | static void Main(string[] args) 16 | { 17 | var filename = args.Length >= 1 ? args[0] : ""; 18 | var duration = TimeSpanEx.Parse(args.Length >= 2 ? args[1] : "60s"); 19 | 20 | var e = new TestExecution(filename, duration); 21 | 22 | e.Execute(); 23 | 24 | Console.ReadKey(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /LiteDB.Stress/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "LiteDB.Stress": { 4 | "commandName": "Project", 5 | "commandLineArgs": "../../../Examples/test-01.xml" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /LiteDB.Stress/Test/ITaskItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Xml; 5 | 6 | namespace LiteDB.Stress 7 | { 8 | public interface ITestItem 9 | { 10 | string Name { get; } 11 | int TaskCount { get; } 12 | TimeSpan Sleep { get; } 13 | BsonValue Execute(LiteDatabase db); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /LiteDB.Stress/Test/SqlTaskItem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Xml; 6 | 7 | namespace LiteDB.Stress 8 | { 9 | public class SqlTaskItem : ITestItem 10 | { 11 | public string Name { get; } 12 | public int TaskCount { get; } 13 | public TimeSpan Sleep { get; } 14 | public string Sql { get; } 15 | 16 | public SqlTaskItem(XmlElement el) 17 | { 18 | this.Name = string.IsNullOrEmpty(el.GetAttribute("name")) ? el.InnerText.Split(' ').First() : el.GetAttribute("name"); 19 | this.TaskCount = string.IsNullOrEmpty(el.GetAttribute("tasks")) ? 1 : int.Parse(el.GetAttribute("tasks")); 20 | this.Sleep = TimeSpanEx.Parse(el.GetAttribute("sleep")); 21 | this.Sql = el.InnerText; 22 | } 23 | 24 | public BsonValue Execute(LiteDatabase db) 25 | { 26 | using (var reader = db.Execute(this.Sql)) 27 | { 28 | return reader.FirstOrDefault(); 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LiteDB.Stress/Test/TestFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | using System.Xml; 6 | 7 | namespace LiteDB.Stress 8 | { 9 | public class TestFile 10 | { 11 | public TimeSpan Timeout { get; } 12 | public string Filename { get; } 13 | public string Output { get; } 14 | public bool Delete { get; } 15 | public List Setup { get; } 16 | public List Tasks { get; } 17 | 18 | public TestFile(string filename) 19 | { 20 | var doc = new XmlDocument(); 21 | doc.Load(filename); 22 | 23 | var root = doc.DocumentElement; 24 | var children = root.SelectNodes("*"); 25 | 26 | this.Timeout = TimeSpanEx.Parse(root.GetAttribute("timeout")); 27 | this.Filename = root.GetAttribute("filename"); 28 | this.Delete = bool.Parse(root.GetAttribute("delete")); 29 | this.Output = Path.Combine(Path.GetDirectoryName(this.Filename), Path.GetFileNameWithoutExtension(this.Filename) + ".log"); 30 | 31 | this.Setup = new List(); 32 | this.Tasks = new List(); 33 | 34 | foreach(XmlElement el in children) 35 | { 36 | if (el.Name == "setup") 37 | { 38 | this.Setup.Add(el.InnerText); 39 | } 40 | else 41 | { 42 | var item = el.Name == "insert" ? 43 | (ITestItem)new InsertTaskItem(el) : 44 | (ITestItem)new SqlTaskItem(el); 45 | 46 | this.Tasks.Add(item); 47 | } 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /LiteDB.Stress/Test/ThreadInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Xml; 7 | 8 | namespace LiteDB.Stress 9 | { 10 | public class ThreadInfo 11 | { 12 | public ITestItem Task { get; set; } 13 | public int Counter { get; set; } = 0; 14 | public bool Running { get; set; } = false; 15 | public Stopwatch Elapsed { get; } = new Stopwatch(); 16 | public DateTime LastRun { get; set; } = DateTime.Now; 17 | public BsonValue Result { get; set; } = null; 18 | public long ResultSum { get; set; } 19 | public TimeSpan TotalRun { get; set; } = TimeSpan.Zero; 20 | public Exception Exception { get; set; } 21 | public Thread Thread { get; set; } 22 | public CancellationTokenSource CancellationTokenSource { get; } = new CancellationTokenSource(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /LiteDB.Stress/Test/TimeSpanEx.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Text; 5 | using System.Text.RegularExpressions; 6 | using System.Xml; 7 | 8 | namespace LiteDB.Stress 9 | { 10 | public class TimeSpanEx 11 | { 12 | public static TimeSpan Parse(string duration) 13 | { 14 | var re = new Regex(@"(?\d+)\s*(?ms|s|m|h)?"); 15 | 16 | var match = re.Match(duration); 17 | 18 | if (match.Success) 19 | { 20 | var num = double.Parse(match.Groups["num"].Value, CultureInfo.InvariantCulture.NumberFormat); 21 | var unit = match.Groups["unit"].Value.ToLower(); 22 | 23 | switch(unit) 24 | { 25 | case "": 26 | case "ms": return TimeSpan.FromMilliseconds(num); 27 | case "s": return TimeSpan.FromSeconds(num); 28 | case "m": return TimeSpan.FromMinutes(num); 29 | case "h": return TimeSpan.FromHours(num); 30 | } 31 | } 32 | 33 | throw new ArgumentException("Duration must be in format: number + unit (ms, s, m, h)"); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LiteDB.Tests/Database/Create_Database_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using FluentAssertions; 5 | using LiteDB.Engine; 6 | using Xunit; 7 | 8 | namespace LiteDB.Tests.Database 9 | { 10 | public class Create_Database_Tests 11 | { 12 | [Fact] 13 | public void Create_Database_With_Initial_Size() 14 | { 15 | var initial = 10 * 8192; // initial size: 80kb 16 | //var minimal = 8192 * 5; // 1 header + 1 collection + 1 data + 1 index = 4 pages minimal 17 | 18 | using (var file = new TempFile()) 19 | { 20 | using (var db = new LiteDatabase("filename=" + file.Filename + ";initial size=" + initial)) 21 | { 22 | var col = db.GetCollection("col"); 23 | 24 | // just ensure open datafile 25 | col.FindAll().ToArray(); 26 | 27 | // test if file has 40kb 28 | file.Size.Should().Be(initial); 29 | 30 | // simple insert to test if datafile still with 40kb 31 | col.Insert(new BsonDocument { ["_id"] = 1 }); // use 3 pages to this 32 | 33 | file.Size.Should().Be(initial); 34 | 35 | // ok, now shrink and test if file are minimal size 36 | //** db.Shrink(); 37 | //** 38 | //** file.Size.Should().Be(minimal); 39 | } 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Database/Database_Pragmas_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using LiteDB; 5 | using FluentAssertions; 6 | using Xunit; 7 | using System.Globalization; 8 | 9 | namespace LiteDB.Tests.Database 10 | { 11 | public class Database_Pragmas_Tests 12 | { 13 | [Fact] 14 | public void Database_Pragmas_Get_Set() 15 | { 16 | using (var db = new LiteDatabase(":memory:")) 17 | { 18 | db.Timeout.TotalSeconds.Should().Be(60.0); 19 | db.UtcDate.Should().Be(false); 20 | db.Collation.SortOptions.Should().Be(CompareOptions.IgnoreCase); 21 | db.LimitSize.Should().Be(long.MaxValue); 22 | db.UserVersion.Should().Be(0); 23 | db.CheckpointSize.Should().Be(1000); 24 | 25 | // changing values 26 | db.Timeout = TimeSpan.FromSeconds(30); 27 | db.UtcDate = true; 28 | db.LimitSize = 1024 * 1024; 29 | db.UserVersion = 99; 30 | db.CheckpointSize = 0; 31 | 32 | // testing again 33 | db.Timeout.TotalSeconds.Should().Be(30); 34 | db.UtcDate.Should().Be(true); 35 | db.LimitSize.Should().Be(1024 * 1024); 36 | db.UserVersion.Should().Be(99); 37 | db.CheckpointSize.Should().Be(0); 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Database/DbRef_Index_Tests.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using FluentAssertions; 3 | using Xunit; 4 | 5 | namespace LiteDB.Tests.Database 6 | { 7 | public class DbRef_Index_Tests 8 | { 9 | #region Model 10 | 11 | public class Customer 12 | { 13 | public string Login { get; set; } 14 | public string Name { get; set; } 15 | } 16 | 17 | public class Order 18 | { 19 | public int OrderNumber { get; set; } 20 | public Customer Customer { get; set; } 21 | } 22 | 23 | #endregion 24 | 25 | [Fact] 26 | public void DbRef_Index() 27 | { 28 | var mapper = new BsonMapper(); 29 | 30 | mapper.Entity() 31 | .Id(x => x.Login) 32 | .Field(x => x.Name, "customer_name"); 33 | 34 | mapper.Entity() 35 | .Id(x => x.OrderNumber) 36 | .Field(x => x.Customer, "cust") 37 | .DbRef(x => x.Customer, "customers"); 38 | 39 | using (var db = new LiteDatabase(new MemoryStream(), mapper, new MemoryStream())) 40 | { 41 | var customer = new Customer { Login = "jd", Name = "John Doe" }; 42 | var order = new Order { Customer = customer }; 43 | 44 | var customers = db.GetCollection("Customers"); 45 | var orders = db.GetCollection("Orders"); 46 | 47 | customers.Insert(customer); 48 | orders.Insert(order); 49 | 50 | // create an index in Customer.Id ref 51 | // x.Customer.Login == "Customer.$id" 52 | orders.EnsureIndex(x => x.Customer.Login); 53 | 54 | var query = orders 55 | .Include(x => x.Customer) 56 | .FindOne(x => x.Customer.Login == "jd"); 57 | 58 | query.Customer.Name.Should().Be(customer.Name); 59 | } 60 | } 61 | } 62 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Database/DeleteMany_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using LiteDB; 5 | using FluentAssertions; 6 | using Xunit; 7 | 8 | namespace LiteDB.Tests.Database 9 | { 10 | public class DeleteMany_Tests 11 | { 12 | [Fact] 13 | public void DeleteMany_With_Arguments() 14 | { 15 | using (var db = new LiteDatabase(":memory:")) 16 | { 17 | var c1 = db.GetCollection("Test"); 18 | 19 | var d1 = new BsonDocument() { ["_id"] = 1, ["p1"] = 1 }; 20 | c1.Insert(d1); 21 | 22 | c1.Count().Should().Be(1); 23 | 24 | // try BsonExpression predicate with argument - not deleted 25 | var e1 = BsonExpression.Create("$._id = @0", 1); 26 | var r1 = c1.DeleteMany(e1); 27 | 28 | r1.Should().Be(1); 29 | 30 | // the same BsonExpression predicate works fine in FindOne 31 | var r = c1.FindOne(e1); 32 | 33 | } 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Database/Delete_By_Name_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using FluentAssertions; 5 | using Xunit; 6 | 7 | namespace LiteDB.Tests.Database 8 | { 9 | public class Delete_By_Name_Tests 10 | { 11 | #region Model 12 | 13 | public class Person 14 | { 15 | public int Id { get; set; } 16 | public string Fullname { get; set; } 17 | } 18 | 19 | #endregion 20 | 21 | [Fact] 22 | public void Delete_By_Name() 23 | { 24 | using (var f = new TempFile()) 25 | using (var db = new LiteDatabase(f.Filename)) 26 | { 27 | var col = db.GetCollection("Person"); 28 | 29 | col.Insert(new Person { Fullname = "John" }); 30 | col.Insert(new Person { Fullname = "Doe" }); 31 | col.Insert(new Person { Fullname = "Joana" }); 32 | col.Insert(new Person { Fullname = "Marcus" }); 33 | 34 | // lets auto-create index in FullName and delete from a non-pk node 35 | var del = col.DeleteMany(x => x.Fullname.StartsWith("J")); 36 | 37 | del.Should().Be(2); 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Database/Document_Size_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Linq; 5 | using FluentAssertions; 6 | using LiteDB.Engine; 7 | using Xunit; 8 | 9 | namespace LiteDB.Tests.Database 10 | { 11 | public class Document_Size_Tests 12 | { 13 | const int ARRAY_SIZE = 10 * 1024 * 1024; 14 | 15 | [Fact] 16 | public void Very_Large_Single_Document_Support_With_Partial_Load_Memory_Usage() 17 | { 18 | using (var file = new TempFile()) 19 | using (var db = new LiteDatabase(file.Filename)) 20 | { 21 | var col = db.GetCollection("col"); 22 | 23 | // insert 10 mb document 24 | 25 | col.Insert(new BsonDocument 26 | { 27 | ["_id"] = 1, 28 | ["name"] = "John", 29 | ["data"] = new byte[ARRAY_SIZE] 30 | }); 31 | 32 | GC.Collect(); 33 | GC.WaitForPendingFinalizers(); 34 | 35 | var initialMemory = Process.GetCurrentProcess().WorkingSet64; 36 | 37 | // get name only document 38 | var d0 = col.Query().Select("{ _id, name }").First(); 39 | 40 | d0["name"].Should().Be("John"); 41 | 42 | var memoryForNameOnly = Process.GetCurrentProcess().WorkingSet64; 43 | 44 | // getting full document 45 | var d1 = col.Query().First(); 46 | 47 | var memoryFullDocument = Process.GetCurrentProcess().WorkingSet64; 48 | 49 | // memory after full document must be at least 10Mb more than with name only 50 | 51 | //memoryFullDocument.Should().BeGreaterOrEqualTo(memoryForNameOnly + (ARRAY_SIZE / 2)); 52 | } 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Database/FindAll_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using FluentAssertions; 5 | using Xunit; 6 | 7 | namespace LiteDB.Tests.Database 8 | { 9 | public class FindAll_Tests 10 | { 11 | #region Model 12 | 13 | public class Person 14 | { 15 | public int Id { get; set; } 16 | public string Fullname { get; set; } 17 | } 18 | 19 | #endregion 20 | 21 | [Fact] 22 | public void FindAll() 23 | { 24 | using (var f = new TempFile()) 25 | { 26 | using (var db = new LiteDatabase(f.Filename)) 27 | { 28 | var col = db.GetCollection("Person"); 29 | 30 | col.Insert(new Person { Fullname = "John" }); 31 | col.Insert(new Person { Fullname = "Doe" }); 32 | col.Insert(new Person { Fullname = "Joana" }); 33 | col.Insert(new Person { Fullname = "Marcus" }); 34 | } 35 | // close datafile 36 | 37 | using (var db = new LiteDatabase(f.Filename)) 38 | { 39 | var p = db.GetCollection("Person").Find(Query.All("Fullname", Query.Ascending)); 40 | 41 | p.Count().Should().Be(4); 42 | } 43 | } 44 | 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Database/NonIdPoco_Tests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using FluentAssertions; 5 | using Xunit; 6 | 7 | namespace LiteDB.Tests.Database 8 | { 9 | public class MissingIdDocTest 10 | { 11 | #region Model 12 | 13 | public class MissingIdDoc 14 | { 15 | public string Name { get; set; } 16 | public int Age { get; set; } 17 | } 18 | 19 | #endregion 20 | 21 | [Fact] 22 | public void MissingIdDoc_Test() 23 | { 24 | using (var file = new TempFile()) 25 | using (var db = new LiteDatabase(file.Filename)) 26 | { 27 | var col = db.GetCollection("col"); 28 | 29 | var p = new MissingIdDoc { Name = "John", Age = 39 }; 30 | 31 | // ObjectID will be generated 32 | var id = col.Insert(p); 33 | 34 | p.Age = 41; 35 | 36 | col.Update(id, p); 37 | 38 | var r = col.FindById(id); 39 | 40 | r.Name.Should().Be(p.Name); 41 | } 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Database/Query_Min_Max_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using FluentAssertions; 5 | using Xunit; 6 | 7 | namespace LiteDB.Tests.Database 8 | { 9 | public class Query_Min_Max_Tests 10 | { 11 | #region Model 12 | 13 | public class EntityMinMax 14 | { 15 | public int Id { get; set; } 16 | public byte ByteValue { get; set; } 17 | public int IntValue { get; set; } 18 | public uint UintValue { get; set; } 19 | public long LongValue { get; set; } 20 | } 21 | 22 | #endregion 23 | 24 | [Fact] 25 | public void Query_Min_Max() 26 | { 27 | using (var f = new TempFile()) 28 | using (var db = new LiteDatabase(f.Filename)) 29 | { 30 | var c = db.GetCollection("col"); 31 | 32 | c.Insert(new EntityMinMax { }); 33 | c.Insert(new EntityMinMax 34 | { 35 | ByteValue = 200, 36 | IntValue = 443500, 37 | LongValue = 443500, 38 | UintValue = 443500 39 | }); 40 | 41 | c.EnsureIndex(x => x.ByteValue); 42 | c.EnsureIndex(x => x.IntValue); 43 | c.EnsureIndex(x => x.LongValue); 44 | c.EnsureIndex(x => x.UintValue); 45 | 46 | c.Max(x => x.ByteValue).Should().Be(200); 47 | c.Max(x => x.IntValue).Should().Be(443500); 48 | c.Max(x => x.LongValue).Should().Be(443500); 49 | c.Max(x => x.UintValue).Should().Be(443500); 50 | 51 | } 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Database/Snapshot_Upgrade_Tests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using System.Linq; 4 | using FluentAssertions; 5 | using Xunit; 6 | 7 | namespace LiteDB.Tests.Database 8 | { 9 | public class Snapshot_Upgrade_Tests 10 | { 11 | [Fact] 12 | public void Transaction_Update_Upsert() 13 | { 14 | using var db = new LiteDatabase(":memory:"); 15 | var col = db.GetCollection("test"); 16 | 17 | bool transactionCreated = db.BeginTrans(); 18 | Assert.True(transactionCreated); 19 | 20 | int updatedDocs = col.UpdateMany("{name: \"xxx\"}", BsonExpression.Create("_id = 1")); 21 | Assert.Equal(0, updatedDocs); 22 | 23 | col.Upsert(new BsonDocument() { ["_id"] = 1, ["name"] = "xxx" }); 24 | var result = col.FindById(1); 25 | Assert.NotNull(result); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Database/Upgrade_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using LiteDB; 5 | using FluentAssertions; 6 | using Xunit; 7 | using Microsoft.VisualStudio.TestPlatform.CommunicationUtilities.ObjectModel; 8 | 9 | namespace LiteDB.Tests.Database 10 | { 11 | public class Upgrade_Tests 12 | { 13 | [Fact] 14 | public void Migrage_From_V4() 15 | { 16 | // v5 upgrades only from v4! 17 | using(var tempFile = new TempFile("../../../Resources/v4.db")) 18 | { 19 | using (var db = new LiteDatabase($"filename={tempFile};upgrade=true")) 20 | { 21 | // convert and open database 22 | var col1 = db.GetCollection("col1"); 23 | 24 | col1.Count().Should().Be(3); 25 | } 26 | 27 | using (var db = new LiteDatabase($"filename={tempFile};upgrade=true")) 28 | { 29 | // database already converted 30 | var col1 = db.GetCollection("col1"); 31 | 32 | col1.Count().Should().Be(3); 33 | } 34 | } 35 | } 36 | 37 | [Fact] 38 | public void Migrage_From_V4_No_FileExtension() 39 | { 40 | // v5 upgrades only from v4! 41 | using (var tempFile = new TempFile("../../../Resources/v4.db")) 42 | { 43 | using (var db = new LiteDatabase($"filename={tempFile};upgrade=true")) 44 | { 45 | // convert and open database 46 | var col1 = db.GetCollection("col1"); 47 | 48 | col1.Count().Should().Be(3); 49 | } 50 | 51 | using (var db = new LiteDatabase($"filename={tempFile};upgrade=true")) 52 | { 53 | // database already converted 54 | var col1 = db.GetCollection("col1"); 55 | 56 | col1.Count().Should().Be(3); 57 | } 58 | } 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Database/Writing_While_Reading_Test.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using Xunit; 3 | 4 | namespace LiteDB.Tests.Database; 5 | 6 | public class Writing_While_Reading_Test 7 | { 8 | [Fact] 9 | public void Test() 10 | { 11 | using var f = new TempFile(); 12 | using (var db = new LiteDatabase(f.Filename)) 13 | { 14 | var col = db.GetCollection("col"); 15 | col.Insert(new MyClass { Name = "John", Description = "Doe" }); 16 | col.Insert(new MyClass { Name = "Joana", Description = "Doe" }); 17 | col.Insert(new MyClass { Name = "Doe", Description = "Doe" }); 18 | } 19 | 20 | 21 | using (var db = new LiteDatabase(f.Filename)) 22 | { 23 | var col = db.GetCollection("col"); 24 | foreach (var item in col.FindAll()) 25 | { 26 | item.Description += " Changed"; 27 | col.Update(item); 28 | } 29 | 30 | db.Commit(); 31 | } 32 | 33 | 34 | using (var db = new LiteDatabase(f.Filename)) 35 | { 36 | var col = db.GetCollection("col"); 37 | foreach (var item in col.FindAll()) 38 | { 39 | Assert.EndsWith("Changed", item.Description); 40 | } 41 | } 42 | } 43 | 44 | class MyClass 45 | { 46 | public int Id { get; set; } 47 | 48 | public string Name { get; set; } 49 | 50 | public string Description { get; set; } 51 | } 52 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Document/Case_Insensitive_Tests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using FluentAssertions; 3 | using Xunit; 4 | 5 | namespace LiteDB.Tests.Document 6 | { 7 | public class Case_Insensitive_Tests 8 | { 9 | [Fact] 10 | public void Get_Document_Fields_Case_Insensitive() 11 | { 12 | var doc = new BsonDocument 13 | { 14 | ["_id"] = 10, 15 | ["name"] = "John", 16 | ["Last Job This Year"] = "admin" 17 | }; 18 | 19 | doc["_id"].AsInt32.Should().Be(10); 20 | doc["_ID"].AsInt32.Should().Be(10); 21 | doc["_Id"].AsInt32.Should().Be(10); 22 | 23 | doc["name"].AsString.Should().Be("John"); 24 | doc["Name"].AsString.Should().Be("John"); 25 | doc["NamE"].AsString.Should().Be("John"); 26 | 27 | doc["Last Job This Year"].AsString.Should().Be("admin"); 28 | doc["last JOB this YEAR"].AsString.Should().Be("admin"); 29 | 30 | // using expr 31 | BsonExpression.Create("$.['Last Job This Year']").Execute(doc).First().AsString.Should().Be("admin"); 32 | BsonExpression.Create("$.['Last JOB THIS Year']").Execute(doc).First().AsString.Should().Be("admin"); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Document/Decimal_Tests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Xunit; 3 | 4 | namespace LiteDB.Tests.Document 5 | { 6 | public class Decimal_Tests 7 | { 8 | [Fact] 9 | public void BsonValue_New_Decimal_Type() 10 | { 11 | var d0 = 0m; 12 | var d1 = 1m; 13 | var dmin = new BsonValue(decimal.MinValue); 14 | var dmax = new BsonValue(decimal.MaxValue); 15 | 16 | JsonSerializer.Serialize(d0).Should().Be("{\"$numberDecimal\":\"0\"}"); 17 | JsonSerializer.Serialize(d1).Should().Be("{\"$numberDecimal\":\"1\"}"); 18 | JsonSerializer.Serialize(dmin).Should().Be("{\"$numberDecimal\":\"-79228162514264337593543950335\"}"); 19 | JsonSerializer.Serialize(dmax).Should().Be("{\"$numberDecimal\":\"79228162514264337593543950335\"}"); 20 | 21 | var b0 = BsonSerializer.Serialize(new BsonDocument {{"A", d0}}); 22 | var b1 = BsonSerializer.Serialize(new BsonDocument {{"A", d1}}); 23 | var bmin = BsonSerializer.Serialize(new BsonDocument {{"A", dmin}}); 24 | var bmax = BsonSerializer.Serialize(new BsonDocument {{"A", dmax}}); 25 | 26 | var x0 = BsonSerializer.Deserialize(b0); 27 | var x1 = BsonSerializer.Deserialize(b1); 28 | var xmin = BsonSerializer.Deserialize(bmin); 29 | var xmax = BsonSerializer.Deserialize(bmax); 30 | 31 | x0["A"].AsDecimal.Should().Be(d0); 32 | x1["A"].AsDecimal.Should().Be(d1); 33 | xmin["A"].AsDecimal.Should().Be(dmin.AsDecimal); 34 | xmax["A"].AsDecimal.Should().Be(dmax.AsDecimal); 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Document/Implicit_Tests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System; 3 | using Xunit; 4 | 5 | namespace LiteDB.Tests.Document 6 | { 7 | public class Implicit_Tests 8 | { 9 | [Fact] 10 | public void BsonValue_Implicit_Convert() 11 | { 12 | int i = int.MaxValue; 13 | long l = long.MaxValue; 14 | ulong u = ulong.MaxValue; 15 | 16 | BsonValue bi = i; 17 | BsonValue bl = l; 18 | BsonValue bu = u; 19 | 20 | bi.IsInt32.Should().BeTrue(); 21 | bl.IsInt64.Should().BeTrue(); 22 | bu.IsDouble.Should().BeTrue(); 23 | 24 | bi.AsInt32.Should().Be(i); 25 | bl.AsInt64.Should().Be(l); 26 | bu.AsDouble.Should().Be(u); 27 | } 28 | 29 | [Fact] 30 | public void BsonDocument_Inner() 31 | { 32 | var customer = new BsonDocument(); 33 | customer["_id"] = ObjectId.NewObjectId(); 34 | customer["Name"] = "John Doe"; 35 | customer["CreateDate"] = DateTime.Now; 36 | customer["Phones"] = new BsonArray { "8000-0000", "9000-000" }; 37 | customer["IsActive"] = true; 38 | customer["IsAdmin"] = new BsonValue(true); 39 | customer["Address"] = new BsonDocument 40 | { 41 | ["Street"] = "Av. Protasio Alves" 42 | }; 43 | 44 | customer["Address"]["Number"] = "1331"; 45 | 46 | var json = JsonSerializer.Serialize(customer); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Document/ObjectId_Tests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Xunit; 3 | 4 | namespace LiteDB.Tests.Document 5 | { 6 | public class ObjectId_Tests 7 | { 8 | [Fact] 9 | public void ObjectId_BsonValue() 10 | { 11 | var oid0 = ObjectId.Empty; 12 | var oid1 = ObjectId.NewObjectId(); 13 | var oid2 = ObjectId.NewObjectId(); 14 | var oid3 = ObjectId.NewObjectId(); 15 | 16 | var c1 = new ObjectId(oid1); 17 | var c2 = new ObjectId(oid2.ToString()); 18 | var c3 = new ObjectId(oid3.ToByteArray()); 19 | 20 | oid0.Should().Be(ObjectId.Empty); 21 | oid1.Should().Be(c1); 22 | oid2.Should().Be(c2); 23 | oid3.Should().Be(c3); 24 | 25 | c2.CompareTo(c3).Should().Be(-1); // 1 < 2 26 | c1.CompareTo(c2).Should().Be(-1); // 2 < 3 27 | 28 | // serializations 29 | var joid = JsonSerializer.Serialize(c1); 30 | var jc1 = JsonSerializer.Deserialize(joid).AsObjectId; 31 | 32 | jc1.Should().Be(c1); 33 | } 34 | 35 | [Fact] 36 | public void ObjectId_Equals_Null_Does_Not_Throw() 37 | { 38 | var oid0 = default(ObjectId); 39 | var oid1 = ObjectId.NewObjectId(); 40 | 41 | oid1.Equals(null).Should().BeFalse(); 42 | oid1.Equals(oid0).Should().BeFalse(); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Engine/Create_Database_Tests.cs: -------------------------------------------------------------------------------- 1 | using LiteDB.Engine; 2 | using Xunit; 3 | 4 | namespace LiteDB.Tests.Engine 5 | { 6 | /*public class Create_Database_Tests 7 | { 8 | [Fact] 9 | public void Create_Database_With_Initial_Size() 10 | { 11 | var initial = 163840; // initial size: 20 x 8192 = 163.840 bytes 12 | var minimal = 8192 * 4; // 1 header + 1 collection + 1 data + 1 index = 4 pages minimal 13 | 14 | using (var file = new TempFile()) 15 | using (var db = new LiteEngine(new EngineSettings { Filename = file.Filename, InitialSize = initial })) 16 | { 17 | // test if file has 40kb 18 | Assert.Equal(initial, file.Size); 19 | 20 | // insert minimal data 21 | db.Insert("col1", new BsonDocument { ["a"] = 1 }); 22 | 23 | Assert.Equal(initial, file.Size); 24 | 25 | // ok, now shrink and test if file are minimal size 26 | db.Shrink(); 27 | 28 | Assert.Equal(minimal, file.Size); 29 | } 30 | } 31 | }*/ 32 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Engine/DropCollection_Tests.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using FluentAssertions; 3 | using Xunit; 4 | 5 | namespace LiteDB.Tests.Engine 6 | { 7 | public class DropCollection_Tests 8 | { 9 | [Fact] 10 | public void DropCollection() 11 | { 12 | using (var file = new TempFile()) 13 | using (var db = new LiteDatabase(file.Filename)) 14 | { 15 | db.GetCollectionNames().Should().NotContain("col"); 16 | 17 | var col = db.GetCollection("col"); 18 | 19 | col.Insert(new BsonDocument {["a"] = 1}); 20 | 21 | db.GetCollectionNames().Should().Contain("col"); 22 | 23 | db.DropCollection("col"); 24 | 25 | db.GetCollectionNames().Should().NotContain("col"); 26 | } 27 | } 28 | 29 | [Fact] 30 | public void InsertDropCollection() 31 | { 32 | using (var file = new TempFile()) 33 | { 34 | using (var db = new LiteDatabase(file.Filename)) 35 | { 36 | var col = db.GetCollection("test"); 37 | col.Insert(new BsonDocument { ["_id"] = 1 }); 38 | db.DropCollection("test"); 39 | db.Rebuild(); 40 | } 41 | 42 | using (var db = new LiteDatabase(file.Filename)) 43 | { 44 | var col = db.GetCollection("test"); 45 | col.Insert(new BsonDocument { ["_id"] = 1 }); 46 | } 47 | } 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Engine/ParallelQuery_Tests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using FluentAssertions; 7 | using Xunit; 8 | 9 | namespace LiteDB.Tests.Engine 10 | { 11 | public class ParallelQuery_Tests 12 | { 13 | [Fact(Skip = "Must fix parallel query fetch")] 14 | public void Query_Parallel() 15 | { 16 | using(var db = new LiteDatabase(new MemoryStream())) 17 | { 18 | var col = db.GetCollection("person"); 19 | var all = DataGen.Person().ToArray(); 20 | 21 | col.Insert(all); 22 | 23 | var bag = new ConcurrentBag(); 24 | var people = col.FindAll(); 25 | 26 | Parallel.ForEach(people, person => 27 | //foreach(var person in people) 28 | { 29 | var col2 = db.GetCollection("person"); 30 | var exists = col2.Exists(x => x.Id == person.Id); 31 | 32 | if (exists) 33 | { 34 | var col3 = db.GetCollection("person"); 35 | 36 | var item = col3.FindOne(x => x.Id == person.Id); 37 | 38 | bag.Add(item); 39 | } 40 | }); 41 | 42 | all.Length.Should().Be(bag.Count); 43 | 44 | } 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Engine/Recursion_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Xunit; 3 | 4 | namespace LiteDB.Tests.Engine; 5 | 6 | public class Recursion_Tests 7 | { 8 | [Fact] 9 | public void UpdateInFindAll() 10 | { 11 | Test(collection => 12 | { 13 | foreach (BsonDocument document in collection.FindAll()) 14 | { 15 | collection.Update(document); 16 | } 17 | }); 18 | } 19 | [Fact] 20 | public void InsertDeleteInFindAll() 21 | { 22 | Test(collection => 23 | { 24 | foreach (BsonDocument document in collection.FindAll()) 25 | { 26 | BsonValue id = collection.Insert(new BsonDocument()); 27 | collection.Delete(id); 28 | } 29 | }); 30 | } 31 | [Fact] 32 | public void QueryInFindAll() 33 | { 34 | Test(collection => 35 | { 36 | foreach (BsonDocument document in collection.FindAll()) 37 | { 38 | collection.Query().Count(); 39 | } 40 | }); 41 | } 42 | 43 | private void Test(Action> action) 44 | { 45 | using LiteDatabase database = new(new ConnectionString() 46 | { 47 | Filename = "Demo.db", 48 | Connection = ConnectionType.Shared, 49 | }); 50 | 51 | ILiteCollection accounts = database.GetCollection("Recursion"); 52 | 53 | if (accounts.Count() < 3) 54 | { 55 | accounts.Insert(new BsonDocument()); 56 | accounts.Insert(new BsonDocument()); 57 | accounts.Insert(new BsonDocument()); 58 | } 59 | action(accounts); 60 | } 61 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Engine/UserVersion_Tests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using Xunit; 3 | 4 | namespace LiteDB.Tests.Engine 5 | { 6 | public class UserVersion_Tests 7 | { 8 | [Fact] 9 | public void UserVersion_Get_Set() 10 | { 11 | using (var file = new TempFile()) 12 | { 13 | using (var db = new LiteDatabase(file.Filename)) 14 | { 15 | db.UserVersion.Should().Be(0); 16 | db.UserVersion = 5; 17 | db.Checkpoint(); 18 | } 19 | 20 | using (var db = new LiteDatabase(file.Filename)) 21 | { 22 | db.UserVersion.Should().Be(5); 23 | } 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Internals/ExtendedLength_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using Xunit; 5 | 6 | namespace LiteDB.Internals 7 | { 8 | public class ExtendedLength_Tests 9 | { 10 | [Fact] 11 | public void ExtendedLengthHelper_Tests() 12 | { 13 | byte typeByte, lengthByte; 14 | BsonType type; 15 | ushort length; 16 | ExtendedLengthHelper.WriteLength(BsonType.String, 1010, out typeByte, out lengthByte); 17 | ExtendedLengthHelper.ReadLength(typeByte, lengthByte, out type, out length); 18 | Assert.Equal(BsonType.String, type); 19 | Assert.Equal((ushort)1010, length); 20 | } 21 | 22 | [Fact] 23 | public void IndexExtendedLength_Tests() 24 | { 25 | using var db = new LiteDatabase(":memory:"); 26 | var col = db.GetCollection("customers", BsonAutoId.Int32); 27 | col.EnsureIndex("$.Name"); 28 | col.Insert(new BsonDocument { ["Name"] = new string('A', 1010) }); 29 | col.Insert(new BsonDocument { ["Name"] = new string('B', 230) }); 30 | 31 | var results = db.Execute("select $ from customers where $.Name < 'B'").ToArray(); 32 | Assert.Single(results); 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LiteDB.Tests/Internals/Extensions_Test.cs: -------------------------------------------------------------------------------- 1 | using LiteDB.Utils.Extensions; 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | using Xunit; 10 | 11 | namespace LiteDB.Tests.Internals; 12 | 13 | public class Extensions_Test 14 | { 15 | // Asserts that chained IEnumerable.OnDispose(()=> { }) calls the action on dispose, even when chained 16 | [Fact] 17 | public void EnumerableExtensions_OnDispose() 18 | { 19 | var disposed = false; 20 | var disposed1 = false; 21 | var enumerable = new[] { 1, 2, 3 }.OnDispose(() => disposed = true).OnDispose(() => disposed1 = true); 22 | 23 | foreach (var item in enumerable) 24 | { 25 | // do nothing 26 | } 27 | 28 | Assert.True(disposed); 29 | Assert.True(disposed1); 30 | } 31 | 32 | // tests IDisposable StartDisposable(this Stopwatch stopwatch) 33 | [Fact] 34 | public async Task StopWatchExtensions_StartDisposable() 35 | { 36 | var stopwatch = new System.Diagnostics.Stopwatch(); 37 | using (stopwatch.StartDisposable()) 38 | { 39 | await Task.Delay(100); 40 | } 41 | 42 | Assert.True(stopwatch.ElapsedMilliseconds > 0); 43 | } 44 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Internals/Pragma_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Collections.Generic; 5 | using FluentAssertions; 6 | using LiteDB.Engine; 7 | using Xunit; 8 | 9 | namespace LiteDB.Internals 10 | { 11 | public class Pragma_Tests 12 | { 13 | [Fact] 14 | public void Pragma_RunTests() 15 | { 16 | var data = new byte[Constants.PAGE_SIZE]; 17 | var buffer = new PageBuffer(data, 0, 1); 18 | 19 | // create new header page 20 | var header = new HeaderPage(buffer, 0); 21 | 22 | this.Invoking(x => header.Pragmas.Get("INEXISTENT_PRAGMA")).Should().Throw(); 23 | 24 | this.Invoking(x => header.Pragmas.Set(Pragmas.USER_VERSION, "invalid value", true)).Should().Throw(); 25 | this.Invoking(x => header.Pragmas.Set(Pragmas.USER_VERSION, 1, true)).Should().NotThrow(); 26 | 27 | this.Invoking(x => header.Pragmas.Set(Pragmas.COLLATION, "en-US/IgnoreCase", true)).Should().Throw(); 28 | 29 | this.Invoking(x => header.Pragmas.Set(Pragmas.TIMEOUT, -1, true)).Should().Throw(); 30 | this.Invoking(x => header.Pragmas.Set(Pragmas.TIMEOUT, 1, true)).Should().NotThrow(); 31 | 32 | this.Invoking(x => header.Pragmas.Set(Pragmas.LIMIT_SIZE, 1000, true)).Should().Throw(); 33 | this.Invoking(x => header.Pragmas.Set(Pragmas.LIMIT_SIZE, (Convert.ToInt32(header.LastPageID)) * Constants.PAGE_SIZE - 1, true)).Should().Throw(); 34 | this.Invoking(x => header.Pragmas.Set(Pragmas.LIMIT_SIZE, 1024L*1024L*1024L*1024L, true)).Should().NotThrow(); 35 | 36 | this.Invoking(x => header.Pragmas.Set(Pragmas.UTC_DATE, true, true)).Should().NotThrow(); 37 | 38 | this.Invoking(x => header.Pragmas.Set(Pragmas.CHECKPOINT, -1, true)).Should().Throw(); 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /LiteDB.Tests/Issues/Issue1695_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using FluentAssertions; 6 | using LiteDB.Engine; 7 | using Xunit; 8 | 9 | namespace LiteDB.Tests.Issues 10 | { 11 | public class Issue1695_Tests 12 | { 13 | public class StateModel 14 | { 15 | [BsonId] 16 | public ObjectId Id { get; set; } 17 | } 18 | 19 | [Fact] 20 | public void ICollection_Parameter_Test() 21 | { 22 | using var db = new LiteDatabase(":memory:"); 23 | var col = db.GetCollection("col"); 24 | 25 | ICollection ids = new List(); 26 | for (var i = 1; i <= 10; i++) 27 | ids.Add(col.Insert(new StateModel())); 28 | 29 | var items = col.Query() 30 | .Where(x => ids.Contains(x.Id)) 31 | .ToList(); 32 | 33 | items.Should().HaveCount(10); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Issues/Issue1701_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Xunit; 4 | using System.Linq; 5 | 6 | namespace LiteDB.Tests.Issues 7 | { 8 | public class Issue1701_Tests 9 | { 10 | [Fact] 11 | public void Deleted_Index_Slot_Test() 12 | { 13 | using var db = new LiteDatabase(":memory:"); 14 | var col = db.GetCollection("col", BsonAutoId.Int32); 15 | var id = col.Insert(new BsonDocument { ["attr1"] = "attr", ["attr2"] = "attr", ["attr3"] = "attr" }); 16 | 17 | col.EnsureIndex("attr1", "$.attr1"); 18 | col.EnsureIndex("attr2", "$.attr2"); 19 | col.EnsureIndex("attr3", "$.attr3"); 20 | col.DropIndex("attr2"); 21 | 22 | col.Update(id, new BsonDocument { ["attr1"] = "new" }); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /LiteDB.Tests/Issues/Issue1838_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Xunit; 4 | using System.Linq; 5 | 6 | namespace LiteDB.Tests.Issues 7 | { 8 | public class Issue1838_Tests 9 | { 10 | [Fact] 11 | public void Find_ByDatetime_Offset() 12 | { 13 | using var db = new LiteDatabase(":memory:"); 14 | var collection = db.GetCollection(nameof(TestType)); 15 | 16 | // sample data 17 | collection.Insert(new TestType() 18 | { 19 | Foo = "abc", 20 | Timestamp = DateTimeOffset.UtcNow, 21 | }); 22 | collection.Insert(new TestType() 23 | { 24 | Foo = "def", 25 | Timestamp = DateTimeOffset.UtcNow, 26 | }); 27 | 28 | // filter from 1 hour in the past to 1 hour in the future 29 | var timeRange = TimeSpan.FromHours(2); 30 | 31 | var result = collection // throws exception 32 | .Find(x => x.Timestamp > (DateTimeOffset.UtcNow - timeRange)) 33 | .ToList(); 34 | 35 | Assert.NotNull(result); 36 | Assert.Equal(2, result.Count); 37 | } 38 | 39 | public class TestType 40 | { 41 | [BsonId] 42 | public int Id { get; set; } 43 | [BsonField] 44 | public string Foo { get; set; } 45 | [BsonField] 46 | public DateTimeOffset Timestamp { get; set; } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /LiteDB.Tests/Issues/Issue2112_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using Xunit; 4 | using System.Linq; 5 | 6 | namespace LiteDB.Tests.Issues 7 | { 8 | public class Issue2112_Tests 9 | { 10 | private readonly BsonMapper _mapper = new BsonMapper(); 11 | 12 | [Fact] 13 | public void Serialize_covariant_collection_has_type() 14 | { 15 | IA a = new A { Bs = new List { new B() } }; 16 | 17 | var docA = _mapper.Serialize(a).AsDocument; 18 | var docB = docA["Bs"].AsArray[0].AsDocument; 19 | 20 | Assert.True(docA.ContainsKey("_type")); 21 | Assert.True(docB.ContainsKey("_type")); 22 | } 23 | 24 | [Fact] 25 | public void Deserialize_covariant_collection_succeed() 26 | { 27 | IA a = new A { Bs = new List { new B() } }; 28 | var serialized = _mapper.Serialize(a); 29 | 30 | var deserialized = _mapper.Deserialize(serialized); 31 | 32 | Assert.Equal(1, deserialized.Bs.Count); 33 | } 34 | 35 | interface IA 36 | { 37 | // at runtime this will be a List 38 | IReadOnlyCollection Bs { get; set; } 39 | } 40 | 41 | class A : IA 42 | { 43 | public IReadOnlyCollection Bs { get; set; } 44 | } 45 | 46 | interface IB 47 | { 48 | 49 | } 50 | 51 | class B : IB 52 | { 53 | 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /LiteDB.Tests/Issues/Issue2129_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using Xunit; 5 | 6 | namespace LiteDB.Tests.Issues 7 | { 8 | public class Issue2129_Tests 9 | { 10 | [Fact] 11 | public void TestInsertAfterDeleteAll() 12 | { 13 | var db = new LiteDatabase(":memory:"); 14 | var col = db.GetCollection(nameof(SwapChance)); 15 | col.EnsureIndex(x => x.Accounts1to2); 16 | col.EnsureIndex(x => x.Accounts2to1); 17 | 18 | col.InsertBulk(this.GenerateItems()); 19 | col.DeleteAll(); 20 | col.InsertBulk(this.GenerateItems()); 21 | } 22 | 23 | private IEnumerable GenerateItems() 24 | { 25 | var r = new Random(); 26 | int seq = 1; 27 | return Enumerable.Range(0, 150).Select(x => new SwapChance 28 | { 29 | Rarity = "Uncommon", 30 | Template1Id = r.Next(15023), 31 | Template2Id = r.Next(142, 188645), 32 | Accounts1to2 = Enumerable.Range(0, 8).Select(a => Guid.NewGuid().ToString().Substring(0, 10) + ".wam").ToList(), 33 | Accounts2to1 = Enumerable.Range(0, 6).Select(a => Guid.NewGuid().ToString().Substring(0, 10) + ".wam").ToList(), 34 | Sequence = seq++ 35 | }); 36 | } 37 | } 38 | 39 | public class SwapChance 40 | { 41 | public ObjectId Id { get; set; } 42 | public int Sequence { get; set; } = 0; 43 | public string Rarity { get; set; } = string.Empty; 44 | public int Template1Id { get; set; } 45 | public int Template2Id { get; set; } 46 | public List Accounts1to2 { get; set; } = new List(); 47 | public List Accounts2to1 { get; set; } = new List(); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /LiteDB.Tests/Issues/Issue2265_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using Xunit; 4 | 5 | namespace LiteDB.Tests.Issues; 6 | 7 | // issue 2265 8 | public class Issue2265_Tests 9 | { 10 | public class Weights 11 | { 12 | public int Id { get; set; } = 0; 13 | 14 | // comment out [BsonRef] and the the test works 15 | [BsonRef("weights")] 16 | public Weights[] Parents { get; set; } 17 | 18 | public Weights(int id, Weights[] parents) 19 | { 20 | Id = id; 21 | Parents = parents; 22 | } 23 | 24 | public Weights() 25 | { 26 | Id = 0; 27 | Parents = Array.Empty(); 28 | } 29 | } 30 | 31 | [Fact] 32 | public void Test() 33 | { 34 | using (var db = new LiteDatabase(":memory:")) 35 | { 36 | var c = db.GetCollection("weights"); 37 | Weights? w = c.FindOne(x => true); 38 | if (w == null) 39 | { 40 | w = new Weights(); 41 | c.Insert(w); 42 | } 43 | 44 | //return w; 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Issues/Issue2458_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using Xunit; 4 | 5 | namespace LiteDB.Tests.Issues; 6 | 7 | public class Issue2458_Tests 8 | { 9 | [Fact] 10 | public void NegativeSeekFails() 11 | { 12 | using var db = new LiteDatabase(":memory:"); 13 | var fs = db.FileStorage; 14 | AddTestFile("test", 1, fs); 15 | using Stream stream = fs.OpenRead("test"); 16 | Assert.Throws(() => stream.Position = -1); 17 | } 18 | 19 | //https://learn.microsoft.com/en-us/dotnet/api/system.io.stream.position?view=net-8.0 says seeking to a position 20 | //beyond the end of a stream is supported, so implementations should support it (error on read). 21 | [Fact] 22 | public void SeekPastFileSucceds() 23 | { 24 | using var db = new LiteDatabase(":memory:"); 25 | var fs = db.FileStorage; 26 | AddTestFile("test", 1, fs); 27 | using Stream stream = fs.OpenRead("test"); 28 | stream.Position = Int32.MaxValue; 29 | } 30 | 31 | [Fact] 32 | public void SeekShortChunks() 33 | { 34 | using var db = new LiteDatabase(":memory:"); 35 | var fs = db.FileStorage; 36 | using(Stream writeStream = fs.OpenWrite("test", "test")) 37 | { 38 | writeStream.WriteByte(0); 39 | writeStream.Flush(); //Create single-byte chunk just containing a 0 40 | writeStream.WriteByte(1); 41 | writeStream.Flush(); 42 | writeStream.WriteByte(2); 43 | } 44 | using Stream readStream = fs.OpenRead("test"); 45 | readStream.Position = 2; 46 | Assert.Equal(2, readStream.ReadByte()); 47 | } 48 | 49 | private void AddTestFile(string id, long length, ILiteStorage fs) 50 | { 51 | using Stream writeStream = fs.OpenWrite(id, id); 52 | writeStream.Write(new byte[length]); 53 | } 54 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Issues/Issue2487_Tests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | 3 | using System.Diagnostics; 4 | 5 | using Xunit; 6 | 7 | namespace LiteDB.Tests.Issues; 8 | 9 | public class Issue2487_tests 10 | { 11 | private class DataClass 12 | { 13 | [BsonId] 14 | public int Id { get; set; } 15 | 16 | public string Foo { get; set; } 17 | 18 | public string Bar { get; set; } 19 | } 20 | 21 | [Fact] 22 | public void Test_Contains_EmptyStrings() 23 | { 24 | using var engine = new ConnectionString(":memory:").CreateEngine(); 25 | 26 | using var db = new LiteDatabase(engine); 27 | var collection = db.GetCollection("data"); 28 | 29 | collection.Insert(new DataClass { Foo = "bar", Bar = "abc" }); 30 | collection.Insert(new DataClass { Foo = " ", Bar = "def" }); 31 | collection.Insert(new DataClass { Foo = "fo bar", Bar = "def" }); 32 | collection.Insert(new DataClass { Foo = "", Bar = "def" }); 33 | collection.Insert(new DataClass { Foo = null, Bar = "def" }); 34 | 35 | var containsAction = () => collection.FindOne(x => x.Foo.Contains(" ")); 36 | containsAction.Should().NotThrow(); 37 | 38 | var def = containsAction(); 39 | def.Should().NotBeNull(); 40 | def.Bar.Should().Be("def"); 41 | 42 | var shouldExecute = () => engine.Query("data", Query.All(Query.Contains("Foo", " "))); 43 | shouldExecute.Should().NotThrow(); 44 | } 45 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Issues/Issue2494_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | using Xunit; 9 | 10 | namespace LiteDB.Tests.Issues; 11 | 12 | public class Issue2494_Tests 13 | { 14 | [Fact] 15 | public static void Test() 16 | { 17 | var original = "../../../Resources/Issue_2494_EncryptedV4.db"; 18 | using var filename = new TempFile(original); 19 | 20 | var connectionString = new ConnectionString(filename) 21 | { 22 | Password = "pass123", 23 | Upgrade = true, 24 | }; 25 | 26 | using (var db = new LiteDatabase(connectionString)) // <= throws as of version 5.0.18 27 | { 28 | var col = db.GetCollection(); 29 | col.FindAll(); 30 | } 31 | } 32 | 33 | public class PlayerDto 34 | { 35 | [BsonId] 36 | public Guid Id { get; set; } 37 | 38 | public string Name { get; set; } 39 | 40 | public PlayerDto(Guid id, string name) 41 | { 42 | Id = id; 43 | Name = name; 44 | } 45 | 46 | public PlayerDto() 47 | { 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Issues/Issue2506_Tests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.IO; 3 | using Xunit; 4 | 5 | namespace LiteDB.Tests.Issues; 6 | 7 | public class Issue2506_Tests 8 | { 9 | [Fact] 10 | public void Test() 11 | { 12 | // Open database connection 13 | using LiteDatabase dataBase = new("demo.db"); 14 | 15 | // Get the file metadata/chunks storage 16 | ILiteStorage fileStorage = dataBase.GetStorage("myFiles", "myChunks"); 17 | 18 | // Upload empty test file to file storage 19 | using MemoryStream emptyStream = new(); 20 | fileStorage.Upload("photos/2014/picture-01.jpg", "picture-01.jpg", emptyStream); 21 | 22 | // Find file reference by its ID (returns null if not found) 23 | LiteFileInfo file = fileStorage.FindById("photos/2014/picture-01.jpg"); 24 | Assert.NotNull(file); 25 | 26 | // Load and save file bytes to hard drive 27 | file.SaveAs(Path.Combine(Path.GetTempPath(), "new-picture.jpg")); 28 | 29 | // Find all files matching pattern 30 | IEnumerable> files = fileStorage.Find("_id LIKE 'photos/2014/%'"); 31 | Assert.Single(files); 32 | // Find all files matching pattern using parameters 33 | IEnumerable> files2 = fileStorage.Find("_id LIKE @0", "photos/2014/%"); 34 | Assert.Single(files2); 35 | } 36 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Issues/Issue2534_Tests.cs: -------------------------------------------------------------------------------- 1 | using Xunit; 2 | 3 | namespace LiteDB.Tests.Issues; 4 | 5 | public class Issue2534_Tests 6 | { 7 | [Fact] 8 | public void Test() 9 | { 10 | using LiteDatabase database = new(new ConnectionString() 11 | { 12 | Filename = "Demo.db", 13 | Connection = ConnectionType.Shared, 14 | }); 15 | 16 | ILiteCollection accounts = database.GetCollection("Issue2534"); 17 | 18 | if (accounts.Count() < 3) 19 | { 20 | accounts.Insert(new BsonDocument()); 21 | accounts.Insert(new BsonDocument()); 22 | accounts.Insert(new BsonDocument()); 23 | } 24 | 25 | foreach (BsonDocument document in accounts.FindAll()) 26 | { 27 | accounts.Update(document); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Issues/Pull2468_Tests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | using Xunit; 10 | 11 | using static LiteDB.Tests.Issues.Issue1838_Tests; 12 | 13 | namespace LiteDB.Tests.Issues; 14 | 15 | public class Pull2468_Tests 16 | { 17 | // tests if lowerinvariant works 18 | [Fact] 19 | public void Supports_LowerInvariant() 20 | { 21 | using var db = new LiteDatabase(":memory:"); 22 | var collection = db.GetCollection(nameof(TestType)); 23 | 24 | collection.Insert(new TestType() 25 | { 26 | Foo = "Abc", 27 | Timestamp = DateTimeOffset.UtcNow, 28 | }); 29 | 30 | collection.Insert(new TestType() 31 | { 32 | Foo = "Def", 33 | Timestamp = DateTimeOffset.UtcNow, 34 | }); 35 | 36 | var result = collection.Query() 37 | .Where(x => x.Foo.ToLowerInvariant() == "abc") 38 | .ToList(); 39 | 40 | Assert.NotNull(result); 41 | Assert.Single(result); 42 | } 43 | 44 | // tests if upperinvariant works 45 | [Fact] 46 | public void Supports_UpperInvariant() 47 | { 48 | using var db = new LiteDatabase(":memory:"); 49 | var collection = db.GetCollection(nameof(TestType)); 50 | 51 | collection.Insert(new TestType() 52 | { 53 | Foo = "Abc", 54 | Timestamp = DateTimeOffset.UtcNow, 55 | }); 56 | 57 | collection.Insert(new TestType() 58 | { 59 | Foo = "Def", 60 | Timestamp = DateTimeOffset.UtcNow, 61 | }); 62 | 63 | var result = collection.Query() 64 | .Where(x => x.Foo.ToUpperInvariant() == "ABC") 65 | .ToList(); 66 | 67 | Assert.NotNull(result); 68 | Assert.Single(result); 69 | } 70 | } -------------------------------------------------------------------------------- /LiteDB.Tests/LiteDB.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net8 5 | LiteDB.Tests 6 | LiteDB.Tests 7 | Maurício David 8 | MIT 9 | en-US 10 | false 11 | 1701;1702;1705;1591;0618 12 | True 13 | ..\LiteDB\LiteDB.snk 14 | 15 | 16 | 17 | 18 | PreserveNewest 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | all 37 | runtime; build; native; contentfiles; analyzers; buildtransitive 38 | 39 | 40 | 41 | all 42 | runtime; build; native; contentfiles; analyzers; buildtransitive 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /LiteDB.Tests/Mapper/CustomInterface_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using FluentAssertions; 4 | using Xunit; 5 | 6 | namespace LiteDB.Tests.Mapper 7 | { 8 | public class CustomInterface_Tests 9 | { 10 | public class User 11 | { 12 | private List strings = new List(); 13 | public int Id { get; set; } 14 | public IReadOnlyList Strings 15 | { 16 | get => strings; 17 | set => strings = new List(value); 18 | } 19 | } 20 | 21 | [Fact] 22 | public void Custom_Interface_Implements_IEnumerable() 23 | { 24 | var mapper = new BsonMapper(); 25 | 26 | var user = new User { Id = 1, Strings = new List { "aaa", "bbb" } }; 27 | var doc = mapper.ToDocument(user); 28 | var user2 = mapper.ToObject(doc); 29 | 30 | Assert.Equal(user.Id, user2.Id); 31 | Assert.Equal(user.Strings.Count, user2.Strings.Count); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Mapper/CustomMapping_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using FluentAssertions; 4 | using Xunit; 5 | 6 | namespace LiteDB.Tests.Mapper 7 | { 8 | public class CustomMappingCtor_Tests 9 | { 10 | public class UserWithCustomId 11 | { 12 | public int Key { get; } 13 | public string Name { get; } 14 | 15 | public UserWithCustomId(int key, string name) 16 | { 17 | this.Key = key; 18 | this.Name = name; 19 | } 20 | } 21 | 22 | [Fact] 23 | public void Custom_Ctor_With_Custom_Id() 24 | { 25 | var mapper = new BsonMapper(); 26 | 27 | mapper.Entity() 28 | .Id(u => u.Key, false); 29 | 30 | var doc = new BsonDocument { ["_id"] = 10, ["name"] = "John" }; 31 | 32 | var user = mapper.ToObject(doc); 33 | 34 | user.Key.Should().Be(10); // Expected user.Key to be 10, but found 0. 35 | user.Name.Should().Be("John"); 36 | } 37 | 38 | public abstract class BaseClass 39 | { 40 | [BsonId] 41 | public string CustomId { get; set; } 42 | 43 | [BsonField("CustomName")] 44 | public string Name { get; set; } 45 | } 46 | 47 | public class ConcreteClass : BaseClass 48 | { 49 | 50 | } 51 | 52 | [Fact] 53 | public void Custom_Id_In_Interface() 54 | { 55 | var mapper = new BsonMapper(); 56 | 57 | var obj = new ConcreteClass { CustomId = "myid", Name = "myname" }; 58 | var doc = mapper.Serialize(obj) as BsonDocument; 59 | doc["_id"].Should().NotBeNull(); 60 | doc["_id"].Should().Be("myid"); 61 | doc["CustomName"].Should().NotBe(BsonValue.Null); 62 | doc["CustomName"].Should().Be("myname"); 63 | doc["Name"].Should().Be(BsonValue.Null); 64 | doc.Keys.ExpectCount(2); 65 | } 66 | } 67 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Mapper/Mapper_Tests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System; 3 | using System.Reflection; 4 | using Xunit; 5 | 6 | namespace LiteDB.Tests.Mapper 7 | { 8 | public class Mapper_Tests 9 | { 10 | private readonly BsonMapper _mapper = new BsonMapper(); 11 | 12 | [Fact] 13 | public void ToDocument_ReturnsNull_WhenFail() 14 | { 15 | var array = new int[] { 1, 2, 3, 4, 5 }; 16 | var doc1 = _mapper.ToDocument(array); 17 | doc1.Should().Be(null); 18 | 19 | var doc2 = _mapper.ToDocument(typeof(int[]), array); 20 | doc2.Should().Be(null); 21 | } 22 | 23 | [Fact] 24 | public void Class_Not_Assignable() 25 | { 26 | using (var db = new LiteDatabase(":memory:")) 27 | { 28 | var col = db.GetCollection("Test"); 29 | col.Insert(new MyClass { Id = 1, Member = null }); 30 | var type = typeof(OtherClass); 31 | var typeName = type.FullName + ", " + type.GetTypeInfo().Assembly.GetName().Name; 32 | 33 | db.Execute($"update Test set Member = {{_id: 1, Name: null, _type: \"{typeName}\"}} where _id = 1"); 34 | 35 | Func func = (() => col.FindById(1)); 36 | func.Should().Throw(); 37 | } 38 | } 39 | 40 | public class MyClass 41 | { 42 | public int Id { get; set; } 43 | public MyClass Member { get; set; } 44 | } 45 | 46 | public class OtherClass 47 | { 48 | public string Name { get; set; } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /LiteDB.Tests/Mapper/Records_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using FluentAssertions; 4 | using Xunit; 5 | 6 | namespace LiteDB.Tests.Mapper 7 | { 8 | public class Records_Tests 9 | { 10 | public record User(int Id, string Name); 11 | 12 | 13 | [Fact] 14 | public void Record_Simple_Mapper() 15 | { 16 | var mapper = new BsonMapper(); 17 | 18 | var user = new User(1, "John"); 19 | var doc = mapper.ToDocument(user); 20 | var user2 = mapper.ToObject(doc); 21 | 22 | Assert.Equal(user.Id, user2.Id); 23 | Assert.Equal(user.Name, user2.Name); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Mapper/StructField_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using FluentAssertions; 6 | using Xunit; 7 | 8 | namespace LiteDB.Tests.Mapper 9 | { 10 | public class StructFields_Tests 11 | { 12 | public struct Point2D 13 | { 14 | public int X; 15 | public int Y; 16 | } 17 | 18 | [Fact] 19 | public void Serialize_Struct_Fields() 20 | { 21 | var m = new BsonMapper(); 22 | 23 | m.IncludeFields = true; 24 | 25 | using (var db = new LiteDatabase(new MemoryStream(), m, new MemoryStream())) 26 | { 27 | var col = db.GetCollection("mytable"); 28 | 29 | col.Insert(new Point2D { X = 10, Y = 120 }); 30 | col.Insert(new Point2D { X = 15, Y = 130 }); 31 | col.Insert(new Point2D { X = 20, Y = 140 }); 32 | 33 | var col2 = db.GetCollection("mytable"); 34 | 35 | var allY = col2.Query().Select(p => p.Y).ToArray(); 36 | var allX = col2.Query().Select(p => p.X).ToArray(); 37 | var allStruct = col2.Query().ToArray(); 38 | var allNewStruct = col2.Query().Select(p => new { NewX = p.X, NewY = p.Y }).ToArray(); 39 | 40 | } 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Query/Data/PersonGroupByData.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System; 3 | using System.IO; 4 | using System.Linq; 5 | using Xunit; 6 | 7 | namespace LiteDB.Tests.QueryTest 8 | { 9 | public class PersonGroupByData : IDisposable 10 | { 11 | private readonly Person[] _local; 12 | private readonly ILiteDatabase _db; 13 | private readonly ILiteCollection _collection; 14 | 15 | public PersonGroupByData() 16 | { 17 | _local = DataGen.Person(1, 1000).ToArray(); 18 | _db = new LiteDatabase(new MemoryStream()); 19 | _collection = _db.GetCollection(); 20 | 21 | _collection.Insert(_local); 22 | _collection.EnsureIndex(x => x.Age); 23 | } 24 | 25 | public (ILiteCollection, Person[]) GetData() => (_collection, _local); 26 | 27 | public void Dispose() 28 | { 29 | _db.Dispose(); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Query/Data/PersonQueryData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | 4 | namespace LiteDB.Tests.QueryTest 5 | { 6 | public class PersonQueryData : IDisposable 7 | { 8 | private readonly Person[] _local; 9 | private readonly ILiteDatabase _db; 10 | private readonly ILiteCollection _collection; 11 | 12 | public PersonQueryData() 13 | { 14 | _local = DataGen.Person().ToArray(); 15 | 16 | _db = new LiteDatabase(":memory:"); 17 | _collection = _db.GetCollection("person"); 18 | _collection.Insert(this._local); 19 | } 20 | 21 | public (ILiteCollection, Person[]) GetData() => (_collection, _local); 22 | 23 | public void Dispose() 24 | { 25 | _db.Dispose(); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Query/QueryApi_Tests.cs: -------------------------------------------------------------------------------- 1 | using FluentAssertions; 2 | using System; 3 | using System.Linq; 4 | using Xunit; 5 | 6 | namespace LiteDB.Tests.QueryTest 7 | { 8 | public class QueryApi_Tests : PersonQueryData 9 | { 10 | [Fact] 11 | public void Query_And() 12 | { 13 | using var db = new PersonQueryData(); 14 | var (collection, local) = db.GetData(); 15 | 16 | var r0 = local.Where(x => x.Age == 22 && x.Active == true).ToArray(); 17 | 18 | var r1 = collection.Find(Query.And(Query.EQ("Age", 22), Query.EQ("Active", true))).ToArray(); 19 | 20 | AssertEx.ArrayEqual(r0, r1, true); 21 | } 22 | 23 | [Fact] 24 | public void Query_And_Same_Field() 25 | { 26 | using var db = new PersonQueryData(); 27 | var (collection, local) = db.GetData(); 28 | 29 | var r0 = local.Where(x => x.Age > 22 && x.Age < 25).ToArray(); 30 | 31 | var r1 = collection.Find(Query.And(Query.GT("Age", 22), Query.LT("Age", 25))).ToArray(); 32 | 33 | AssertEx.ArrayEqual(r0, r1, true); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Resources/Issue2417_MyData.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litedb-org/LiteDB/2cc00c7094a966b7912c321c1ff7a754433c6d14/LiteDB.Tests/Resources/Issue2417_MyData.db -------------------------------------------------------------------------------- /LiteDB.Tests/Resources/Issue2417_TestCacheDb.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litedb-org/LiteDB/2cc00c7094a966b7912c321c1ff7a754433c6d14/LiteDB.Tests/Resources/Issue2417_TestCacheDb.db -------------------------------------------------------------------------------- /LiteDB.Tests/Resources/Issue_2494_EncryptedV4.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litedb-org/LiteDB/2cc00c7094a966b7912c321c1ff7a754433c6d14/LiteDB.Tests/Resources/Issue_2494_EncryptedV4.db -------------------------------------------------------------------------------- /LiteDB.Tests/Resources/v4.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litedb-org/LiteDB/2cc00c7094a966b7912c321c1ff7a754433c6d14/LiteDB.Tests/Resources/v4.db -------------------------------------------------------------------------------- /LiteDB.Tests/Utils/Faker.Names.cs: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litedb-org/LiteDB/2cc00c7094a966b7912c321c1ff7a754433c6d14/LiteDB.Tests/Utils/Faker.Names.cs -------------------------------------------------------------------------------- /LiteDB.Tests/Utils/LiteEngineExtensions.cs: -------------------------------------------------------------------------------- 1 | using LiteDB.Engine; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace LiteDB.Tests 6 | { 7 | public static class LiteEngineExtensions 8 | { 9 | public static int Insert(this LiteEngine engine, string collection, BsonDocument doc, BsonAutoId autoId = BsonAutoId.ObjectId) 10 | { 11 | return engine.Insert(collection, new BsonDocument[] {doc}, autoId); 12 | } 13 | 14 | public static int Update(this LiteEngine engine, string collection, BsonDocument doc) 15 | { 16 | return engine.Update(collection, new BsonDocument[] {doc}); 17 | } 18 | 19 | public static List Find(this LiteEngine engine, string collection, BsonExpression where) 20 | { 21 | var q = new LiteDB.Query(); 22 | 23 | if (where != null) 24 | { 25 | q.Where.Add(where); 26 | } 27 | 28 | var docs = new List(); 29 | 30 | using (var r = engine.Query(collection, q)) 31 | { 32 | while (r.Read()) 33 | { 34 | docs.Add(r.Current.AsDocument); 35 | } 36 | } 37 | 38 | return docs; 39 | } 40 | 41 | public static BsonDocument GetPageLog(this LiteEngine engine, int pageID) 42 | { 43 | return engine.Find($"$dump({pageID})", "1=1").Last(); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Utils/Models/Person.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace LiteDB.Tests 5 | { 6 | public class Person : IEqualityComparer, IComparable 7 | { 8 | public int Id { get; set; } 9 | public string Name { get; set; } 10 | public int Age { get; set; } 11 | public string[] Phones { get; set; } 12 | public string Email { get; set; } 13 | public Address Address { get; set; } 14 | public DateTime Date { get; set; } 15 | public bool Active { get; set; } 16 | 17 | public int CompareTo(Person other) 18 | { 19 | return this.Id.CompareTo(other.Id); 20 | } 21 | 22 | public bool Equals(Person x, Person y) 23 | { 24 | return x.Id == y.Id; 25 | } 26 | 27 | public int GetHashCode(Person obj) 28 | { 29 | return obj.Id.GetHashCode(); 30 | } 31 | 32 | public override string ToString() 33 | { 34 | return new BsonMapper().Serialize(this).ToString(); 35 | } 36 | } 37 | 38 | public class Address 39 | { 40 | public string Street { get; set; } 41 | public string City { get; set; } 42 | public string State { get; set; } 43 | } 44 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Utils/Models/Zip.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace LiteDB.Tests 5 | { 6 | // { 7 | // "_id": "01001", 8 | // "city": "AGAWAM", 9 | // "loc": [ -72.622739, 42.070206 ], 10 | // "pop": 15338, 11 | // "state": "MA" 12 | // } 13 | public class Zip : IEqualityComparer, IComparable 14 | { 15 | public string Id { get; set; } 16 | public string City { get; set; } 17 | public double[] Loc { get; set; } 18 | public string State { get; set; } 19 | 20 | public int CompareTo(Zip other) 21 | { 22 | return this.Id.CompareTo(other.Id); 23 | } 24 | 25 | public bool Equals(Zip x, Zip y) 26 | { 27 | return x.Id == y.Id && 28 | x.City == y.City && 29 | x.Loc == y.Loc && 30 | x.State == y.State; 31 | } 32 | 33 | public int GetHashCode(Zip obj) 34 | { 35 | return this.Id.GetHashCode(); 36 | } 37 | 38 | public override string ToString() 39 | { 40 | return new BsonMapper().Serialize(this).ToString(); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /LiteDB.Tests/Utils/TempFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace LiteDB.Tests 5 | { 6 | public class TempFile : IDisposable 7 | { 8 | public string Filename { get; private set; } 9 | 10 | public TempFile() 11 | { 12 | var path = Path.GetTempPath(); 13 | var name = "litedb-" + Guid.NewGuid().ToString("d").Substring(0, 5) + ".db"; 14 | 15 | this.Filename = Path.Combine(path, name); 16 | } 17 | 18 | public TempFile(string original) 19 | { 20 | var rnd = Guid.NewGuid().ToString("d").Substring(0, 5); 21 | var path = Path.GetTempPath(); 22 | var name = $"litedb-{rnd}.db"; 23 | var filename = Path.Combine(path, name); 24 | 25 | File.Copy(original, filename, true); 26 | 27 | this.Filename = filename; 28 | } 29 | 30 | #region Dispose 31 | 32 | public static implicit operator String(TempFile value) 33 | { 34 | return value.Filename; 35 | } 36 | 37 | private bool _disposed; 38 | 39 | public void Dispose() 40 | { 41 | Dispose(true); 42 | GC.SuppressFinalize(this); 43 | } 44 | 45 | ~TempFile() 46 | { 47 | Dispose(false); 48 | } 49 | 50 | protected virtual void Dispose(bool disposing) 51 | { 52 | if (_disposed) 53 | return; 54 | 55 | if (disposing) 56 | { 57 | // free other managed objects that implement 58 | // IDisposable only 59 | } 60 | 61 | // check file integrity 62 | 63 | File.Delete(this.Filename); 64 | 65 | _disposed = true; 66 | } 67 | 68 | #endregion 69 | 70 | public long Size => new FileInfo(this.Filename).Length; 71 | 72 | public string ReadAsText() => File.ReadAllText(this.Filename); 73 | 74 | public override string ToString() => this.Filename; 75 | } 76 | } -------------------------------------------------------------------------------- /LiteDB.Tests/xunit.runner.json: -------------------------------------------------------------------------------- 1 | { 2 | "parallelizeAssembly": false, 3 | "parallelizeTestCollections": false 4 | } -------------------------------------------------------------------------------- /LiteDB/Client/Database/Collections/Include.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Linq.Expressions; 4 | using static LiteDB.Constants; 5 | 6 | namespace LiteDB 7 | { 8 | public partial class LiteCollection 9 | { 10 | /// 11 | /// Run an include action in each document returned by Find(), FindById(), FindOne() and All() methods to load DbRef documents 12 | /// Returns a new Collection with this action included 13 | /// 14 | public ILiteCollection Include(Expression> keySelector) 15 | { 16 | if (keySelector == null) throw new ArgumentNullException(nameof(keySelector)); 17 | 18 | var path = _mapper.GetExpression(keySelector); 19 | 20 | return this.Include(path); 21 | } 22 | 23 | /// 24 | /// Run an include action in each document returned by Find(), FindById(), FindOne() and All() methods to load DbRef documents 25 | /// Returns a new Collection with this action included 26 | /// 27 | public ILiteCollection Include(BsonExpression keySelector) 28 | { 29 | if (string.IsNullOrEmpty(keySelector)) throw new ArgumentNullException(nameof(keySelector)); 30 | 31 | // cloning this collection and adding this include 32 | var newcol = new LiteCollection(_collection, _autoId, _engine, _mapper); 33 | 34 | newcol._includes.AddRange(_includes); 35 | newcol._includes.Add(keySelector); 36 | 37 | return newcol; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /LiteDB/Client/Database/Collections/Upsert.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using static LiteDB.Constants; 5 | 6 | namespace LiteDB 7 | { 8 | public partial class LiteCollection 9 | { 10 | /// 11 | /// Insert or Update a document in this collection. 12 | /// 13 | public bool Upsert(T entity) 14 | { 15 | if (entity == null) throw new ArgumentNullException(nameof(entity)); 16 | 17 | return this.Upsert(new T[] { entity }) == 1; 18 | } 19 | 20 | /// 21 | /// Insert or Update all documents 22 | /// 23 | public int Upsert(IEnumerable entities) 24 | { 25 | if (entities == null) throw new ArgumentNullException(nameof(entities)); 26 | 27 | return _engine.Upsert(_collection, this.GetBsonDocs(entities), _autoId); 28 | } 29 | 30 | /// 31 | /// Insert or Update a document in this collection. 32 | /// 33 | public bool Upsert(BsonValue id, T entity) 34 | { 35 | if (entity == null) throw new ArgumentNullException(nameof(entity)); 36 | if (id == null || id.IsNull) throw new ArgumentNullException(nameof(id)); 37 | 38 | // get BsonDocument from object 39 | var doc = _mapper.ToDocument(entity); 40 | 41 | // set document _id using id parameter 42 | doc["_id"] = id; 43 | 44 | return _engine.Upsert(_collection, new[] { doc }, _autoId) > 0; 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /LiteDB/Client/Mapper/Attributes/BsonCtorAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static LiteDB.Constants; 3 | 4 | namespace LiteDB 5 | { 6 | /// 7 | /// Indicate which constructor method will be used in this entity 8 | /// 9 | public class BsonCtorAttribute : Attribute 10 | { 11 | } 12 | } -------------------------------------------------------------------------------- /LiteDB/Client/Mapper/Attributes/BsonFieldAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static LiteDB.Constants; 3 | 4 | namespace LiteDB 5 | { 6 | /// 7 | /// Set a name to this property in BsonDocument 8 | /// 9 | public class BsonFieldAttribute : Attribute 10 | { 11 | public string Name { get; set; } 12 | 13 | public BsonFieldAttribute(string name) 14 | { 15 | this.Name = name; 16 | } 17 | 18 | public BsonFieldAttribute() 19 | { 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /LiteDB/Client/Mapper/Attributes/BsonIdAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static LiteDB.Constants; 3 | 4 | namespace LiteDB 5 | { 6 | /// 7 | /// Indicate that property will be used as BsonDocument Id 8 | /// 9 | public class BsonIdAttribute : Attribute 10 | { 11 | public bool AutoId { get; private set; } 12 | 13 | public BsonIdAttribute() 14 | { 15 | this.AutoId = true; 16 | } 17 | 18 | public BsonIdAttribute(bool autoId) 19 | { 20 | this.AutoId = autoId; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /LiteDB/Client/Mapper/Attributes/BsonIgnoreAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static LiteDB.Constants; 3 | 4 | namespace LiteDB 5 | { 6 | /// 7 | /// Indicate that property will not be persist in Bson serialization 8 | /// 9 | public class BsonIgnoreAttribute : Attribute 10 | { 11 | } 12 | } -------------------------------------------------------------------------------- /LiteDB/Client/Mapper/Attributes/BsonRefAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static LiteDB.Constants; 3 | 4 | namespace LiteDB 5 | { 6 | /// 7 | /// Indicate that field are not persisted inside this document but it's a reference for another document (DbRef) 8 | /// 9 | public class BsonRefAttribute : Attribute 10 | { 11 | public string Collection { get; set; } 12 | 13 | public BsonRefAttribute(string collection) 14 | { 15 | this.Collection = collection; 16 | } 17 | 18 | public BsonRefAttribute() 19 | { 20 | this.Collection = null; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /LiteDB/Client/Mapper/Linq/ParameterExpressionVisitor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Reflection; 7 | using System.Text; 8 | using static LiteDB.Constants; 9 | 10 | namespace LiteDB 11 | { 12 | /// 13 | /// Class used to test in an Expression member expression is based on parameter `x => x.Name` or variable `x => externalVar` 14 | /// 15 | internal class ParameterExpressionVisitor : ExpressionVisitor 16 | { 17 | public bool IsParameter { get; private set; } = false; 18 | 19 | protected override Expression VisitParameter(ParameterExpression node) 20 | { 21 | this.IsParameter = true; 22 | 23 | return base.VisitParameter(node); 24 | } 25 | 26 | public static bool Test(Expression node) 27 | { 28 | var instance = new ParameterExpressionVisitor(); 29 | 30 | instance.Visit(node); 31 | 32 | return instance.IsParameter; 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /LiteDB/Client/Mapper/Linq/TypeResolver/ConvertResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Reflection; 7 | using System.Text; 8 | using static LiteDB.Constants; 9 | 10 | namespace LiteDB 11 | { 12 | internal class ConvertResolver : ITypeResolver 13 | { 14 | public string ResolveMethod(MethodInfo method) 15 | { 16 | switch (method.Name) 17 | { 18 | case "ToInt32": return "INT32(@0)"; 19 | case "ToInt64": return "INT64(@0)"; 20 | case "ToDouble": return "DOUBLE(@0)"; 21 | case "ToDecimal": return "DECIMAL(@0)"; 22 | 23 | case "ToDateTime": return "DATE(@0)"; 24 | case "FromBase64String": return "BINARY(@0)"; 25 | case "ToBoolean": return "BOOL(@0)"; 26 | case "ToString": return "STRING(@0)"; 27 | } 28 | 29 | return null; 30 | } 31 | 32 | public string ResolveMember(MemberInfo member) => null; 33 | public string ResolveCtor(ConstructorInfo ctor) => null; 34 | } 35 | } -------------------------------------------------------------------------------- /LiteDB/Client/Mapper/Linq/TypeResolver/GuidResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Reflection; 7 | using System.Text; 8 | using static LiteDB.Constants; 9 | 10 | namespace LiteDB 11 | { 12 | internal class GuidResolver : ITypeResolver 13 | { 14 | public string ResolveMethod(MethodInfo method) 15 | { 16 | switch (method.Name) 17 | { 18 | // instance methods 19 | case "ToString": return "STRING(#)"; 20 | 21 | // static methods 22 | case "NewGuid": return "GUID()"; 23 | case "Parse": return "GUID(@0)"; 24 | case "TryParse": throw new NotSupportedException("There is no TryParse translate. Use Guid.Parse()"); 25 | case "Equals": return "# = @0"; 26 | } 27 | 28 | return null; 29 | } 30 | 31 | public string ResolveMember(MemberInfo member) 32 | { 33 | switch (member.Name) 34 | { 35 | // static properties 36 | case "Empty": return "GUID('00000000-0000-0000-0000-000000000000')"; 37 | } 38 | 39 | return null; 40 | } 41 | 42 | public string ResolveCtor(ConstructorInfo ctor) 43 | { 44 | var pars = ctor.GetParameters(); 45 | 46 | if (pars.Length == 1) 47 | { 48 | // string s 49 | if (pars[0].ParameterType == typeof(string)) 50 | { 51 | return "GUID(@0)"; 52 | } 53 | } 54 | 55 | return null; 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /LiteDB/Client/Mapper/Linq/TypeResolver/ICollectionResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Reflection; 7 | using System.Text; 8 | using static LiteDB.Constants; 9 | 10 | namespace LiteDB 11 | { 12 | internal class ICollectionResolver : EnumerableResolver 13 | { 14 | public override string ResolveMethod(MethodInfo method) 15 | { 16 | // special Contains method 17 | switch(method.Name) 18 | { 19 | case "Contains": return "# ANY = @0"; 20 | }; 21 | 22 | return base.ResolveMethod(method); 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /LiteDB/Client/Mapper/Linq/TypeResolver/ITypeResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Text.RegularExpressions; 9 | using static LiteDB.Constants; 10 | 11 | namespace LiteDB 12 | { 13 | internal interface ITypeResolver 14 | { 15 | string ResolveMethod(MethodInfo method); 16 | 17 | string ResolveMember(MemberInfo member); 18 | 19 | string ResolveCtor(ConstructorInfo ctor); 20 | } 21 | } -------------------------------------------------------------------------------- /LiteDB/Client/Mapper/Linq/TypeResolver/MathResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Reflection; 7 | using System.Text; 8 | using static LiteDB.Constants; 9 | 10 | namespace LiteDB 11 | { 12 | internal class MathResolver : ITypeResolver 13 | { 14 | public string ResolveMethod(MethodInfo method) 15 | { 16 | var qtParams = method.GetParameters().Length; 17 | 18 | switch (method.Name) 19 | { 20 | case "Abs": return "ABS(@0)"; 21 | case "Pow": return "POW(@0, @1)"; 22 | case "Round": 23 | if (qtParams != 2) throw new ArgumentOutOfRangeException("Method Round need 2 arguments when convert to BsonExpression"); 24 | return "ROUND(@0, @1)"; 25 | } 26 | 27 | return null; 28 | } 29 | 30 | public string ResolveMember(MemberInfo member) => null; 31 | public string ResolveCtor(ConstructorInfo ctor) => null; 32 | } 33 | } -------------------------------------------------------------------------------- /LiteDB/Client/Mapper/Linq/TypeResolver/NullableResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Reflection; 7 | using System.Text; 8 | using static LiteDB.Constants; 9 | 10 | namespace LiteDB 11 | { 12 | internal class NullableResolver : ITypeResolver 13 | { 14 | public string ResolveMethod(MethodInfo method) 15 | { 16 | return null; 17 | } 18 | 19 | public string ResolveMember(MemberInfo member) 20 | { 21 | switch (member.Name) 22 | { 23 | case "HasValue": return "(IS_NULL(#) = false)"; 24 | case "Value": return "#"; 25 | } 26 | 27 | return null; 28 | } 29 | 30 | public string ResolveCtor(ConstructorInfo ctor) => null; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LiteDB/Client/Mapper/Linq/TypeResolver/NumberResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Reflection; 7 | using System.Text; 8 | using static LiteDB.Constants; 9 | 10 | namespace LiteDB 11 | { 12 | internal class NumberResolver : ITypeResolver 13 | { 14 | private readonly string _parseMethod; 15 | 16 | public NumberResolver(string parseMethod) 17 | { 18 | _parseMethod = parseMethod; 19 | } 20 | 21 | public string ResolveMethod(MethodInfo method) 22 | { 23 | switch (method.Name) 24 | { 25 | // instance methods 26 | case "ToString": 27 | var pars = method.GetParameters(); 28 | if (pars.Length == 0) return "STRING(#)"; 29 | else if (pars.Length == 1 && pars[0].ParameterType == typeof(string)) return "FORMAT(#, @0)"; 30 | break; 31 | 32 | // static methods 33 | case "Parse": return $"{_parseMethod}(@0)"; 34 | case "Equals": return "# = @0"; 35 | }; 36 | 37 | return null; 38 | } 39 | 40 | public string ResolveMember(MemberInfo member) => null; 41 | public string ResolveCtor(ConstructorInfo ctor) => null; 42 | } 43 | } -------------------------------------------------------------------------------- /LiteDB/Client/Mapper/Linq/TypeResolver/ObjectIdResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Reflection; 7 | using System.Text; 8 | using static LiteDB.Constants; 9 | 10 | namespace LiteDB 11 | { 12 | internal class ObjectIdResolver : ITypeResolver 13 | { 14 | public string ResolveMethod(MethodInfo method) 15 | { 16 | switch (method.Name) 17 | { 18 | // instance methods 19 | case "ToString": return "STRING(#)"; 20 | case "Equals": return "# = @0"; 21 | }; 22 | 23 | return null; 24 | } 25 | 26 | public string ResolveMember(MemberInfo member) 27 | { 28 | switch (member.Name) 29 | { 30 | // static properties 31 | case "Empty": return "OBJECTID('000000000000000000000000')"; 32 | 33 | // instance properties 34 | case "CreationTime": return "OID_CREATIONTIME(#)"; 35 | } 36 | 37 | return null; 38 | } 39 | 40 | public string ResolveCtor(ConstructorInfo ctor) 41 | { 42 | var pars = ctor.GetParameters(); 43 | 44 | if (pars.Length == 1) 45 | { 46 | // string value 47 | if (pars[0].ParameterType == typeof(string)) 48 | { 49 | return "OBJECTID(@0)"; 50 | } 51 | } 52 | 53 | return null; 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /LiteDB/Client/Mapper/Linq/TypeResolver/RegexResolver.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Reflection; 7 | using System.Text; 8 | using System.Text.RegularExpressions; 9 | using static LiteDB.Constants; 10 | 11 | namespace LiteDB 12 | { 13 | internal class RegexResolver : ITypeResolver 14 | { 15 | public string ResolveMethod(MethodInfo method) 16 | { 17 | switch (method.Name) 18 | { 19 | case "Split": return "SPLIT(@0, @1, true)"; 20 | case "IsMatch": return "IS_MATCH(@0, @1)"; 21 | // missing "Match" 22 | } 23 | 24 | return null; 25 | } 26 | 27 | public string ResolveMember(MemberInfo member) => null; 28 | public string ResolveCtor(ConstructorInfo ctor) => null; 29 | } 30 | } -------------------------------------------------------------------------------- /LiteDB/Client/Mapper/TypeNameBinder/ITypeNameBinder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LiteDB 4 | { 5 | public interface ITypeNameBinder 6 | { 7 | string GetName(Type type); 8 | Type GetType(string name); 9 | } 10 | } -------------------------------------------------------------------------------- /LiteDB/Client/Shared/SharedDataReader.cs: -------------------------------------------------------------------------------- 1 | using LiteDB.Engine; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | 7 | namespace LiteDB 8 | { 9 | public class SharedDataReader : IBsonDataReader 10 | { 11 | private readonly IBsonDataReader _reader; 12 | private readonly Action _dispose; 13 | 14 | private bool _disposed = false; 15 | 16 | public SharedDataReader(IBsonDataReader reader, Action dispose) 17 | { 18 | _reader = reader; 19 | _dispose = dispose; 20 | } 21 | 22 | public BsonValue this[string field] => _reader[field]; 23 | 24 | public string Collection => _reader.Collection; 25 | 26 | public BsonValue Current => _reader.Current; 27 | 28 | public bool HasValues => _reader.HasValues; 29 | 30 | public bool Read() => _reader.Read(); 31 | 32 | public void Dispose() 33 | { 34 | this.Dispose(true); 35 | GC.SuppressFinalize(this); 36 | } 37 | 38 | ~SharedDataReader() 39 | { 40 | this.Dispose(false); 41 | } 42 | 43 | protected virtual void Dispose(bool disposing) 44 | { 45 | if (_disposed) return; 46 | 47 | _disposed = true; 48 | 49 | if (disposing) 50 | { 51 | _reader.Dispose(); 52 | _dispose(); 53 | } 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /LiteDB/Client/SqlParser/Commands/Begin.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LiteDB.Engine; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB 8 | { 9 | internal partial class SqlParser 10 | { 11 | /// 12 | /// BEGIN [ TRANS | TRANSACTION ] 13 | /// 14 | private BsonDataReader ParseBegin() 15 | { 16 | _tokenizer.ReadToken().Expect("BEGIN"); 17 | 18 | var token = _tokenizer.ReadToken().Expect(TokenType.Word, TokenType.EOF, TokenType.SemiColon); 19 | 20 | if (token.Is("TRANS") || token.Is("TRANSACTION")) 21 | { 22 | _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); 23 | } 24 | 25 | var transactionId = _engine.BeginTrans(); 26 | 27 | return new BsonDataReader(transactionId); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /LiteDB/Client/SqlParser/Commands/Checkpoint.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LiteDB.Engine; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB 8 | { 9 | internal partial class SqlParser 10 | { 11 | /// 12 | /// CHECKPOINT 13 | /// 14 | private BsonDataReader ParseCheckpoint() 15 | { 16 | _tokenizer.ReadToken().Expect(Pragmas.CHECKPOINT); 17 | 18 | // read or ; 19 | _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); 20 | 21 | var result = _engine.Checkpoint(); 22 | 23 | return new BsonDataReader(result); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /LiteDB/Client/SqlParser/Commands/Commit.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LiteDB.Engine; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB 8 | { 9 | internal partial class SqlParser 10 | { 11 | /// 12 | /// COMMIT [ TRANS | TRANSACTION ] 13 | /// 14 | private BsonDataReader ParseCommit() 15 | { 16 | _tokenizer.ReadToken().Expect("COMMIT"); 17 | 18 | var token = _tokenizer.ReadToken().Expect(TokenType.Word, TokenType.EOF, TokenType.SemiColon); 19 | 20 | if (token.Is("TRANS") || token.Is("TRANSACTION")) 21 | { 22 | _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); 23 | } 24 | 25 | var result = _engine.Commit(); 26 | 27 | return new BsonDataReader(result); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /LiteDB/Client/SqlParser/Commands/Create.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LiteDB.Engine; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB 8 | { 9 | internal partial class SqlParser 10 | { 11 | /// 12 | /// CREATE [ UNIQUE ] INDEX {indexName} ON {collection} ({indexExpr}) 13 | /// 14 | private BsonDataReader ParseCreate() 15 | { 16 | _tokenizer.ReadToken().Expect("CREATE"); 17 | 18 | var token = _tokenizer.ReadToken().Expect(TokenType.Word); 19 | var unique = token.Is("UNIQUE"); 20 | 21 | if (unique) 22 | { 23 | token = _tokenizer.ReadToken(); 24 | } 25 | 26 | token.Expect("INDEX"); 27 | 28 | // read indexName 29 | var name = _tokenizer.ReadToken().Expect(TokenType.Word).Value; 30 | 31 | _tokenizer.ReadToken().Expect("ON"); 32 | 33 | // read collectionName 34 | var collection = _tokenizer.ReadToken().Expect(TokenType.Word).Value; 35 | 36 | // read ( 37 | _tokenizer.ReadToken().Expect(TokenType.OpenParenthesis); 38 | 39 | // read index expression 40 | var expr = BsonExpression.Create(_tokenizer, BsonExpressionParserMode.Full, new BsonDocument()); 41 | 42 | // read ) 43 | _tokenizer.ReadToken().Expect(TokenType.CloseParenthesis); 44 | 45 | // read EOF or ; 46 | _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); 47 | 48 | var result = _engine.EnsureIndex(collection, name, expr, unique); 49 | 50 | return new BsonDataReader(result); 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /LiteDB/Client/SqlParser/Commands/Delete.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LiteDB.Engine; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB 8 | { 9 | internal partial class SqlParser 10 | { 11 | /// 12 | /// DELETE {collection} WHERE {whereExpr} 13 | /// 14 | private BsonDataReader ParseDelete() 15 | { 16 | _tokenizer.ReadToken().Expect("DELETE"); 17 | 18 | var collection = _tokenizer.ReadToken().Expect(TokenType.Word).Value; 19 | 20 | BsonExpression where = null; 21 | 22 | if (_tokenizer.LookAhead().Is("WHERE")) 23 | { 24 | // read WHERE 25 | _tokenizer.ReadToken(); 26 | 27 | where = BsonExpression.Create(_tokenizer, BsonExpressionParserMode.Full, _parameters); 28 | } 29 | 30 | _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); 31 | 32 | _tokenizer.ReadToken(); 33 | 34 | var result = _engine.DeleteMany(collection, where); 35 | 36 | return new BsonDataReader(result); 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /LiteDB/Client/SqlParser/Commands/Drop.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LiteDB.Engine; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB 8 | { 9 | internal partial class SqlParser 10 | { 11 | /// 12 | /// DROP INDEX {collection}.{indexName} 13 | /// DROP COLLECTION {collection} 14 | /// 15 | private BsonDataReader ParseDrop() 16 | { 17 | _tokenizer.ReadToken().Expect("DROP"); 18 | 19 | var token = _tokenizer.ReadToken().Expect(TokenType.Word); 20 | 21 | if (token.Is("INDEX")) 22 | { 23 | var collection = _tokenizer.ReadToken().Expect(TokenType.Word).Value; 24 | _tokenizer.ReadToken().Expect(TokenType.Period); 25 | var name = _tokenizer.ReadToken().Expect(TokenType.Word).Value; 26 | 27 | _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); 28 | 29 | var result = _engine.DropIndex(collection, name); 30 | 31 | return new BsonDataReader(result); 32 | } 33 | else if(token.Is("COLLECTION")) 34 | { 35 | var collection = _tokenizer.ReadToken().Expect(TokenType.Word).Value; 36 | 37 | _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); 38 | 39 | var result = _engine.DropCollection(collection); 40 | 41 | return new BsonDataReader(result); 42 | } 43 | else 44 | { 45 | throw LiteException.UnexpectedToken(token, "INDEX|COLLECTION"); 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /LiteDB/Client/SqlParser/Commands/Insert.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LiteDB.Engine; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB 8 | { 9 | internal partial class SqlParser 10 | { 11 | /// 12 | /// INSERT INTO {collection} VALUES {doc0} [, {docN}] [ WITH ID={type} ] ] 13 | /// 14 | private BsonDataReader ParseInsert() 15 | { 16 | _tokenizer.ReadToken().Expect("INSERT"); 17 | _tokenizer.ReadToken().Expect("INTO"); 18 | 19 | var collection = _tokenizer.ReadToken().Expect(TokenType.Word).Value; 20 | 21 | var autoId = this.ParseWithAutoId(); 22 | 23 | _tokenizer.ReadToken().Expect("VALUES"); 24 | 25 | // get list of documents (return an IEnumerable) 26 | // will validate EOF or ; 27 | var docs = this.ParseListOfDocuments(); 28 | 29 | var result = _engine.Insert(collection, docs, autoId); 30 | 31 | return new BsonDataReader(result); 32 | } 33 | 34 | /// 35 | /// Parse :[type] for AutoId (just after collection name) 36 | /// 37 | private BsonAutoId ParseWithAutoId() 38 | { 39 | var with = _tokenizer.LookAhead(); 40 | 41 | if (with.Type == TokenType.Colon) 42 | { 43 | _tokenizer.ReadToken(); 44 | 45 | var type = _tokenizer.ReadToken().Expect(TokenType.Word); 46 | 47 | switch(type.Value.ToUpper()) 48 | { 49 | case "GUID": return BsonAutoId.Guid; 50 | case "INT": return BsonAutoId.Int32; 51 | case "LONG": return BsonAutoId.Int64; 52 | case "OBJECTID": return BsonAutoId.ObjectId; 53 | default: throw LiteException.UnexpectedToken(type, "DATE, GUID, INT, LONG, OBJECTID"); 54 | } 55 | } 56 | 57 | return BsonAutoId.ObjectId; 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /LiteDB/Client/SqlParser/Commands/Pragma.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using LiteDB.Engine; 6 | using static LiteDB.Constants; 7 | 8 | namespace LiteDB 9 | { 10 | internal partial class SqlParser 11 | { 12 | /// 13 | /// PRAGMA [DB_PARAM] = VALUE 14 | /// PRAGMA [DB_PARAM] 15 | /// 16 | private IBsonDataReader ParsePragma() 17 | { 18 | _tokenizer.ReadToken().Expect("PRAGMA"); 19 | 20 | var name = _tokenizer.ReadToken().Expect(TokenType.Word).Value; 21 | 22 | var eof = _tokenizer.LookAhead(); 23 | 24 | if (eof.Type == TokenType.EOF || eof.Type == TokenType.SemiColon) 25 | { 26 | _tokenizer.ReadToken(); 27 | 28 | var result = _engine.Pragma(name); 29 | 30 | return new BsonDataReader(result); 31 | } 32 | else if (eof.Type == TokenType.Equals) 33 | { 34 | // read = 35 | _tokenizer.ReadToken().Expect(TokenType.Equals); 36 | 37 | // read 38 | var reader = new JsonReader(_tokenizer); 39 | var value = reader.Deserialize(); 40 | 41 | // read last ; \ 42 | _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); 43 | 44 | var result = _engine.Pragma(name, value); 45 | 46 | return new BsonDataReader(result); 47 | } 48 | 49 | throw LiteException.UnexpectedToken(eof); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /LiteDB/Client/SqlParser/Commands/Rebuild.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LiteDB.Engine; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB 8 | { 9 | internal partial class SqlParser 10 | { 11 | /// 12 | /// SHRINK 13 | /// 14 | private BsonDataReader ParseRebuild() 15 | { 16 | _tokenizer.ReadToken().Expect("REBUILD"); 17 | 18 | var options = new RebuildOptions(); 19 | 20 | // read or ; 21 | var next = _tokenizer.LookAhead(); 22 | 23 | if (next.Type == TokenType.EOF || next.Type == TokenType.SemiColon) 24 | { 25 | options = null; 26 | 27 | _tokenizer.ReadToken(); 28 | } 29 | else 30 | { 31 | var reader = new JsonReader(_tokenizer); 32 | var json = reader.Deserialize(); 33 | 34 | if (json.IsDocument == false) throw LiteException.UnexpectedToken(next); 35 | 36 | if (json["password"].IsString) 37 | { 38 | options.Password = json["password"]; 39 | } 40 | 41 | if (json["collation"].IsString) 42 | { 43 | options.Collation = new Collation(json["collation"].AsString); 44 | } 45 | } 46 | 47 | var diff = _engine.Rebuild(options); 48 | 49 | return new BsonDataReader((int)diff); 50 | } 51 | } 52 | } -------------------------------------------------------------------------------- /LiteDB/Client/SqlParser/Commands/Rename.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LiteDB.Engine; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB 8 | { 9 | internal partial class SqlParser 10 | { 11 | /// 12 | /// RENAME COLLECTION {collection} TO {newName} 13 | /// 14 | private BsonDataReader ParseRename() 15 | { 16 | _tokenizer.ReadToken().Expect("RENAME"); 17 | _tokenizer.ReadToken().Expect("COLLECTION"); 18 | 19 | var collection = _tokenizer.ReadToken().Expect(TokenType.Word).Value; 20 | 21 | _tokenizer.ReadToken().Expect("TO"); 22 | 23 | var newName = _tokenizer.ReadToken().Expect(TokenType.Word).Value; 24 | 25 | _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); 26 | 27 | var result = _engine.RenameCollection(collection, newName); 28 | 29 | return new BsonDataReader(result); 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /LiteDB/Client/SqlParser/Commands/Rollback.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LiteDB.Engine; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB 8 | { 9 | internal partial class SqlParser 10 | { 11 | /// 12 | /// ROLLBACK [ TRANS | TRANSACTION ] 13 | /// 14 | private BsonDataReader ParseRollback() 15 | { 16 | _tokenizer.ReadToken().Expect("ROLLBACK"); 17 | 18 | var token = _tokenizer.ReadToken().Expect(TokenType.Word, TokenType.EOF, TokenType.SemiColon); 19 | 20 | if (token.Is("TRANS") || token.Is("TRANSACTION")) 21 | { 22 | _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); 23 | } 24 | 25 | var result = _engine.Rollback(); 26 | 27 | return new BsonDataReader(result); 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /LiteDB/Client/SqlParser/Commands/Update.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using LiteDB.Engine; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB 8 | { 9 | internal partial class SqlParser 10 | { 11 | /// 12 | /// UPDATE - update documents - if used with {key} = {exprValue} will merge current document with this fields 13 | /// if used with { key: value } will replace current document with new document 14 | /// UPDATE {collection} 15 | /// SET [{key} = {exprValue}, {key} = {exprValue} | { newDoc }] 16 | /// [ WHERE {whereExpr} ] 17 | /// 18 | private BsonDataReader ParseUpdate() 19 | { 20 | _tokenizer.ReadToken().Expect("UPDATE"); 21 | 22 | var collection = _tokenizer.ReadToken().Expect(TokenType.Word).Value; 23 | _tokenizer.ReadToken().Expect("SET"); 24 | 25 | var transform = BsonExpression.Create(_tokenizer, BsonExpressionParserMode.UpdateDocument, _parameters); 26 | 27 | // optional where 28 | BsonExpression where = null; 29 | var token = _tokenizer.LookAhead(); 30 | 31 | if (token.Is("WHERE")) 32 | { 33 | // read WHERE 34 | _tokenizer.ReadToken(); 35 | 36 | where = BsonExpression.Create(_tokenizer, BsonExpressionParserMode.Full, _parameters); 37 | } 38 | 39 | // read eof 40 | _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon); 41 | 42 | var result = _engine.UpdateMany(collection, transform, where); 43 | 44 | return new BsonDataReader(result); 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /LiteDB/Client/Structures/ConnectionType.cs: -------------------------------------------------------------------------------- 1 | using LiteDB.Engine; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB 8 | { 9 | public enum ConnectionType 10 | { 11 | Direct, 12 | Shared 13 | // MimePipes 14 | // Tcp 15 | // Rest 16 | } 17 | } -------------------------------------------------------------------------------- /LiteDB/Document/Bson/BsonSerializer.cs: -------------------------------------------------------------------------------- 1 | using LiteDB.Engine; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB 8 | { 9 | /// 10 | /// Class to call method for convert BsonDocument to/from byte[] - based on http://bsonspec.org/spec.html 11 | /// In v5 this class use new BufferRead/Writer to work with byte[] segments. This class are just a shortchut 12 | /// 13 | public class BsonSerializer 14 | { 15 | /// 16 | /// Serialize BsonDocument into a binary array 17 | /// 18 | public static byte[] Serialize(BsonDocument doc) 19 | { 20 | if (doc == null) throw new ArgumentNullException(nameof(doc)); 21 | 22 | var buffer = new byte[doc.GetBytesCount(true)]; 23 | 24 | using (var writer = new BufferWriter(buffer)) 25 | { 26 | writer.WriteDocument(doc, false); 27 | } 28 | 29 | return buffer; 30 | } 31 | 32 | /// 33 | /// Deserialize binary data into BsonDocument 34 | /// 35 | public static BsonDocument Deserialize(byte[] buffer, bool utcDate = false, HashSet fields = null) 36 | { 37 | if (buffer == null || buffer.Length == 0) throw new ArgumentNullException(nameof(buffer)); 38 | 39 | using (var reader = new BufferReader(buffer, utcDate)) 40 | { 41 | return reader.ReadDocument(fields).GetValue(); 42 | } 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /LiteDB/Document/BsonAutoId.cs: -------------------------------------------------------------------------------- 1 | namespace LiteDB 2 | { 3 | /// 4 | /// All supported BsonTypes supported in AutoId insert operation 5 | /// 6 | public enum BsonAutoId 7 | { 8 | Int32 = 2, 9 | Int64 = 3, 10 | ObjectId = 10, 11 | Guid = 11 12 | } 13 | } -------------------------------------------------------------------------------- /LiteDB/Document/BsonType.cs: -------------------------------------------------------------------------------- 1 | namespace LiteDB 2 | { 3 | /// 4 | /// All supported BsonTypes in sort order 5 | /// 6 | public enum BsonType : byte 7 | { 8 | MinValue = 0, 9 | 10 | Null = 1, 11 | 12 | Int32 = 2, 13 | Int64 = 3, 14 | Double = 4, 15 | Decimal = 5, 16 | 17 | String = 6, 18 | 19 | Document = 7, 20 | Array = 8, 21 | 22 | Binary = 9, 23 | ObjectId = 10, 24 | Guid = 11, 25 | 26 | Boolean = 12, 27 | DateTime = 13, 28 | 29 | MaxValue = 14 30 | } 31 | } -------------------------------------------------------------------------------- /LiteDB/Document/DataReader/BsonDataReaderExtensions.cs: -------------------------------------------------------------------------------- 1 | using LiteDB.Engine; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using static LiteDB.Constants; 7 | 8 | namespace LiteDB 9 | { 10 | /// 11 | /// Implement some Enumerable methods to IBsonDataReader 12 | /// 13 | public static class BsonDataReaderExtensions 14 | { 15 | public static IEnumerable ToEnumerable(this IBsonDataReader reader) 16 | { 17 | try 18 | { 19 | while (reader.Read()) 20 | { 21 | yield return reader.Current; 22 | } 23 | } 24 | finally 25 | { 26 | reader.Dispose(); 27 | } 28 | } 29 | 30 | public static BsonValue[] ToArray(this IBsonDataReader reader) => ToEnumerable(reader).ToArray(); 31 | 32 | public static IList ToList(this IBsonDataReader reader) => ToEnumerable(reader).ToList(); 33 | 34 | public static BsonValue First(this IBsonDataReader reader) => ToEnumerable(reader).First(); 35 | 36 | public static BsonValue FirstOrDefault(this IBsonDataReader reader) => ToEnumerable(reader).FirstOrDefault(); 37 | 38 | public static BsonValue Single(this IBsonDataReader reader) => ToEnumerable(reader).Single(); 39 | 40 | public static BsonValue SingleOrDefault(this IBsonDataReader reader) => ToEnumerable(reader).SingleOrDefault(); 41 | } 42 | } -------------------------------------------------------------------------------- /LiteDB/Document/DataReader/IBsonDataReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LiteDB 4 | { 5 | public interface IBsonDataReader : IDisposable 6 | { 7 | BsonValue this[string field] { get; } 8 | 9 | string Collection { get; } 10 | BsonValue Current { get; } 11 | bool HasValues { get; } 12 | 13 | bool Read(); 14 | } 15 | } -------------------------------------------------------------------------------- /LiteDB/Document/Expression/Parser/BsonExpressionAttributes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Text; 7 | using static LiteDB.Constants; 8 | 9 | namespace LiteDB 10 | { 11 | /// 12 | /// When a method are decorated with this attribute means that this method are not immutable 13 | /// 14 | internal class VolatileAttribute: Attribute 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LiteDB/Document/Expression/Parser/BsonExpressionType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Text; 7 | using static LiteDB.Constants; 8 | 9 | namespace LiteDB 10 | { 11 | public enum BsonExpressionType : byte 12 | { 13 | Double = 1, 14 | Int = 2, 15 | String = 3, 16 | Boolean = 4, 17 | Null = 5, 18 | Array = 6, 19 | Document = 7, 20 | 21 | Parameter = 8, 22 | Call = 9, 23 | Path = 10, 24 | 25 | Modulo = 11, 26 | Add = 12, 27 | Subtract = 13, 28 | Multiply = 14, 29 | Divide = 15, 30 | 31 | Equal = 16, 32 | Like = 17, 33 | Between = 18, 34 | GreaterThan = 19, 35 | GreaterThanOrEqual = 20, 36 | LessThan = 21, 37 | LessThanOrEqual = 22, 38 | NotEqual = 23, 39 | In = 24, 40 | 41 | Or = 25, 42 | And = 26, 43 | 44 | Map = 27, 45 | Filter = 28, 46 | Sort = 29, 47 | Source = 30 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /LiteDB/Document/Expression/Parser/DocumentScope.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Linq.Expressions; 6 | using System.Text; 7 | using static LiteDB.Constants; 8 | 9 | namespace LiteDB 10 | { 11 | internal enum DocumentScope 12 | { 13 | Source, 14 | Root, 15 | Current 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LiteDB/Document/Expression/Parser/ExpressionContext.cs: -------------------------------------------------------------------------------- 1 | using LiteDB.Engine; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Linq.Expressions; 7 | using System.Text; 8 | using static LiteDB.Constants; 9 | 10 | namespace LiteDB 11 | { 12 | internal class ExpressionContext 13 | { 14 | public ExpressionContext() 15 | { 16 | this.Source = Expression.Parameter(typeof(IEnumerable), "source"); 17 | this.Root = Expression.Parameter(typeof(BsonDocument), "root"); 18 | this.Current = Expression.Parameter(typeof(BsonValue), "current"); 19 | this.Collation = Expression.Parameter(typeof(Collation), "collation"); 20 | this.Parameters = Expression.Parameter(typeof(BsonDocument), "parameters"); 21 | } 22 | 23 | public ParameterExpression Source { get; } 24 | public ParameterExpression Root { get; } 25 | public ParameterExpression Current { get; } 26 | public ParameterExpression Collation { get; } 27 | public ParameterExpression Parameters { get; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LiteDB/Engine/Disk/StreamFactory/IStreamFactory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Concurrent; 4 | using System.Collections.Generic; 5 | using System.IO; 6 | using static LiteDB.Constants; 7 | 8 | namespace LiteDB.Engine 9 | { 10 | /// 11 | /// Interface factory to provider new Stream instances for datafile/walfile resources. It's useful to multiple threads can read same datafile 12 | /// 13 | internal interface IStreamFactory 14 | { 15 | /// 16 | /// Get Stream name (filename) 17 | /// 18 | string Name { get; } 19 | 20 | /// 21 | /// Get new Stream instance 22 | /// 23 | Stream GetStream(bool canWrite, bool sequencial); 24 | 25 | /// 26 | /// Get file length 27 | /// 28 | /// 29 | long GetLength(); 30 | 31 | /// 32 | /// Checks if file exists 33 | /// 34 | bool Exists(); 35 | 36 | /// 37 | /// Delete physical file on disk 38 | /// 39 | void Delete(); 40 | 41 | /// 42 | /// Test if this file are used by another process 43 | /// 44 | bool IsLocked(); 45 | 46 | /// 47 | /// Indicate that factory must be dispose on finish 48 | /// 49 | bool CloseOnDispose { get; } 50 | } 51 | } -------------------------------------------------------------------------------- /LiteDB/Engine/Engine/Pragma.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB.Engine 8 | { 9 | public partial class LiteEngine 10 | { 11 | /// 12 | /// Get engine internal pragma value 13 | /// 14 | public BsonValue Pragma(string name) 15 | { 16 | return _header.Pragmas.Get(name); 17 | } 18 | 19 | /// 20 | /// Set engine pragma new value (some pragmas will be affected only after realod) 21 | /// 22 | public bool Pragma(string name, BsonValue value) 23 | { 24 | if (this.Pragma(name) == value) return false; 25 | 26 | if (_locker.IsInTransaction) throw LiteException.AlreadyExistsTransaction(); 27 | 28 | // do a inside transaction to edit pragma on commit event 29 | return this.AutoTransaction(transaction => 30 | { 31 | transaction.Pages.Commit += (h) => 32 | { 33 | h.Pragmas.Set(name, value, true); 34 | }; 35 | 36 | return true; 37 | }); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /LiteDB/Engine/Engine/Query.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB.Engine 8 | { 9 | public partial class LiteEngine 10 | { 11 | /// 12 | /// Run query over collection using a query definition. 13 | /// Returns a new IBsonDataReader that run and return first document result (open transaction) 14 | /// 15 | public IBsonDataReader Query(string collection, Query query) 16 | { 17 | if (string.IsNullOrWhiteSpace(collection)) throw new ArgumentNullException(nameof(collection)); 18 | if (query == null) throw new ArgumentNullException(nameof(query)); 19 | 20 | IEnumerable source = null; 21 | 22 | // test if is an system collection 23 | if (collection.StartsWith("$")) 24 | { 25 | SqlParser.ParseCollection(new Tokenizer(collection), out var name, out var options); 26 | 27 | // get registered system collection to get data source 28 | var sys = this.GetSystemCollection(name); 29 | 30 | source = sys.Input(options); 31 | collection = sys.Name; 32 | } 33 | 34 | var exec = new QueryExecutor( 35 | this, 36 | _state, 37 | _monitor, 38 | _sortDisk, 39 | _disk, 40 | _header.Pragmas, 41 | collection, 42 | query, 43 | source); 44 | 45 | return exec.ExecuteQuery(); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /LiteDB/Engine/Engine/Recovery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | 7 | using static LiteDB.Constants; 8 | 9 | namespace LiteDB.Engine 10 | { 11 | public partial class LiteEngine 12 | { 13 | /// 14 | /// Recovery datafile using a rebuild process. Run only on "Open" database 15 | /// 16 | private void Recovery(Collation collation) 17 | { 18 | // run build service 19 | var rebuilder = new RebuildService(_settings); 20 | var options = new RebuildOptions 21 | { 22 | Collation = collation, 23 | Password = _settings.Password, 24 | IncludeErrorReport = true 25 | }; 26 | 27 | // run rebuild process 28 | rebuilder.Rebuild(options); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /LiteDB/Engine/Engine/SystemCollections.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using static LiteDB.Constants; 4 | 5 | namespace LiteDB.Engine 6 | { 7 | public partial class LiteEngine 8 | { 9 | /// 10 | /// Get registered system collection 11 | /// 12 | internal SystemCollection GetSystemCollection(string name) 13 | { 14 | if (_systemCollections.TryGetValue(name, out var sys)) 15 | { 16 | return sys; 17 | } 18 | 19 | throw new LiteException(0, $"System collection '{name}' are not registered as system collection"); 20 | } 21 | 22 | /// 23 | /// Register a new system collection that can be used in query for input/output data 24 | /// Collection name must starts with $ 25 | /// 26 | internal void RegisterSystemCollection(SystemCollection systemCollection) 27 | { 28 | if (systemCollection == null) throw new ArgumentNullException(nameof(systemCollection)); 29 | 30 | _systemCollections[systemCollection.Name] = systemCollection; 31 | } 32 | 33 | /// 34 | /// Register a new system collection that can be used in query for input data 35 | /// Collection name must starts with $ 36 | /// 37 | internal void RegisterSystemCollection(string collectionName, Func> factory) 38 | { 39 | if (collectionName.IsNullOrWhiteSpace()) throw new ArgumentNullException(nameof(collectionName)); 40 | if (factory == null) throw new ArgumentNullException(nameof(factory)); 41 | 42 | _systemCollections[collectionName] = new SystemCollection(collectionName, factory); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /LiteDB/Engine/EngineState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Runtime; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | using static LiteDB.Constants; 11 | 12 | namespace LiteDB.Engine 13 | { 14 | internal class EngineState 15 | { 16 | public bool Disposed = false; 17 | private Exception _exception; 18 | private readonly LiteEngine _engine; // can be null for unit tests 19 | private readonly EngineSettings _settings; 20 | 21 | #if DEBUG 22 | public Action SimulateDiskReadFail = null; 23 | public Action SimulateDiskWriteFail = null; 24 | #endif 25 | 26 | public EngineState(LiteEngine engine, EngineSettings settings) 27 | { 28 | _engine = engine; 29 | _settings = settings; 30 | } 31 | 32 | public void Validate() 33 | { 34 | if (this.Disposed) throw _exception ?? LiteException.EngineDisposed(); 35 | } 36 | 37 | public bool Handle(Exception ex) 38 | { 39 | LOG(ex.Message, "ERROR"); 40 | 41 | if (ex is IOException || 42 | (ex is LiteException lex && lex.ErrorCode == LiteException.INVALID_DATAFILE_STATE)) 43 | { 44 | _exception = ex; 45 | 46 | _engine?.Close(ex); 47 | 48 | return false; 49 | } 50 | 51 | return true; 52 | } 53 | 54 | public BsonValue ReadTransform(string collection, BsonValue value) 55 | { 56 | if (_settings?.ReadTransform is null) return value; 57 | 58 | return _settings.ReadTransform(collection, value); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /LiteDB/Engine/FileReader/FileReaderError.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB.Engine 8 | { 9 | /// 10 | /// 11 | internal class FileReaderError 12 | { 13 | public DateTime Created { get; } = DateTime.Now; 14 | public FileOrigin Origin { get; set; } 15 | public long Position { get; set; } 16 | public uint? PageID { get; set; } 17 | public PageType PageType { get; set; } 18 | public string Collection { get; set; } 19 | public string Message { get; set; } 20 | public Exception Exception { get; set; } 21 | } 22 | } -------------------------------------------------------------------------------- /LiteDB/Engine/FileReader/IFileReader.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace LiteDB.Engine 5 | { 6 | /// 7 | /// Interface to read current or old datafile structure - Used to shirnk/upgrade datafile from old LiteDB versions 8 | /// 9 | interface IFileReader : IDisposable 10 | { 11 | /// 12 | /// Open and initialize file reader (run before any other command) 13 | /// 14 | void Open(); 15 | 16 | /// 17 | /// Get all database pragma variables 18 | /// 19 | IDictionary GetPragmas(); 20 | 21 | /// 22 | /// Get all collections name from database 23 | /// 24 | IEnumerable GetCollections(); 25 | 26 | /// 27 | /// Get all indexes from collection (except _id index) 28 | /// 29 | IEnumerable GetIndexes(string name); 30 | 31 | /// 32 | /// Get all documents from a collection 33 | /// 34 | IEnumerable GetDocuments(string collection); 35 | } 36 | } -------------------------------------------------------------------------------- /LiteDB/Engine/FileReader/IndexInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using static LiteDB.Constants; 7 | 8 | namespace LiteDB.Engine 9 | { 10 | internal class IndexInfo 11 | { 12 | public string Collection { get; set; } 13 | public string Name { get; set; } 14 | public string Expression { get; set; } 15 | public bool Unique { get; set; } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /LiteDB/Engine/ILiteEngine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace LiteDB.Engine 5 | { 6 | public interface ILiteEngine : IDisposable 7 | { 8 | int Checkpoint(); 9 | long Rebuild(RebuildOptions options); 10 | 11 | bool BeginTrans(); 12 | bool Commit(); 13 | bool Rollback(); 14 | 15 | IBsonDataReader Query(string collection, Query query); 16 | 17 | int Insert(string collection, IEnumerable docs, BsonAutoId autoId); 18 | int Update(string collection, IEnumerable docs); 19 | int UpdateMany(string collection, BsonExpression transform, BsonExpression predicate); 20 | int Upsert(string collection, IEnumerable docs, BsonAutoId autoId); 21 | int Delete(string collection, IEnumerable ids); 22 | int DeleteMany(string collection, BsonExpression predicate); 23 | 24 | bool DropCollection(string name); 25 | bool RenameCollection(string name, string newName); 26 | 27 | bool EnsureIndex(string collection, string name, BsonExpression expression, bool unique); 28 | bool DropIndex(string collection, string name); 29 | 30 | BsonValue Pragma(string name); 31 | bool Pragma(string name, BsonValue value); 32 | } 33 | } -------------------------------------------------------------------------------- /LiteDB/Engine/Pragmas.cs: -------------------------------------------------------------------------------- 1 | namespace LiteDB.Engine 2 | { 3 | public static class Pragmas 4 | { 5 | public const string USER_VERSION = nameof(USER_VERSION); 6 | public const string COLLATION = nameof(COLLATION); 7 | public const string TIMEOUT = nameof(TIMEOUT); 8 | public const string LIMIT_SIZE = nameof(LIMIT_SIZE); 9 | public const string UTC_DATE = nameof(UTC_DATE); 10 | public const string CHECKPOINT = nameof(CHECKPOINT); 11 | } 12 | } -------------------------------------------------------------------------------- /LiteDB/Engine/Query/IndexQuery/Index.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using static LiteDB.Constants; 5 | 6 | namespace LiteDB.Engine 7 | { 8 | /// 9 | /// Class that implement higher level of index search operations (equals, greater, less, ...) 10 | /// 11 | internal abstract class Index 12 | { 13 | /// 14 | /// Index name 15 | /// 16 | public string Name { get; private set; } 17 | 18 | /// 19 | /// Get/Set index order 20 | /// 21 | public int Order { get; set; } 22 | 23 | internal Index(string name, int order) 24 | { 25 | this.Name = name; 26 | this.Order = order; 27 | } 28 | 29 | #region Executing Index Search 30 | 31 | /// 32 | /// Calculate cost based on type/value/collection - Lower is best (1) 33 | /// 34 | public abstract uint GetCost(CollectionIndex index); 35 | 36 | /// 37 | /// Abstract method that must be implement for index seek/scan - Returns IndexNodes that match with index 38 | /// 39 | public abstract IEnumerable Execute(IndexService indexer, CollectionIndex index); 40 | 41 | /// 42 | /// Find witch index will be used and run Execute method 43 | /// 44 | public virtual IEnumerable Run(CollectionPage col, IndexService indexer) 45 | { 46 | // get index for this query 47 | var index = col.GetCollectionIndex(this.Name); 48 | 49 | if (index == null) throw LiteException.IndexNotFound(this.Name); 50 | 51 | // execute query to get all IndexNodes 52 | return this.Execute(indexer, index) 53 | .DistinctBy(x => x.DataBlock, null); 54 | } 55 | 56 | #endregion 57 | } 58 | } -------------------------------------------------------------------------------- /LiteDB/Engine/Query/IndexQuery/IndexAll.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using static LiteDB.Constants; 5 | 6 | namespace LiteDB.Engine 7 | { 8 | /// 9 | /// Return all index nodes 10 | /// 11 | internal class IndexAll : Index 12 | { 13 | public IndexAll(string name, int order) 14 | : base(name, order) 15 | { 16 | } 17 | 18 | public override uint GetCost(CollectionIndex index) 19 | { 20 | return 100; // worst index cost 21 | } 22 | 23 | public override IEnumerable Execute(IndexService indexer, CollectionIndex index) 24 | { 25 | return indexer.FindAll(index, this.Order); 26 | } 27 | 28 | public override string ToString() 29 | { 30 | return string.Format("FULL INDEX SCAN({0})", this.Name); 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /LiteDB/Engine/Query/IndexQuery/IndexIn.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using static LiteDB.Constants; 5 | 6 | namespace LiteDB.Engine 7 | { 8 | /// 9 | /// Implement IN index operation. Value must be an array 10 | /// 11 | internal class IndexIn : Index 12 | { 13 | private readonly BsonArray _values; 14 | 15 | public IndexIn(string name, BsonArray values, int order) 16 | : base(name, order) 17 | { 18 | _values = values; 19 | } 20 | 21 | public override uint GetCost(CollectionIndex index) 22 | { 23 | return index.Unique ? 24 | (uint)_values.Count * 1 : 25 | (uint)_values.Count * 10; 26 | } 27 | 28 | public override IEnumerable Execute(IndexService indexer, CollectionIndex index) 29 | { 30 | foreach (var value in _values.Distinct()) 31 | { 32 | var idx = new IndexEquals(this.Name, value); 33 | 34 | foreach (var node in idx.Execute(indexer, index)) 35 | { 36 | yield return node; 37 | } 38 | } 39 | } 40 | 41 | public override string ToString() 42 | { 43 | return string.Format("INDEX SEEK({0} IN {1})", this.Name, JsonSerializer.Serialize(_values)); 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /LiteDB/Engine/Query/IndexQuery/IndexScan.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using static LiteDB.Constants; 5 | 6 | namespace LiteDB.Engine 7 | { 8 | /// 9 | /// Execute an "index scan" passing a Func as where 10 | /// 11 | internal class IndexScan : Index 12 | { 13 | private readonly Func _func; 14 | 15 | public IndexScan(string name, Func func, int order) 16 | : base(name, order) 17 | { 18 | _func = func; 19 | } 20 | 21 | public override uint GetCost(CollectionIndex index) 22 | { 23 | return 80; 24 | } 25 | 26 | public override IEnumerable Execute(IndexService indexer, CollectionIndex index) 27 | { 28 | return indexer 29 | .FindAll(index, this.Order) 30 | .Where(i => _func(i.Key)); 31 | } 32 | 33 | public override string ToString() 34 | { 35 | return string.Format("FULL INDEX SCAN({0})", this.Name); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /LiteDB/Engine/Query/Lookup/DatafileLookup.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using static LiteDB.Constants; 3 | 4 | namespace LiteDB.Engine 5 | { 6 | /// 7 | /// Implement basic document loader based on data service/bson reader 8 | /// 9 | internal class DatafileLookup : IDocumentLookup 10 | { 11 | protected readonly DataService _data; 12 | protected readonly bool _utcDate; 13 | protected readonly HashSet _fields; 14 | 15 | public DatafileLookup(DataService data, bool utcDate, HashSet fields) 16 | { 17 | _data = data; 18 | _utcDate = utcDate; 19 | _fields = fields; 20 | } 21 | 22 | public virtual BsonDocument Load(IndexNode node) 23 | { 24 | ENSURE(node.DataBlock != PageAddress.Empty, "data block must be a valid block address"); 25 | 26 | return this.Load(node.DataBlock); 27 | } 28 | 29 | public virtual BsonDocument Load(PageAddress rawId) 30 | { 31 | using (var reader = new BufferReader(_data.Read(rawId), _utcDate)) 32 | { 33 | var doc = reader.ReadDocument(_fields).GetValue(); 34 | 35 | doc.RawId = rawId; 36 | 37 | return doc; 38 | } 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /LiteDB/Engine/Query/Lookup/IDocumentLookup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace LiteDB.Engine 4 | { 5 | /// 6 | /// Interface for abstract document lookup that can be direct from datafile or by virtual collections 7 | /// 8 | internal interface IDocumentLookup 9 | { 10 | BsonDocument Load(IndexNode node); 11 | BsonDocument Load(PageAddress rawId); 12 | } 13 | } -------------------------------------------------------------------------------- /LiteDB/Engine/Query/Lookup/IndexKeyLoader.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using static LiteDB.Constants; 3 | 4 | namespace LiteDB.Engine 5 | { 6 | /// 7 | /// Implement lookup based only in index Key 8 | /// 9 | internal class IndexLookup : IDocumentLookup 10 | { 11 | private readonly IndexService _indexer; 12 | private readonly string _name; 13 | 14 | public IndexLookup(IndexService indexer, string name) 15 | { 16 | _indexer = indexer; 17 | _name = name; 18 | } 19 | 20 | public BsonDocument Load(IndexNode node) 21 | { 22 | ENSURE(node.DataBlock.IsEmpty == false, "Never should be empty rawid"); 23 | 24 | var doc = new BsonDocument 25 | { 26 | [_name] = node.Key, 27 | }; 28 | 29 | doc.RawId = node.DataBlock; 30 | 31 | return doc; 32 | } 33 | 34 | public BsonDocument Load(PageAddress rawId) 35 | { 36 | var node = _indexer.GetNode(rawId); 37 | 38 | return this.Load(node); 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /LiteDB/Engine/Query/Structures/GroupBy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using static LiteDB.Constants; 7 | 8 | namespace LiteDB.Engine 9 | { 10 | /// 11 | /// Represent an GroupBy definition (is based on OrderByDefinition) 12 | /// 13 | internal class GroupBy 14 | { 15 | public BsonExpression Expression { get; } 16 | 17 | public BsonExpression Select { get; } 18 | 19 | public BsonExpression Having { get; } 20 | 21 | public GroupBy(BsonExpression expression, BsonExpression select, BsonExpression having) 22 | { 23 | this.Expression = expression; 24 | this.Select = select; 25 | this.Having = having; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /LiteDB/Engine/Query/Structures/OrderBy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using static LiteDB.Constants; 7 | 8 | namespace LiteDB.Engine 9 | { 10 | /// 11 | /// Represent an OrderBy definition 12 | /// 13 | internal class OrderBy 14 | { 15 | public BsonExpression Expression { get; } 16 | 17 | public int Order { get; set; } 18 | 19 | public OrderBy(BsonExpression expression, int order) 20 | { 21 | this.Expression = expression; 22 | this.Order = order; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /LiteDB/Engine/Query/Structures/Select.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using static LiteDB.Constants; 7 | 8 | namespace LiteDB.Engine 9 | { 10 | /// 11 | /// Represent a Select expression 12 | /// 13 | internal class Select 14 | { 15 | public BsonExpression Expression { get; } 16 | 17 | public bool All { get; } 18 | 19 | public Select(BsonExpression expression, bool all) 20 | { 21 | this.Expression = expression; 22 | this.All = all; 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /LiteDB/Engine/Structures/CursorInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Diagnostics; 5 | using System.IO; 6 | using static LiteDB.Constants; 7 | 8 | namespace LiteDB.Engine 9 | { 10 | /// 11 | /// Represent a single query featching data from engine 12 | /// 13 | internal class CursorInfo 14 | { 15 | public CursorInfo(string collection, Query query) 16 | { 17 | this.Collection = collection; 18 | this.Query = query; 19 | } 20 | 21 | public string Collection { get; } 22 | 23 | public Query Query { get; set; } 24 | 25 | public int Fetched { get; set; } 26 | 27 | public Stopwatch Elapsed { get; } = new Stopwatch(); 28 | } 29 | } -------------------------------------------------------------------------------- /LiteDB/Engine/Structures/Done.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using static LiteDB.Constants; 4 | 5 | namespace LiteDB.Engine 6 | { 7 | /// 8 | /// Simple parameter class to be passed into IEnumerable classes loop ("ref" do not works) 9 | /// 10 | internal class Done 11 | { 12 | public bool Running = false; 13 | public int Count = 0; 14 | } 15 | } -------------------------------------------------------------------------------- /LiteDB/Engine/Structures/FileOrigin.cs: -------------------------------------------------------------------------------- 1 | namespace LiteDB.Engine 2 | { 3 | public enum FileOrigin : byte 4 | { 5 | /// 6 | /// There is no origin (new page) 7 | /// 8 | None = 0, 9 | 10 | /// 11 | /// Data file 12 | /// 13 | Data = 1, 14 | 15 | /// 16 | /// Log file (-log) 17 | /// 18 | Log = 2 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /LiteDB/Engine/Structures/LockMode.cs: -------------------------------------------------------------------------------- 1 | namespace LiteDB.Engine 2 | { 3 | /// 4 | /// Represents a snapshot lock mode 5 | /// 6 | internal enum LockMode 7 | { 8 | /// 9 | /// Read only snap with read lock 10 | /// 11 | Read, 12 | 13 | /// 14 | /// Read/Write snapshot with reserved lock 15 | /// 16 | Write 17 | } 18 | } -------------------------------------------------------------------------------- /LiteDB/Engine/Structures/PagePosition.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using static LiteDB.Constants; 4 | 5 | namespace LiteDB.Engine 6 | { 7 | /// 8 | /// Represents a page position after save in disk. Used in WAL files where PageID do not match with PagePosition 9 | /// 10 | internal struct PagePosition 11 | { 12 | public static PagePosition Empty => new PagePosition(uint.MaxValue, long.MaxValue); 13 | 14 | /// 15 | /// PageID (4 bytes) 16 | /// 17 | public uint PageID; 18 | 19 | /// 20 | /// Position in disk 21 | /// 22 | public long Position; 23 | 24 | /// 25 | /// Checks if current PagePosition is empty value 26 | /// 27 | public bool IsEmpty => this.PageID == uint.MaxValue && this.Position == long.MaxValue; 28 | 29 | public override bool Equals(object obj) 30 | { 31 | var other = (PagePosition)obj; 32 | 33 | return this.PageID == other.PageID && this.Position == other.PageID; 34 | } 35 | 36 | public override int GetHashCode() 37 | { 38 | unchecked 39 | { 40 | int hash = 17; 41 | hash = hash * 23 + (int)this.PageID; 42 | hash = hash * 23 + (int)this.Position; 43 | return hash; 44 | } 45 | } 46 | 47 | public PagePosition(uint pageID, long position) 48 | { 49 | this.PageID = pageID; 50 | this.Position = position; 51 | } 52 | 53 | public override string ToString() 54 | { 55 | return this.IsEmpty ? "----:----" : 56 | (this.PageID == uint.MaxValue ? "----" : this.PageID.ToString()) + ":" + this.Position.ToString(); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /LiteDB/Engine/Structures/Pragma.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using static LiteDB.Constants; 4 | 5 | namespace LiteDB.Engine 6 | { 7 | /// 8 | /// Represent a single internal engine variable that user can read/change 9 | /// 10 | internal class Pragma 11 | { 12 | public string Name { get; set; } 13 | public Func Get { get; set; } 14 | public Action Set { get; set; } 15 | public Action Read { get; set; } 16 | public Action Validate { get; set; } 17 | public Action Write { get; set; } 18 | } 19 | } -------------------------------------------------------------------------------- /LiteDB/Engine/Structures/TransactionState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB.Engine 8 | { 9 | internal enum TransactionState 10 | { 11 | Active, 12 | Committed, 13 | Aborted, 14 | Disposed 15 | } 16 | } -------------------------------------------------------------------------------- /LiteDB/Engine/SystemCollections/Register.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB.Engine 8 | { 9 | public partial class LiteEngine 10 | { 11 | /// 12 | /// Register all internal system collections avaiable by default 13 | /// 14 | private void InitializeSystemCollections() 15 | { 16 | this.RegisterSystemCollection("$database", () => this.SysDatabase()); 17 | 18 | this.RegisterSystemCollection("$cols", () => this.SysCols()); 19 | this.RegisterSystemCollection("$indexes", () => this.SysIndexes()); 20 | 21 | this.RegisterSystemCollection("$sequences", () => this.SysSequences()); 22 | 23 | this.RegisterSystemCollection("$transactions", () => this.SysTransactions()); 24 | this.RegisterSystemCollection("$snapshots", () => this.SysSnapshots()); 25 | this.RegisterSystemCollection("$open_cursors", () => this.SysOpenCursors()); 26 | 27 | this.RegisterSystemCollection(new SysFile()); // use single $file(?) for all file formats 28 | this.RegisterSystemCollection(new SysDump(_header, _monitor)); 29 | this.RegisterSystemCollection(new SysPageList(_header, _monitor)); 30 | 31 | this.RegisterSystemCollection(new SysQuery(this)); 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /LiteDB/Engine/SystemCollections/SysCols.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB.Engine 8 | { 9 | public partial class LiteEngine 10 | { 11 | private IEnumerable SysCols() 12 | { 13 | foreach (var col in _header.GetCollections()) 14 | { 15 | yield return new BsonDocument 16 | { 17 | ["name"] = col.Key, 18 | ["type"] = "user" 19 | }; 20 | } 21 | 22 | foreach (var item in _systemCollections) 23 | { 24 | yield return new BsonDocument 25 | { 26 | ["name"] = item.Key, 27 | ["type"] = "system" 28 | }; 29 | } 30 | 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /LiteDB/Engine/SystemCollections/SysFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using static LiteDB.Constants; 8 | 9 | namespace LiteDB.Engine 10 | { 11 | internal class SysFile : SystemCollection 12 | { 13 | private readonly Dictionary _formats = new Dictionary(StringComparer.OrdinalIgnoreCase) 14 | { 15 | ["json"] = new SysFileJson(), 16 | ["csv"] = new SysFileCsv() 17 | }; 18 | 19 | public SysFile() : base("$file") 20 | { 21 | } 22 | 23 | public override IEnumerable Input(BsonValue options) 24 | { 25 | var format = this.GetFormat(options); 26 | 27 | if (_formats.TryGetValue(format, out var factory)) 28 | { 29 | return factory.Input(options); 30 | } 31 | 32 | throw new LiteException(0, $"Unknow file format in $file: `{format}`"); 33 | } 34 | 35 | public override int Output(IEnumerable source, BsonValue options) 36 | { 37 | var format = this.GetFormat(options); 38 | 39 | if (_formats.TryGetValue(format, out var factory)) 40 | { 41 | return factory.Output(source, options); 42 | } 43 | 44 | throw new LiteException(0, $"Unknow file format in $file: `{format}`"); 45 | } 46 | 47 | private string GetFormat(BsonValue options) 48 | { 49 | var filename = GetOption(options, "filename")?.AsString ?? throw new LiteException(0, $"Collection $file requires string as 'filename' or a document field 'filename'"); 50 | var format = GetOption(options, "format", Path.GetExtension(filename)).AsString; 51 | 52 | return format.StartsWith(".") ? format.Substring(1) : format; 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /LiteDB/Engine/SystemCollections/SysIndexes.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB.Engine 8 | { 9 | public partial class LiteEngine 10 | { 11 | private IEnumerable SysIndexes() 12 | { 13 | // get any transaction from current thread ID 14 | var transaction = _monitor.GetThreadTransaction(); 15 | 16 | foreach (var collection in _header.GetCollections()) 17 | { 18 | var snapshot = transaction.CreateSnapshot(LockMode.Read, collection.Key, false); 19 | 20 | foreach(var index in snapshot.CollectionPage.GetCollectionIndexes()) 21 | { 22 | yield return new BsonDocument 23 | { 24 | ["collection"] = collection.Key, 25 | ["name"] = index.Name, 26 | ["expression"] = index.Expression, 27 | ["unique"] = index.Unique, 28 | }; 29 | } 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /LiteDB/Engine/SystemCollections/SysOpenCursors.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using static LiteDB.Constants; 7 | 8 | namespace LiteDB.Engine 9 | { 10 | public partial class LiteEngine 11 | { 12 | private IEnumerable SysOpenCursors() 13 | { 14 | foreach (var transaction in _monitor.Transactions) 15 | { 16 | foreach(var cursor in transaction.OpenCursors) 17 | { 18 | yield return new BsonDocument 19 | { 20 | ["threadID"] = transaction.ThreadID, 21 | ["transactionID"] = (int)transaction.TransactionID, 22 | ["elapsedMS"] = (int)cursor.Elapsed.ElapsedMilliseconds, 23 | ["collection"] = cursor.Collection, 24 | ["mode"] = cursor.Query.ForUpdate ? "write" : "read", 25 | ["sql"] = cursor.Query.ToSQL(cursor.Collection).Replace(Environment.NewLine, " "), 26 | ["running"] = cursor.Elapsed.IsRunning, 27 | ["fetched"] = cursor.Fetched 28 | }; 29 | } 30 | } 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /LiteDB/Engine/SystemCollections/SysQuery.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using static LiteDB.Constants; 7 | 8 | namespace LiteDB.Engine 9 | { 10 | /// 11 | /// This class implement $query experimental system function to run sub-queries. It's experimental only - possible not be present in final release 12 | /// 13 | internal class SysQuery : SystemCollection 14 | { 15 | private readonly ILiteEngine _engine; 16 | 17 | public SysQuery(ILiteEngine engine) : base("$query") 18 | { 19 | _engine = engine; 20 | } 21 | 22 | public override IEnumerable Input(BsonValue options) 23 | { 24 | var query = options?.AsString ?? throw new LiteException(0, $"Collection $query(sql) requires `sql` string parameter"); 25 | 26 | var sql = new SqlParser(_engine, new Tokenizer(query), null); 27 | 28 | using (var reader = sql.Execute()) 29 | { 30 | while(reader.Read()) 31 | { 32 | var value = reader.Current; 33 | 34 | yield return value.IsDocument ? value.AsDocument : new BsonDocument { ["expr"] = value }; 35 | } 36 | } 37 | } 38 | } 39 | } -------------------------------------------------------------------------------- /LiteDB/Engine/SystemCollections/SysSequences.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using static LiteDB.Constants; 7 | 8 | namespace LiteDB.Engine 9 | { 10 | public partial class LiteEngine 11 | { 12 | private IEnumerable SysSequences() 13 | { 14 | var values = _sequences.ToArray(); 15 | 16 | foreach(var value in values) 17 | { 18 | yield return new BsonDocument 19 | { 20 | ["collection"] = value.Key, 21 | ["value"] = value.Value 22 | }; 23 | } 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /LiteDB/Engine/SystemCollections/SysSnapshots.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB.Engine 8 | { 9 | public partial class LiteEngine 10 | { 11 | private IEnumerable SysSnapshots() 12 | { 13 | foreach (var transaction in _monitor.Transactions) 14 | { 15 | foreach (var snapshot in transaction.Snapshots) 16 | { 17 | yield return new BsonDocument 18 | { 19 | ["transactionID"] = (int)transaction.TransactionID, 20 | ["collection"] = snapshot.CollectionName, 21 | ["mode"] = snapshot.Mode.ToString(), 22 | ["readVersion"] = snapshot.ReadVersion, 23 | ["pagesInMemory"] = snapshot.LocalPages.Count, 24 | ["collectionDirty"] = snapshot.CollectionPage?.IsDirty ?? false 25 | }; 26 | } 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /LiteDB/Engine/SystemCollections/SysTransactions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading; 6 | using static LiteDB.Constants; 7 | 8 | namespace LiteDB.Engine 9 | { 10 | public partial class LiteEngine 11 | { 12 | private IEnumerable SysTransactions() 13 | { 14 | foreach (var transaction in _monitor.Transactions) 15 | { 16 | yield return new BsonDocument 17 | { 18 | ["threadID"] = transaction.ThreadID, 19 | ["transactionID"] = (int)transaction.TransactionID, 20 | ["startTime"] = transaction.StartTime, 21 | ["mode"] = transaction.Mode.ToString(), 22 | ["transactionSize"] = transaction.Pages.TransactionSize, 23 | ["maxTransactionSize"] = transaction.MaxTransactionSize, 24 | ["pagesInLogFile"] = transaction.Pages.DirtyPages.Count, 25 | ["newPages"] = transaction.Pages.NewPages.Count, 26 | ["deletedPages"] = transaction.Pages.DeletedPages, 27 | ["modifiedPages"] = transaction.Snapshots.Select(x => x.GetWritablePages(true, true).Count()).Sum() 28 | }; 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /LiteDB/LiteDB.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litedb-org/LiteDB/2cc00c7094a966b7912c321c1ff7a754433c6d14/LiteDB/LiteDB.snk -------------------------------------------------------------------------------- /LiteDB/Utils/AsyncManualResetEvent.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace LiteDB 5 | { 6 | /// 7 | /// Async implementation of ManualResetEvent 8 | /// https://devblogs.microsoft.com/pfxteam/building-async-coordination-primitives-part-1-asyncmanualresetevent/ 9 | /// 10 | internal class AsyncManualResetEvent 11 | { 12 | private volatile TaskCompletionSource _tcs = new TaskCompletionSource(); 13 | 14 | public Task WaitAsync() 15 | { 16 | return _tcs.Task; 17 | } 18 | 19 | public void Set() 20 | { 21 | _tcs.TrySetResult(true); 22 | } 23 | 24 | public void Reset() 25 | { 26 | while (true) 27 | { 28 | var tcs = _tcs; 29 | if (!tcs.Task.IsCompleted || 30 | Interlocked.CompareExchange(ref _tcs, new TaskCompletionSource(), tcs) == tcs) 31 | return; 32 | } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /LiteDB/Utils/Encoding.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace LiteDB 4 | { 5 | internal class StringEncoding 6 | { 7 | // Original Encoding.UTF8 will replace unpaired surrogate with U+FFFD, which is not suitable for database 8 | // so, we need to use new UTF8Encoding(false, true) to make throw exception when unpaired surrogate is found 9 | //public static System.Text.Encoding UTF8 = new UTF8Encoding(false, true); 10 | public static Encoding UTF8 = new UTF8Encoding(false, true); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /LiteDB/Utils/ExtendedLengthHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | using static LiteDB.Constants; 8 | 9 | namespace LiteDB 10 | { 11 | /// 12 | /// Class to help extend IndexNode key up to 1023 bytes length (for string/byte[]) using 2 first bits in BsonType 13 | /// 14 | internal static class ExtendedLengthHelper 15 | { 16 | /// 17 | /// Read BsonType and UShort length from 2 bytes 18 | /// 19 | public static void ReadLength(byte typeByte, byte lengthByte, out BsonType type, out ushort length) 20 | { 21 | var bsonType = (byte)(typeByte & 0b0011_1111); 22 | var lengthLSByte = lengthByte; 23 | var lengthMSByte = (byte)(typeByte & 0b1100_0000); 24 | type = (BsonType)bsonType; 25 | length = (ushort)((lengthMSByte << 2) | (lengthLSByte)); 26 | } 27 | 28 | /// 29 | /// Write BsonType and UShort length in 2 bytes 30 | /// 31 | public static void WriteLength(BsonType type, ushort length, out byte typeByte, out byte lengthByte) 32 | { 33 | if (length > 1023) throw new ArgumentOutOfRangeException(nameof(length)); 34 | var bsonType = (byte)type; 35 | var lengthLSByte = unchecked((byte)length); 36 | var lengthMSByte = (byte)((length & 0b11_0000_0000) >> 2); 37 | typeByte = (byte)(lengthMSByte | bsonType); 38 | lengthByte = lengthLSByte; 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /LiteDB/Utils/Extensions/DateExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static LiteDB.Constants; 3 | 4 | namespace LiteDB 5 | { 6 | internal static class DateExtensions 7 | { 8 | /// 9 | /// Truncate DateTime in milliseconds 10 | /// 11 | public static DateTime Truncate(this DateTime dt) 12 | { 13 | if (dt == DateTime.MaxValue || dt == DateTime.MinValue) 14 | { 15 | return dt; 16 | } 17 | 18 | return new DateTime(dt.Year, dt.Month, dt.Day, 19 | dt.Hour, dt.Minute, dt.Second, dt.Millisecond, 20 | dt.Kind); 21 | } 22 | 23 | public static int MonthDifference(this DateTime startDate, DateTime endDate) 24 | { 25 | // https://stackoverflow.com/a/1526116/3286260 26 | 27 | int compMonth = (endDate.Month + endDate.Year * 12) - (startDate.Month + startDate.Year * 12); 28 | double daysInEndMonth = (endDate - endDate.AddMonths(1)).Days; 29 | double months = compMonth + (startDate.Day - endDate.Day) / daysInEndMonth; 30 | 31 | return Convert.ToInt32(Math.Truncate(months)); 32 | } 33 | 34 | public static int YearDifference(this DateTime startDate, DateTime endDate) 35 | { 36 | // https://stackoverflow.com/a/28444291/3286260 37 | 38 | //Excel documentation says "COMPLETE calendar years in between dates" 39 | int years = endDate.Year - startDate.Year; 40 | 41 | if (startDate.Month == endDate.Month &&// if the start month and the end month are the same 42 | endDate.Day < startDate.Day)// BUT the end day is less than the start day 43 | { 44 | years--; 45 | } 46 | else if (endDate.Month < startDate.Month)// if the end month is less than the start month 47 | { 48 | years--; 49 | } 50 | 51 | return years; 52 | } 53 | } 54 | } -------------------------------------------------------------------------------- /LiteDB/Utils/Extensions/EnumerableExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace LiteDB.Utils.Extensions 5 | { 6 | internal static class EnumerableExtensions 7 | { 8 | // calls method on dispose 9 | public static IEnumerable OnDispose(this IEnumerable source, Action onDispose) 10 | { 11 | try 12 | { 13 | foreach (var item in source) 14 | { 15 | yield return item; 16 | } 17 | } 18 | finally 19 | { 20 | onDispose(); 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /LiteDB/Utils/Extensions/HashSetExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | using System.Text.RegularExpressions; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB 8 | { 9 | internal static class HashSetExtensions 10 | { 11 | public static HashSet AddRange(this HashSet hash, IEnumerable items) 12 | { 13 | if (items == null) return hash; 14 | 15 | foreach(var item in items) 16 | { 17 | hash.Add(item); 18 | } 19 | 20 | return hash; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /LiteDB/Utils/Extensions/IOExceptionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Runtime.InteropServices; 3 | using static LiteDB.Constants; 4 | 5 | namespace LiteDB 6 | { 7 | internal static class IOExceptionExtensions 8 | { 9 | private const int ERROR_SHARING_VIOLATION = 32; 10 | private const int ERROR_LOCK_VIOLATION = 33; 11 | 12 | /// 13 | /// Detect if exception is an Locked exception 14 | /// 15 | public static bool IsLocked(this IOException ex) 16 | { 17 | var errorCode = Marshal.GetHRForException(ex) & ((1 << 16) - 1); 18 | 19 | return 20 | errorCode == ERROR_SHARING_VIOLATION || 21 | errorCode == ERROR_LOCK_VIOLATION; 22 | } 23 | 24 | /// 25 | /// Wait current thread for N milliseconds if exception is about Locking 26 | /// 27 | public static void WaitIfLocked(this IOException ex, int timerInMilliseconds) 28 | { 29 | if (ex.IsLocked()) 30 | { 31 | if (timerInMilliseconds > 0) 32 | { 33 | System.Threading.Tasks.Task.Delay(timerInMilliseconds).Wait(); 34 | } 35 | } 36 | else 37 | { 38 | throw ex; 39 | } 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /LiteDB/Utils/Extensions/StopWatchExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | 4 | namespace LiteDB.Utils.Extensions 5 | { 6 | public static class StopWatchExtensions 7 | { 8 | // Start the stopwatch and returns an IDisposable that will stop the stopwatch when disposed 9 | public static IDisposable StartDisposable(this Stopwatch stopwatch) 10 | { 11 | stopwatch.Start(); 12 | return new DisposableAction(stopwatch.Stop); 13 | } 14 | 15 | private class DisposableAction : IDisposable 16 | { 17 | private readonly Action _action; 18 | 19 | public DisposableAction(Action action) 20 | { 21 | _action = action; 22 | } 23 | 24 | public void Dispose() 25 | { 26 | _action(); 27 | } 28 | } 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /LiteDB/Utils/Extensions/StreamExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using static LiteDB.Constants; 4 | 5 | namespace LiteDB 6 | { 7 | internal static class StreamExtensions 8 | { 9 | /// 10 | /// If Stream are FileStream, flush content direct to disk (avoid OS cache) 11 | /// 12 | public static void FlushToDisk(this Stream stream) 13 | { 14 | if (stream is FileStream fstream) 15 | { 16 | fstream.Flush(true); 17 | } 18 | else 19 | { 20 | stream.Flush(); 21 | } 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /LiteDB/Utils/Extensions/TypeInfoExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | using System.Reflection; 5 | using System.Runtime.CompilerServices; 6 | using System.Collections; 7 | using static LiteDB.Constants; 8 | 9 | namespace LiteDB 10 | { 11 | internal static class TypeInfoExtensions 12 | { 13 | public static bool IsAnonymousType(this Type type) 14 | { 15 | bool isAnonymousType = 16 | type.FullName.Contains("AnonymousType") && 17 | type.GetTypeInfo().GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Any(); 18 | 19 | return isAnonymousType; 20 | } 21 | 22 | public static bool IsEnumerable(this Type type) 23 | { 24 | return 25 | type != typeof(String) && 26 | typeof(IEnumerable).IsAssignableFrom(type); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /LiteDB/Utils/Pool/BucketHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using static LiteDB.Constants; 3 | 4 | namespace LiteDB 5 | { 6 | internal static class BucketHelper 7 | { 8 | public const int BucketCount = 17; 9 | 10 | private static readonly int[] _bucketSize; 11 | 12 | static BucketHelper() 13 | { 14 | _bucketSize = new int[BucketCount]; 15 | for (var i = 0; i < BucketCount; ++i) 16 | { 17 | _bucketSize[i] = GetMaxSizeForBucket(i); 18 | } 19 | } 20 | 21 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 22 | internal static int GetBucketIndex(int bufferSize) 23 | { 24 | for (var i = 0; i < _bucketSize.Length; i++) 25 | { 26 | if (_bucketSize[i] >= bufferSize) 27 | { 28 | return i; 29 | } 30 | } 31 | 32 | return -1; 33 | } 34 | 35 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 36 | internal static int GetMaxSizeForBucket(int binIndex) 37 | { 38 | return 16 << binIndex; 39 | } 40 | } 41 | } -------------------------------------------------------------------------------- /LiteDB/Utils/Randomizer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using static LiteDB.Constants; 3 | 4 | namespace LiteDB 5 | { 6 | 7 | /// 8 | /// A singleton shared randomizer class 9 | /// 10 | internal static class Randomizer 11 | { 12 | private static readonly Random _random = new Random(RANDOMIZER_SEED); 13 | 14 | public static int Next() 15 | { 16 | lock (_random) 17 | { 18 | return _random.Next(); 19 | } 20 | } 21 | 22 | public static int Next(int minValue, int maxValue) 23 | { 24 | lock (_random) 25 | { 26 | return _random.Next(minValue, maxValue); 27 | } 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /LiteDB/Utils/Result.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | using static LiteDB.Constants; 6 | 7 | namespace LiteDB 8 | { 9 | /// 10 | /// Implement a generic result structure with value and exception. This value can be partial value (like BsonDocument/Array) 11 | /// 12 | internal struct Result 13 | where T : class 14 | { 15 | public T Value; 16 | public Exception Exception; 17 | 18 | public bool Ok => this.Exception == null; 19 | public bool Fail => this.Exception != null; 20 | 21 | /// 22 | /// Get array result or throw exception if there is any error on read result 23 | /// 24 | public T GetValue() => this.Ok ? this.Value : throw this.Exception; 25 | 26 | public Result(T value, Exception ex = null) 27 | { 28 | this.Value = value; 29 | this.Exception = ex; 30 | } 31 | 32 | 33 | public static implicit operator T(Result value) 34 | { 35 | return value.Value; 36 | } 37 | 38 | public static implicit operator Result(T value) 39 | { 40 | return new Result(value, null); 41 | } 42 | } 43 | } -------------------------------------------------------------------------------- /LiteDB/Utils/TryCatch.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Runtime.InteropServices; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace LiteDB.Utils 10 | { 11 | internal class TryCatch 12 | { 13 | public readonly List Exceptions = new List(); 14 | 15 | public TryCatch() 16 | { 17 | } 18 | 19 | public TryCatch(Exception initial) 20 | { 21 | this.Exceptions.Add(initial); 22 | } 23 | 24 | public bool InvalidDatafileState => this.Exceptions.Any(ex => 25 | ex is LiteException liteEx && 26 | liteEx.ErrorCode == LiteException.INVALID_DATAFILE_STATE); 27 | 28 | [DebuggerHidden] 29 | public void Catch(Action action) 30 | { 31 | try 32 | { 33 | action(); 34 | } 35 | catch (Exception ex) 36 | { 37 | this.Exceptions.Add(ex); 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 5.0.{build} 2 | branches: 3 | only: 4 | - master 5 | image: Visual Studio 2022 6 | configuration: 7 | - Debug 8 | - Release 9 | before_build: 10 | - cmd: nuget restore LiteDB.sln 11 | build: 12 | project: LiteDB.sln 13 | publish_nuget: true 14 | verbosity: minimal 15 | for: 16 | - 17 | matrix: 18 | only: 19 | - configuration: Release 20 | artifacts: 21 | - path: LiteDB\bin\Release\LiteDB*.nupkg 22 | deploy: 23 | - provider: Webhook 24 | url: https://app.signpath.io/API/v1/f5b329b8-705f-4d6c-928a-19465b83716b/Integrations/AppVeyor?ProjectKey=LiteDB.git&SigningPolicyKey=release-signing 25 | authorization: 26 | secure: 3eLjGkpQC1wg1s5GIEqs7yk/V8OZNnpKmpwdsaloGExc5jMspM4nA7u/UlG5ugraEyXRC05ZxLU4FIfH2V2BEg== 27 | -------------------------------------------------------------------------------- /icon_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litedb-org/LiteDB/2cc00c7094a966b7912c321c1ff7a754433c6d14/icon_64x64.png --------------------------------------------------------------------------------