├── .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
--------------------------------------------------------------------------------