├── .gitignore
├── LICENSE
├── README.md
└── src
├── .vscode
├── launch.json
└── tasks.json
├── ConsoleApp1
├── ConsoleApp1.csproj
├── Program.cs
├── Tests
│ ├── AllocTest.cs
│ ├── AmpCalcs.cs
│ ├── ConditionalExpr.cs
│ ├── DumpExamples.cs
│ ├── ExtendValueTests.cs
│ ├── Faker.Names.cs
│ ├── Faker.cs
│ ├── GZip.cs
│ ├── PointerTest.cs
│ └── StructTests.cs
├── Tools
│ └── StaticUtils.cs
└── _Imports.cs
├── LiteDB.Benchmark
├── LiteDB.Benchmark.csproj
├── Program.cs
├── Tests
│ └── BsonExpressionTests.cs
└── _Imports.cs
├── LiteDB.Generator.Tests
├── LiteDB.Generator.Tests.csproj
└── Program.cs
├── LiteDB.Generator.sln
├── LiteDB.Generator
├── Gen
│ └── InterfaceGen.cs
├── LiteDB.Generator.csproj
├── LiteGenerator.cs
├── Utils
│ ├── AttributeDataExtensions.cs
│ ├── Attributes.cs
│ ├── CodeBase.cs
│ ├── CodeWriter.cs
│ ├── CompilerServices.cs
│ ├── DEBUG.cs
│ ├── StringExtensions.cs
│ ├── SymbolExtensions.cs
│ ├── SyntaxReceiver.cs
│ └── TypeParameterSymbolExtensions.cs
└── _Imports.cs
├── LiteDB.Tests
├── Expressions
│ ├── BsonExpressions_Execute_Tests.cs
│ ├── BsonExpressions_Parser_Tests.cs
│ ├── BsonExpressions_ToString_Tests.cs
│ └── BsonExpressions_TypeAndIsPredicate_Tests.cs
├── Internals
│ └── Engine
│ │ ├── CheckpointAction_Tests.cs
│ │ ├── Helpers
│ │ ├── MockEnumerator.cs
│ │ └── MockIndexService.cs
│ │ ├── Query
│ │ └── Indexes
│ │ │ └── IndexEqualsEnumerator_Tests.cs
│ │ └── SortService_Tests.cs
├── LiteDB.Tests.csproj
├── Utils
│ ├── AssertEx.cs
│ └── JsonTokenizer_Tests.cs
└── _Imports.cs
├── LiteDB.sln
└── LiteDB
├── .editorconfig
├── Document
├── Bson
│ ├── BsonReadResult.cs
│ ├── BsonReader.cs
│ ├── BsonTypeCode.cs
│ └── BsonWriter.cs
├── Json
│ ├── JsonReader.cs
│ ├── JsonReaderStatic.cs
│ ├── JsonSerializer.cs
│ ├── JsonTokenizer
│ │ ├── JsonToken.cs
│ │ ├── JsonTokenType.cs
│ │ └── JsonTokenizer.cs
│ ├── JsonWriter.cs
│ └── JsonWriterStatic.cs
└── ObjectModel
│ ├── BsonArray.cs
│ ├── BsonAutoId.cs
│ ├── BsonBinary.cs
│ ├── BsonBoolean.cs
│ ├── BsonDateTime.cs
│ ├── BsonDecimal.cs
│ ├── BsonDocument.cs
│ ├── BsonDouble.cs
│ ├── BsonGuid.cs
│ ├── BsonInt32.cs
│ ├── BsonInt64.cs
│ ├── BsonMaxValue.cs
│ ├── BsonMinValue.cs
│ ├── BsonNull.cs
│ ├── BsonObjectId.cs
│ ├── BsonString.cs
│ ├── BsonType.cs
│ ├── BsonValue.cs
│ └── ObjectId.cs
├── Engine
├── Commands
│ ├── Engine.Execute.cs
│ ├── Engine.ExecuteQuery.cs
│ ├── Engine.Open.cs
│ └── Engine.Shutdown.cs
├── DocumentStore
│ ├── DocumentStoreFactory.cs
│ ├── FileJson
│ │ └── FileJsonDocumentStore.cs
│ ├── Interfaces
│ │ ├── IDataService.cs
│ │ ├── IDocumentStore.cs
│ │ └── IIndexService.cs
│ ├── SubQuery
│ │ └── SubQueryStore.cs
│ └── UserCollection
│ │ ├── Services
│ │ ├── DataService.cs
│ │ └── IndexService.cs
│ │ └── UserCollectionStore.cs
├── LiteEngine.cs
├── Services
│ ├── AllocMapService
│ │ └── AllocationMapService.cs
│ ├── AutoIdService
│ │ └── AutoIdService.cs
│ ├── DiskService
│ │ ├── DiskService.cs
│ │ ├── DiskStream.cs
│ │ ├── NewDatafile.cs
│ │ ├── StreamFactory
│ │ │ ├── FileSortStreamFactory.cs
│ │ │ ├── FileStreamFactory.cs
│ │ │ ├── IStreamFactory.cs
│ │ │ └── MemoryStreamFactory.cs
│ │ └── Streams
│ │ │ ├── AesStream.cs
│ │ │ └── TempStream.cs
│ ├── LockService
│ │ ├── AsyncReadWriteLock.cs
│ │ └── LockService.cs
│ ├── LogService
│ │ ├── Actions
│ │ │ └── CheckpointActions.cs
│ │ └── LogService.cs
│ ├── MasterService
│ │ ├── Mapper
│ │ │ └── MasterMapper.cs
│ │ ├── MasterService.cs
│ │ └── ObjectModel
│ │ │ ├── CollectionDocument.cs
│ │ │ ├── IndexDocument.cs
│ │ │ ├── MasterDocument.cs
│ │ │ └── PragmaDocument.cs
│ ├── MemoryCache
│ │ └── MemoryCache.cs
│ ├── MemoryFactory
│ │ └── MemoryFactory.cs
│ ├── MonitorService
│ │ └── MonitorService.cs
│ ├── QueryService
│ │ ├── AggreagteFunc
│ │ │ ├── AnyFunc.cs
│ │ │ ├── ArrayFunc.cs
│ │ │ ├── AvgFunc.cs
│ │ │ ├── CountFunc.cs
│ │ │ ├── FirstFunc.cs
│ │ │ ├── IAggregateFunc.cs
│ │ │ ├── LastFunc.cs
│ │ │ ├── MaxFunc.cs
│ │ │ ├── MinFunc.cs
│ │ │ └── SumFunc.cs
│ │ ├── Indexes
│ │ │ ├── IndexAllEnumerator.cs
│ │ │ ├── IndexEqualsEnumerator.cs
│ │ │ ├── IndexInEnumerator.cs
│ │ │ ├── IndexLikeEnumerator.cs
│ │ │ ├── IndexRangeEnumerator.cs
│ │ │ └── IndexScanEnumerator.cs
│ │ ├── Lookup
│ │ │ ├── DataLookup.cs
│ │ │ ├── IDocumentLookup.cs
│ │ │ └── IndexLookup.cs
│ │ ├── Optimization
│ │ │ ├── AggregateOptimization.cs
│ │ │ └── QueryOptimization.cs
│ │ ├── PipelineBuilder
│ │ │ └── PipelineBuilder.cs
│ │ ├── PipelineEnumerators
│ │ │ ├── AggregateEnumerator.cs
│ │ │ ├── FilterEnumerator.cs
│ │ │ ├── IPipeEnumerator.cs
│ │ │ ├── InMemoryOrderByEnumerator.cs
│ │ │ ├── IncludeEnumerator.cs
│ │ │ ├── LimitEnumerator.cs
│ │ │ ├── LookupEnumerator.cs
│ │ │ ├── OffsetEnumerator.cs
│ │ │ ├── OrderByEnumerator.cs
│ │ │ └── TransformEnumerator.cs
│ │ └── QueryService.cs
│ ├── RecoveryService
│ │ └── RecoveryService.cs
│ ├── ServiceFactory
│ │ └── ServiceFactory.cs
│ ├── SortService
│ │ ├── Container
│ │ │ ├── SortContainer.cs
│ │ │ └── SortOperation.cs
│ │ └── SortService.cs
│ └── WalIndexService
│ │ └── WalIndexService.cs
├── SqlParser
│ ├── Commands
│ │ ├── SqlParser.CreateCollection.cs
│ │ ├── SqlParser.CreateIndex.cs
│ │ ├── SqlParser.Insert.cs
│ │ └── SqlParser.Select.cs
│ ├── Core
│ │ ├── SqlParser.AutoId.cs
│ │ ├── SqlParser.DocumentStore.cs
│ │ ├── SqlParser.ListOfExpressions.cs
│ │ ├── SqlParser.Parameters.cs
│ │ └── SqlParser.SelectFields.cs
│ └── SqlParser.cs
├── Statements
│ ├── BsonDataReader
│ │ ├── BsonDataReader.cs
│ │ ├── BsonDataReaderExtensions.cs
│ │ ├── BsonScalarReader.cs
│ │ └── IDataReader.cs
│ ├── CheckpointStatement.cs
│ ├── CreateCollectionStatement.cs
│ ├── CreateIndexStatement.cs
│ ├── DeleteStatement.cs
│ ├── InsertStatement.cs
│ ├── Interfaces
│ │ ├── EngineStatementType.cs
│ │ └── IEngineStatement.cs
│ └── SelectStatement.cs
├── Structures
│ ├── Core
│ │ ├── EngineSettings.cs
│ │ ├── FileHeader.cs
│ │ ├── RowID.cs
│ │ └── Sequence.cs
│ ├── Enums
│ │ ├── CheckpointActionType.cs
│ │ ├── EngineState.cs
│ │ └── PageType.cs
│ ├── Extend
│ │ ├── ExtendLocation.cs
│ │ └── ExtendPageValue.cs
│ ├── Log
│ │ ├── CheckpointAction.cs
│ │ ├── LogPageHeader.cs
│ │ └── LogPosition.cs
│ ├── Query
│ │ ├── Cursor.cs
│ │ ├── ExplainPlainBuilder.cs
│ │ ├── Into.cs
│ │ ├── OrderBy.cs
│ │ ├── PipeContext.cs
│ │ ├── PipeEmit.cs
│ │ ├── PipeValue.cs
│ │ ├── Query.cs
│ │ ├── Resultset.cs
│ │ ├── SelectField.cs
│ │ └── SelectFields.cs
│ └── Sort
│ │ ├── SortItem.cs
│ │ └── SortItemDocument.cs
├── Transaction
│ ├── Transaction.Abort.cs
│ ├── Transaction.Commit.cs
│ ├── Transaction.GetPage.cs
│ ├── Transaction.Safepoint.cs
│ └── Transaction.cs
└── Unsafe
│ ├── Enumerator
│ └── IndexNodeEnumerator.cs
│ ├── IndexKey
│ ├── IndexKey.Compare.cs
│ ├── IndexKey.GetSize.cs
│ ├── IndexKey.Initialize.cs
│ ├── IndexKey.ToBsonValue.cs
│ └── IndexKey.cs
│ ├── PageMemory
│ ├── PageMemory.AllocMap.cs
│ ├── PageMemory.DataBlock.cs
│ ├── PageMemory.IndexNode.cs
│ ├── PageMemory.Segment.cs
│ └── PageMemory.cs
│ ├── Results
│ ├── DataBlockResult.cs
│ └── IndexNodeResult.cs
│ ├── Sort
│ ├── SortContainer2.cs
│ ├── SortItem2.cs
│ └── SortPage2.cs
│ └── Structs
│ ├── DataBlock.cs
│ ├── IndexNode.cs
│ ├── IndexNodeLevel.cs
│ └── PageSegment.cs
├── Expression
├── Attributes
│ └── VolatileAttribute.cs
├── BsonExpression.Static.cs
├── BsonExpression.Static.cs.bak
├── BsonExpression.cs
├── BsonExpressionContext.cs
├── BsonExpressionInfo.cs
├── BsonExpressionParser.cs
├── BsonExpressionParser.cs.bak
├── BsonExpressionType.cs
├── ExpressionMethods
│ ├── Aggregate.cs
│ ├── DataTypes.cs
│ ├── Date.cs
│ ├── Math.cs
│ ├── Misc.cs
│ └── String.cs
├── InternalExpressions
│ ├── ArrayIndexBsonExpression.cs
│ ├── BinaryBsonExpression.cs
│ ├── CallBsonExpression.cs
│ ├── ConditionalBsonExpression.cs
│ ├── ConstantBsonExpression.cs
│ ├── EmptyBsonExpression.cs
│ ├── FilterBsonExpression.cs
│ ├── InnerBsonExpression.cs
│ ├── MakeArrayBsonExpression.cs
│ ├── MakeDocumentBsonExpression.cs
│ ├── MapBsonExpression.cs
│ ├── ParameterBsonExpression.cs
│ ├── PathBsonExpression.cs
│ └── ScopeBsonExpression.cs
└── Tokenizer
│ ├── Token.cs
│ ├── Token.cs.bak
│ ├── TokenType.cs
│ ├── Tokenizer.cs
│ └── Tokenizer.cs.bak
├── LiteDB.csproj
├── Utils
├── Collation.cs
├── CompilerServices.cs
├── Constants.cs
├── Crc8.cs
├── Debugger
│ ├── CleanerExtensions.cs
│ ├── CodeContract.cs
│ ├── CodeContractExpression.cs
│ ├── DumpExtensions.cs
│ ├── PageDump.cs
│ └── Profiler.cs
├── ExceptionExtensions.cs
├── Exceptions
│ ├── LiteException.Errors.cs
│ └── LiteException.cs
├── Extensions
│ ├── DateExtensions.cs
│ ├── DictionaryExtensions.cs
│ ├── HashSetExtensions.cs
│ ├── IOExceptionExtensions.cs
│ ├── LinqExtensions.cs
│ ├── MarshalExtensions.cs
│ ├── SpanExtensions.cs
│ └── StringExtensions.cs
├── FileHelper.cs
├── Interfaces
│ └── IIsEmpty.cs
├── Randomizer.cs
├── Reflection.Expression.cs
├── Reflection.cs
├── SharedArray.cs
└── StringBuilderCache.cs
├── _Imports.cs
└── icon_64x64.png
/LICENSE:
--------------------------------------------------------------------------------
1 | LiteDB vNext
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # LiteDB vNext
2 |
3 | LiteDB-vNew is now in the main repository (`dev` branch) - https://github.com/mbdavid/LiteDB/tree/dev
4 |
--------------------------------------------------------------------------------
/src/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "0.2.0",
3 | "configurations": [
4 | {
5 | // Use IntelliSense to find out which attributes exist for C# debugging
6 | // Use hover for the description of the existing attributes
7 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
8 | "name": ".NET Core Launch (console)",
9 | "type": "coreclr",
10 | "request": "launch",
11 | "preLaunchTask": "build",
12 | // If you have changed target frameworks, make sure to update the program path.
13 | "program": "${workspaceFolder}/ConsoleApp1/bin/Debug/net6.0/ConsoleApp1.dll",
14 | "args": [],
15 | "cwd": "${workspaceFolder}/ConsoleApp1",
16 | // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
17 | "console": "internalConsole",
18 | "stopAtEntry": false
19 | },
20 | {
21 | "name": ".NET Core Attach",
22 | "type": "coreclr",
23 | "request": "attach"
24 | }
25 | ]
26 | }
--------------------------------------------------------------------------------
/src/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "build",
6 | "command": "dotnet",
7 | "type": "process",
8 | "args": [
9 | "build",
10 | "${workspaceFolder}/ConsoleApp1/ConsoleApp1.csproj",
11 | "/property:GenerateFullPaths=true",
12 | "/consoleloggerparameters:NoSummary"
13 | ],
14 | "problemMatcher": "$msCompile"
15 | },
16 | {
17 | "label": "publish",
18 | "command": "dotnet",
19 | "type": "process",
20 | "args": [
21 | "publish",
22 | "${workspaceFolder}/ConsoleApp1/ConsoleApp1.csproj",
23 | "/property:GenerateFullPaths=true",
24 | "/consoleloggerparameters:NoSummary"
25 | ],
26 | "problemMatcher": "$msCompile"
27 | },
28 | {
29 | "label": "watch",
30 | "command": "dotnet",
31 | "type": "process",
32 | "args": [
33 | "watch",
34 | "run",
35 | "--project",
36 | "${workspaceFolder}/ConsoleApp1/ConsoleApp1.csproj"
37 | ],
38 | "problemMatcher": "$msCompile"
39 | }
40 | ]
41 | }
--------------------------------------------------------------------------------
/src/ConsoleApp1/ConsoleApp1.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net7.0
6 | enable
7 | enable
8 | True
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/ConsoleApp1/Tests/AllocTest.cs:
--------------------------------------------------------------------------------
1 | //using System.Runtime.InteropServices;
2 |
3 | //using static LiteDB.Constants;
4 |
5 |
6 | //var start = GC.GetAllocatedBytesForCurrentThread();
7 |
8 | //Console.WriteLine(start);
9 |
10 | //var m = Marshal.AllocHGlobal(819200);
11 |
12 | ////await Task.Delay(1000);
13 |
14 | //var end = GC.GetAllocatedBytesForCurrentThread();
15 |
16 | //var diff = end - start;
17 |
18 | //Console.WriteLine(end);
19 | //Console.WriteLine($"Total Allocated: {diff}");
20 |
21 | //Marshal.FreeHGlobal(m);
--------------------------------------------------------------------------------
/src/ConsoleApp1/Tests/AmpCalcs.cs:
--------------------------------------------------------------------------------
1 | //using static LiteDB.Constants;
2 |
3 |
4 | //var tests = new uint[] { 1, 2, 8, 9, 16, 17, 16319, 16320, 16321, 16322, 32641, 32642, 32643, 32650, 32651, 16333, 32656 };
5 |
6 | //Console.WriteLine("AM_EXTEND_COUNT: {0}", AM_EXTEND_COUNT);
7 | //Console.WriteLine("AM_PAGE_STEP: {0}", AM_PAGE_STEP);
8 | //Console.WriteLine("AM_MAP_PAGES_COUNT: {0}", AM_MAP_PAGES_COUNT);
9 |
10 | //foreach (var test in tests)
11 | //{
12 | // var page = new
13 | // {
14 | // PageID = test
15 | // };
16 |
17 | // var allocationMapID = (int)(page.PageID / AM_PAGE_STEP);
18 | // var extendIndex = (page.PageID - 1 - allocationMapID * AM_PAGE_STEP) / AM_EXTEND_SIZE;
19 | // var pageIndex = page.PageID - 1 - allocationMapID * AM_PAGE_STEP - extendIndex * AM_EXTEND_SIZE;
20 |
21 | // Console.WriteLine("{0}: {1}, {2}, {3}", page.PageID, allocationMapID, extendIndex, pageIndex);
22 | // Console.WriteLine("{0}: {1}", page.PageID, GetBlockPageID(allocationMapID, extendIndex, pageIndex));
23 | //}
24 |
25 | //uint GetBlockPageID(int allocationMapID, long extendIndex, long pageIndex)
26 | //{
27 | // return (uint)
28 | // (allocationMapID * AM_PAGE_STEP +
29 | // extendIndex * AM_EXTEND_SIZE +
30 | // pageIndex + 1);
31 | //}
32 |
33 |
--------------------------------------------------------------------------------
/src/ConsoleApp1/Tests/ConditionalExpr.cs:
--------------------------------------------------------------------------------
1 | //var a = true;
2 | //var x = false;
3 | //var n = 10;
4 |
5 | //var rr = a == true ? x == a ? 'r' : a == false ? 'f' : a == false && a == true ? 'x' + 'y' : 'z' : 'd';
6 |
7 | //var ss = new string[]
8 | //{
9 | // "a = true ? 's' : 'f'",
10 | // "a = true ? 't' : a = false ? 'f' : 'xx'",
11 | // "a = true ? a != false ? 'vv' : 'f' : 'ff'",
12 | // "a = true ? 's' : a = false ? 'f' : 'nenhum'",
13 | // "$.a=true?$.x=$.a?'r':$.a=false?'f':$.a=false AND $.a=true?'x'+'y':'z':'d'",
14 | // "(a = true) ? (x = a) ? 'r' : (a = false) ? 'f' : (a = false and a = true) ? 'x' + 'y' : 'z' : 'd'",
15 | // "(a = true) ? (x = a) ? (x = a) ? 'r' : (a = false) ? 'f' : (a = false and a = true) ? 'x' + 'y' : 'z' : 'd' : 'p'",
16 | // "n = 1 ? '1' : n = 2 ? '2' : n = 3 ? '3' : n = 4 ? '4' : n = 5 ? '5' : n = 6 ? '6' : 'outro'",
17 | // "'teste' + a = true ? 's' : 'f'",
18 | // "'teste' + (a = true ? 's' : 'f')",
19 | //};
20 |
21 |
22 | ////Console.WriteLine(x.ToString());
23 |
24 | //foreach (var s in ss)
25 | //{
26 | // var doc = new BsonDocument { ["a"] = true };
27 | // var exp = BsonExpression.Create(s);
28 |
29 | // Console.WriteLine("Str : " + s);
30 | // Console.WriteLine("Expr: " + exp.ToString());
31 | // Console.WriteLine("Exec: " + exp.Execute(doc).ToString());
32 | // Console.WriteLine("----------------------------------------------");
33 | //}
34 |
35 |
36 | //Console.ReadKey();
37 |
38 | //return;
--------------------------------------------------------------------------------
/src/ConsoleApp1/Tests/DumpExamples.cs:
--------------------------------------------------------------------------------
1 | //using LiteDB;
2 | //using LiteDB.Engine;
3 |
4 | //var _demo = "test";
5 | //var tests = new uint[] { 1, 2 };
6 | //var sortItem = new SortItem(new RowID(8,3), new BsonDocument { ["ola"] = 10 });
7 |
8 | //var dump = Dump.Object(new { _demo, nome = tests, sortItem });
9 |
10 |
11 | //Console.WriteLine(dump);
--------------------------------------------------------------------------------
/src/ConsoleApp1/Tests/ExtendValueTests.cs:
--------------------------------------------------------------------------------
1 | //using LiteDB;
2 | //using LiteDB.Engine;
3 |
4 | //var extendValues = new uint[]
5 | //{
6 | // 0b01100010_111_111_111_111_111_111_111_111,
7 | // 0b01100010_000_000_000_000_000_000_000_000,
8 |
9 | // 0b01100010_111_111_111_111_111_111_111_000,
10 | // 0b01100010_000_000_000_000_000_000_000_001,
11 | // 0b01100010_000_000_000_000_000_000_000_010,
12 |
13 | // 0b01100010_000_111_111_111_111_111_111_111,
14 | // 0b01100010_111_000_111_111_111_111_111_111,
15 | // 0b01100010_111_111_000_111_111_111_111_111,
16 | // 0b01100010_111_111_111_000_111_111_111_111,
17 | // 0b01100010_111_111_111_111_000_111_111_111,
18 | // 0b01100010_111_111_111_111_111_000_111_111,
19 | // 0b01100010_111_111_111_111_111_111_000_111,
20 | // 0b01100010_111_111_111_111_111_111_111_000,
21 |
22 | // 0b01100010_001_111_111_111_111_111_111_111,
23 |
24 | //};
25 |
26 | //byte colID = 98;
27 | //var pageType = PageType.Data;
28 |
29 | //foreach (var value in extendValues)
30 | //{
31 | // Print(value);
32 | //}
33 |
34 |
35 | //void Print(uint value)
36 | //{
37 | // var result = AllocationMapPage.HasFreeSpaceInExtend(value, colID, pageType);
38 |
39 | // Console.WriteLine(Dump.ExtendValue(value) + " => " + result);
40 | //}
41 |
42 |
--------------------------------------------------------------------------------
/src/ConsoleApp1/Tests/Faker.cs:
--------------------------------------------------------------------------------
1 | internal static partial class Faker
2 | {
3 | private static Random _random = new Random(420);
4 |
5 |
6 | public static string Fullname()
7 | {
8 | var names = _random.NextBool() ? _maleNames : _femaleNames;
9 |
10 | return names[_random.Next(names.Length - 1)] + " " + _surNames[_random.Next(_surNames.Length - 1)];
11 | }
12 |
13 | public static int Age()
14 | {
15 | return _random.Next(18, 96);
16 | }
17 |
18 | public static DateTime Birthday()
19 | {
20 | var oldest = DateTime.Today.AddYears(-110).Ticks;
21 | var now = DateTime.Now.Ticks;
22 | var range = now - oldest;
23 |
24 | var date = new DateTime(oldest + _random.NextLong(0, range));
25 |
26 | return date;
27 | }
28 |
29 | public static string Lorem(int size, int end = -1)
30 | {
31 | return string.Join(" ", Enumerable.Range(1, end == -1 ? size : _random.Next(size, end))
32 | .Select(x => _lorem[_random.Next(_lorem.Length - 1)]));
33 | }
34 |
35 | public static int Next(int start, int end)
36 | {
37 | return _random.Next(start, end);
38 | }
39 |
40 | // https://stackoverflow.com/a/13095144/3286260
41 | public static long NextLong(this Random random, long min, long max)
42 | {
43 | if (max <= min)
44 | throw new ArgumentOutOfRangeException("max", "max must be > min!");
45 |
46 | //Working with ulong so that modulo works correctly with values > long.MaxValue
47 | ulong uRange = (ulong)(max - min);
48 |
49 | //Prevent a modolo bias; see https://stackoverflow.com/a/10984975/238419
50 | //for more information.
51 | //In the worst case, the expected number of calls is 2 (though usually it's
52 | //much closer to 1) so this loop doesn't really hurt performance at all.
53 | ulong ulongRand;
54 | do
55 | {
56 | byte[] buf = new byte[8];
57 | random.NextBytes(buf);
58 | ulongRand = (ulong)BitConverter.ToInt64(buf, 0);
59 | } while (ulongRand > ulong.MaxValue - ((ulong.MaxValue % uRange) + 1) % uRange);
60 |
61 | return (long)(ulongRand % uRange) + min;
62 | }
63 |
64 | public static bool NextBool(this Random random)
65 | {
66 | return random.NextSingle() >= 0.5;
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/src/ConsoleApp1/Tests/GZip.cs:
--------------------------------------------------------------------------------
1 | internal class GZip
2 | {
3 | public static void Run()
4 | {
5 |
6 |
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/src/ConsoleApp1/Tests/StructTests.cs:
--------------------------------------------------------------------------------
1 | //public ref struct Token
2 | //{
3 | // public ReadOnlySpan Value;
4 |
5 | // public Token(ReadOnlySpan value)
6 | // {
7 | // this.Value = value;
8 | // }
9 | //}
10 |
11 |
12 | //internal class Tokenizer
13 | //{
14 | // public Tokenizer(string source)
15 | // {
16 |
17 | // }
18 |
19 | // // nao anda
20 | // public Token GetCurrent(bool eatWhitespace)
21 | // {
22 |
23 | // }
24 |
25 | // // anda
26 | // public Token GetNext(bool eatWhitespace)
27 | // {
28 | // }
29 |
30 | // // nao anda
31 | // public Token LookAhead(bool eatWhitespace, int count)
32 | // {
33 | // }
34 |
35 | //}
36 |
--------------------------------------------------------------------------------
/src/ConsoleApp1/_Imports.cs:
--------------------------------------------------------------------------------
1 | global using LiteDB;
2 | global using LiteDB.Engine;
3 | global using static LiteDB.Constants;
4 | global using static LiteDB.BsonExpression;
5 | global using System.Diagnostics;
6 | global using System.Runtime.InteropServices;
--------------------------------------------------------------------------------
/src/LiteDB.Benchmark/LiteDB.Benchmark.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 | enable
7 | enable
8 | true
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/LiteDB.Benchmark/Program.cs:
--------------------------------------------------------------------------------
1 | BenchmarkRunner.Run();
2 |
--------------------------------------------------------------------------------
/src/LiteDB.Benchmark/Tests/BsonExpressionTests.cs:
--------------------------------------------------------------------------------
1 | [RPlotExporter]
2 | [MemoryDiagnoser]
3 | public class BsonExpressionTests
4 | {
5 | private BsonDocument _doc = new() { ["a"] = 34m };
6 |
7 | [Benchmark]
8 | public void ExprExec() => BsonExpression.Create("(45 + 12 * a) > 99").Execute(_doc);
9 |
10 | [Benchmark]
11 | public void ExprParseCompileExec() => BsonExpressionParser.ParseFullExpression(new Tokenizer("(45 + 12 * a) > 99"), true).Execute(_doc);
12 | }
13 |
--------------------------------------------------------------------------------
/src/LiteDB.Benchmark/_Imports.cs:
--------------------------------------------------------------------------------
1 | global using System;
2 | global using System.Globalization;
3 | global using System.Collections;
4 | global using System.Collections.Generic;
5 | global using System.Collections.Concurrent;
6 | global using System.Text;
7 | global using System.Threading;
8 | global using System.Threading.Tasks;
9 | global using System.Linq;
10 | global using System.IO;
11 | global using System.Text.RegularExpressions;
12 | global using System.Runtime.InteropServices;
13 | global using System.Runtime.CompilerServices;
14 | global using System.Security;
15 | global using System.Diagnostics;
16 | global using System.Security.Cryptography;
17 | global using System.Reflection;
18 | global using System.Buffers;
19 | global using System.Buffers.Binary;
20 |
21 | global using BenchmarkDotNet.Attributes;
22 | global using BenchmarkDotNet.Running;
23 |
24 | global using LiteDB;
25 | global using LiteDB.Engine;
26 | global using static LiteDB.Constants;
27 | global using static LiteDB.LiteException;
28 |
29 |
--------------------------------------------------------------------------------
/src/LiteDB.Generator.Tests/LiteDB.Generator.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6.0
6 | enable
7 | enable
8 | True
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/src/LiteDB.Generator.Tests/Program.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | public class Program
4 | {
5 | public static void Main(string[] args)
6 | {
7 |
8 | }
9 | }
10 |
11 |
12 |
13 | [AutoInterface(typeof(IDisposable))]
14 | public unsafe class DiskService : IDiskService
15 | {
16 | public DiskService()
17 | {
18 | }
19 |
20 | public void Dispose()
21 | {
22 | throw new NotImplementedException();
23 | }
24 |
25 | public Stream RendStreamReader()
26 | {
27 | throw new NotImplementedException();
28 | }
29 |
30 | public void ReturnReader(Stream stream)
31 | {
32 | }
33 | }
34 |
35 | [AutoInterface]
36 | public class StreamPool : IStreamPool
37 | {
38 | public StreamPool(int limit)
39 | {
40 | }
41 |
42 | public Stream RendStreamReader()
43 | {
44 | return new MemoryStream();
45 | }
46 |
47 | public void ReturnReader(Stream stream)
48 | {
49 | }
50 |
51 | }
--------------------------------------------------------------------------------
/src/LiteDB.Generator.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.4.33213.308
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LiteDB.Generator", "LiteDB.Generator\LiteDB.Generator.csproj", "{450640F8-A2BF-4DED-A3B8-232D588E8ACA}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LiteDB.Generator.Tests", "LiteDB.Generator.Tests\LiteDB.Generator.Tests.csproj", "{948EA78B-0083-499D-BA3F-AAE95A585207}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {450640F8-A2BF-4DED-A3B8-232D588E8ACA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {450640F8-A2BF-4DED-A3B8-232D588E8ACA}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {450640F8-A2BF-4DED-A3B8-232D588E8ACA}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {450640F8-A2BF-4DED-A3B8-232D588E8ACA}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {948EA78B-0083-499D-BA3F-AAE95A585207}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {948EA78B-0083-499D-BA3F-AAE95A585207}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {948EA78B-0083-499D-BA3F-AAE95A585207}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {948EA78B-0083-499D-BA3F-AAE95A585207}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {1AE6FEF2-9F53-4CC5-A9EC-ECC923DED05D}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/src/LiteDB.Generator/LiteDB.Generator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2
5 | 11.0
6 | enable
7 | true
8 | false
9 | false
10 | true
11 |
12 |
13 |
14 |
15 |
16 |
17 | runtime; build; native; contentfiles; analyzers; buildtransitive
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/src/LiteDB.Generator/LiteGenerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.CodeDom.Compiler;
3 | using System.Collections.Generic;
4 | using System.Diagnostics;
5 | using System.Globalization;
6 | using System.IO;
7 | using System.Linq;
8 | using System.Text;
9 | using System.Threading;
10 | using Microsoft.CodeAnalysis;
11 | using Microsoft.CodeAnalysis.CSharp;
12 | using Microsoft.CodeAnalysis.Text;
13 |
14 | namespace LiteDB.Generator;
15 |
16 | [Generator]
17 | public class LiteGenerator : ISourceGenerator
18 | {
19 | public void Initialize(GeneratorInitializationContext context)
20 | {
21 | context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
22 | }
23 |
24 | public void Execute(GeneratorExecutionContext context)
25 | {
26 | try
27 | {
28 | ExecuteCore(context);
29 | }
30 | catch (Exception exception)
31 | {
32 | RaiseExceptionDiagnostic(context, exception);
33 | }
34 | }
35 |
36 | private void ExecuteCore(GeneratorExecutionContext context)
37 | {
38 | // adding AutoInterfaceAttribute to source code
39 | context.AddSource(
40 | Attributes.AutoInterfaceClassname,
41 | SourceText.From(Attributes.AttributesSourceCode, Encoding.UTF8));
42 |
43 | var codeBase = new CodeBase(context);
44 |
45 | InterfaceGen.GenerateCode(codeBase);
46 | }
47 |
48 | private static void RaiseExceptionDiagnostic(GeneratorExecutionContext context, Exception exception)
49 | {
50 | var descriptor = new DiagnosticDescriptor(
51 | "InterfaceGenerator.CriticalError",
52 | $"Exception thrown in InterfaceGenerator",
53 | $"{exception.GetType().FullName} {exception.Message} {exception.StackTrace.Trim()}",
54 | "InterfaceGenerator",
55 | DiagnosticSeverity.Error,
56 | true,
57 | customTags: WellKnownDiagnosticTags.AnalyzerException);
58 |
59 | var diagnostic = Diagnostic.Create(descriptor, null);
60 |
61 | context.ReportDiagnostic(diagnostic);
62 | }
63 | }
--------------------------------------------------------------------------------
/src/LiteDB.Generator/Utils/AttributeDataExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Generator;
2 |
3 | internal static class AttributeDataExtensions
4 | {
5 | public static string? GetNamedParamValue(this AttributeData attributeData, string paramName)
6 | {
7 | var pair = attributeData.NamedArguments.FirstOrDefault(x => x.Key == paramName);
8 | return pair.Value.Value?.ToString();
9 | }
10 |
11 | public static string? GetConstructorValue(this AttributeData attributeData)
12 | {
13 | var value = attributeData.ConstructorArguments.FirstOrDefault();
14 | return value.Value?.ToString();
15 | }
16 | }
--------------------------------------------------------------------------------
/src/LiteDB.Generator/Utils/Attributes.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Generator;
2 |
3 | internal class Attributes
4 | {
5 | public const string AttributesNamespace = "LiteDB";
6 |
7 | public const string AutoInterfaceClassname = "AutoInterfaceAttribute";
8 |
9 | public static readonly string AttributesSourceCode = $@"
10 |
11 | using System;
12 | using System.Diagnostics;
13 |
14 | #nullable enable
15 |
16 | namespace {AttributesNamespace}
17 | {{
18 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false)]
19 | [Conditional(""CodeGeneration"")]
20 | internal sealed class {AutoInterfaceClassname} : Attribute
21 | {{
22 | public {AutoInterfaceClassname}()
23 | {{
24 | }}
25 |
26 | public {AutoInterfaceClassname}(Type inheritInterface)
27 | {{
28 | }}
29 | }}
30 | }}
31 | ";
32 | }
--------------------------------------------------------------------------------
/src/LiteDB.Generator/Utils/CompilerServices.cs:
--------------------------------------------------------------------------------
1 | namespace System.Runtime.CompilerServices;
2 |
3 | ///
4 | /// Class definition for works with "init;" keyword in .netstandard project
5 | ///
6 | public class IsExternalInit
7 | {
8 | }
--------------------------------------------------------------------------------
/src/LiteDB.Generator/Utils/DEBUG.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 |
3 | public static class SourceGenDebugger
4 | {
5 | [Conditional("DEBUG")]
6 | public static void DEBUG()
7 | {
8 | #if DEBUG
9 | if (!Debugger.IsAttached)
10 | {
11 | // sadly this is Windows only so as of now :(
12 | //Debugger.Launch();
13 | }
14 | #endif
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/LiteDB.Generator/Utils/SyntaxReceiver.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Generator;
2 |
3 | internal class SyntaxReceiver : ISyntaxReceiver
4 | {
5 | public IList CandidateTypes { get; } = new List();
6 |
7 | public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
8 | {
9 | if (syntaxNode is TypeDeclarationSyntax typeDeclarationSyntax &&
10 | IsClassOrRecord(typeDeclarationSyntax) &&
11 | typeDeclarationSyntax.AttributeLists.Count > 0)
12 | {
13 | this.CandidateTypes.Add(typeDeclarationSyntax);
14 | }
15 | }
16 |
17 | private static bool IsClassOrRecord(TypeDeclarationSyntax typeDeclarationSyntax)
18 | {
19 | return typeDeclarationSyntax is ClassDeclarationSyntax || typeDeclarationSyntax is RecordDeclarationSyntax;
20 | }
21 | }
--------------------------------------------------------------------------------
/src/LiteDB.Generator/Utils/TypeParameterSymbolExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Generator;
2 |
3 | internal static class TypeParameterSymbolExtensions
4 | {
5 | public static IEnumerable EnumGenericConstraints(this ITypeParameterSymbol symbol)
6 | {
7 | // the class/struct/unmanaged/notnull constraint has to be the last
8 | if (symbol.HasNotNullConstraint)
9 | {
10 | yield return "notnull";
11 | }
12 |
13 | if (symbol.HasValueTypeConstraint)
14 | {
15 | yield return "struct";
16 | }
17 |
18 | if (symbol.HasUnmanagedTypeConstraint)
19 | {
20 | yield return "unmanaged";
21 | }
22 |
23 | if (symbol.HasReferenceTypeConstraint)
24 | {
25 | yield return symbol.ReferenceTypeConstraintNullableAnnotation == NullableAnnotation.Annotated
26 | ? "class?"
27 | : "class";
28 | }
29 |
30 | // types go in the middle
31 | foreach (var constraintType in symbol.ConstraintTypes)
32 | {
33 | yield return constraintType.ToDisplayString();
34 | }
35 |
36 | // the new() constraint has to be the last
37 | if (symbol.HasConstructorConstraint)
38 | {
39 | yield return "new()";
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/src/LiteDB.Generator/_Imports.cs:
--------------------------------------------------------------------------------
1 | global using System;
2 | global using System.Linq;
3 | global using System.Text;
4 | global using System.Collections.Generic;
5 | global using System.IO;
6 |
7 | global using Microsoft.CodeAnalysis;
8 | global using Microsoft.CodeAnalysis.CSharp;
9 | global using Microsoft.CodeAnalysis.CSharp.Syntax;
10 | global using Microsoft.CodeAnalysis.Text;
11 |
12 | global using static SourceGenDebugger;
13 |
--------------------------------------------------------------------------------
/src/LiteDB.Tests/Expressions/BsonExpressions_TypeAndIsPredicate_Tests.cs:
--------------------------------------------------------------------------------
1 | using static LiteDB.BsonExpression;
2 |
3 | namespace LiteDB.Tests.Expressions;
4 |
5 | public class BsonExpressions_TypeAndIsPredicate_Tests
6 | {
7 | [Theory]
8 | [InlineData("21", BsonExpressionType.Constant, false)]
9 | [InlineData("2.6", BsonExpressionType.Constant, false)]
10 | [InlineData("'string'", BsonExpressionType.Constant, false)]
11 | [InlineData("2+1", BsonExpressionType.Add, false)]
12 | [InlineData("2-1", BsonExpressionType.Subtract, false)]
13 | [InlineData("2*1", BsonExpressionType.Multiply, false)]
14 | [InlineData("2/1", BsonExpressionType.Divide, false)]
15 | [InlineData("[1,2,3]", BsonExpressionType.Array, false)]
16 | [InlineData("1=1", BsonExpressionType.Equal, true)]
17 | [InlineData("2!=1", BsonExpressionType.NotEqual, true)]
18 | [InlineData("2>1", BsonExpressionType.GreaterThan, true)]
19 | [InlineData("2>=1", BsonExpressionType.GreaterThanOrEqual, true)]
20 | [InlineData("1<2", BsonExpressionType.LessThan, true)]
21 | [InlineData("1<=2", BsonExpressionType.LessThanOrEqual, true)]
22 | [InlineData("@p0", BsonExpressionType.Parameter, false)]
23 | [InlineData("UPPER(@p0)", BsonExpressionType.Call, false)]
24 | [InlineData("'LiteDB' LIKE 'L%'", BsonExpressionType.Like, true)]
25 | [InlineData("7 BETWEEN 4 AND 10", BsonExpressionType.Between, true)]
26 | [InlineData("7 IN [1,4,7]", BsonExpressionType.In, true)]
27 | [InlineData("true AND true", BsonExpressionType.And, false)]
28 | [InlineData("true OR false", BsonExpressionType.Or, false)]
29 | [InlineData("arr=>@", BsonExpressionType.Map, false)]
30 | public void BsonExpressionTypeANDIsPredicate_Theory(string exp, BsonExpressionType type, bool isPredicate)
31 | {
32 | var expr = Create(exp);
33 | expr.Type.Should().Be(type);
34 | expr.IsPredicate.Should().Be(isPredicate);
35 | }
36 |
37 | }
--------------------------------------------------------------------------------
/src/LiteDB.Tests/Internals/Engine/Helpers/MockEnumerator.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Tests.Internals.Engine;
2 |
3 | internal class MockEnumerator : IPipeEnumerator
4 | {
5 | private readonly Queue _items;
6 |
7 | public MockEnumerator(IEnumerable values)
8 | {
9 | _items = new Queue(values);
10 | }
11 |
12 | public PipeEmit Emit => new(true, true, true);
13 |
14 | public PipeValue MoveNext(PipeContext context)
15 | {
16 | if (_items.Count == 0) return PipeValue.Empty;
17 |
18 | var item = _items.Dequeue();
19 |
20 | return item;
21 | }
22 |
23 | public void GetPlan(ExplainPlainBuilder builder, int deep)
24 | {
25 | throw new NotImplementedException();
26 | }
27 |
28 | public void Dispose()
29 | {
30 | }
31 | }
--------------------------------------------------------------------------------
/src/LiteDB.Tests/Internals/Engine/Query/Indexes/IndexEqualsEnumerator_Tests.cs:
--------------------------------------------------------------------------------
1 | //using NSubstitute;
2 | //using System.Threading.Tasks;
3 |
4 | //namespace LiteDB.Tests.Internals.Engine;
5 |
6 | //public class IndexEqualsEnumerator_Tests
7 | //{
8 | // private readonly IndexDocument _doc = Substitute.For();
9 | // private readonly IIndexService _indexService = new MockIndexService();
10 |
11 | // [Fact]
12 | // public void MoveNextAsync()
13 | // {
14 | // #region Arrange
15 | // var indexDocument = new IndexDocument()
16 | // {
17 | // Slot = 0,
18 | // Name = "_id",
19 | // Expression = "$._id",
20 | // Unique = true,
21 | // HeadIndexNodeID = new RowID(0, 1),
22 | // TailIndexNodeID = new RowID(10, 1)
23 |
24 | // };
25 | // var _sut = new IndexEqualsEnumerator(1, indexDocument, Collation.Default);
26 |
27 | // var pipeContext = new PipeContext(null, _indexService, new BsonDocument());
28 |
29 | // #endregion
30 |
31 |
32 | // #region Act
33 | // var value = _sut.MoveNextAsync(pipeContext);
34 | // #endregion
35 |
36 |
37 | // #region Asserts
38 | // value.Should().Be(new RowID(0, 0));
39 | // #endregion
40 | // }
41 | //}
--------------------------------------------------------------------------------
/src/LiteDB.Tests/LiteDB.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net7
5 | LiteDB.Tests
6 | LiteDB.Tests
7 | Maurício David
8 | MIT
9 | en-US
10 | false
11 | 1701;1702;1705;1591;0618
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 | all
22 | runtime; build; native; contentfiles; analyzers; buildtransitive
23 |
24 |
25 |
26 | all
27 | runtime; build; native; contentfiles; analyzers; buildtransitive
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/src/LiteDB.Tests/_Imports.cs:
--------------------------------------------------------------------------------
1 | global using System;
2 | global using System.Globalization;
3 | global using System.Collections;
4 | global using System.Collections.Generic;
5 | global using System.Collections.Concurrent;
6 | global using System.Text;
7 | global using System.Threading;
8 | global using System.Threading.Tasks;
9 | global using System.Linq;
10 | global using System.IO;
11 | global using System.Text.RegularExpressions;
12 | global using System.Runtime.InteropServices;
13 | global using System.Runtime.CompilerServices;
14 | global using System.Security;
15 | global using System.Diagnostics;
16 | global using System.Security.Cryptography;
17 | global using System.Reflection;
18 | global using System.Buffers;
19 | global using System.Buffers.Binary;
20 |
21 | global using LiteDB;
22 | global using LiteDB.Engine;
23 | global using static LiteDB.Constants;
24 | global using static LiteDB.LiteException;
25 |
26 | global using Xunit;
27 | global using NSubstitute;
28 | global using FluentAssertions;
29 | global using Bogus;
--------------------------------------------------------------------------------
/src/LiteDB/.editorconfig:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mbdavid/LiteDB-vNext/2941736fcf4e8efb23e8c8228a6d6532e361702a/src/LiteDB/.editorconfig
--------------------------------------------------------------------------------
/src/LiteDB/Document/Bson/BsonReadResult.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | public readonly struct BsonReadResult : IIsEmpty
4 | {
5 | public static readonly BsonReadResult Empty = new();
6 |
7 | private readonly BsonValue? _value;
8 | private readonly Exception? _exception;
9 |
10 | public BsonValue Value => _value!;
11 | public Exception Exception => _exception!;
12 |
13 | public bool Ok => _exception is null;
14 | public bool Fail => _exception is not null;
15 |
16 | public bool IsEmpty => _value is null;
17 |
18 | public BsonReadResult()
19 | {
20 | _value = null;
21 | _exception = null;
22 | }
23 |
24 | public BsonReadResult(BsonValue value)
25 | {
26 | _value = value;
27 | _exception = null;
28 | }
29 |
30 | public BsonReadResult(Exception ex)
31 | {
32 | _value = default;
33 | _exception = ex;
34 | }
35 |
36 | public BsonReadResult(BsonValue value, Exception ex)
37 | {
38 | _value = value;
39 | _exception = ex;
40 | }
41 |
42 | public static implicit operator BsonReadResult(BsonValue value) => new (value);
43 |
44 | public static implicit operator BsonReadResult(Exception ex) => new (ex);
45 |
46 | public override string ToString()
47 | {
48 | return IsEmpty ? "" : Dump.Object(new { Ok, Value = Value.ToString() });
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/LiteDB/Document/Bson/BsonTypeCode.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// Represent BSON data type in disk only (used when serialize/deserialize).
5 | /// Do not change values in current FILE_VERSION (can be added)
6 | /// Must use 5 bits only (3 bis are reserved)
7 | ///
8 | public enum BsonTypeCode : byte
9 | {
10 | MinValue = 0,
11 |
12 | Null = 1,
13 |
14 | Int32 = 2,
15 | Int64 = 3,
16 | Double = 4,
17 | Decimal = 5,
18 |
19 | String = 6,
20 |
21 | Document = 7,
22 | Array = 8,
23 |
24 | Binary = 9,
25 | ObjectId = 10,
26 | Guid = 11,
27 | DateTime = 12,
28 | // reserved for DateTimeOffset
29 | False = 20, // Boolean False
30 | True = 21, // Boolean True
31 |
32 | MaxValue = 31, // use 5 bits max (keep 3 for extends)
33 |
34 | }
--------------------------------------------------------------------------------
/src/LiteDB/Document/Json/JsonTokenizer/JsonToken.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// Represent a single string token
5 | ///
6 | internal struct JsonToken
7 | {
8 | public JsonToken(JsonTokenType tokenType, string value, long position)
9 | {
10 | this.Position = position;
11 | this.Value = value;
12 | this.Type = tokenType;
13 | }
14 |
15 | public JsonTokenType Type { get; private set; }
16 | public string Value { get; private set; }
17 | public long Position { get; private set; }
18 |
19 | ///
20 | /// Expect for one of the types (if not, throw UnexpectedToken)
21 | ///
22 | public JsonToken Expect(params JsonTokenType[] types)
23 | {
24 | foreach(var type in types)
25 | {
26 | if (this.Type == type)
27 | {
28 | return this;
29 | }
30 | }
31 | throw new Exception();
32 | }
33 |
34 | public bool Is(string value, bool ignoreCase = true)
35 | {
36 | return
37 | this.Type == JsonTokenType.Word &&
38 | value.Equals(this.Value, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal);
39 | }
40 |
41 | public override string ToString()
42 | {
43 | return $"{Value} ({Type})";
44 | }
45 | }
--------------------------------------------------------------------------------
/src/LiteDB/Document/Json/JsonTokenizer/JsonTokenType.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal enum JsonTokenType
4 | {
5 | /// {
6 | OpenBrace,
7 | /// }
8 | CloseBrace,
9 | /// [
10 | OpenBracket,
11 | /// ]
12 | CloseBracket,
13 | /// ,
14 | Comma,
15 | /// :
16 | Colon,
17 | /// ;
18 | SemiColon,
19 | /// .
20 | Period,
21 | /// -
22 | Minus,
23 | /// +
24 | Plus,
25 | /// *
26 | Asterisk,
27 | /// /
28 | Slash,
29 | /// \
30 | Backslash,
31 | /// "..." or '...'
32 | String,
33 | /// [0-9]+
34 | Int,
35 | /// [0-9]+.[0-9]
36 | Double,
37 | /// \n\r\t \u0032
38 | Whitespace,
39 | /// [a-Z_$]+[a-Z0-9_$]
40 | Word,
41 | EOF,
42 | Unknown
43 | }
--------------------------------------------------------------------------------
/src/LiteDB/Document/ObjectModel/BsonAutoId.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// All supported BsonTypes supported in AutoId insert operation
5 | ///
6 | public enum BsonAutoId : byte
7 | {
8 | Int32 = 2,
9 | Int64 = 3,
10 | ObjectId = 10,
11 | Guid = 11
12 | }
--------------------------------------------------------------------------------
/src/LiteDB/Document/ObjectModel/BsonBinary.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// Represent a Binary value (byte array) in Bson object model
5 | ///
6 | public class BsonBinary : BsonValue
7 | {
8 | public byte[] Value { get; }
9 |
10 | public BsonBinary(byte[] value)
11 | {
12 | this.Value = value ?? throw new ArgumentNullException(nameof(value));
13 | }
14 |
15 | public override BsonType Type => BsonType.Binary;
16 |
17 | public override int GetBytesCount() => this.Value.Length;
18 |
19 | public override int GetHashCode() => this.Value.GetHashCode();
20 |
21 | #region Implement CompareTo
22 |
23 | public override int CompareTo(BsonValue other, Collation collation)
24 | {
25 | if (other is BsonBinary otherBinary) return this.Value.AsSpan().SequenceCompareTo(otherBinary.Value);
26 |
27 | return this.CompareType(other);
28 | }
29 |
30 | #endregion
31 | }
32 |
--------------------------------------------------------------------------------
/src/LiteDB/Document/ObjectModel/BsonBoolean.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// Represent a Boolean value in Bson object model
5 | ///
6 | internal class BsonBoolean : BsonValue
7 | {
8 | public static readonly BsonBoolean True = new (true);
9 | public static readonly BsonBoolean False = new (false);
10 |
11 | public bool Value { get; }
12 |
13 | public BsonBoolean(bool value)
14 | {
15 | this.Value = value;
16 | }
17 |
18 | public override BsonType Type => BsonType.Boolean;
19 |
20 | public override int GetBytesCount() => 0; // use 2 different BsonTypeCode for true|false
21 |
22 | public override int GetHashCode() => this.Value.GetHashCode();
23 |
24 | #region Implement CompareTo
25 |
26 | public override int CompareTo(BsonValue other, Collation collation)
27 | {
28 | if (other is BsonBoolean otherBoolean) return this.Value.CompareTo(otherBoolean.Value);
29 |
30 | return this.CompareType(other);
31 | }
32 |
33 | #endregion
34 |
35 | #region Convert Types
36 |
37 | public override bool ToBoolean() => this.Value;
38 |
39 | public override int ToInt32() => this.Value ? 1 : 0;
40 |
41 | public override long ToInt64() => this.Value ? 1 : 0;
42 |
43 | public override double ToDouble() => this.Value ? 1 : 0;
44 |
45 | public override decimal ToDecimal() => this.Value ? 1 : 0;
46 |
47 | #endregion
48 | }
49 |
--------------------------------------------------------------------------------
/src/LiteDB/Document/ObjectModel/BsonDateTime.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// Represent a DateTime value in Bson object model
5 | ///
6 | internal class BsonDateTime : BsonValue
7 | {
8 | public static readonly DateTime UnixEpoch = new (1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
9 |
10 | public DateTime Value { get; }
11 |
12 | public BsonDateTime(DateTime value)
13 | {
14 | this.Value = value;
15 | }
16 |
17 | public override BsonType Type => BsonType.DateTime;
18 |
19 | public override int GetBytesCount() => 8;
20 |
21 | public override int GetHashCode() => this.Value.GetHashCode();
22 |
23 | #region Implement CompareTo
24 |
25 | public override int CompareTo(BsonValue other, Collation collation)
26 | {
27 | if (other is BsonDateTime otherDateTime) return this.Value.CompareTo(otherDateTime.Value);
28 |
29 | return this.CompareType(other);
30 | }
31 |
32 | #endregion
33 | }
34 |
--------------------------------------------------------------------------------
/src/LiteDB/Document/ObjectModel/BsonDecimal.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// Represent an integer value in Bson object model
5 | ///
6 | internal class BsonDecimal : BsonValue
7 | {
8 | public static BsonDecimal Zero = new(0);
9 | public static BsonDecimal One = new(1);
10 | public static BsonDecimal MinusOne = new(-1);
11 |
12 | public decimal Value { get; }
13 |
14 | public BsonDecimal(decimal value)
15 | {
16 | this.Value = value;
17 | }
18 |
19 | public override BsonType Type => BsonType.Decimal;
20 |
21 | public override int GetBytesCount() => sizeof(decimal);
22 |
23 | public override int GetHashCode() => this.Value.GetHashCode();
24 |
25 | #region Implement CompareTo
26 |
27 | public override int CompareTo(BsonValue other, Collation collation)
28 | {
29 | if (other is BsonDecimal otherDecimal) return this.Value.CompareTo(otherDecimal.Value);
30 | if (other is BsonInt32 otherInt32) return this.Value.CompareTo(otherInt32.ToDecimal());
31 | if (other is BsonInt64 otherInt64) return this.Value.CompareTo(otherInt64.ToDecimal());
32 | if (other is BsonDouble otherDouble) return this.Value.CompareTo(otherDouble.ToDecimal());
33 |
34 | return this.CompareType(other);
35 | }
36 |
37 | #endregion
38 |
39 | #region Convert Types
40 |
41 | public override bool ToBoolean() => this.Value != 0;
42 |
43 | public override int ToInt32() => Convert.ToInt32(this.Value);
44 |
45 | public override long ToInt64() => Convert.ToInt64(this.Value);
46 |
47 | public override double ToDouble() => Convert.ToDouble(this.Value);
48 |
49 | public override decimal ToDecimal() => this.Value;
50 |
51 | #endregion
52 | }
53 |
--------------------------------------------------------------------------------
/src/LiteDB/Document/ObjectModel/BsonDouble.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// Represent a double value in Bson object model
5 | ///
6 | internal class BsonDouble : BsonValue
7 | {
8 | public static BsonDouble Zero = new(0);
9 | public static BsonDouble One = new(1);
10 | public static BsonDouble MinusOne = new(-1);
11 |
12 | public double Value { get; }
13 |
14 | public BsonDouble(double value)
15 | {
16 | this.Value = value;
17 | }
18 |
19 | public override BsonType Type => BsonType.Double;
20 |
21 | public override int GetBytesCount() => sizeof(double);
22 |
23 | public override int GetHashCode() => this.Value.GetHashCode();
24 |
25 | #region Implement CompareTo
26 |
27 | public override int CompareTo(BsonValue other, Collation collation)
28 | {
29 | if (other is BsonDouble otherDouble) return this.Value.CompareTo(otherDouble.Value);
30 | if (other is BsonInt32 otherInt32) return this.Value.CompareTo(otherInt32.ToDouble());
31 | if (other is BsonInt64 otherInt64) return this.Value.CompareTo(otherInt64.ToDouble());
32 | if (other is BsonDecimal otherDecimal) return this.ToDecimal().CompareTo(otherDecimal.Value);
33 |
34 | return this.CompareType(other);
35 | }
36 |
37 | #endregion
38 |
39 | #region Convert Types
40 |
41 | public override bool ToBoolean() => this.Value != 0;
42 |
43 | public override int ToInt32() => Convert.ToInt32(this.Value);
44 |
45 | public override long ToInt64() => Convert.ToInt64(this.Value);
46 |
47 | public override double ToDouble() => this.Value;
48 |
49 | public override decimal ToDecimal() => Convert.ToDecimal(this.Value);
50 |
51 | #endregion
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/LiteDB/Document/ObjectModel/BsonGuid.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// Represent a Guid value in Bson object model
5 | ///
6 | internal class BsonGuid : BsonValue
7 | {
8 | public static BsonGuid Empty = new (Guid.Empty);
9 |
10 | public Guid Value { get; }
11 |
12 | public BsonGuid(Guid value)
13 | {
14 | if (value == null) throw new ArgumentNullException(nameof(value));
15 |
16 | this.Value = value;
17 | }
18 |
19 | public override BsonType Type => BsonType.Guid;
20 |
21 | public override int GetBytesCount() => 16;
22 |
23 | public override int GetHashCode() => this.Value.GetHashCode();
24 |
25 | #region Implement CompareTo
26 |
27 | public override int CompareTo(BsonValue other, Collation collation)
28 | {
29 | if (other is BsonGuid otherGuid) return this.Value.CompareTo(otherGuid.Value);
30 |
31 | return this.CompareType(other);
32 | }
33 |
34 | #endregion
35 | }
36 |
--------------------------------------------------------------------------------
/src/LiteDB/Document/ObjectModel/BsonInt32.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// Represent an integer value in Bson object model
5 | ///
6 | public class BsonInt32 : BsonValue
7 | {
8 | public static BsonInt32 Zero = new(0);
9 | public static BsonInt32 One = new(1);
10 | public static BsonInt32 MinusOne = new(-1);
11 |
12 | public int Value { get; }
13 |
14 | public BsonInt32(int value)
15 | {
16 | this.Value = value;
17 | }
18 |
19 | public override BsonType Type => BsonType.Int32;
20 |
21 | public override int GetBytesCount() => sizeof(int);
22 |
23 | public override int GetHashCode() => this.Value.GetHashCode();
24 |
25 | #region Implement CompareTo
26 |
27 | public override int CompareTo(BsonValue other, Collation collation)
28 | {
29 | if (other is BsonInt32 otherInt32) return this.Value.CompareTo(otherInt32.Value);
30 | if (other is BsonInt64 otherInt64) return this.ToInt64().CompareTo(otherInt64.Value);
31 | if (other is BsonDouble otherDouble) return this.ToDouble().CompareTo(otherDouble.Value);
32 | if (other is BsonDecimal otherDecimal) return this.ToDecimal().CompareTo(otherDecimal.Value);
33 |
34 | return this.CompareType(other);
35 | }
36 |
37 | #endregion
38 |
39 | #region Implicit Ctor
40 |
41 | public static implicit operator int(BsonInt32 value) => value.AsInt32;
42 |
43 | public static implicit operator BsonInt32(int value) => value switch
44 | {
45 | -1 => BsonInt32.MinusOne,
46 | 0 => BsonInt32.Zero,
47 | 1 => BsonInt32.One,
48 | _ => new BsonInt32(value),
49 | };
50 |
51 | #endregion
52 |
53 | #region Convert Types
54 |
55 | public override bool ToBoolean() => this.Value != 0;
56 |
57 | public override int ToInt32() => this.Value;
58 |
59 | public override long ToInt64() => this.Value;
60 |
61 | public override double ToDouble() => this.Value;
62 |
63 | public override decimal ToDecimal() => this.Value;
64 |
65 | #endregion
66 | }
67 |
--------------------------------------------------------------------------------
/src/LiteDB/Document/ObjectModel/BsonInt64.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// Represent an Int64 value in Bson object model
5 | ///
6 | internal class BsonInt64 : BsonValue
7 | {
8 | public static BsonInt64 Zero = new(0);
9 | public static BsonInt64 One = new(1);
10 | public static BsonInt64 MinusOne = new(-1);
11 |
12 | public long Value { get; }
13 |
14 | public BsonInt64(long value)
15 | {
16 | this.Value = value;
17 | }
18 |
19 | public override BsonType Type => BsonType.Int64;
20 |
21 | public override int GetBytesCount() => sizeof(long);
22 |
23 | public override int GetHashCode() => this.Value.GetHashCode();
24 |
25 | #region Implement CompareTo
26 |
27 | public override int CompareTo(BsonValue other, Collation collation)
28 | {
29 | if (other is BsonInt64 otherInt64) return this.Value.CompareTo(otherInt64.Value);
30 | if (other is BsonInt32 otherInt32) return this.Value.CompareTo(otherInt32.ToInt64());
31 | if (other is BsonDouble otherDouble) return this.Value.CompareTo(otherDouble.Value);
32 | if (other is BsonDecimal otherDecimal) return this.Value.CompareTo(otherDecimal.Value);
33 |
34 | return this.CompareType(other);
35 | }
36 |
37 | #endregion
38 |
39 | #region Convert Types
40 |
41 | public override bool ToBoolean() => this.Value != 0;
42 |
43 | public override int ToInt32() => Convert.ToInt32(this.Value);
44 |
45 | public override long ToInt64() => this.Value;
46 |
47 | public override double ToDouble() => this.Value;
48 |
49 | public override decimal ToDecimal() => this.Value;
50 |
51 | #endregion
52 | }
53 |
--------------------------------------------------------------------------------
/src/LiteDB/Document/ObjectModel/BsonMaxValue.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// Represent a max value constant in Bson object model
5 | ///
6 | internal class BsonMaxValue : BsonValue
7 | {
8 | public override BsonType Type => BsonType.MaxValue;
9 |
10 | public override int GetBytesCount() => 0;
11 |
12 | public override int GetHashCode() => this.Type.GetHashCode();
13 |
14 | #region Implement CompareTo
15 |
16 | public override int CompareTo(BsonValue other, Collation collation)
17 | {
18 | if (other is BsonMaxValue) return 0;
19 |
20 | return 1; // all types are lower than MaxValue
21 | }
22 |
23 | #endregion
24 | }
25 |
--------------------------------------------------------------------------------
/src/LiteDB/Document/ObjectModel/BsonMinValue.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// Represent a min value constant in Bson object model
5 | ///
6 | internal class BsonMinValue : BsonValue
7 | {
8 | public override BsonType Type => BsonType.MinValue;
9 |
10 | public override int GetBytesCount() => 0;
11 |
12 | public override int GetHashCode() => this.Type.GetHashCode();
13 |
14 | #region Implement CompareTo
15 |
16 | public override int CompareTo(BsonValue other, Collation collation)
17 | {
18 | if (other is BsonMinValue) return 0;
19 |
20 | return -1; // all types are heigher than MinValue
21 | }
22 |
23 | #endregion
24 | }
25 |
--------------------------------------------------------------------------------
/src/LiteDB/Document/ObjectModel/BsonNull.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// Represent a null value constant in Bson object model (BsonNull is a valid value)
5 | ///
6 | internal class BsonNull : BsonValue
7 | {
8 | public override BsonType Type => BsonType.Null;
9 |
10 | public override int GetBytesCount() => 0;
11 |
12 | public override int GetHashCode() => this.Type.GetHashCode();
13 |
14 | #region Implement CompareTo
15 |
16 | public override int CompareTo(BsonValue other, Collation collation)
17 | {
18 | if (other is BsonNull) return 0;
19 |
20 | return this.CompareType(other);
21 | }
22 |
23 | #endregion
24 | }
25 |
--------------------------------------------------------------------------------
/src/LiteDB/Document/ObjectModel/BsonObjectId.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// Represent an ObjectId value (12 bytes sequencial guid-like) in Bson object model
5 | ///
6 | internal class BsonObjectId : BsonValue
7 | {
8 | public static BsonObjectId Empty = new (ObjectId.Empty);
9 |
10 | public ObjectId Value { get; }
11 |
12 | public BsonObjectId(ObjectId value)
13 | {
14 | this.Value = value;
15 | }
16 |
17 | public override BsonType Type => BsonType.ObjectId;
18 |
19 | public override int GetBytesCount() => 12;
20 |
21 | public override int GetHashCode() => this.Value.GetHashCode();
22 |
23 | #region Implement CompareTo
24 |
25 | public override int CompareTo(BsonValue other, Collation collation)
26 | {
27 | if (other is BsonObjectId otherObjectId) return this.Value.CompareTo(otherObjectId.Value);
28 |
29 | return this.CompareType(other);
30 | }
31 |
32 | #endregion
33 | }
34 |
--------------------------------------------------------------------------------
/src/LiteDB/Document/ObjectModel/BsonString.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// Represent a String value in Bson object model
5 | ///
6 | internal class BsonString : BsonValue
7 | {
8 | public static BsonString Emtpy = new("");
9 |
10 | public static BsonString Id = new("_id");
11 |
12 | public string Value { get; }
13 |
14 | public BsonString(string value)
15 | {
16 | this.Value = value ?? throw new ArgumentNullException(nameof(value));
17 | }
18 |
19 | public override BsonType Type => BsonType.String;
20 |
21 | public override int GetBytesCount() => Encoding.UTF8.GetByteCount(this.Value);
22 |
23 | public override int GetHashCode() => this.Value.GetHashCode();
24 |
25 | #region Implement CompareTo
26 |
27 | public override int CompareTo(BsonValue other, Collation collation)
28 | {
29 | if (other is BsonString otherString) return collation.Compare(this.Value, otherString.Value);
30 |
31 | return this.CompareType(other);
32 | }
33 |
34 | #endregion
35 |
36 | #region Implicit Ctor
37 |
38 | public static implicit operator string(BsonString value) => value.Value;
39 |
40 | public static implicit operator BsonString(string value)
41 | {
42 | if (value == null) throw new ArgumentNullException(nameof(value));
43 |
44 | if (value.Length == 0) return Emtpy;
45 |
46 | return new BsonString(value);
47 | }
48 |
49 | #endregion
50 |
51 | #region Convert Types
52 |
53 | public override bool ToBoolean() => Convert.ToBoolean(this.Value);
54 |
55 | public override int ToInt32() => Convert.ToInt32(this.Value, CultureInfo.InvariantCulture.NumberFormat);
56 |
57 | public override long ToInt64() => Convert.ToInt64(this.Value, CultureInfo.InvariantCulture.NumberFormat);
58 |
59 | public override double ToDouble() => Convert.ToDouble(this.Value, CultureInfo.InvariantCulture.NumberFormat);
60 |
61 | public override decimal ToDecimal() => Convert.ToDecimal(this.Value, CultureInfo.InvariantCulture.NumberFormat);
62 |
63 | #endregion
64 | }
65 |
--------------------------------------------------------------------------------
/src/LiteDB/Document/ObjectModel/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 | DateTime = 12,
26 | Boolean = 13,
27 | // 13 reserved for (true|false)
28 |
29 | MaxValue = 31
30 | }
31 |
32 | public static class BsonTypeExtensions
33 | {
34 | ///
35 | /// Checks if BsonType is a numeric data type (can be cast to be compare)
36 | ///
37 | public static bool IsNumeric(this BsonType bsonType) =>
38 | bsonType == BsonType.Int32 ||
39 | bsonType == BsonType.Double ||
40 | bsonType == BsonType.Int64 ||
41 | bsonType == BsonType.Decimal ||
42 | bsonType == BsonType.Boolean;
43 | }
44 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Commands/Engine.Execute.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | public partial class LiteEngine : ILiteEngine
4 | {
5 | public ValueTask ExecuteAsync(string sql)
6 | => this.ExecuteAsync(sql, BsonDocument.Empty);
7 |
8 | public ValueTask ExecuteAsync(string sql, BsonValue args0)
9 | => this.ExecuteAsync(sql, new BsonDocument { ["0"] = args0 });
10 |
11 | public ValueTask ExecuteAsync(string sql, BsonValue args0, BsonValue args1)
12 | => this.ExecuteAsync(sql, new BsonDocument { ["0"] = args0, ["1"] = args1 });
13 |
14 | ///
15 | /// Parse and execute an ah-hoc sql statement
16 | ///
17 | public ValueTask ExecuteAsync(string sql, BsonDocument parameters)
18 | {
19 | var collation = _factory.FileHeader.Collation;
20 | var tokenizer = new Tokenizer(sql);
21 | var parser = new SqlParser(tokenizer, collation);
22 |
23 | var statement = parser.ParseStatement();
24 |
25 | var result = statement.ExecuteAsync(_factory, parameters);
26 |
27 | return result;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Commands/Engine.ExecuteQuery.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | public partial class LiteEngine : ILiteEngine
4 | {
5 | public ValueTask ExecuteReaderAsync(string sql)
6 | => this.ExecuteReaderAsync(sql, BsonDocument.Empty);
7 |
8 | public ValueTask ExecuteReaderAsync(string sql, BsonValue args0)
9 | => this.ExecuteReaderAsync(sql, new BsonDocument { ["0"] = args0 });
10 |
11 | public ValueTask ExecuteReaderAsync(string sql, BsonValue args0, BsonValue args1)
12 | => this.ExecuteReaderAsync(sql, new BsonDocument { ["0"] = args0, ["1"] = args1 });
13 |
14 | ///
15 | /// Parse and execute an ah-hoc sql statement
16 | ///
17 | public ValueTask ExecuteReaderAsync(string sql, BsonDocument parameters)
18 | {
19 | var collation = _factory.FileHeader.Collation;
20 | var tokenizer = new Tokenizer(sql);
21 | var parser = new SqlParser(tokenizer, collation);
22 |
23 | var statement = parser.ParseStatement();
24 |
25 | var reader = statement.ExecuteReaderAsync(_factory, parameters);
26 |
27 | return reader;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Commands/Engine.Open.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | public partial class LiteEngine : ILiteEngine
4 | {
5 | public async Task OpenAsync()
6 | {
7 | var lockService = _factory.LockService;
8 | var diskService = _factory.DiskService;
9 | var logService = _factory.LogService;
10 | var allocationMapService = _factory.AllocationMapService;
11 | var masterService = _factory.MasterService;
12 | var recoveryService = _factory.RecoveryService;
13 |
14 | if (_factory.State != EngineState.Close) throw ERR("must be closed");
15 |
16 | // clean last database exception
17 | _factory.Exception = null;
18 |
19 | // must run in exclusive mode
20 | await lockService.EnterExclusiveAsync();
21 |
22 | if (_factory.State != EngineState.Close) throw ERR("must be closed");
23 |
24 | try
25 | {
26 | var stream = diskService.GetDiskWriter();
27 |
28 | // open/create data file and returns file header
29 | _factory.FileHeader = await diskService.InitializeAsync();
30 |
31 | // checks if datafile was finish correctly
32 | if (_factory.FileHeader.IsDirty)
33 | {
34 | _factory.State = EngineState.Recovery;
35 |
36 | // do a database recovery
37 | await recoveryService.DoRecoveryAsync();
38 |
39 | stream.WriteFlag(FileHeader.P_IS_DIRTY, 0);
40 |
41 | _factory.FileHeader.IsDirty = false;
42 | }
43 |
44 | // initialize log service based on disk
45 | logService.Initialize();
46 |
47 | // initialize AM service
48 | allocationMapService.Initialize();
49 |
50 | // read $master
51 | masterService.Initialize();
52 |
53 | // update header/state
54 | _factory.State = EngineState.Open;
55 |
56 | // release exclusive
57 | lockService.ExitExclusive();
58 |
59 | }
60 | catch (Exception ex)
61 | {
62 | ex.HandleError(_factory);
63 | throw;
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Commands/Engine.Shutdown.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | public partial class LiteEngine : ILiteEngine
4 | {
5 | public async Task ShutdownAsync()
6 | {
7 | if (_factory.State != EngineState.Open) throw ERR("must be open");
8 |
9 | var lockService = _factory.LockService;
10 | var diskService = _factory.DiskService;
11 | var logService = _factory.LogService;
12 | var allocationMapService = _factory.AllocationMapService;
13 | var stream = diskService.GetDiskWriter();
14 |
15 | // must enter in exclusive lock
16 | await lockService.EnterExclusiveAsync();
17 |
18 | // set engine state to shutdown
19 | _factory.State = EngineState.Shutdown;
20 |
21 | // do checkpoint
22 | await logService.CheckpointAsync(true, false);
23 |
24 | // persist all dirty amp into disk
25 | allocationMapService.WriteAllChanges();
26 |
27 | // if file was changed, update file header check byte
28 | if (_factory.FileHeader.IsDirty)
29 | {
30 | stream.WriteFlag(FileHeader.P_IS_DIRTY, 0);
31 |
32 | _factory.FileHeader.IsDirty = false;
33 | }
34 |
35 | // release exclusive
36 | lockService.ExitExclusive();
37 |
38 | // call sync dispose (release disk/memory)
39 | _factory.Dispose();
40 | }
41 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/DocumentStore/DocumentStoreFactory.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | ///
4 | /// Document store factory to cache results
5 | ///
6 | [AutoInterface]
7 | internal class DocumentStoreFactory : IDocumentStoreFactory
8 | {
9 | public IDocumentStore GetUserCollection(string name)
10 | {
11 | //TODO: como reaproveitar? cache direto pode não ser uma boa
12 | return new UserCollectionStore(name);
13 | }
14 |
15 | public IDocumentStore GetVirtualCollection(string name, BsonDocument parameters)
16 | {
17 | //TODO: vai conter SWITCH para decidir
18 | throw new NotImplementedException();
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/DocumentStore/FileJson/FileJsonDocumentStore.cs:
--------------------------------------------------------------------------------
1 | //namespace LiteDB.Engine;
2 |
3 | //internal class SubQueryStore : ISourceStore
4 | //{
5 | // public string Name { get; }
6 | // public byte ColID { get; }
7 | // public IReadOnlyList Indexes { get; }
8 |
9 | // private Query _subQuery;
10 |
11 | // public SubQueryStore(Query query)
12 | // {
13 | // _subQuery = query;
14 | // }
15 |
16 | // internal void Loa0d(IMasterService masterService)
17 | // {
18 | // }
19 |
20 | // public IPipeEnumerator GetPipeEnumerator(BsonExpression expression)
21 | // {
22 | // throw new NotImplementedException();
23 | // }
24 | //}
25 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/DocumentStore/Interfaces/IDataService.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal interface IDataService
4 | {
5 | ///
6 | /// Insert BsonDocument into new data pages
7 | ///
8 | RowID InsertDocument(byte colID, BsonDocument doc);
9 |
10 | ///
11 | /// Update existing document in a single or multiple pages
12 | ///
13 | void UpdateDocument(RowID dataBlockID, BsonDocument doc);
14 |
15 | ///
16 | /// Read a single document in a single/multiple pages
17 | ///
18 | BsonReadResult ReadDocument(RowID dataBlockID, string[] fields);
19 |
20 | ///
21 | /// Delete a full document from a single or multiple pages
22 | ///
23 | void DeleteDocument(RowID dataBlockID);
24 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/DocumentStore/Interfaces/IDocumentStore.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal interface IDocumentStore : IDisposable
4 | {
5 | byte ColID { get; }
6 | string Name { get; }
7 |
8 | ///
9 | /// Should be call this method just after enter in execution on statement. Will load internal collection
10 | ///
11 | void Initialize(IMasterService masterService);
12 |
13 | IReadOnlyList GetIndexes();
14 |
15 | (IDataService dataService, IIndexService indexService) GetServices(IServicesFactory factory, ITransaction transaction);
16 |
17 | IPipeEnumerator GetPipeEnumerator(BsonExpression expression);
18 |
19 | // Dipose will be run in statement dispose
20 | }
21 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/DocumentStore/Interfaces/IIndexService.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal interface IIndexService
4 | {
5 | ///
6 | /// Create head and tail nodes for a new index
7 | ///
8 | (RowID head, RowID tail) CreateHeadTailNodes(byte colID);
9 |
10 | ///
11 | /// Insert a new node index inside an collection index. Flip coin to know level
12 | ///
13 | IndexNodeResult AddNode(byte colID, IndexDocument index, BsonValue key, RowID dataBlockID, IndexNodeResult last, out bool defrag);
14 |
15 | ///
16 | /// Flip coin (skipped list): returns how many levels the node will have (starts in 1, max of INDEX_MAX_LEVELS)
17 | ///
18 | int Flip();
19 |
20 | ///
21 | /// Get a node/pageBuffer inside a page using RowID. IndexNodeID must be a valid position
22 | ///
23 | IndexNodeResult GetNode(RowID indexNodeID);
24 |
25 | ///
26 | /// Find first node that index match with value .
27 | /// If index are unique, return unique value - if index are not unique, return first found (can start, middle or end)
28 | /// If not found but sibling = true and key are not found, returns next value index node (if order = Asc) or prev node (if order = Desc)
29 | ///
30 | IndexNodeResult Find(IndexDocument index, BsonValue key, bool sibling, int order);
31 |
32 | ///
33 | /// Deletes all indexes nodes from pkNode
34 | ///
35 | void DeleteAll(IndexNodeResult nodeResult);
36 | }
37 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/DocumentStore/SubQuery/SubQueryStore.cs:
--------------------------------------------------------------------------------
1 | //namespace LiteDB.Engine;
2 |
3 | //internal class SubQueryStore : ISourceStore
4 | //{
5 | // public string Name { get; }
6 | // public byte ColID { get; }
7 | // public IReadOnlyList Indexes { get; }
8 |
9 | // private Query _subQuery;
10 |
11 | // public SubQueryStore(Query query)
12 | // {
13 | // _subQuery = query;
14 | // }
15 |
16 | // internal void Loa0d(IMasterService masterService)
17 | // {
18 | // }
19 |
20 | // public IPipeEnumerator GetPipeEnumerator(BsonExpression expression)
21 | // {
22 | // throw new NotImplementedException();
23 | // }
24 | //}
25 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/DocumentStore/UserCollection/UserCollectionStore.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal class UserCollectionStore : IDocumentStore
4 | {
5 | private readonly string _name;
6 | private CollectionDocument? _collection; // will load on Initialize()
7 |
8 | public byte ColID => _collection?.ColID ?? 0;
9 | public string Name => _name;
10 | public IReadOnlyList Indexes => _collection?.Indexes ?? (IReadOnlyList)Array.Empty();
11 |
12 |
13 | public UserCollectionStore(string name)
14 | {
15 | _name = name;
16 | }
17 |
18 | public void Initialize(IMasterService masterService)
19 | {
20 | var master = masterService.GetMaster(false);
21 |
22 | if (master.Collections.TryGetValue(_name, out var collection))
23 | {
24 | _collection = collection;
25 | }
26 | else
27 | {
28 | throw ERR($"Collection {_name} does not exist");
29 | }
30 | }
31 |
32 | public IReadOnlyList GetIndexes() =>
33 | _collection!.Indexes;
34 |
35 | public (IDataService dataService, IIndexService indexService) GetServices(IServicesFactory factory, ITransaction transaction) =>
36 | (factory.CreateDataService(transaction), factory.CreateIndexService(transaction));
37 |
38 | public IPipeEnumerator GetPipeEnumerator(BsonExpression expression)
39 | {
40 | throw new NotImplementedException();
41 | }
42 |
43 | public void Dispose()
44 | {
45 | // in user collection, there is nothing to dispose
46 | _collection = null;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/LiteEngine.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | ///
4 | /// A public class that take care of all engine data structure access - it´s basic implementation of a NoSql database
5 | /// Its isolated from complete solution - works on low level only (no linq, no poco... just BSON objects)
6 | /// [ThreadSafe]
7 | ///
8 | [AutoInterface(typeof(IDisposable))]
9 | public partial class LiteEngine : ILiteEngine
10 | {
11 | private readonly IServicesFactory _factory;
12 |
13 | public EngineState State => _factory.State;
14 |
15 | #region Ctor
16 |
17 | ///
18 | /// Initialize LiteEngine using in-memory database
19 | ///
20 | public LiteEngine()
21 | : this(new EngineSettings { DataStream = new MemoryStream() })
22 | {
23 | }
24 |
25 | ///
26 | /// Initialize LiteEngine using file system
27 | ///
28 | public LiteEngine(string filename)
29 | : this (new EngineSettings { Filename = filename })
30 | {
31 | }
32 |
33 | ///
34 | /// Initialize LiteEngine using all engine settings
35 | ///
36 | public LiteEngine(EngineSettings settings)
37 | : this (new ServicesFactory(settings))
38 | {
39 | }
40 |
41 | ///
42 | /// To initialize LiteEngine we need classes factory and engine settings
43 | /// Current version still using IServiceFactory as internal...
44 | ///
45 | internal LiteEngine(IServicesFactory factory)
46 | {
47 | _factory = factory;
48 | }
49 |
50 | #endregion
51 |
52 | // to see all methods, look at /Commands files (partial class from I__LiteEngine)
53 |
54 | public void Dispose()
55 | {
56 | this.Dispose(true);
57 | GC.SuppressFinalize(this);
58 | }
59 |
60 | ~LiteEngine()
61 | {
62 | this.Dispose(false);
63 | }
64 |
65 | protected virtual void Dispose(bool disposing)
66 | {
67 | //if (_disposed) return;
68 |
69 | if (disposing)
70 | {
71 | //_services.CloseAsync().Wait;
72 | }
73 |
74 | //_disposed = true;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/DiskService/StreamFactory/FileSortStreamFactory.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | ///
4 | /// File implementation for sort stream
5 | ///
6 | internal class FileSortStreamFactory : IStreamFactory
7 | {
8 | private readonly string _filename;
9 |
10 | public string Name => Path.GetFileName(_filename);
11 |
12 | public FileSortStreamFactory(string dataFilename)
13 | {
14 | _filename = FileHelper.GetSufixFile(dataFilename, "-sort", true);
15 | }
16 |
17 | public void Delete()
18 | {
19 | File.Delete(_filename);
20 | }
21 |
22 | public bool Exists()
23 | {
24 | return File.Exists(_filename);
25 | }
26 |
27 | public long GetLength()
28 | {
29 | // if file don't exists, returns 0
30 | if (!this.Exists()) return 0;
31 |
32 | // get physical file length from OS
33 | var length = new FileInfo(_filename).Length;
34 |
35 | return length;
36 | }
37 |
38 | ///
39 | /// Get a new stream (open/create) from sort temp
40 | ///
41 | public Stream GetStream(bool canWrite, FileOptions options)
42 | {
43 | var stream = new FileStream(
44 | _filename,
45 | FileMode.OpenOrCreate,
46 | FileAccess.ReadWrite,
47 | FileShare.ReadWrite,
48 | PAGE_SIZE,
49 | FileOptions.Asynchronous);
50 |
51 | return stream;
52 | }
53 |
54 | public bool DisposeOnClose => true;
55 | }
56 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/DiskService/StreamFactory/FileStreamFactory.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal class FileStreamFactory : IStreamFactory
4 | {
5 | private readonly string _filename;
6 | private readonly bool _readOnly;
7 |
8 | public string Name => Path.GetFileName(_filename);
9 |
10 | public FileStreamFactory(string filename, bool readOnly)
11 | {
12 | _filename = filename;
13 | _readOnly = readOnly;
14 | }
15 |
16 | public void Delete()
17 | {
18 | File.Delete(_filename);
19 | }
20 |
21 | public bool Exists()
22 | {
23 | return File.Exists(_filename);
24 | }
25 |
26 | public long GetLength()
27 | {
28 | // if file don't exists, returns 0
29 | if (!this.Exists()) return 0;
30 |
31 | // get physical file length from OS
32 | var length = new FileInfo(_filename).Length;
33 |
34 | return length;
35 | }
36 |
37 | ///
38 | /// Open an existing data file and return FileStream implementation
39 | ///
40 | public Stream GetStream(bool canWrite, FileOptions options)
41 | {
42 | var write = canWrite && (_readOnly == false);
43 |
44 | var stream = new FileStream(
45 | _filename,
46 | _readOnly ? FileMode.Open : FileMode.OpenOrCreate,
47 | write ? FileAccess.ReadWrite : FileAccess.Read,
48 | write ? FileShare.Read : FileShare.ReadWrite,
49 | PAGE_SIZE,
50 | options);
51 |
52 | return stream;
53 | }
54 |
55 | public bool DisposeOnClose => true;
56 | }
57 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/DiskService/StreamFactory/IStreamFactory.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal interface IStreamFactory
4 | {
5 | string Name { get; }
6 | bool Exists();
7 | long GetLength();
8 | void Delete();
9 | Stream GetStream(bool canWrite, FileOptions options);
10 | bool DisposeOnClose { get; }
11 | }
12 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/DiskService/StreamFactory/MemoryStreamFactory.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal class MemoryStreamFactory : IStreamFactory
4 | {
5 | private readonly Stream _stream;
6 |
7 | public string Name => ":memory:";
8 |
9 | public MemoryStreamFactory(Stream stream)
10 | {
11 | _stream = stream;
12 | }
13 |
14 | public void Delete()
15 | {
16 | }
17 |
18 | public bool Exists()
19 | {
20 | return true;
21 | }
22 |
23 | public long GetLength()
24 | {
25 | return _stream.Length;
26 | }
27 |
28 | public Stream GetStream(bool canWrite, FileOptions options)
29 | {
30 | return _stream;
31 | }
32 |
33 | public bool DisposeOnClose => true;
34 | }
35 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/LockService/AsyncReadWriteLock.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | ///
4 | /// Implement multiple readers/single writer for async task
5 | ///
6 | internal class AsyncReaderWriterLock : IDisposable
7 | {
8 | private readonly SemaphoreSlim _readSemaphore = new (1, 1);
9 | private readonly SemaphoreSlim _writeSemaphore = new (1, 1);
10 | private readonly TimeSpan _timeout;
11 | private int _readerCount;
12 |
13 | public int ReaderCount => _readerCount;
14 |
15 | public AsyncReaderWriterLock(TimeSpan timeout)
16 | {
17 | //TODO: antes de usar o timeout, verificar que ao adicionar o metodo Wait retorna bool
18 | _timeout = timeout;
19 | }
20 |
21 | public async ValueTask AcquireWriterLock(CancellationToken token = default)
22 | {
23 | await _writeSemaphore.WaitAsync(_timeout, token).ConfigureAwait(false);
24 |
25 | try
26 | {
27 | await _readSemaphore.WaitAsync(_timeout, token).ConfigureAwait(false);
28 | }
29 | catch
30 | {
31 | _writeSemaphore.Release();
32 | throw;
33 | }
34 | }
35 |
36 | public void ReleaseWriterLock()
37 | {
38 | _readSemaphore.Release();
39 | _writeSemaphore.Release();
40 | }
41 |
42 | public async ValueTask AcquireReaderLock(CancellationToken token = default)
43 | {
44 | await _writeSemaphore.WaitAsync(_timeout, token).ConfigureAwait(false);
45 |
46 | if (Interlocked.Increment(ref _readerCount) == 1)
47 | {
48 | try
49 | {
50 | await _readSemaphore.WaitAsync(_timeout, token).ConfigureAwait(false);
51 | }
52 | catch
53 | {
54 | Interlocked.Decrement(ref _readerCount);
55 | _writeSemaphore.Release();
56 |
57 | throw;
58 | }
59 | }
60 |
61 | _writeSemaphore.Release();
62 | }
63 |
64 | public void ReleaseReaderLock()
65 | {
66 | if (Interlocked.Decrement(ref _readerCount) == 0)
67 | {
68 | _readSemaphore.Release();
69 | }
70 | }
71 |
72 | public void Dispose()
73 | {
74 | _writeSemaphore.Dispose();
75 | _readSemaphore.Dispose();
76 | }
77 | }
78 |
79 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/MasterService/ObjectModel/CollectionDocument.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal class CollectionDocument
4 | {
5 | public required byte ColID { get; init; }
6 | public required string Name { get; set; } // can be changed in RenameCollection
7 | public required List Indexes { get; init; }
8 |
9 | public IndexDocument PK => this.Indexes[0];
10 |
11 | public CollectionDocument()
12 | {
13 | }
14 |
15 | ///
16 | /// Clone object instance constructor
17 | ///
18 | public CollectionDocument(CollectionDocument other)
19 | {
20 | this.ColID = other.ColID;
21 | this.Name = other.Name;
22 | this.Indexes = new List(other.Indexes);
23 | }
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/MasterService/ObjectModel/IndexDocument.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal class IndexDocument
4 | {
5 | public required byte Slot { get; init; }
6 | public required string Name { get; init; }
7 | public required BsonExpression Expression { get; init; }
8 | public required bool Unique { get; init; }
9 | public required RowID HeadIndexNodeID { get; init; }
10 | public required RowID TailIndexNodeID { get; init; }
11 |
12 | public IndexDocument()
13 | {
14 | }
15 |
16 | ///
17 | /// Clone object instance constructor
18 | ///
19 | public IndexDocument(IndexDocument other)
20 | {
21 | this.Slot = other.Slot;
22 | this.Name = other.Name;
23 | this.Expression = other.Expression;
24 | this.Unique = other.Unique;
25 | this.HeadIndexNodeID = other.HeadIndexNodeID;
26 | this.TailIndexNodeID = other.TailIndexNodeID;
27 | }
28 | }
29 |
30 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/MasterService/ObjectModel/MasterDocument.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal class MasterDocument
4 | {
5 |
6 | ///
7 | /// A dictionary with all collection indexed by collection name
8 | ///
9 | public Dictionary Collections { get; init; } = new(StringComparer.OrdinalIgnoreCase);
10 |
11 | ///
12 | /// Get current database pragma values
13 | ///
14 | public PragmaDocument Pragmas { get; init; } = new();
15 |
16 | ///
17 | /// Initial master document
18 | ///
19 | public MasterDocument()
20 | {
21 | }
22 |
23 | ///
24 | /// Clone object instance constructor
25 | ///
26 | public MasterDocument(MasterDocument other)
27 | {
28 | this.Collections = new(other.Collections);
29 | this.Pragmas = new PragmaDocument(other.Pragmas);
30 | }
31 | }
32 |
33 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/MasterService/ObjectModel/PragmaDocument.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal class PragmaDocument
4 | {
5 | ///
6 | /// Internal user version control to detect database changes
7 | ///
8 | public int UserVersion { get; set; } = 0;
9 |
10 | ///
11 | /// Max limit of datafile (in bytes) (default: MaxValue)
12 | ///
13 | public int LimitSizeID { get; set; } = 0;
14 |
15 | ///
16 | /// When LOG file gets larger than checkpoint size (in pages), do a soft checkpoint (and also do a checkpoint at shutdown)
17 | /// Checkpoint = 0 means there's no auto-checkpoint nor shutdown checkpoint
18 | ///
19 | public int Checkpoint { get; set; } = CHECKPOINT_SIZE;
20 |
21 | ///
22 | /// Initial pragma values
23 | ///
24 | public PragmaDocument()
25 | {
26 | }
27 |
28 | ///
29 | /// Clone object instance constructor
30 | ///
31 | public PragmaDocument(PragmaDocument other)
32 | {
33 | this.UserVersion = other.UserVersion;
34 | this.LimitSizeID = other.LimitSizeID;
35 | this.Checkpoint = other.Checkpoint;
36 | }
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/AggreagteFunc/AnyFunc.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | public class AnyFunc : IAggregateFunc
4 | {
5 | private readonly BsonExpression _expr;
6 |
7 | private BsonValue _state = false;
8 |
9 | public AnyFunc(BsonExpression expr)
10 | {
11 | _expr = expr;
12 | }
13 |
14 | public BsonExpression Expression => _expr;
15 |
16 | public void Iterate(BsonValue key, BsonDocument document, Collation collation)
17 | {
18 | if (_state == false)
19 | {
20 | _state = true;
21 | }
22 | }
23 |
24 | public BsonValue GetResult()
25 | {
26 | return _state;
27 | }
28 |
29 | public void Reset()
30 | {
31 | _state = false;
32 | }
33 |
34 | public override string ToString()
35 | {
36 | return $"ANY({_expr})";
37 | }
38 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/AggreagteFunc/ArrayFunc.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | public class ArrayFunc : IAggregateFunc
4 | {
5 | private readonly BsonExpression _expr;
6 |
7 | private BsonArray _state = new BsonArray();
8 |
9 | public ArrayFunc(BsonExpression expr)
10 | {
11 | _expr = expr;
12 | }
13 |
14 | public BsonExpression Expression => _expr;
15 |
16 | public void Iterate(BsonValue key, BsonDocument document, Collation collation)
17 | {
18 | var result = _expr.Execute(document, null, collation);
19 |
20 | _state.Add(result);
21 | }
22 |
23 | public BsonValue GetResult()
24 | {
25 | return _state;
26 | }
27 |
28 | public void Reset()
29 | {
30 | _state = new BsonArray();
31 | }
32 |
33 | public override string ToString()
34 | {
35 | return $"ARRAY({_expr})";
36 | }
37 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/AggreagteFunc/AvgFunc.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | public class AvgFunc : IAggregateFunc
4 | {
5 | private readonly BsonExpression _expr;
6 |
7 | private int _count = 0;
8 | private int _sumInt = 0;
9 | private long _sumLong = 0L;
10 | private double _sumDouble = 0d;
11 | private decimal _sumDecimal = 0m;
12 |
13 | public AvgFunc(BsonExpression expr)
14 | {
15 | _expr = expr;
16 | }
17 |
18 | public BsonExpression Expression => _expr;
19 |
20 | public void Iterate(BsonValue key, BsonDocument document, Collation collation)
21 | {
22 | var result = _expr.Execute(document, null, collation);
23 |
24 | _count++;
25 |
26 | if (result is BsonInt32 int32) _sumInt = unchecked(_sumInt + int32);
27 | else if (result is BsonInt64 int64) _sumLong = unchecked(_sumLong + int64);
28 | else if (result is BsonDouble double64) _sumDouble = unchecked(_sumDouble + double64);
29 | else if (result is BsonDecimal decimal128) _sumDecimal = unchecked(_sumDecimal + decimal128);
30 | else _count--;
31 | }
32 |
33 | public BsonValue GetResult()
34 | {
35 | if (_sumDecimal > 0) return (_sumDecimal + Convert.ToDecimal(_sumDouble) + _sumLong + _sumInt) / _count;
36 | if (_sumDouble > 0) return (_sumDouble + _sumLong + _sumInt) / _count;
37 | if (_sumLong > 0) return (_sumLong + _sumInt) / _count;
38 | if (_sumInt > 0) return (_sumInt) / _count;
39 |
40 | return 0;
41 | }
42 |
43 | public void Reset()
44 | {
45 | _count = _sumInt = 0;
46 | _sumLong = 0L;
47 | _sumDouble = 0d;
48 | _sumDecimal = 0m;
49 | }
50 |
51 | public override string ToString()
52 | {
53 | return $"AVG({_expr})";
54 | }
55 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/AggreagteFunc/CountFunc.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | public class CountFunc : IAggregateFunc
4 | {
5 | private readonly BsonExpression _expr;
6 |
7 | private long _state = 0;
8 |
9 | public CountFunc(BsonExpression expr)
10 | {
11 | _expr = expr;
12 | }
13 |
14 | public BsonExpression Expression => _expr;
15 |
16 | public void Iterate(BsonValue key, BsonDocument document, Collation collation)
17 | {
18 | var result = _expr.Execute(document, null, collation);
19 |
20 | if (!result.IsNull) _state++;
21 | }
22 |
23 | public BsonValue GetResult()
24 | {
25 | return _state < int.MaxValue ? new BsonInt32((int)_state) : new BsonInt64(_state);
26 | }
27 |
28 | public void Reset()
29 | {
30 | _state = 0;
31 | }
32 |
33 | public override string ToString()
34 | {
35 | return $"COUNT({_expr})";
36 | }
37 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/AggreagteFunc/FirstFunc.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | public class FirstFunc : IAggregateFunc
4 | {
5 | private readonly BsonExpression _expr;
6 |
7 | private BsonValue _state = BsonValue.MinValue;
8 |
9 | public FirstFunc(BsonExpression expr)
10 | {
11 | _expr = expr;
12 | }
13 |
14 | public BsonExpression Expression => _expr;
15 |
16 | public void Iterate(BsonValue key, BsonDocument document, Collation collation)
17 | {
18 | if (_state.IsMinValue)
19 | {
20 | var result = _expr.Execute(document, null, collation);
21 |
22 | _state = result;
23 | }
24 | }
25 |
26 | public BsonValue GetResult()
27 | {
28 | return _state.IsMinValue ? BsonValue.Null : _state;
29 | }
30 |
31 | public void Reset()
32 | {
33 | _state = BsonValue.MinValue;
34 | }
35 |
36 | public override string ToString()
37 | {
38 | return $"FIRST({_expr})";
39 | }
40 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/AggreagteFunc/IAggregateFunc.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | ///
4 | ///
5 | public interface IAggregateFunc
6 | {
7 | BsonExpression Expression { get; }
8 | void Iterate(BsonValue key, BsonDocument document, Collation collation);
9 | BsonValue GetResult();
10 | void Reset();
11 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/AggreagteFunc/LastFunc.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | public class LastFunc : IAggregateFunc
4 | {
5 | private readonly BsonExpression _expr;
6 |
7 | private BsonValue _state = BsonValue.Null;
8 |
9 | public LastFunc(BsonExpression expr)
10 | {
11 | _expr = expr;
12 | }
13 |
14 | public BsonExpression Expression => _expr;
15 |
16 | public void Iterate(BsonValue key, BsonDocument document, Collation collation)
17 | {
18 | var result = _expr.Execute(document, null, collation);
19 |
20 | _state = result;
21 | }
22 |
23 | public BsonValue GetResult()
24 | {
25 | return _state;
26 | }
27 |
28 | public void Reset()
29 | {
30 | _state = BsonValue.Null;
31 | }
32 |
33 | public override string ToString()
34 | {
35 | return $"LAST({_expr})";
36 | }
37 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/AggreagteFunc/MaxFunc.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | public class MaxFunc : IAggregateFunc
4 | {
5 | private readonly BsonExpression _expr;
6 |
7 | private BsonValue _state = BsonValue.MinValue;
8 |
9 | public MaxFunc(BsonExpression expr)
10 | {
11 | _expr = expr;
12 | }
13 |
14 | public BsonExpression Expression => _expr;
15 |
16 | public void Iterate(BsonValue key, BsonDocument document, Collation collation)
17 | {
18 | var result = _expr.Execute(document, null, collation);
19 |
20 | if (result.CompareTo(_state) >= 0)
21 | {
22 | _state = result;
23 | }
24 | }
25 |
26 | public BsonValue GetResult()
27 | {
28 | return _state;
29 | }
30 |
31 | public void Reset()
32 | {
33 | _state = BsonValue.MinValue;
34 | }
35 |
36 | public override string ToString()
37 | {
38 | return $"MAX({_expr})";
39 | }
40 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/AggreagteFunc/MinFunc.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | public class MinFunc : IAggregateFunc
4 | {
5 | private readonly BsonExpression _expr;
6 |
7 | private BsonValue _state = BsonValue.MaxValue;
8 |
9 | public MinFunc(BsonExpression expr)
10 | {
11 | _expr = expr;
12 | }
13 |
14 | public BsonExpression Expression => _expr;
15 |
16 | public void Iterate(BsonValue key, BsonDocument document, Collation collation)
17 | {
18 | var result = _expr.Execute(document, null, collation);
19 |
20 | if (result.CompareTo(_state) <= 0)
21 | {
22 | _state = result;
23 | }
24 | }
25 |
26 | public BsonValue GetResult()
27 | {
28 | return _state;
29 | }
30 |
31 | public void Reset()
32 | {
33 | _state = BsonValue.MaxValue;
34 | }
35 |
36 | public override string ToString()
37 | {
38 | return $"MIN({_expr})";
39 | }
40 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/AggreagteFunc/SumFunc.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | public class SumFunc : IAggregateFunc
4 | {
5 | private readonly BsonExpression _expr;
6 |
7 | private int _sumInt = 0;
8 | private long _sumLong = 0L;
9 | private double _sumDouble = 0d;
10 | private decimal _sumDecimal = 0m;
11 |
12 | public SumFunc(BsonExpression expr)
13 | {
14 | _expr = expr;
15 | }
16 |
17 | public BsonExpression Expression => _expr;
18 |
19 | public void Iterate(BsonValue key, BsonDocument document, Collation collation)
20 | {
21 | var result = _expr.Execute(document, null, collation);
22 |
23 | if (result is BsonInt32 int32) _sumInt = unchecked(_sumInt + int32);
24 | else if (result is BsonInt64 int64) _sumLong = unchecked(_sumLong + int64);
25 | else if (result is BsonDouble double64) _sumDouble = unchecked(_sumDouble + double64);
26 | else if (result is BsonDecimal decimal128) _sumDecimal = unchecked(_sumDecimal + decimal128);
27 | }
28 |
29 | public BsonValue GetResult()
30 | {
31 | if (_sumDecimal > 0) return (_sumDecimal + Convert.ToDecimal(_sumDouble) + _sumLong + _sumInt);
32 | if (_sumDouble > 0) return (_sumDouble + _sumLong + _sumInt);
33 | if (_sumLong > 0) return (_sumLong + _sumInt);
34 | if (_sumInt > 0) return (_sumInt);
35 |
36 | return 0;
37 | }
38 |
39 | public void Reset()
40 | {
41 | _sumInt = 0;
42 | _sumLong = 0L;
43 | _sumDouble = 0d;
44 | _sumDecimal = 0m;
45 | }
46 |
47 | public override string ToString()
48 | {
49 | return $"SUM({_expr})";
50 | }
51 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/Indexes/IndexAllEnumerator.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal class IndexAllEnumerator : IPipeEnumerator
4 | {
5 | private readonly IndexDocument _indexDocument;
6 | private readonly int _order;
7 | private readonly bool _returnKey;
8 |
9 | private bool _init = false;
10 | private bool _eof = false;
11 |
12 | private RowID _next = RowID.Empty; // all nodes from right of first node found
13 |
14 | public IndexAllEnumerator(
15 | IndexDocument indexDocument,
16 | int order,
17 | bool returnKey)
18 | {
19 | _indexDocument = indexDocument;
20 | _order = order;
21 | _returnKey = returnKey;
22 | }
23 |
24 | public PipeEmit Emit => new(indexNodeID: true, dataBlockID: true, value: _returnKey);
25 |
26 | public unsafe PipeValue MoveNext(PipeContext context)
27 | {
28 | if (_eof) return PipeValue.Empty;
29 |
30 | var indexService = context.IndexService;
31 |
32 | var head = _order == Query.Ascending ? _indexDocument.HeadIndexNodeID : _indexDocument.TailIndexNodeID;
33 | var tail = _order == Query.Ascending ? _indexDocument.TailIndexNodeID : _indexDocument.HeadIndexNodeID;
34 |
35 | // in first run, gets head node
36 | if (_init == false)
37 | {
38 | _init = true;
39 |
40 | var first = indexService.GetNode(head);
41 |
42 | // get pointer to first element
43 | _next = first[0]->GetNext(_order);
44 |
45 | // check if not empty
46 | if (_next == tail)
47 | {
48 | _eof = true;
49 | return PipeValue.Empty;
50 | }
51 | }
52 |
53 | // go forward
54 | var node = indexService.GetNode(_next);
55 |
56 | _next = node[0]->GetNext(_order);
57 |
58 | if (_next == tail) _eof = true;
59 |
60 | var value = _returnKey ? IndexKey.ToBsonValue(node.Key) : BsonValue.Null;
61 |
62 | return new PipeValue(node.IndexNodeID, node.DataBlockID, value);
63 | }
64 |
65 | public void GetPlan(ExplainPlainBuilder builder, int deep)
66 | {
67 | builder.Add($"INDEX FULL SCAN \"{_indexDocument.Name}\" {(_order > 0 ? "ASC" : "DESC")}", deep);
68 | }
69 |
70 | public void Dispose()
71 | {
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/Indexes/IndexInEnumerator.cs:
--------------------------------------------------------------------------------
1 | using System.Xml.Linq;
2 |
3 | namespace LiteDB.Engine;
4 |
5 | unsafe internal class IndexInEnumerator : IPipeEnumerator
6 | {
7 | private readonly Collation _collation;
8 | private readonly IndexDocument _indexDocument;
9 | private readonly bool _returnKey;
10 |
11 | private readonly BsonValue[] _values;
12 | private int _valueIndex = 0;
13 |
14 | private bool _init = false;
15 | private bool _eof = false;
16 |
17 | private IndexEqualsEnumerator? _currentIndex;
18 |
19 | public IndexInEnumerator(
20 | IEnumerable values,
21 | IndexDocument indexDocument,
22 | Collation collation,
23 | bool returnKey)
24 | {
25 | _values = values.Distinct().ToArray();
26 | _indexDocument = indexDocument;
27 | _collation = collation;
28 | _returnKey = returnKey;
29 | }
30 |
31 | public PipeEmit Emit => new(indexNodeID: true, dataBlockID: true, value: _returnKey);
32 |
33 | public unsafe PipeValue MoveNext(PipeContext context)
34 | {
35 | if (_eof) return PipeValue.Empty;
36 |
37 | // in first run, gets head node
38 | if (_init == false)
39 | {
40 | _init = true;
41 |
42 | var first = _values[_valueIndex];
43 |
44 | _currentIndex = new IndexEqualsEnumerator(first, _indexDocument, _collation, _returnKey);
45 |
46 | return _currentIndex.MoveNext(context);
47 | }
48 | else
49 | {
50 | var pipeValue = _currentIndex!.MoveNext(context);
51 |
52 | if (pipeValue.IsEmpty)
53 | {
54 | _valueIndex++;
55 |
56 | if (_valueIndex == _values.Length)
57 | {
58 | _eof = true;
59 | return PipeValue.Empty;
60 | }
61 |
62 | var value = _values[_valueIndex];
63 |
64 | _currentIndex = new IndexEqualsEnumerator(value, _indexDocument, _collation, _returnKey);
65 |
66 | return _currentIndex.MoveNext(context);
67 | }
68 |
69 | return pipeValue;
70 | }
71 | }
72 |
73 | public void GetPlan(ExplainPlainBuilder builder, int deep)
74 | {
75 | builder.Add($"INDEX SEEK \"{_indexDocument.Name}\" IN ({string.Join(", ", _values.Select(x => x.ToString()))})", deep);
76 | }
77 |
78 | public void Dispose()
79 | {
80 | }
81 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/Lookup/DataLookup.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal class DataLookup : IDocumentLookup
4 | {
5 | private readonly string[] _fields;
6 |
7 | public DataLookup(string[] fields)
8 | {
9 | _fields = fields;
10 | }
11 |
12 | public BsonDocument Load(PipeValue key, PipeContext context)
13 | {
14 | var result = context.DataService.ReadDocument(key.DataBlockID, _fields);
15 |
16 | if (result.Fail) throw result.Exception;
17 |
18 | return result.Value.AsDocument;
19 | }
20 |
21 | public override string ToString()
22 | {
23 | return $"DATABLOCK {(_fields.Length == 0 ? "FULL DOCUMENT" : "FIELDS " + string.Join(", ", _fields))}";
24 | }
25 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/Lookup/IDocumentLookup.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal interface IDocumentLookup
4 | {
5 | BsonDocument Load(PipeValue key, PipeContext context);
6 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/Lookup/IndexLookup.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal class IndexLookup : IDocumentLookup
4 | {
5 | private readonly string _field;
6 |
7 | public IndexLookup(string field)
8 | {
9 | _field = field;
10 | }
11 |
12 | public BsonDocument Load(PipeValue key, PipeContext context)
13 | {
14 | var doc = new BsonDocument { [_field] = key.Value! };
15 |
16 | return doc;
17 | }
18 |
19 | public override string ToString()
20 | {
21 | return $"INDEX FIELD {_field}";
22 | }
23 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/PipelineEnumerators/FilterEnumerator.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal class FilterEnumerator : IPipeEnumerator
4 | {
5 | // dependency injections
6 | private readonly Collation _collation;
7 |
8 | private readonly IPipeEnumerator _enumerator;
9 | private readonly BsonExpression _filter;
10 |
11 | private bool _eof = false;
12 |
13 | public FilterEnumerator(BsonExpression filter, IPipeEnumerator enumerator, Collation collation)
14 | {
15 | _filter = filter;
16 | _enumerator = enumerator;
17 | _collation = collation;
18 |
19 | if (_enumerator.Emit.Value == false) throw ERR($"Filter pipe enumerator requires document from last pipe");
20 | }
21 |
22 | public PipeEmit Emit => _enumerator.Emit;
23 |
24 | public PipeValue MoveNext(PipeContext context)
25 | {
26 | while (!_eof)
27 | {
28 | var item = _enumerator.MoveNext(context);
29 |
30 | if (item.IsEmpty)
31 | {
32 | _eof = true;
33 | }
34 | else
35 | {
36 | var result = _filter.Execute(item.Value, context.QueryParameters, _collation);
37 |
38 | if (result.IsBoolean && result.AsBoolean)
39 | {
40 | return item;
41 | }
42 | }
43 | }
44 |
45 | return PipeValue.Empty;
46 | }
47 |
48 | public void GetPlan(ExplainPlainBuilder builder, int deep)
49 | {
50 | builder.Add($"FILTER {_filter}", deep);
51 |
52 | _enumerator.GetPlan(builder, ++deep);
53 | }
54 |
55 | public void Dispose()
56 | {
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/PipelineEnumerators/IPipeEnumerator.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | ///
4 | /// Interface for a custom query pipe
5 | ///
6 | internal interface IPipeEnumerator : IDisposable
7 | {
8 | PipeEmit Emit { get; }
9 |
10 | PipeValue MoveNext(PipeContext context);
11 |
12 | void GetPlan(ExplainPlainBuilder builder, int deep);
13 | }
14 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/PipelineEnumerators/InMemoryOrderByEnumerator.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal class InMemoryOrderByEnumerator : IPipeEnumerator
4 | {
5 | // dependency injections
6 | private readonly Collation _collation;
7 |
8 | private readonly OrderBy _orderBy;
9 | private readonly IPipeEnumerator _enumerator;
10 |
11 | private Queue? _sortedItems;
12 |
13 | public InMemoryOrderByEnumerator(
14 | OrderBy orderBy,
15 | IPipeEnumerator enumerator,
16 | Collation collation)
17 | {
18 | _orderBy = orderBy;
19 | _enumerator = enumerator;
20 | _collation = collation;
21 |
22 | if (_enumerator.Emit.Value == false) throw ERR($"InMemoryOrderBy pipe enumerator requires document from last pipe");
23 | }
24 |
25 | public PipeEmit Emit => new(indexNodeID: false, dataBlockID: true, value: true);
26 |
27 | public PipeValue MoveNext(PipeContext context)
28 | {
29 | if (_sortedItems is null)
30 | {
31 | var list = new List();
32 |
33 | while (true)
34 | {
35 | var item = _enumerator.MoveNext(context);
36 |
37 | if (item.IsEmpty) break;
38 |
39 | // get sort key
40 | var key = _orderBy.Expression.Execute(item.Value, context.QueryParameters, _collation);
41 |
42 | //list.Add(new (item.DataBlockID, key, item.Document!));
43 | throw new NotImplementedException();
44 | }
45 |
46 | // sort list in a new enumerable
47 | var query = _orderBy.Order == Query.Ascending ?
48 | list.OrderBy(x => x.Key, _collation) : list.OrderByDescending(x => x.Key, _collation);
49 |
50 | _sortedItems = new Queue(query);
51 | }
52 |
53 | if (_sortedItems.Count > 0)
54 | {
55 | var item = _sortedItems.Dequeue();
56 |
57 | return new PipeValue(RowID.Empty, item.DataBlockID, item.Document);
58 | }
59 |
60 | return PipeValue.Empty;
61 | }
62 |
63 | public void GetPlan(ExplainPlainBuilder builder, int deep)
64 | {
65 | builder.Add($"IN-MEMORY ORDER BY {_orderBy}", deep);
66 |
67 | _enumerator.GetPlan(builder, ++deep);
68 | }
69 |
70 | public void Dispose()
71 | {
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/PipelineEnumerators/LimitEnumerator.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal class LimitEnumerator : IPipeEnumerator
4 | {
5 | private readonly IPipeEnumerator _enumerator;
6 |
7 | private readonly int _limit;
8 |
9 | private int _count = 0;
10 | private bool _eof = false;
11 |
12 | public LimitEnumerator(int limit, IPipeEnumerator enumerator)
13 | {
14 | _limit = limit;
15 | _enumerator = enumerator;
16 | }
17 |
18 | public PipeEmit Emit => _enumerator.Emit;
19 |
20 | public PipeValue MoveNext(PipeContext context)
21 | {
22 | if (_eof) return PipeValue.Empty;
23 |
24 | var item = _enumerator.MoveNext(context);
25 |
26 | if (item.IsEmpty)
27 | {
28 | _eof = true;
29 | return PipeValue.Empty;
30 | }
31 |
32 | _count++;
33 |
34 | if (_count >= _limit)
35 | {
36 | _eof = true;
37 | }
38 |
39 | return item;
40 | }
41 |
42 | public void GetPlan(ExplainPlainBuilder builder, int deep)
43 | {
44 | builder.Add($"LIMIT {_limit}", deep);
45 |
46 | _enumerator.GetPlan(builder, ++deep);
47 | }
48 |
49 | public void Dispose()
50 | {
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/PipelineEnumerators/LookupEnumerator.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal class LookupEnumerator : IPipeEnumerator
4 | {
5 | private readonly IDocumentLookup _lookup;
6 | private readonly IPipeEnumerator _enumerator;
7 |
8 | private bool _eof = false;
9 |
10 | public LookupEnumerator(IDocumentLookup lookup, IPipeEnumerator enumerator)
11 | {
12 | _lookup = lookup;
13 | _enumerator = enumerator;
14 |
15 | if (_enumerator.Emit.DataBlockID == false) throw ERR($"Lookup pipe enumerator requires DataBlockID from last pipe");
16 | }
17 |
18 | public PipeEmit Emit => new(indexNodeID: _enumerator.Emit.IndexNodeID, dataBlockID: true, value: true);
19 |
20 | public PipeValue MoveNext(PipeContext context)
21 | {
22 | if (_eof) return PipeValue.Empty;
23 |
24 | var item = _enumerator.MoveNext(context);
25 |
26 | if (item.IsEmpty)
27 | {
28 | _eof = true;
29 | return PipeValue.Empty;
30 | }
31 |
32 | var doc = _lookup.Load(item, context);
33 |
34 | return new PipeValue(RowID.Empty, item.DataBlockID, doc);
35 | }
36 |
37 | public void GetPlan(ExplainPlainBuilder builder, int deep)
38 | {
39 | builder.Add($"LOOKUP {_lookup}", deep);
40 |
41 | _enumerator.GetPlan(builder, ++deep);
42 | }
43 |
44 | public void Dispose()
45 | {
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/PipelineEnumerators/OffsetEnumerator.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal class OffsetEnumerator : IPipeEnumerator
4 | {
5 | private readonly IPipeEnumerator _enumerator;
6 |
7 | private readonly int _offset;
8 |
9 | private int _count = 0;
10 | private bool _eof = false;
11 |
12 | public OffsetEnumerator(int offset, IPipeEnumerator enumerator)
13 | {
14 | _offset = offset;
15 | _enumerator = enumerator;
16 | }
17 |
18 | public PipeEmit Emit => _enumerator.Emit;
19 |
20 | public PipeValue MoveNext(PipeContext context)
21 | {
22 | if (_eof) return PipeValue.Empty;
23 |
24 | while (_count <= _offset)
25 | {
26 | var skiped = _enumerator.MoveNext(context);
27 |
28 | if (skiped.IsEmpty)
29 | {
30 | _eof = true;
31 |
32 | return PipeValue.Empty;
33 | }
34 |
35 | _count++;
36 | }
37 |
38 | var item = _enumerator.MoveNext(context);
39 |
40 | if (item.IsEmpty)
41 | {
42 | _eof = true;
43 |
44 | return PipeValue.Empty;
45 | }
46 |
47 | return item;
48 | }
49 |
50 | public void GetPlan(ExplainPlainBuilder builder, int deep)
51 | {
52 | builder.Add($"OFFSET {_offset}", deep);
53 |
54 | _enumerator.GetPlan(builder, ++deep);
55 | }
56 |
57 | public void Dispose()
58 | {
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/QueryService/PipelineEnumerators/OrderByEnumerator.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal class OrderByEnumerator : IPipeEnumerator
4 | {
5 | // dependency injections
6 | private readonly ISortOperation _sorter;
7 |
8 | private readonly OrderBy _orderBy;
9 | private readonly IPipeEnumerator _enumerator;
10 | private bool _init;
11 |
12 | public OrderByEnumerator(
13 | OrderBy orderBy,
14 | IPipeEnumerator enumerator,
15 | ISortService sortService)
16 | {
17 | _enumerator = enumerator;
18 |
19 | if (_enumerator.Emit.DataBlockID == false) throw ERR($"OrderBy pipe enumerator requires DataBlockID from last pipe");
20 | if (_enumerator.Emit.Value == false) throw ERR($"OrderBy pipe enumerator requires Document from last pipe");
21 |
22 | _orderBy = orderBy;
23 | _sorter = sortService.CreateSort(orderBy);
24 | }
25 |
26 | public PipeEmit Emit => new(indexNodeID: false, dataBlockID: true, value: false);
27 |
28 | public PipeValue MoveNext(PipeContext context)
29 | {
30 | if(_init == false)
31 | {
32 | // consume all _enumerator and get ready for new enumerator: _sorter
33 | _sorter.InsertData(_enumerator, context);
34 | _init = true;
35 | }
36 |
37 | // get next sorted item (returns Empty when EOF)
38 | var item = _sorter.MoveNext();
39 |
40 | return new PipeValue(RowID.Empty, item.DataBlockID);
41 | }
42 |
43 | public void GetPlan(ExplainPlainBuilder builder, int deep)
44 | {
45 | builder.Add($"ORDER BY {_orderBy}", deep);
46 |
47 | _enumerator.GetPlan(builder, ++deep);
48 | }
49 |
50 | public void Dispose()
51 | {
52 | // dispose/release all used containers
53 | _sorter.Dispose();
54 | }
55 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Services/SortService/SortService.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | [AutoInterface(typeof(IDisposable))]
4 | internal class SortService : ISortService
5 | {
6 | private readonly IServicesFactory _factory;
7 | private readonly IStreamFactory _sortStreamFactory;
8 |
9 | private readonly ConcurrentQueue _availableContainersID = new();
10 | private readonly ConcurrentQueue _streamPool = new();
11 | private int _nextContainerID = -1;
12 |
13 | public SortService(
14 | IStreamFactory sortStreamFactory,
15 | IServicesFactory factory)
16 | {
17 | _sortStreamFactory = sortStreamFactory;
18 | _factory = factory;
19 | }
20 |
21 | public ISortOperation CreateSort(OrderBy orderBy)
22 | {
23 | var sorter = _factory.CreateSortOperation(orderBy);
24 |
25 | return sorter;
26 | }
27 |
28 | public int GetAvailableContainerID()
29 | {
30 | if (_availableContainersID.TryDequeue(out var containerID))
31 | {
32 | return containerID;
33 | }
34 |
35 | return Interlocked.Increment(ref _nextContainerID);
36 | }
37 |
38 | public Stream RentSortStream()
39 | {
40 | if (!_streamPool.TryDequeue(out var stream))
41 | {
42 | stream = _sortStreamFactory.GetStream(true, FileOptions.None); // parameters are not used for sort stream factory
43 | }
44 |
45 | return stream;
46 | }
47 |
48 | public void ReleaseSortStream(Stream stream)
49 | {
50 | _streamPool.Enqueue(stream);
51 | }
52 |
53 | public void Dispose()
54 | {
55 | foreach(var stream in _streamPool)
56 | {
57 | stream.Dispose();
58 | }
59 |
60 | if (_streamPool.Count > 0)
61 | {
62 | _sortStreamFactory.Delete();
63 | }
64 |
65 | // clear service states
66 | _availableContainersID.Clear();
67 | _streamPool.Clear();
68 | _nextContainerID = -1;
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/SqlParser/Commands/SqlParser.CreateCollection.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | ///
4 | /// Internal class to parse and execute sql-like commands
5 | ///
6 | internal partial class SqlParser
7 | {
8 | ///
9 | /// create_collection::
10 | /// "CREATE" _ "COLLECTION" _ user_collection
11 | ///
12 | private IEngineStatement ParseCreateCollection()
13 | {
14 | _tokenizer.ReadToken().Expect("COLLECTION"); // CREATE token already readed
15 |
16 | // create collection name
17 | if (!this.TryParseDocumentStore(out var store)) throw ERR_UNEXPECTED_TOKEN(_tokenizer.Current, "{document_store}");
18 |
19 | // expect end of statement
20 | _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon);
21 |
22 | return new CreateCollectionStatement(store.Name);
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/SqlParser/Commands/SqlParser.CreateIndex.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | ///
4 | /// Internal class to parse and execute sql-like commands
5 | ///
6 | internal partial class SqlParser
7 | {
8 | ///
9 | /// create_index::
10 | /// "CREATE" [_ "UNIQUE"] _ "INDEX" _ word _ "ON" _ user_collection. "(" . expr_single. ")"
11 | ///
12 | private IEngineStatement ParseCreateIndex()
13 | {
14 | var token = _tokenizer.ReadToken().Expect(TokenType.Word);
15 | var unique = token.Value.Eq("UNIQUE");
16 |
17 | if (unique)
18 | {
19 | _tokenizer.ReadToken().Expect("INDEX");
20 | }
21 | else
22 | {
23 | token.Expect("INDEX");
24 | }
25 |
26 | var indexName = _tokenizer.ReadToken().Expect(TokenType.Word).Value; // get index name
27 |
28 | _tokenizer.ReadToken().Expect("ON");
29 |
30 | // get collection name
31 | if (!this.TryParseDocumentStore(out var store)) throw ERR_UNEXPECTED_TOKEN(_tokenizer.Current, "{document_store}");
32 |
33 | // read (
34 | _tokenizer.ReadToken().Expect(TokenType.OpenParenthesis);
35 |
36 | // read index expression
37 | var expr = BsonExpression.Create(_tokenizer, false);
38 |
39 | // read )
40 | _tokenizer.ReadToken().Expect(TokenType.CloseParenthesis);
41 |
42 | // expect end of statement
43 | _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon);
44 |
45 | return new CreateIndexStatement(store.Name, indexName, expr, unique);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/SqlParser/Commands/SqlParser.Insert.cs:
--------------------------------------------------------------------------------
1 | using System.Data;
2 | using System.Text.Json.Nodes;
3 | using System.Text.Json;
4 |
5 | namespace LiteDB.Engine;
6 |
7 | ///
8 | /// Internal class to parse and execute sql-like commands
9 | ///
10 | internal partial class SqlParser
11 | {
12 | ///
13 | /// insert_statement::
14 | /// "INSERT" _ "INTO" _ document_store. [":" auto_id]
15 | /// _ "VALUES" _ (json_document | json_array | sub_query | expr_parameter)
16 | ///
17 | private IEngineStatement ParseInsert()
18 | {
19 | _tokenizer.ReadToken().Expect("INSERT");
20 | _tokenizer.ReadToken().Expect("INTO");
21 |
22 | if (!this.TryParseDocumentStore(out var store)) throw ERR_UNEXPECTED_TOKEN(_tokenizer.Current, "{document_store}");
23 |
24 | TryParseWithAutoId(out var autoId);
25 |
26 | _tokenizer.ReadToken().Expect("VALUES");
27 |
28 | var ahead = _tokenizer.LookAhead();
29 | InsertStatement statement;
30 |
31 | if (ahead.Type == TokenType.At) // @0 - is expression parameter
32 | {
33 | var docExpr = BsonExpression.Create(_tokenizer, false);
34 |
35 | statement = new InsertStatement(store, docExpr, autoId);
36 | }
37 | else if (ahead.Type == TokenType.OpenBrace) // { new json document
38 | {
39 | var doc = JsonReaderStatic.ReadDocument(_tokenizer); // read full json_document
40 |
41 | statement = new InsertStatement(store, doc, autoId);
42 | }
43 | else if (ahead.Type == TokenType.OpenBracket) // [ new json array
44 | {
45 | var array = JsonReaderStatic.ReadArray(_tokenizer); // read full json_array
46 |
47 | statement = new InsertStatement(store, array, autoId);
48 | }
49 | else if (ahead.Type == TokenType.OpenParenthesis) // ( new sub_query
50 | {
51 | throw new NotImplementedException("sub_query");
52 | }
53 | else
54 | {
55 | throw ERR_UNEXPECTED_TOKEN(ahead, "Expression parameter, JsonDocument or JsonArray");
56 | }
57 |
58 | // expect end of statement
59 | _tokenizer.ReadToken().Expect(TokenType.EOF, TokenType.SemiColon);
60 |
61 | return statement;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/SqlParser/Core/SqlParser.AutoId.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal partial class SqlParser
4 | {
5 | ///
6 | /// auto_id::
7 | /// | "GUID"
8 | /// | "INT"
9 | /// | "LONG"
10 | /// | "OBJECTID"
11 | ///
12 | private bool TryParseWithAutoId(out BsonAutoId autoId)
13 | {
14 | var with = _tokenizer.LookAhead();
15 |
16 | if (with.Type == TokenType.Colon)
17 | {
18 | _tokenizer.ReadToken();
19 |
20 | var type = _tokenizer.ReadToken().Expect(TokenType.Word);
21 |
22 | if (type.Value.Eq("GUID"))
23 | autoId = BsonAutoId.Guid;
24 | else if (type.Value.Eq("INT"))
25 | autoId = BsonAutoId.Int32;
26 | else if (type.Value.Eq("LONG"))
27 | autoId = BsonAutoId.Int64;
28 | else if (type.Value.Eq("OBJECTID"))
29 | autoId = BsonAutoId.ObjectId;
30 | else
31 | throw ERR_UNEXPECTED_TOKEN(type, "GUID, INT, LONG, OBJECTID");
32 |
33 | return true;
34 | }
35 |
36 | autoId = BsonAutoId.Int32;
37 |
38 | return false;
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/SqlParser/Core/SqlParser.DocumentStore.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal partial class SqlParser
4 | {
5 | ///
6 | /// document_store::
7 | /// | user_collection
8 | /// | virtual_collection
9 | ///
10 | /// user_collection::
11 | /// char word
12 | ///
13 | /// virtual_collection::
14 | /// "$" word [ collection_parameters ]
15 | ///
16 | private bool TryParseDocumentStore(out IDocumentStore store)
17 | {
18 | var ahead = _tokenizer.LookAhead();
19 |
20 | if (ahead.Type == TokenType.Word) // user_collection
21 | {
22 | var token = _tokenizer.ReadToken(); // read "collection_name";
23 |
24 | store = new UserCollectionStore(token.Value);
25 |
26 | return true;
27 | }
28 | else if (ahead.Type != TokenType.Dollar)
29 | {
30 | _tokenizer.ReadToken(); // read "$";
31 |
32 | var token = _tokenizer.ReadToken().Expect(TokenType.Word); // read "collection-name"
33 |
34 | if (TryParseParameters(out var parameters))
35 | {
36 | //TODO: verificar metodos
37 | }
38 |
39 |
40 | throw new NotImplementedException();
41 |
42 | //return true;
43 | }
44 |
45 | store = default;
46 | return false;
47 |
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/SqlParser/Core/SqlParser.ListOfExpressions.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal partial class SqlParser
4 | {
5 | ///
6 | /// list_of_expressions::
7 | /// expr_single [ . "," . expr_single ]*
8 | ///
9 | private IEnumerable ParseListOfExpressions()
10 | {
11 | while (true)
12 | {
13 | var expr = BsonExpression.Create(_tokenizer, false);
14 |
15 | yield return expr;
16 |
17 | var next = _tokenizer.LookAhead();
18 |
19 | if (next.Type == TokenType.Comma)
20 | {
21 | _tokenizer.ReadToken();
22 | }
23 | else
24 | {
25 | yield break;
26 | }
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/SqlParser/Core/SqlParser.Parameters.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal partial class SqlParser
4 | {
5 | ///
6 | /// collection_parameters::
7 | /// "(" [ . json_value [ . "," . json_value ] ] . ")"
8 | ///
9 | private bool TryParseParameters(out IReadOnlyList parameters)
10 | {
11 | var ahead = _tokenizer.LookAhead();
12 |
13 | if (ahead.Type != TokenType.OpenParenthesis)
14 | {
15 | parameters = Array.Empty();
16 | return false;
17 | }
18 |
19 | var token = _tokenizer.ReadToken(); // read "("
20 |
21 | var result = new List();
22 |
23 | while (token.Type != TokenType.CloseParenthesis)
24 | {
25 | var value = JsonReaderStatic.ReadValue(_tokenizer);
26 |
27 | result.Add(value);
28 |
29 | token = _tokenizer.ReadToken(); // read , "," or ")"
30 |
31 | if (token.Type == TokenType.Comma)
32 | {
33 | token = _tokenizer.ReadToken();
34 | }
35 | }
36 |
37 | parameters = result;
38 | return true;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/SqlParser/SqlParser.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | ///
4 | /// Internal class to parse and execute sql-like commands
5 | ///
6 | internal partial class SqlParser
7 | {
8 | private Tokenizer _tokenizer;
9 | private Collation _collation;
10 | private int _nameIndex = 0; // used for columns with no names will generate "expr1", "expr2"
11 |
12 | public SqlParser(Tokenizer tokenizer, Collation collation)
13 | {
14 | _tokenizer = tokenizer;
15 | _collation = collation;
16 | }
17 |
18 | public IEngineStatement ParseStatement()
19 | {
20 | var ahead = _tokenizer.LookAhead().Expect(TokenType.Word);
21 |
22 | if (ahead.Value.Eq("CREATE"))
23 | {
24 | _tokenizer.ReadToken(); // read CREATE
25 | ahead = _tokenizer.LookAhead();
26 |
27 | if (ahead.Value.Eq("COLLECTION")) return this.ParseCreateCollection();
28 | if (ahead.Value.Eq("INDEX")) return this.ParseCreateIndex();
29 |
30 | throw ERR_UNEXPECTED_TOKEN(ahead);
31 | }
32 |
33 |
34 | if (ahead.Value.Eq("SELECT") || ahead.Value.Eq("EXPLAIN")) return this.ParseSelect();
35 |
36 | if (ahead.Value.Eq("INSERT")) return this.ParseInsert();
37 |
38 | //if (ahead.Value.Eq("DELETE")) return this.ParseDelete();
39 | //if (ahead.Value.Eq("UPDATE")) return this.ParseUpdate();
40 | //if (ahead.Value.Eq("DROP")) return this.ParseDrop();
41 | //if (ahead.Value.Eq("RENAME")) return this.ParseRename();
42 | //if (ahead.Value.Eq("CREATE")) return this.ParseCreate();
43 | //
44 | //if (ahead.Value.Eq("CHECKPOINT")) return this.ParseCheckpoint();
45 | //if (ahead.Value.Eq("REBUILD")) return this.ParseRebuild();
46 | //
47 | //if (ahead.Value.Eq("BEGIN")) return this.ParseBegin();
48 | //if (ahead.Value.Eq("ROLLBACK")) return this.ParseRollback();
49 | //if (ahead.Value.Eq("COMMIT")) return this.ParseCommit();
50 | //
51 | //if (ahead.Value.Eq("PRAGMA")) return this.ParsePragma();
52 |
53 | throw ERR_UNEXPECTED_TOKEN(ahead);
54 | }
55 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Statements/BsonDataReader/BsonDataReaderExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// Implement some Enumerable methods to IBsonDataReader
5 | ///
6 | public static class BsonDataReaderExtensions
7 | {
8 | public static async IAsyncEnumerable ToEnumerableAsync(this IDataReader reader)
9 | {
10 | try
11 | {
12 | while (await reader.ReadAsync())
13 | {
14 | yield return reader.Current;
15 | }
16 | }
17 | finally
18 | {
19 | reader.Dispose();
20 | }
21 | }
22 |
23 | //TODO: tenho que consumier o async enumerable pra gerar isso
24 | //public static ValueTask ToArrayAsync(this IBsonDataReader reader) => ToEnumerableAsync(reader);
25 | //
26 | //public static IList ToListAsync(this IBsonDataReader reader) => ToEnumerable(reader).ToList();
27 | //
28 | //public static BsonValue FirstAsync(this IBsonDataReader reader) => ToEnumerable(reader).First();
29 | //
30 | //public static BsonValue FirstOrDefaultAsync(this IBsonDataReader reader) => ToEnumerable(reader).FirstOrDefault();
31 | //
32 | //public static BsonValue SingleAsync(this IBsonDataReader reader) => ToEnumerable(reader).Single();
33 | //
34 | //public static BsonValue SingleOrDefaultAsync(this IBsonDataReader reader) => ToEnumerable(reader).SingleOrDefault();
35 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Statements/BsonDataReader/BsonScalarReader.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// Implement a data reader for a single value
5 | ///
6 | public class BsonScalarReader : IDataReader
7 | {
8 | private readonly string _collection;
9 | private readonly BsonValue _value;
10 | private int _current = -1;
11 |
12 | ///
13 | /// Initialize data reader with created cursor
14 | ///
15 | internal BsonScalarReader(string collection, BsonValue value)
16 | {
17 | _collection = collection;
18 | _value = value;
19 | }
20 |
21 | ///
22 | /// Return current value
23 | ///
24 | public BsonValue Current =>
25 | _value is BsonArray array ?
26 | (_current >= 0 ? array[_current] : BsonValue.Null) :
27 | _value;
28 |
29 | ///
30 | /// Return collection name
31 | ///
32 | public string Collection => _collection;
33 |
34 | ///
35 | /// Move cursor to next result. Returns true if read was possible
36 | ///
37 | public ValueTask ReadAsync()
38 | {
39 | if (_current == -1)
40 | {
41 | _current = 0;
42 |
43 | return new ValueTask(true);
44 | }
45 | else if (_value is BsonArray array)
46 | {
47 | _current++;
48 |
49 | if (_current >= array.Count)
50 | {
51 | return new ValueTask(false);
52 | }
53 |
54 | return new ValueTask(true);
55 | }
56 | else
57 | {
58 | return new ValueTask(false);
59 | }
60 | }
61 |
62 | public BsonValue this[string field] => _value.AsDocument[field];
63 |
64 | public void Dispose()
65 | {
66 | }
67 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Statements/BsonDataReader/IDataReader.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | public interface IDataReader : IDisposable
4 | {
5 | BsonValue this[string field] { get; }
6 | string Collection { get; }
7 | BsonValue Current { get; }
8 | ValueTask ReadAsync();
9 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Statements/CheckpointStatement.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal class CheckpointStatement : IEngineStatement
4 | {
5 | public EngineStatementType StatementType => EngineStatementType.Checkpoint;
6 |
7 | public CheckpointStatement()
8 | {
9 | }
10 |
11 | public async ValueTask ExecuteAsync(IServicesFactory factory, BsonDocument parameters)
12 | {
13 | using var _pc = PERF_COUNTER(37, nameof(ExecuteAsync), nameof(CheckpointStatement));
14 |
15 | var lockService = factory.LockService;
16 | var logService = factory.LogService;
17 |
18 | // checkpoint require exclusive lock (no readers/writers)
19 | await lockService.EnterExclusiveAsync();
20 |
21 | // do checkpoint and returns how many pages was overrided
22 | var result = await logService.CheckpointAsync(false, /* true*/ false);
23 |
24 | // release exclusive
25 | lockService.ExitExclusive();
26 |
27 | return result;
28 | }
29 |
30 | public ValueTask ExecuteReaderAsync(IServicesFactory factory, BsonDocument parameters) => throw new NotSupportedException();
31 | }
32 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Statements/Interfaces/EngineStatementType.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal enum EngineStatementType
4 | {
5 | // dml
6 | Insert,
7 | Update,
8 | Delete,
9 |
10 | // query
11 | Select,
12 |
13 | // collections
14 | CreateCollection,
15 | RenameCollection,
16 | DropCollection,
17 |
18 | // indexes
19 | CreateIndex,
20 | DropIndex,
21 |
22 | // tools
23 | Checkpoint,
24 | Rebuild
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Statements/Interfaces/IEngineStatement.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal interface IEngineStatement
4 | {
5 | EngineStatementType StatementType { get; }
6 | ValueTask ExecuteAsync(IServicesFactory factory, BsonDocument parameters);
7 | ValueTask ExecuteReaderAsync(IServicesFactory factory, BsonDocument parameters);
8 | }
9 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Statements/SelectStatement.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal class SelectStatement : IEngineStatement
4 | {
5 | private readonly Query _query;
6 | private readonly bool _explain;
7 | private readonly int _fetchSize;
8 |
9 | public EngineStatementType StatementType => EngineStatementType.Select;
10 |
11 | #region Ctor
12 |
13 | public SelectStatement(Query query) :
14 | this(query, 1000, false)
15 | {
16 | }
17 |
18 | public SelectStatement(Query query, bool explain) :
19 | this(query, 1000, explain)
20 | {
21 | }
22 |
23 | public SelectStatement(Query query, int fetchSize) :
24 | this (query, fetchSize, false)
25 | {
26 | }
27 |
28 | public SelectStatement(Query query, int fetchSize, bool explain)
29 | {
30 | _query = query;
31 | _fetchSize = fetchSize;
32 | _explain = explain;
33 | }
34 |
35 | #endregion
36 |
37 | public ValueTask ExecuteReaderAsync(IServicesFactory factory, BsonDocument parameters)
38 | {
39 | using var _pc = PERF_COUNTER(31, nameof(ExecuteReaderAsync), nameof(SelectStatement));
40 |
41 | // get dependencies
42 | var walIndexService = factory.WalIndexService;
43 | var queryService = factory.QueryService;
44 |
45 | // get next read version without open a new transaction
46 | var readVersion = walIndexService.GetNextReadVersion();
47 |
48 | // create cursor after query optimizer and create enumerator pipeline
49 | var cursor = queryService.CreateCursor(_query, parameters, readVersion);
50 |
51 | // create concrete class to reader cursor
52 | IDataReader reader = _explain ?
53 | new BsonScalarReader(cursor.Query.Collection, cursor.GetExplainPlan()) : // for explain plain
54 | factory.CreateDataReader(cursor, _fetchSize, factory);
55 |
56 | return ValueTask.FromResult(reader);
57 | }
58 |
59 | public ValueTask ExecuteAsync(IServicesFactory factory, BsonDocument parameters) => throw new NotSupportedException();
60 | }
61 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Core/RowID.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal struct RowID : IEquatable, IIsEmpty
4 | {
5 | public uint PageID; // 4
6 | public ushort Index; // 2
7 | public ushort Reserved; // 2
8 |
9 | public static readonly RowID Empty = new () { PageID = uint.MaxValue, Index = ushort.MaxValue };
10 |
11 | public bool IsEmpty => this.PageID == uint.MaxValue && this.Index == ushort.MaxValue;
12 |
13 | public RowID()
14 | {
15 | }
16 |
17 | public RowID(uint pageID, ushort index)
18 | {
19 | this.PageID = pageID;
20 | this.Index = index;
21 | }
22 |
23 | public bool Equals(RowID other)
24 | {
25 | return this.PageID == other.PageID && this.Index == other.Index;
26 | }
27 | public override bool Equals(object? other) => this.Equals((RowID)other!);
28 |
29 | public static bool operator ==(RowID left, RowID right)
30 | {
31 | return left.PageID == right.PageID && left.Index == right.Index;
32 | }
33 |
34 | public static bool operator !=(RowID left, RowID right)
35 | {
36 | return !(left == right);
37 | }
38 |
39 | public override int GetHashCode() => HashCode.Combine(PageID, Index);
40 |
41 | public override string ToString()
42 | {
43 | return IsEmpty ? "" : $"{PageID:0000}:{Index:00}";
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Core/Sequence.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal struct Sequence : IIsEmpty
4 | {
5 | public int LastInt;
6 | public long LastLong;
7 |
8 | public bool IsEmpty => this.LastInt == int.MaxValue && this.LastLong == long.MaxValue;
9 |
10 | public Sequence()
11 | {
12 | this.Reset();
13 | }
14 |
15 | public void Reset()
16 | {
17 | this.LastInt = int.MaxValue;
18 | this.LastLong = long.MaxValue;
19 | }
20 |
21 | public override string ToString() => Dump.Object(this);
22 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Enums/CheckpointActionType.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal enum CheckpointActionType : byte
4 | {
5 | CopyToDataFile = 0,
6 | CopyToTempFile = 1,
7 | ClearPage = 2
8 | }
9 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Enums/EngineState.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | ///
4 | /// All engine state
5 | ///
6 | public enum EngineState
7 | {
8 | ///
9 | /// Initial state - all services are disposed no touch on file
10 | ///
11 | Close,
12 |
13 | ///
14 | /// Database are in recovery process
15 | ///
16 | Recovery,
17 |
18 | ///
19 | /// Database are created and initialized ok. Any recovery already made.
20 | /// Ready to use
21 | ///
22 | Open,
23 |
24 | ///
25 | /// Database are in shutdown process - called by user or Critical exception.
26 | /// No one can use until back to initial state: Closed
27 | ///
28 | Shutdown,
29 |
30 | ///
31 | /// Database are in rebuild process
32 | ///
33 | Rebuild
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Enums/PageType.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal enum PageType : byte
4 | {
5 | Empty = 0,
6 | AllocationMap = 1,
7 | Index = 2,
8 | Data = 3,
9 | Unknown = 255
10 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Extend/ExtendLocation.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal struct ExtendLocation : IIsEmpty
4 | {
5 | public readonly int AllocationMapID; // 4
6 | public readonly int ExtendIndex; // 4
7 |
8 | public bool IsEmpty => this.AllocationMapID == -1 && this.ExtendIndex == -1;
9 |
10 | public int ExtendID => this.IsEmpty ? -1 : (this.AllocationMapID * AM_EXTEND_COUNT) + this.ExtendIndex;
11 |
12 | public uint FirstPageID => (uint)(this.AllocationMapID * AM_PAGE_STEP + this.ExtendIndex * AM_EXTEND_SIZE + 1);
13 |
14 | public ExtendLocation()
15 | {
16 | this.AllocationMapID = 0;
17 | this.ExtendIndex = 0;
18 | }
19 |
20 | public ExtendLocation(int allocationMapID, int extendIndex)
21 | {
22 | this.AllocationMapID = allocationMapID;
23 | this.ExtendIndex = extendIndex;
24 |
25 | ENSURE(extendIndex < AM_EXTEND_COUNT, new { self = this });
26 | }
27 |
28 | public ExtendLocation(int extendID)
29 | {
30 | this.AllocationMapID = extendID / AM_EXTEND_COUNT;
31 | this.ExtendIndex = extendID % AM_EXTEND_COUNT;
32 | }
33 |
34 | public override string ToString() => Dump.Object(this);
35 | }
36 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Extend/ExtendPageValue.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal enum ExtendPageValue : byte
4 | {
5 | Empty = 0, // 000
6 | Data = 1, // 001
7 | Index = 2, // 010
8 | // 3-6 reserved
9 | Full = 7, // 111
10 |
11 | NoChange = 255
12 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Log/CheckpointAction.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal struct CheckpointAction
4 | {
5 | public CheckpointActionType Action;
6 | public uint PositionID;
7 | public uint TargetPositionID; // used only in CopyToDataFile and CopyToTemp (MaxValue for ClearPage)
8 | public bool MustClear; // clear page PositionID
9 |
10 | public override string ToString()
11 | {
12 | return Dump.Object(new { Action, PositionID = Dump.PageID(PositionID), TargetPositionID = Dump.PageID(TargetPositionID), MustClear });
13 | }
14 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Log/LogPageHeader.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal struct LogPageHeader
4 | {
5 | public uint PositionID;
6 | public uint PageID;
7 | public int TransactionID;
8 | public bool IsConfirmed;
9 | }
10 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Log/LogPosition.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal struct LogPosition : IEqualityComparer
4 | {
5 | public uint PositionID;
6 | public uint PageID;
7 | public uint PhysicalID;
8 | public bool IsConfirmed;
9 |
10 | public bool Equals(LogPosition x, LogPosition y)
11 | {
12 | return x.PositionID == y.PositionID;
13 | }
14 |
15 | public int GetHashCode(LogPosition obj) => HashCode.Combine(PositionID, PageID, PhysicalID, IsConfirmed);
16 |
17 | public override string ToString()
18 | {
19 | return Dump.Object(new { PhysicalID = Dump.PageID(PhysicalID), PositionID = Dump.PageID(PositionID), PageID = Dump.PageID(PageID), IsConfirmed });
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Query/Cursor.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal class Cursor : IDisposable, IIsEmpty
4 | {
5 | public int CursorID { get; }
6 |
7 | public Query Query { get; }
8 | public BsonDocument Parameters { get; }
9 | public int ReadVersion { get; }
10 | public IPipeEnumerator Enumerator { get; }
11 |
12 | public int FetchCount { get; set; } = 0;
13 | public int Offset { get; set; } = 0;
14 | public bool IsRunning { get; set; } = false;
15 |
16 | public DateTime Start { get; } = DateTime.UtcNow;
17 | public TimeSpan ElapsedTime { get; set; } = TimeSpan.Zero;
18 |
19 | public BsonDocument? NextDocument { get; set; }
20 |
21 | public bool IsEmpty => this.CursorID == 0;
22 |
23 | public static Cursor Empty = new Cursor();
24 |
25 | public Cursor()
26 | {
27 | this.CursorID = 0;
28 | this.Parameters = BsonDocument.Empty;
29 | this.ReadVersion = 0;
30 | this.Enumerator = null;
31 | }
32 |
33 | public Cursor(Query query, BsonDocument parameters, int readVersion, IPipeEnumerator enumerator)
34 | {
35 | this.CursorID = Randomizer.Next(1000, int.MaxValue); // create a random cursorID
36 | this.Query = query;
37 | this.Parameters = parameters;
38 | this.ReadVersion = readVersion;
39 | this.Enumerator = enumerator;
40 | }
41 |
42 | public BsonArray GetExplainPlan()
43 | {
44 | if (this.Enumerator is null) return BsonArray.Empty;
45 |
46 | var explain = new ExplainPlainBuilder();
47 |
48 | this.Enumerator.GetPlan(explain, 0);
49 |
50 | return explain.ToBsonArray();
51 | }
52 |
53 | public void Dispose()
54 | {
55 | this.Enumerator.Dispose();
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Query/ExplainPlainBuilder.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal class ExplainPlainBuilder
4 | {
5 | private List<(string info, int deep)> _infos = new();
6 |
7 | public void Add(string info, int deep)
8 | {
9 | _infos.Add((info, deep));
10 | }
11 |
12 | public BsonArray ToBsonArray()
13 | {
14 | var deeps = _infos.Max(x => x.deep);
15 | var lines = _infos
16 | .OrderByDescending(x => x.deep)
17 | .Select(x => "".PadRight(deeps - x.deep, ' ') + "> " + x.info)
18 | .Select(x => new BsonString(x))
19 | .ToArray();
20 |
21 | return BsonArray.FromArray(lines);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Query/Into.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | public struct Into : IIsEmpty
4 | {
5 | public readonly static Into Empty = new ("", BsonAutoId.ObjectId);
6 |
7 | public readonly string Collection;
8 | public readonly BsonAutoId AutoId;
9 |
10 | public Into(string store, BsonAutoId autoId)
11 | {
12 | this.Collection = store;
13 | this.AutoId = autoId;
14 | }
15 |
16 | public bool IsEmpty => string.IsNullOrEmpty(Collection);
17 |
18 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Query/OrderBy.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | ///
4 | /// Represent an OrderBy definition
5 | ///
6 | public struct OrderBy : IIsEmpty
7 | {
8 | public static OrderBy Empty = new(BsonExpression.Empty, 0);
9 |
10 | public BsonExpression Expression { get; }
11 |
12 | public int Order { get; set; }
13 |
14 | public bool IsEmpty => this.Expression.IsEmpty && this.Order == 0;
15 |
16 | public OrderBy(BsonExpression expression, int order)
17 | {
18 | this.Expression = expression;
19 | this.Order = order;
20 | }
21 |
22 | public override string ToString() => IsEmpty ? "" :
23 | $"{Expression} {(Order > 0 ? "ASC" : "DESC")}";
24 | }
25 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Query/PipeContext.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal struct PipeContext
4 | {
5 | public readonly IDataService DataService;
6 | public readonly IIndexService IndexService;
7 | public readonly BsonDocument QueryParameters;
8 |
9 | public PipeContext(IDataService dataService, IIndexService indexService, BsonDocument queryParameters)
10 | {
11 | this.DataService = dataService;
12 | this.IndexService = indexService;
13 | this.QueryParameters = queryParameters;
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Query/PipeEmit.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | ///
4 | /// Structure to define enumerators emit after pipe (indexBlockID, dataBlockID and/or value)
5 | ///
6 | internal readonly struct PipeEmit
7 | {
8 | public readonly bool IndexNodeID;
9 | public readonly bool DataBlockID;
10 | public readonly bool Value;
11 |
12 | public PipeEmit(bool indexNodeID, bool dataBlockID, bool value)
13 | {
14 | this.IndexNodeID = indexNodeID;
15 | this.DataBlockID = dataBlockID;
16 | this.Value = value;
17 | }
18 |
19 | public override string ToString() => Dump.Object(this);
20 | }
21 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Query/PipeValue.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal readonly struct PipeValue : IIsEmpty
4 | {
5 | public readonly RowID IndexNodeID;
6 | public readonly RowID DataBlockID;
7 | public readonly BsonValue Value;
8 |
9 | public static readonly PipeValue Empty = new();
10 |
11 | public readonly bool IsEmpty => this.IndexNodeID.IsEmpty && this.DataBlockID.IsEmpty && this.Value.IsNull;
12 |
13 | public PipeValue(RowID indexNodeID, RowID dataBlockID)
14 | {
15 | this.IndexNodeID = indexNodeID;
16 | this.DataBlockID = dataBlockID;
17 | this.Value = BsonValue.Null;
18 | }
19 |
20 | public PipeValue(RowID indexNodeID, RowID dataBlockID, BsonValue value)
21 | {
22 | this.IndexNodeID = indexNodeID;
23 | this.DataBlockID = dataBlockID;
24 | this.Value = value;
25 | }
26 |
27 | public PipeValue(BsonValue value)
28 | {
29 | this.IndexNodeID = RowID.Empty;
30 | this.DataBlockID = RowID.Empty;
31 | this.Value = value;
32 | }
33 |
34 | public PipeValue()
35 | {
36 | this.IndexNodeID = RowID.Empty;
37 | this.DataBlockID = RowID.Empty;
38 | this.Value = BsonValue.Null;
39 | }
40 |
41 | public override string ToString() => Dump.Object(this);
42 | }
43 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Query/Query.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | ///
4 | ///
5 | public class Query
6 | {
7 | #region Constants
8 |
9 | ///
10 | /// Indicate when a query must execute in ascending order
11 | ///
12 | public const int Ascending = 1;
13 |
14 | ///
15 | /// Indicate when a query must execute in descending order
16 | ///
17 | public const int Descending = -1;
18 |
19 | #endregion
20 |
21 | public required string Collection { get; init; }
22 | public SelectFields Select { get; init; } = SelectFields.Root;
23 | public bool Distinct { get; init; } = false;
24 | public Into Into { get; init; } = Into.Empty;
25 | public IReadOnlyList Includes { get; init; } = Array.Empty();
26 | public BsonExpression Where { get; init; } = BsonExpression.Empty;
27 | public BsonExpression GroupBy { get; set; } = BsonExpression.Empty;
28 | public BsonExpression Having { get; init; } = BsonExpression.Empty;
29 | public OrderBy OrderBy { get; init; } = OrderBy.Empty;
30 | public int Offset { get; init; } = 0;
31 | public int Limit { get; init; } = int.MaxValue;
32 |
33 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Query/Resultset.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | public struct Resultset : IDisposable
4 | {
5 | public int From;
6 | public int To;
7 | public int DocumentCount;
8 | public bool HasMore;
9 | public readonly SharedArray Results;
10 |
11 | public Resultset(int fetchSize)
12 | {
13 | this.Results = SharedArray.Rent(fetchSize);
14 | }
15 |
16 | public override string ToString() => Dump.Object(this);
17 |
18 | public void Dispose()
19 | {
20 | this.Results.Dispose();
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Query/SelectField.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | public readonly struct SelectField
4 | {
5 | private static Dictionary> _aggregateMethods = new(StringComparer.OrdinalIgnoreCase)
6 | {
7 | ["COUNT"] = (e) => new CountFunc(e),
8 | ["MIN"] = (e) => new MinFunc(e),
9 | ["MAX"] = (e) => new MaxFunc(e),
10 | ["FIRST"] = (e) => new FirstFunc(e),
11 | ["LAST"] = (e) => new LastFunc(e),
12 | ["AVG"] = (e) => new AvgFunc(e),
13 | ["SUM"] = (e) => new SumFunc(e),
14 | ["ANY"] = (e) => new AnyFunc(e),
15 | ["ARRAY"] = (e) => new ArrayFunc(e)
16 | };
17 |
18 | public readonly string Name;
19 | public readonly bool Hidden;
20 | public readonly BsonExpression Expression;
21 |
22 | public SelectField(string name, bool hidden, BsonExpression expression)
23 | {
24 | this.Name = name;
25 | this.Hidden = hidden;
26 | this.Expression = expression;
27 | }
28 |
29 | public bool IsAggregate =>
30 | this.Expression is CallBsonExpression call &&
31 | _aggregateMethods.ContainsKey(call.Method.Name);
32 |
33 | public IAggregateFunc CreateAggregateFunc()
34 | {
35 | var call = this.Expression as CallBsonExpression;
36 |
37 | var fn = _aggregateMethods[call!.Method.Name];
38 |
39 | // get first parameter from method call
40 | var expr = call.Children.FirstOrDefault()!;
41 |
42 | return fn(expr);
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Query/SelectFields.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | public readonly struct SelectFields
4 | {
5 | ///
6 | /// SELECT *
7 | ///
8 | public static readonly SelectFields Root = new (BsonExpression.Root);
9 |
10 | ///
11 | /// SELECT $._id
12 | ///
13 | public static readonly SelectFields Id = new(new SelectField[] { new SelectField("_id", false, BsonExpression.Id) });
14 |
15 | // fields
16 | public readonly BsonExpression SingleExpression;
17 | public readonly IReadOnlyList Fields;
18 |
19 | // properties
20 |
21 | ///
22 | /// Indicate this query will return root/full document. Means "SELECT *"
23 | ///
24 | public bool IsRoot => this.SingleExpression == BsonExpression.Root;
25 |
26 | ///
27 | /// Indicate this SELECT fields contains a single result (single expression document)
28 | ///
29 | public bool IsSingleExpression => Fields.Count == 0;
30 |
31 | ///
32 | /// Indicate this SELECT fields contains at least 1 aggregate expression
33 | ///
34 | public bool HasAggregate => this.Fields.Any(x => x.IsAggregate);
35 |
36 | public SelectFields(BsonExpression docExpr)
37 | {
38 | this.SingleExpression = docExpr;
39 | this.Fields = Array.Empty();
40 | }
41 |
42 | public SelectFields(IReadOnlyList fields)
43 | {
44 | this.SingleExpression = BsonExpression.Empty;
45 | this.Fields = fields;
46 | }
47 |
48 | public override string ToString()
49 | {
50 | return IsSingleExpression ?
51 | SingleExpression.ToString()! :
52 | string.Join(", ", Fields.Select(x => x.Name));
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Sort/SortItem.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal readonly struct SortItem : IIsEmpty
4 | {
5 | public readonly RowID DataBlockID;
6 | public readonly BsonValue Key;
7 |
8 | public static readonly SortItem Empty = new();
9 |
10 | public bool IsEmpty => this.DataBlockID.IsEmpty;
11 |
12 | public SortItem()
13 | {
14 | this.DataBlockID = RowID.Empty;
15 | this.Key = BsonValue.MinValue;
16 | }
17 |
18 | public SortItem(RowID dataBlockID, BsonValue key)
19 | {
20 | this.DataBlockID = dataBlockID;
21 | this.Key = key;
22 | }
23 |
24 | public unsafe int GetBytesCount()
25 | {
26 | return sizeof(RowID) +
27 | 1 + // data type
28 | (this.Key.IsString || this.Key.IsBinary ? sizeof(int) : 0) + // 4 for variant length
29 | this.Key.GetBytesCountCached();
30 | }
31 |
32 | public override string ToString() => Dump.Object(this);
33 | }
34 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Structures/Sort/SortItemDocument.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal readonly struct SortItemDocument
4 | {
5 | public readonly RowID DataBlockID;
6 | public readonly BsonValue Key;
7 | public readonly BsonDocument Document;
8 |
9 | public SortItemDocument(RowID dataBlockID, BsonValue key, BsonDocument document)
10 | {
11 | this.DataBlockID = dataBlockID;
12 | this.Key = key;
13 | this.Document = document;
14 | }
15 |
16 | public override string ToString() => Dump.Object(this);
17 | }
18 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Transaction/Transaction.Abort.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | ///
4 | ///
5 | internal partial class Transaction : ITransaction
6 | {
7 | public unsafe void Abort()
8 | {
9 | using var _pc = PERF_COUNTER(150, nameof(Abort), nameof(Transaction));
10 |
11 | // add pages to cache or decrement sharecount
12 | foreach (var ptr in _localPages.Values)
13 | {
14 | var page = (PageMemory*)ptr;
15 |
16 | if (page->IsDirty)
17 | {
18 | _memoryFactory.DeallocatePage(page);
19 | }
20 | else
21 | {
22 | // test if page is came from the cache
23 | if (page->IsPageInCache)
24 | {
25 | // return page to cache
26 | _memoryCache.ReturnPageToCache(page);
27 | }
28 | else
29 | {
30 | // try add this page in cache
31 | var added = _memoryCache.AddPageInCache(page);
32 |
33 | if (!added)
34 | {
35 | _memoryFactory.DeallocatePage(page);
36 | }
37 | }
38 | }
39 | }
40 |
41 | // clear page buffer references
42 | _localPages.Clear();
43 | _walDirtyPages.Clear();
44 |
45 | // restore initial values in allocation map to return original state before any change
46 | if (_initialExtendValues.Count > 0)
47 | {
48 | _allocationMapService.RestoreExtendValues(_initialExtendValues);
49 | }
50 |
51 | _initialExtendValues.Clear();
52 | }
53 | }
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Unsafe/Enumerator/IndexNodeEnumerator.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | unsafe internal class IndexNodeEnumerator : IEnumerator
4 | {
5 | private readonly IIndexService _indexService;
6 | private readonly IndexDocument _indexDocument;
7 |
8 | private IndexNodeResult _current;
9 | private bool _init = false;
10 | private bool _eof = false;
11 | private RowID _nextID = RowID.Empty;
12 |
13 | public IndexNodeEnumerator(IIndexService indexService, IndexDocument indexDocument)
14 | {
15 | _indexService = indexService;
16 | _indexDocument = indexDocument;
17 |
18 | _current = IndexNodeResult.Empty;
19 | }
20 |
21 | public IndexNodeResult Current => _current;
22 | object IEnumerator.Current => _current;
23 |
24 | public bool MoveNext()
25 | {
26 | if (_eof) return false;
27 |
28 | if (_init == false)
29 | {
30 | _init = true;
31 |
32 | var head = _indexService.GetNode(_indexDocument.HeadIndexNodeID);
33 |
34 | if (head[0]->NextID == _indexDocument.TailIndexNodeID) return false;
35 |
36 | _current = _indexService.GetNode(head[0]->NextID);
37 |
38 | // buffer next in level 0
39 | _nextID = _current[0]->NextID;
40 |
41 | return true;
42 | }
43 |
44 | if (_nextID == _indexDocument.TailIndexNodeID)
45 | {
46 | _eof = true;
47 | return false;
48 | }
49 |
50 | _current = _indexService.GetNode(_nextID);
51 |
52 | // buffer next in level 0
53 | _nextID = _current[0]->NextID;
54 |
55 | return true;
56 | }
57 |
58 | public void Reset()
59 | {
60 | _current = IndexNodeResult.Empty;
61 | _init = false;
62 | _eof = false;
63 | }
64 |
65 | public void Dispose()
66 | {
67 | this.Reset();
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Unsafe/IndexKey/IndexKey.GetSize.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | unsafe internal partial struct IndexKey
4 | {
5 | ///
6 | /// Get how many bytes IndexKey structure will need to represent this BsonValue (should be padded)
7 | ///
8 | public static int GetSize(BsonValue value, out int valueSize)
9 | {
10 | var maxKeyLength = MAX_INDEX_KEY_SIZE - sizeof(IndexKey);
11 |
12 | valueSize = value.Type switch
13 | {
14 | BsonType.MaxValue => 0,
15 | BsonType.MinValue => 0,
16 | BsonType.Null => 0,
17 | BsonType.Boolean => 0, // use indexKey header hi 4-bytes
18 | BsonType.Int32 => 0, // use indexKey header hi 4-bytes
19 | BsonType.Int64 => sizeof(long), // 8
20 | BsonType.Double => sizeof(double), // 8
21 | BsonType.DateTime => sizeof(DateTime), // 8
22 | BsonType.ObjectId => sizeof(ObjectId), // 12
23 | BsonType.Decimal => sizeof(decimal), // 16
24 | BsonType.Guid => sizeof(Guid), // 16
25 | BsonType.String => Encoding.UTF8.GetByteCount(value.AsString),
26 | BsonType.Binary => value.AsBinary.Length,
27 | _ => throw ERR($"This object type `{value.Type}` are not supported as an index key")
28 | };
29 |
30 | if (valueSize > maxKeyLength) throw ERR($"index value too excedded {maxKeyLength}");
31 |
32 | var header = sizeof(IndexKey);
33 | var padding = valueSize % 8 > 0 ? 8 - (valueSize % 8) : 0;
34 | var result = header + valueSize + padding;
35 |
36 | return result;
37 | //Console.WriteLine(Dump.Object(new { Type = value.Type, header, valueSize, padding, result }));
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Unsafe/IndexKey/IndexKey.ToBsonValue.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | unsafe internal partial struct IndexKey
4 | {
5 | public static BsonValue ToBsonValue(IndexKey* indexKey)
6 | {
7 | var ptr = (nint)indexKey + sizeof(long);
8 |
9 | return indexKey->Type switch
10 | {
11 | BsonType.MinValue => BsonValue.MinValue,
12 | BsonType.MaxValue => BsonValue.MaxValue,
13 | BsonType.Null => BsonValue.Null,
14 |
15 | BsonType.Boolean => indexKey->ValueBool,
16 | BsonType.Int32 => indexKey->ValueInt32,
17 |
18 | BsonType.Int64 => *(long*)ptr,
19 | BsonType.Double => *(double*)ptr,
20 | BsonType.DateTime => *(DateTime*)ptr,
21 |
22 | BsonType.ObjectId => *(ObjectId*)ptr,
23 | BsonType.Guid => *(Guid*)ptr,
24 | BsonType.Decimal => *(decimal*)ptr,
25 |
26 | BsonType.String => Encoding.UTF8.GetString((byte*)ptr, indexKey->KeyLength),
27 | BsonType.Binary => new Span((byte*)ptr, indexKey->KeyLength).ToArray(),
28 |
29 | _ => throw new NotSupportedException()
30 | };
31 |
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Unsafe/IndexKey/IndexKey.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | [StructLayout(LayoutKind.Explicit, Size = 8, CharSet = CharSet.Ansi)]
4 | unsafe internal partial struct IndexKey
5 | {
6 | [FieldOffset(0)] public BsonType Type; // 1
7 | [FieldOffset(1)] public byte KeyLength; // 1 // do not include padding
8 | [FieldOffset(2)] public ushort Reserved; // 2
9 |
10 | [FieldOffset(4)] public bool ValueBool; // 1
11 | [FieldOffset(4)] public int ValueInt32; // 4
12 |
13 | public bool IsNull => this.Type == BsonType.Null;
14 | public bool IsMinValue => this.Type == BsonType.MinValue;
15 | public bool IsMaxValue => this.Type == BsonType.MaxValue;
16 |
17 | ///
18 | /// Get how many bytes, in memory, this IndexKey are using
19 | ///
20 | public int IndexKeySize
21 | {
22 | get
23 | {
24 | var header = sizeof(IndexKey);
25 | var valueSize = this.Type switch
26 | {
27 | BsonType.Boolean => 0, // 0 (1 but use header hi-space)
28 | BsonType.Int32 => 0, // 0 (4 but use header hi-space)
29 | _ => this.KeyLength
30 | };
31 | var padding = valueSize % 8 > 0 ? 8 - (valueSize % 8) : 0;
32 |
33 | return header + valueSize + padding;
34 | }
35 | }
36 |
37 | public override string ToString()
38 | {
39 | fixed(IndexKey* indexKey = &this)
40 | {
41 | var value = ToBsonValue(indexKey);
42 |
43 | return Dump.Object(new { Type, KeyLength, IndexKeySize, Value = value.ToString() });
44 | }
45 | }
46 |
47 | public static void CopyIndexKey(IndexKey* sourceIndexKey, IndexKey* targetIndexKey)
48 | {
49 | // get span from each page pointer
50 | var sourceSpan = new Span(sourceIndexKey, PAGE_SIZE);
51 | var targetSpan = new Span(targetIndexKey, PAGE_SIZE);
52 |
53 | sourceSpan.CopyTo(targetSpan);
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Unsafe/PageMemory/PageMemory.IndexNode.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | unsafe internal partial struct PageMemory // PageMemory.IndexNode
4 | {
5 | public static void InitializeAsIndexPage(PageMemory* page, uint pageID, byte colID)
6 | {
7 | page->PageID = pageID;
8 | page->PageType = PageType.Index;
9 | page->ColID = colID;
10 |
11 | page->IsDirty = true;
12 | }
13 |
14 | public static IndexNodeResult InsertIndexNode(PageMemory* page, byte slot, byte levels, BsonValue key, RowID dataBlockID, out bool defrag, out ExtendPageValue newPageValue)
15 | {
16 | // get a new index block
17 | var newIndex = PageMemory.GetFreeIndex(page);
18 |
19 | // get new rowid
20 | var indexNodeID = new RowID(page->PageID, newIndex);
21 |
22 | var nodeSize = IndexNode.GetSize(levels, key);
23 |
24 | // get page segment for this indexNode
25 | var segment = PageMemory.InsertSegment(page, (ushort)nodeSize, newIndex, true, out defrag, out newPageValue);
26 |
27 | var indexNode = (IndexNode*)((nint)page + segment->Location);
28 |
29 | // initialize indexNode
30 | indexNode->Slot = slot;
31 | indexNode->Levels = levels;
32 | indexNode->Reserved1 = 0;
33 | indexNode->Reserved2 = 0;
34 | indexNode->DataBlockID = dataBlockID;
35 | indexNode->NextNodeID = RowID.Empty;
36 |
37 | var levelPtr = (IndexNodeLevel*)((nint)indexNode + sizeof(IndexNode));
38 |
39 | for (var l = 0; l < levels; l++)
40 | {
41 | levelPtr->NextID = levelPtr->PrevID = RowID.Empty;
42 | levelPtr++;
43 | }
44 |
45 | // after write all level nodes, levelPtr are at IndexKey location
46 | var indexKey = (IndexKey*)levelPtr;
47 |
48 | // get new indexKey and copy to memory
49 | IndexKey.Initialize(indexKey, key);
50 |
51 | return new IndexNodeResult(page, indexNodeID);
52 | }
53 | }
54 |
55 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Unsafe/Results/DataBlockResult.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | unsafe internal struct DataBlockResult : IIsEmpty
4 | {
5 | public RowID DataBlockID;
6 |
7 | public PageMemory* Page;
8 | public PageSegment* Segment;
9 | public DataBlock* DataBlock;
10 |
11 | public static DataBlockResult Empty = new() { DataBlockID = RowID.Empty, Page = default };
12 |
13 | public bool IsEmpty => this.DataBlockID.IsEmpty;
14 |
15 | public int ContentLength => this.Segment->Length - sizeof(DataBlock) - this.DataBlock->Padding;
16 | public int DocumentLength => this.DataBlock->Extend ? -1 :
17 | *(int*)((nint)this.Page + this.Segment->Location + sizeof(DataBlock)); // read first 4 bytes on datablock as int32 in first page only
18 |
19 | public DataBlockResult(PageMemory* page, RowID dataBlockID)
20 | {
21 | ENSURE(page->PageID == dataBlockID.PageID);
22 |
23 | this.Page = page;
24 | this.DataBlockID = dataBlockID;
25 |
26 | this.Reload();
27 | }
28 |
29 | public void Reload()
30 | {
31 | this.Segment = PageMemory.GetSegmentPtr(this.Page, this.DataBlockID.Index);
32 | this.DataBlock = (DataBlock*)((nint)this.Page + this.Segment->Location);
33 | }
34 |
35 | ///
36 | /// Get DataContent as Span
37 | ///
38 | public Span AsSpan()
39 | {
40 | return new Span((byte*)((nint)this.Page + this.Segment->Location + sizeof(DataBlock)), this.ContentLength);
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Unsafe/Results/IndexNodeResult.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | unsafe internal struct IndexNodeResult : IIsEmpty
4 | {
5 | public RowID IndexNodeID;
6 |
7 | public PageMemory* Page;
8 | public PageSegment* Segment;
9 | public IndexNode* Node;
10 | public IndexKey* Key;
11 |
12 | ///
13 | /// Shortcut for get Node->DataBlockID (safe)
14 | ///
15 | public RowID DataBlockID => this.Node->DataBlockID;
16 |
17 | ///
18 | /// Shortcut for get/set Node->NextNodeID (safe) MUST set page as dirty in set!!
19 | ///
20 | public RowID NextNodeID { get => this.Node->NextNodeID; set => this.Node->NextNodeID = value; }
21 |
22 | public static IndexNodeResult Empty = new() { IndexNodeID = RowID.Empty, Page = default };
23 |
24 | public bool IsEmpty => this.IndexNodeID.IsEmpty;
25 |
26 | public IndexNodeResult(PageMemory* page, RowID indexNodeID)
27 | {
28 | ENSURE(page->PageID == indexNodeID.PageID);
29 |
30 | this.Page = page;
31 | this.IndexNodeID = indexNodeID;
32 |
33 | this.Reload();
34 |
35 | ENSURE(this.Segment->AsSpan(page).IsFullZero() == false);
36 | ENSURE(this.Node->Levels > 0 && this.Node->Levels <= INDEX_MAX_LEVELS);
37 | }
38 |
39 | public void Reload()
40 | {
41 | // load all pointer based on indexNodeID and &page
42 | this.Segment = PageMemory.GetSegmentPtr(this.Page, this.IndexNodeID.Index);
43 | this.Node = (IndexNode*)((nint)this.Page + this.Segment->Location);
44 | var keyOffset = this.Segment->Location + sizeof(IndexNode) + (this.Node->Levels * sizeof(IndexNodeLevel));
45 | this.Key = (IndexKey*)((nint)this.Page + keyOffset);
46 | }
47 |
48 | public IndexNodeLevel* this[int level]
49 | {
50 | get
51 | {
52 | ENSURE(level <= this.Node->Levels);
53 |
54 | var ptr = ((nint)this.Node + sizeof(IndexNode) + (level * sizeof(IndexNodeLevel)));
55 |
56 | return (IndexNodeLevel*)ptr;
57 | }
58 | }
59 |
60 | public override string ToString()
61 | {
62 | return Dump.Object( new { IndexNodeID, DataBlockID = Node->DataBlockID, KeyType = Key->Type, KeyValue = IndexKey.ToBsonValue(Key) });
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Unsafe/Sort/SortContainer2.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | [StructLayout(LayoutKind.Sequential, Size = (CONTAINER_SORT_SIZE_IN_PAGES * PAGE_SIZE) , Pack = 1)]
4 | unsafe internal struct SortContainer2
5 | {
6 | }
7 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Unsafe/Sort/SortItem2.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal struct SortItem2 // 16 + (n * 8)
4 | {
5 | public RowID DataBlockID; // 8
6 | public IndexKey Key; // 8 + (n * 8)
7 | }
8 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Unsafe/Sort/SortPage2.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | [StructLayout(LayoutKind.Sequential, Size = PAGE_SIZE, Pack = 1)]
4 | internal struct SortPage2
5 | {
6 | public int ItemsCount; // 4
7 | public int Reserved; // 4
8 | }
9 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Unsafe/Structs/DataBlock.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
4 | unsafe internal struct DataBlock // 12
5 | {
6 | public byte DataFormat; // 1
7 | public bool Extend; // 1
8 | public byte Padding; // 1 (0-7)
9 | [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
10 | public byte Reserved; // 1
11 |
12 | public RowID NextBlockID; // 8
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Unsafe/Structs/IndexNode.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | unsafe internal struct IndexNode // 24
4 | {
5 | public byte Slot; // 1
6 | public byte Levels; // 1
7 | [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
8 | public ushort Reserved1; // 2
9 | [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
10 | public uint Reserved2; // 4
11 |
12 | public RowID DataBlockID; // 8
13 | public RowID NextNodeID; // 8
14 |
15 |
16 | #region Static Helpers
17 |
18 | ///
19 | /// Calculate how many bytes this node will need on page block
20 | ///
21 | public unsafe static int GetSize(int levels, BsonValue key)
22 | {
23 | return
24 | sizeof(IndexNode) +
25 | (levels * sizeof(IndexNodeLevel)) + // prev/next
26 | IndexKey.GetSize(key, out _);
27 | }
28 |
29 | #endregion
30 | }
31 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Unsafe/Structs/IndexNodeLevel.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal struct IndexNodeLevel // 16
4 | {
5 | public RowID PrevID; // 8
6 | public RowID NextID; // 8
7 |
8 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
9 | public RowID GetNext(int order)
10 | {
11 | return order > 0 ? this.NextID : this.PrevID;
12 | }
13 |
14 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
15 | public RowID GetPrev(int order)
16 | {
17 | return order > 0 ? this.PrevID : this.NextID;
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/LiteDB/Engine/Unsafe/Structs/PageSegment.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | ///
4 | /// Represent a single page segment with Location (position) and Length
5 | ///
6 | unsafe internal struct PageSegment : IIsEmpty
7 | {
8 | public ushort Location; // 2
9 | public ushort Length; // 2
10 |
11 | ///
12 | /// Get final location (Location + Length)
13 | ///
14 | public ushort EndLocation => (ushort)(this.Location + this.Length);
15 |
16 | ///
17 | /// Indicate this segment are clear (no reference)
18 | ///
19 | public bool IsEmpty => this.Location == 0 && this.Length == 0;
20 |
21 | public PageSegment()
22 | {
23 | }
24 |
25 | public PageSegment(ushort location, ushort length)
26 | {
27 | this.Location = location;
28 | this.Length = length;
29 | }
30 |
31 | public void Reset()
32 | {
33 | this.Location = 0;
34 | this.Length = 0;
35 | }
36 |
37 | public Span AsSpan(PageMemory* page)
38 | {
39 | ENSURE(!this.IsEmpty);
40 |
41 | var ptr = (byte*)((nint)page + this.Location);
42 |
43 | return new Span(ptr, this.Length);
44 | }
45 |
46 | public override string ToString() => Dump.Object(this);
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/LiteDB/Expression/Attributes/VolatileAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// When a method are decorated with this attribute means that this method can have different results for same input
5 | ///
6 | internal class VolatileAttribute: Attribute
7 | {
8 | }
9 |
--------------------------------------------------------------------------------
/src/LiteDB/Expression/BsonExpressionContext.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal class BsonExpressionContext
4 | {
5 | public BsonValue Root { get; }
6 | public BsonValue Current { get; set; }
7 | public BsonDocument Parameters { get; }
8 | public Collation Collation { get; }
9 |
10 | public BsonExpressionContext(BsonValue root, BsonDocument parameters, Collation collation)
11 | {
12 | this.Root = root;
13 | this.Current = this.Root;
14 | this.Parameters = parameters;
15 | this.Collation = collation;
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/src/LiteDB/Expression/BsonExpressionType.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// Represent all types of BsonExpressions
5 | ///
6 | public enum BsonExpressionType : byte
7 | {
8 | Constant = 1,
9 |
10 | ArrayIndex = 2,
11 |
12 | Array = 4,
13 | Document = 5,
14 |
15 | Parameter = 6,
16 | Call = 7,
17 | Root = 8,
18 | Current = 9,
19 | Path = 10,
20 |
21 | Modulo = 11,
22 | Add = 12,
23 | Subtract = 13,
24 | Multiply = 14,
25 | Divide = 15,
26 |
27 | Equal = 16,
28 | Like = 17,
29 | Between = 18,
30 | GreaterThan = 19,
31 | GreaterThanOrEqual = 20,
32 | LessThan = 21,
33 | LessThanOrEqual = 22,
34 | NotEqual = 23,
35 | In = 24,
36 | Contains = 25,
37 |
38 | Or = 30,
39 | And = 31,
40 |
41 | Inner = 32,
42 |
43 | Conditional = 40,
44 | Map = 41,
45 | Filter = 42,
46 | Sort = 43,
47 |
48 | Empty = 255
49 | }
50 |
51 | internal static class BsonExpressionExtensions
52 | {
53 | ///
54 | /// Returns if BsonExpressionType is a predicate (return a boolean). AND/OR are not in this list
55 | ///
56 | internal static bool IsPredicate(this BsonExpressionType type)
57 | {
58 | return type == BsonExpressionType.Equal ||
59 | type == BsonExpressionType.Like ||
60 | type == BsonExpressionType.Between ||
61 | type == BsonExpressionType.GreaterThan ||
62 | type == BsonExpressionType.GreaterThanOrEqual ||
63 | type == BsonExpressionType.LessThan ||
64 | type == BsonExpressionType.LessThanOrEqual ||
65 | type == BsonExpressionType.NotEqual ||
66 | type == BsonExpressionType.In ||
67 | type == BsonExpressionType.Contains;
68 |
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/LiteDB/Expression/ExpressionMethods/Math.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal partial class BsonExpressionMethods
4 | {
5 | ///
6 | /// Apply absolute value (ABS) method in all number values
7 | ///
8 | public static BsonValue ABS(BsonValue value)
9 | {
10 | return value.Type switch
11 | {
12 | BsonType.Int32 => Math.Abs(value.AsInt32),
13 | BsonType.Int64 => Math.Abs(value.AsInt64),
14 | BsonType.Double => Math.Abs(value.AsDouble),
15 | BsonType.Decimal => Math.Abs(value.AsDecimal),
16 | _ => BsonValue.Null,
17 | };
18 | }
19 |
20 | ///
21 | /// Round number method in all number values
22 | ///
23 | public static BsonValue ROUND(BsonValue value, BsonValue digits)
24 | {
25 | if (digits.IsNumber)
26 | {
27 | switch (value.Type)
28 | {
29 | case BsonType.Int32: return value.AsInt32;
30 | case BsonType.Int64: return value.AsInt64;
31 | case BsonType.Double: return Math.Round(value.AsDouble, digits.AsInt32);
32 | case BsonType.Decimal: return Math.Round(value.AsDecimal, digits.AsInt32);
33 | }
34 |
35 | }
36 |
37 | return BsonValue.Null;
38 | }
39 |
40 | ///
41 | /// Implement POWER (x and y)
42 | ///
43 | public static BsonValue POW(BsonValue x, BsonValue y)
44 | {
45 | if (x.IsNumber && y.IsNumber)
46 | {
47 | return Math.Pow(x.AsDouble, y.AsDouble);
48 | }
49 |
50 | return BsonValue.Null;
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/LiteDB/Expression/InternalExpressions/ArrayIndexBsonExpression.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal class ArrayIndexBsonExpression : BsonExpression
4 | {
5 | public override BsonExpressionType Type => BsonExpressionType.ArrayIndex;
6 |
7 | internal override IEnumerable Children => new[] { this.Array, this.Index };
8 |
9 | public BsonExpression Array { get; }
10 |
11 | public BsonExpression Index { get; }
12 |
13 | public ArrayIndexBsonExpression(BsonExpression array, BsonExpression index)
14 | {
15 | this.Array = array;
16 | this.Index = index;
17 | }
18 |
19 | internal override BsonValue Execute(BsonExpressionContext context)
20 | {
21 | var arr = this.Array.Execute(context);
22 | var idx = this.Index.Execute(context);
23 |
24 | if (!arr.IsArray || !idx.IsNumber) return BsonValue.Null;
25 |
26 | var index = idx.AsInt32;
27 | var array = arr.AsArray;
28 |
29 | // adding support for negative values (backward)
30 | var i = index < 0 ? array.Count + index : index;
31 |
32 | if (i >= array.Count) return BsonValue.Null;
33 |
34 | return array[i];
35 | }
36 |
37 | public override bool Equals(BsonExpression expr) =>
38 | expr is ArrayIndexBsonExpression other &&
39 | other.Array.Equals(this.Array) &&
40 | other.Index.Equals(this.Index);
41 |
42 | public override int GetHashCode() => HashCode.Combine(this.Array, this.Index);
43 |
44 | public override string ToString()
45 | {
46 | return this.Array.ToString() + "[" + this.Index.ToString() + "]";
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/LiteDB/Expression/InternalExpressions/CallBsonExpression.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal class CallBsonExpression : BsonExpression
4 | {
5 | public override BsonExpressionType Type => BsonExpressionType.Call;
6 |
7 | internal override IEnumerable Children => this.Parameters;
8 |
9 | public BsonExpression[] Parameters { get; }
10 |
11 | public MethodInfo Method { get; }
12 |
13 | public bool IsVolatile { get; }
14 |
15 | private readonly bool _collation;
16 |
17 | public CallBsonExpression(MethodInfo method, BsonExpression[] parameters)
18 | {
19 | this.Method = method;
20 | this.Parameters = parameters;
21 | this.IsVolatile = method.GetCustomAttribute() is not null;
22 |
23 | _collation = method.GetParameters().FirstOrDefault()?.ParameterType == typeof(Collation);
24 | }
25 |
26 | internal override BsonValue Execute(BsonExpressionContext context)
27 | {
28 | // if contains collation parameter, must be fist parameter
29 | var values = _collation ?
30 | new object[] { context.Collation }.Union(this.Parameters.Select(x => x.Execute(context))) :
31 | this.Parameters.Select(x => x.Execute(context));
32 |
33 | return (BsonValue)this.Method.Invoke(null, values.ToArray());
34 | }
35 |
36 | public override bool Equals(BsonExpression expr) =>
37 | expr is CallBsonExpression other &&
38 | other.Method.Name == this.Method.Name &&
39 | other.Parameters.SequenceEqual(this.Parameters) &&
40 | other.IsVolatile == this.IsVolatile;
41 |
42 | public override int GetHashCode() => HashCode.Combine(this.Method, this.Parameters);
43 |
44 | public override string ToString()
45 | {
46 | return this.Method.Name + "(" + string.Join(",", this.Parameters.Select(x => x.ToString())) + ")";
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/LiteDB/Expression/InternalExpressions/ConditionalBsonExpression.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal class ConditionalBsonExpression : BsonExpression
4 | {
5 | public override BsonExpressionType Type => BsonExpressionType.Conditional;
6 |
7 | internal override IEnumerable Children => new BsonExpression[] { IfTest, TrueExpr, FalseExpr };
8 |
9 | public BsonExpression IfTest { get; }
10 | public BsonExpression TrueExpr { get; }
11 | public BsonExpression FalseExpr { get; }
12 |
13 | public ConditionalBsonExpression(BsonExpression ifTest, BsonExpression trueExpr, BsonExpression falseExpr)
14 | {
15 | this.IfTest = ifTest;
16 | this.TrueExpr = trueExpr;
17 | this.FalseExpr = falseExpr;
18 | }
19 |
20 | internal override BsonValue Execute(BsonExpressionContext context)
21 | {
22 | var result = this.IfTest.Execute(context);
23 |
24 | if (result.IsBoolean && result.AsBoolean)
25 | {
26 | return this.TrueExpr.Execute(context);
27 | }
28 | else
29 | {
30 | return this.FalseExpr.Execute(context);
31 | }
32 | }
33 |
34 | public override bool Equals(BsonExpression expr) =>
35 | expr is ConditionalBsonExpression other &&
36 | other.IfTest == this.IfTest &&
37 | other.TrueExpr == this.TrueExpr &&
38 | other.FalseExpr == this.FalseExpr;
39 |
40 | public override int GetHashCode() => HashCode.Combine(IfTest, TrueExpr, FalseExpr);
41 |
42 | public override string ToString()
43 | {
44 | return this.IfTest.ToString() + "?" + this.TrueExpr.ToString() + ":" + this.FalseExpr.ToString();
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/src/LiteDB/Expression/InternalExpressions/ConstantBsonExpression.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal class ConstantBsonExpression : BsonExpression
4 | {
5 | public override BsonExpressionType Type => BsonExpressionType.Constant;
6 |
7 | public BsonValue Value { get; }
8 |
9 | public ConstantBsonExpression(BsonValue value)
10 | {
11 | this.Value = value;
12 | }
13 |
14 | internal override BsonValue Execute(BsonExpressionContext context)
15 | {
16 | return this.Value;
17 | }
18 |
19 | public override bool Equals(BsonExpression expr) =>
20 | expr is ConstantBsonExpression other &&
21 | other.Value.Equals(this.Value);
22 |
23 | public override int GetHashCode() => this.Value.GetHashCode();
24 |
25 | public override string ToString()
26 | {
27 | return this.Value.ToString();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/LiteDB/Expression/InternalExpressions/EmptyBsonExpression.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal class EmptyBsonExpression : BsonExpression
4 | {
5 | public override BsonExpressionType Type => BsonExpressionType.Empty;
6 |
7 | public EmptyBsonExpression()
8 | {
9 | }
10 |
11 | internal override BsonValue Execute(BsonExpressionContext context)
12 | {
13 | throw new NotSupportedException();
14 | }
15 |
16 | public override bool Equals(BsonExpression expr) => expr is EmptyBsonExpression;
17 |
18 | public override int GetHashCode() => this.Type.GetHashCode();
19 |
20 | public override string ToString()
21 | {
22 | return "";
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/LiteDB/Expression/InternalExpressions/FilterBsonExpression.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal class FilterBsonExpression : BsonExpression
4 | {
5 | public override BsonExpressionType Type => BsonExpressionType.Map;
6 |
7 | internal override IEnumerable Children => new[] { this.Source, this.Selector };
8 |
9 | public BsonExpression Source { get; }
10 |
11 | public BsonExpression Selector { get; }
12 |
13 | public FilterBsonExpression(BsonExpression source, BsonExpression selector)
14 | {
15 | this.Source = source;
16 | this.Selector = selector;
17 | }
18 |
19 | internal override BsonValue Execute(BsonExpressionContext context)
20 | {
21 | IEnumerable source()
22 | {
23 | var src = this.Source.Execute(context);
24 |
25 | if (!src.IsArray) yield break;
26 |
27 | foreach(var item in src.AsArray)
28 | {
29 | context.Current = item;
30 |
31 | var value = this.Selector.Execute(context);
32 |
33 | if (value.IsBoolean && value.AsBoolean)
34 | {
35 | yield return item;
36 | }
37 |
38 | context.Current = context.Root;
39 | }
40 | };
41 |
42 | return new BsonArray(source());
43 | }
44 |
45 | public override bool Equals(BsonExpression expr) =>
46 | expr is FilterBsonExpression other &&
47 | other.Source.Equals(this.Source) &&
48 | other.Selector.Equals(this.Selector);
49 |
50 | public override int GetHashCode() => HashCode.Combine(this.Source, this.Selector);
51 |
52 | public override string ToString()
53 | {
54 | return this.Source.ToString() + "[" + this.Selector.ToString() + "]";
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/LiteDB/Expression/InternalExpressions/InnerBsonExpression.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal class InnerBsonExpression : BsonExpression
4 | {
5 | public override BsonExpressionType Type => BsonExpressionType.Inner;
6 |
7 | internal override IEnumerable Children => new[] { this.InnerExpression };
8 |
9 | public BsonExpression InnerExpression { get; }
10 |
11 | public InnerBsonExpression(BsonExpression inner)
12 | {
13 | this.InnerExpression = inner;
14 | }
15 |
16 | internal override BsonValue Execute(BsonExpressionContext context)
17 | {
18 | return this.InnerExpression.Execute(context);
19 | }
20 |
21 | public override bool Equals(BsonExpression expr) =>
22 | expr is InnerBsonExpression other &&
23 | other.InnerExpression.Equals(this.InnerExpression);
24 |
25 | public override int GetHashCode() => this.InnerExpression.GetHashCode();
26 |
27 | public override string ToString()
28 | {
29 | return "(" + this.InnerExpression.ToString() + ")";
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/LiteDB/Expression/InternalExpressions/MakeArrayBsonExpression.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal class MakeArrayBsonExpression : BsonExpression
4 | {
5 | public override BsonExpressionType Type => BsonExpressionType.Array;
6 |
7 | internal override IEnumerable Children => this.Items;
8 |
9 | public IEnumerable Items { get; }
10 |
11 | public MakeArrayBsonExpression(IEnumerable items)
12 | {
13 | this.Items = items;
14 | }
15 |
16 | internal override BsonValue Execute(BsonExpressionContext context)
17 | {
18 | return new BsonArray(this.Items.Select(x => x.Execute(context)));
19 | }
20 |
21 | public override bool Equals(BsonExpression expr) =>
22 | expr is MakeArrayBsonExpression other &&
23 | other.Items.SequenceEqual(this.Items);
24 |
25 | public override int GetHashCode() => this.Items.GetHashCode();
26 |
27 | public override string ToString()
28 | {
29 | return "[" + String.Join(",", this.Items.Select(x => x.ToString())) + "]";
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/LiteDB/Expression/InternalExpressions/MakeDocumentBsonExpression.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal class MakeDocumentBsonExpression : BsonExpression
4 | {
5 | public override BsonExpressionType Type => BsonExpressionType.Document;
6 |
7 | internal override IEnumerable Children => this.Values.Values;
8 |
9 | public IDictionary Values { get; }
10 |
11 | public MakeDocumentBsonExpression(IDictionary values)
12 | {
13 | this.Values = values;
14 | }
15 |
16 | internal override BsonValue Execute(BsonExpressionContext context)
17 | {
18 | return new BsonDocument(this.Values.ToDictionary(x => x.Key, x => x.Value.Execute(context)));
19 | }
20 |
21 | public override bool Equals(BsonExpression expr) =>
22 | expr is MakeDocumentBsonExpression other &&
23 | other.Values.Keys.SequenceEqual(this.Values.Keys) &&
24 | other.Values.Values.SequenceEqual(this.Values.Values);
25 |
26 | public override int GetHashCode() => this.Values.GetHashCode();
27 |
28 | public override string ToString()
29 | {
30 | return "{" + String.Join(",", this.Values.Select(x =>
31 | (x.Key.IsWord() ? x.Key : $"\"{x.Key}\"") + ":" +
32 | x.Value.ToString())) + "}";
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/LiteDB/Expression/InternalExpressions/MapBsonExpression.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal class MapBsonExpression : BsonExpression
4 | {
5 | public override BsonExpressionType Type => BsonExpressionType.Map;
6 |
7 | internal override IEnumerable Children => new[] { this.Source, this.Selector };
8 |
9 | public BsonExpression Source { get; }
10 |
11 | public BsonExpression Selector { get; }
12 |
13 | public MapBsonExpression(BsonExpression source, BsonExpression selector)
14 | {
15 | this.Source = source;
16 | this.Selector = selector;
17 | }
18 |
19 | internal override BsonValue Execute(BsonExpressionContext context)
20 | {
21 | return new BsonArray(getSource(this.Source, this.Selector, context));
22 |
23 | static IEnumerable getSource(BsonExpression source, BsonExpression selector, BsonExpressionContext context)
24 | {
25 | var src = source.Execute(context);
26 |
27 | if (src is BsonArray array)
28 | {
29 | foreach (var item in array)
30 | {
31 | context.Current = item;
32 |
33 | var value = selector.Execute(context);
34 |
35 | yield return value;
36 |
37 | context.Current = context.Root;
38 | }
39 | }
40 | };
41 | }
42 |
43 | public override bool Equals(BsonExpression expr) =>
44 | expr is MapBsonExpression other &&
45 | other.Source.Equals(this.Source) &&
46 | other.Selector.Equals(this.Selector);
47 |
48 | public override int GetHashCode() => HashCode.Combine(this.Source, this.Selector);
49 |
50 | public override string ToString()
51 | {
52 | return this.Source.ToString() + "=>" + this.Selector.ToString();
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/LiteDB/Expression/InternalExpressions/ParameterBsonExpression.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal class ParameterBsonExpression : BsonExpression
4 | {
5 | public override BsonExpressionType Type => BsonExpressionType.Parameter;
6 |
7 | public string Name { get; }
8 |
9 | public ParameterBsonExpression(string name)
10 | {
11 | this.Name = name;
12 | }
13 |
14 | internal override BsonValue Execute(BsonExpressionContext context)
15 | {
16 | return context.Parameters[this.Name];
17 | }
18 |
19 | public override bool Equals(BsonExpression expr) =>
20 | expr is ParameterBsonExpression other &&
21 | other.Name == this.Name;
22 |
23 | public override int GetHashCode() => this.Name.GetHashCode();
24 |
25 | public override string ToString()
26 | {
27 | return "@" + this.Name;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/LiteDB/Expression/InternalExpressions/PathBsonExpression.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal class PathBsonExpression : BsonExpression
4 | {
5 | public override BsonExpressionType Type => BsonExpressionType.Path;
6 |
7 | internal override IEnumerable Children => new[] { this.Source };
8 |
9 | public string Field { get; }
10 |
11 | public BsonExpression Source { get; }
12 |
13 | public PathBsonExpression(BsonExpression source, string field)
14 | {
15 | this.Field = field;
16 | this.Source = source;
17 | }
18 |
19 | internal override BsonValue Execute(BsonExpressionContext context)
20 | {
21 | var source = this.Source.Execute(context);
22 |
23 | if (this.Field == null) return source;
24 |
25 | if (source.IsDocument)
26 | {
27 | // return document field (or null if not exists)
28 | return source.AsDocument[this.Field];
29 | }
30 | else if (source.IsArray)
31 | {
32 | // returns document fields inside array (only for sub documents)
33 | // ex: $.Name - where $ = [{Name:.., Age:..},{Name:..}] => [Name0, Name1, ...]
34 | return new BsonArray(source.AsArray.Select(x => x.IsDocument ? x.AsDocument[this.Field] : BsonValue.Null));
35 | }
36 | else
37 | {
38 | return BsonValue.Null;
39 | }
40 | }
41 |
42 | public override bool Equals(BsonExpression expr) =>
43 | expr is PathBsonExpression other &&
44 | other.Source.Equals(this.Source) &&
45 | other.Field == this.Field;
46 |
47 | public override int GetHashCode() => HashCode.Combine(this.Source, this.Field);
48 |
49 | public override string ToString()
50 | {
51 | var field = this.Field.IsWord() ?
52 | this.Field :
53 | "[\"" + this.Field + "\"]";
54 |
55 | return this.Source.ToString() + "." + field;
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/LiteDB/Expression/InternalExpressions/ScopeBsonExpression.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal class ScopeBsonExpression : BsonExpression
4 | {
5 | public override BsonExpressionType Type => this.IsRoot ? BsonExpressionType.Root : BsonExpressionType.Current;
6 |
7 | private bool IsRoot { get; }
8 |
9 | public ScopeBsonExpression(bool root)
10 | {
11 | this.IsRoot = root;
12 | }
13 |
14 | internal override BsonValue Execute(BsonExpressionContext context)
15 | {
16 | return this.IsRoot ? context.Root : context.Current;
17 | }
18 |
19 |
20 | public override bool Equals(BsonExpression expr) =>
21 | expr is ScopeBsonExpression other &&
22 | other.IsRoot == this.IsRoot;
23 |
24 | public override int GetHashCode() => this.IsRoot.GetHashCode();
25 |
26 | public override string ToString()
27 | {
28 | return this.IsRoot ? "$" : "@";
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/LiteDB/Expression/Tokenizer/TokenType.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// ASCII char names: https://www.ascii.cl/htmlcodes.htm
5 | ///
6 | internal enum TokenType
7 | {
8 | /// {
9 | OpenBrace,
10 | /// }
11 | CloseBrace,
12 | /// [
13 | OpenBracket,
14 | /// ]
15 | CloseBracket,
16 | /// (
17 | OpenParenthesis,
18 | /// )
19 | CloseParenthesis,
20 | /// ,
21 | Comma,
22 | /// :
23 | Colon,
24 | /// ;
25 | SemiColon,
26 | /// =>
27 | Arrow,
28 | /// @
29 | At,
30 | /// #
31 | Hashtag,
32 | /// ~
33 | Til,
34 | /// .
35 | Period,
36 | /// &
37 | Ampersand,
38 | /// ?
39 | Question,
40 | /// $
41 | Dollar,
42 | /// !
43 | Exclamation,
44 | /// !=
45 | NotEquals,
46 | /// =
47 | Equals,
48 | /// >
49 | Greater,
50 | /// >=
51 | GreaterOrEquals,
52 | /// <
53 | Less,
54 | /// <=
55 | LessOrEquals,
56 | /// -
57 | Minus,
58 | /// +
59 | Plus,
60 | /// *
61 | Asterisk,
62 | /// /
63 | Slash,
64 | /// \
65 | Backslash,
66 | /// %
67 | Percent,
68 | /// "..." or '...'
69 | String,
70 | /// [0-9]+
71 | Int,
72 | /// [0-9]+.[0-9]
73 | Double,
74 | /// \n\r\t \u0032
75 | Whitespace,
76 | /// [a-Z_$]+[a-Z0-9_$]
77 | Word,
78 | EOF,
79 | Unknown,
80 |
81 | /// Uninitalized token
82 | Empty
83 | }
84 |
--------------------------------------------------------------------------------
/src/LiteDB/LiteDB.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6
5 | 11
6 | 6.0.0
7 | 6.0.0
8 | 6.0.0
9 | enable
10 | Maurício David
11 | LiteDB
12 | LiteDB - A lightweight embedded .NET NoSQL document store in a single datafile
13 | Apache
14 | en-US
15 | LiteDB
16 | LiteDB
17 | 6.0.0
18 | database nosql embedded
19 | icon_64x64.png
20 | LICENSE
21 | https://www.litedb.org
22 | https://github.com/mbdavid/LiteDB
23 | git
24 | LiteDB
25 | LiteDB
26 | true
27 | 1701;1702;1705;1591;0618
28 | bin\$(Configuration)\$(TargetFramework)\LiteDB.xml
29 | true
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/src/LiteDB/Utils/CompilerServices.cs:
--------------------------------------------------------------------------------
1 | namespace System.Runtime.CompilerServices;
2 |
3 | ///
4 | /// Class definition for works with "init;" keyword in .netstandard project
5 | ///
6 | public class IsExternalInit
7 | {
8 | }
9 |
10 | ///
11 | /// Indicates that compiler support for a particular feature is required for the location where this attribute is applied.
12 | ///
13 | [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
14 | public sealed class CompilerFeatureRequiredAttribute : Attribute
15 | {
16 | public CompilerFeatureRequiredAttribute(string featureName)
17 | {
18 | FeatureName = featureName;
19 | }
20 |
21 | ///
22 | /// The name of the compiler feature.
23 | ///
24 | public string FeatureName { get; }
25 |
26 | ///
27 | /// If true, the compiler can choose to allow access to the location where this attribute is applied if it does not understand .
28 | ///
29 | public bool IsOptional { get; init; }
30 |
31 | ///
32 | /// The used for the ref structs C# feature.
33 | ///
34 | public const string RefStructs = nameof(RefStructs);
35 |
36 | ///
37 | /// The used for the required members C# feature.
38 | ///
39 | public const string RequiredMembers = nameof(RequiredMembers);
40 | }
41 |
42 | /// Specifies that a type has required members or that a member is required.
43 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
44 | #if SYSTEM_PRIVATE_CORELIB
45 | public
46 | #else
47 | internal
48 | #endif
49 | sealed class RequiredMemberAttribute : Attribute
50 | {
51 | }
--------------------------------------------------------------------------------
/src/LiteDB/Utils/Crc8.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// Calculate CRC-8 (1 byte)
5 | /// https://blog.csdn.net/plutus_sutulp/article/details/8473377
6 | ///
7 | internal static class Crc8
8 | {
9 | static byte[] _table = new byte[256];
10 | // x8 + x7 + x6 + x4 + x2 + 1
11 | const byte poly = 0xd5;
12 |
13 | ///
14 | /// Static initializer _table
15 | ///
16 | static Crc8()
17 | {
18 | for (var i = 0; i < 256; ++i)
19 | {
20 | var temp = i;
21 |
22 | for (var j = 0; j < 8; ++j)
23 | {
24 | if ((temp & 0x80) != 0)
25 | {
26 | temp = (temp << 1) ^ poly;
27 | }
28 | else
29 | {
30 | temp <<= 1;
31 | }
32 | }
33 |
34 | _table[i] = (byte)temp;
35 | }
36 | }
37 |
38 | public static byte ComputeChecksum(Span span)
39 | {
40 | using var _pc = PERF_COUNTER(170, nameof(ComputeChecksum), nameof(Crc8));
41 |
42 | byte crc = 0;
43 |
44 | for (var i = 0; i < span.Length; i++)
45 | {
46 | var b = span[i];
47 |
48 | crc = _table[crc ^ b];
49 | }
50 |
51 | return crc;
52 | }
53 | }
--------------------------------------------------------------------------------
/src/LiteDB/Utils/Debugger/CleanerExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// A quick and dirty cleaner names from LINQ expression (debug use only)
5 | ///
6 | internal static class CleanerExtensions
7 | {
8 | ///
9 | /// Clean TypeName (remove async strings)
10 | ///
11 | public static string CleanName(this Type type)
12 | {
13 | var str = type.Name;
14 |
15 | str = Regex.Replace(str, @"^<(.*)>.*", "$1");
16 |
17 | return str;
18 | }
19 |
20 | ///
21 | /// Clean a LINQ expression
22 | ///
23 | public static string Clean(this Expression e)
24 | {
25 | var str = e.ToString();
26 |
27 | str = Regex.Replace(str, @"value\(.*?\)\.", "");
28 | str = Regex.Replace(str, @"^value\(.*\.(.*)\)$", "$1");
29 | str = Regex.Replace(str, @" AndAlso ", " && ");
30 | str = Regex.Replace(str, @" OrElse ", " || ");
31 |
32 | str = Regex.Replace(str, @"^\((.*)\)$", "$1");
33 |
34 | return str;
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/LiteDB/Utils/ExceptionExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB.Engine;
2 |
3 | internal static class ExceptionExtensions
4 | {
5 | ///
6 | /// Handle critial LiteDB exceptions to avoid
7 | ///
8 | public static void HandleError(this Exception exception, IServicesFactory factory)
9 | {
10 | // any .net exception is critical (except "ArgumentException")
11 | // or LiteException with code >= 900
12 | var isCritical = exception is LiteException ex ?
13 | ex.IsCritical : exception is not ArgumentException;
14 |
15 | //TODO: do log from factory
16 |
17 | if (isCritical)
18 | {
19 | factory.Exception = exception;
20 | factory.Dispose();
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/src/LiteDB/Utils/Exceptions/LiteException.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// The main exception for LiteDB
5 | ///
6 | public partial class LiteException : Exception
7 | {
8 | public int ErrorCode { get; }
9 | public long Position { get; private set; }
10 |
11 | public bool IsCritical => this.ErrorCode >= 900;
12 |
13 | private LiteException(int code, string message)
14 | : base(message)
15 | {
16 | this.ErrorCode = code;
17 | }
18 |
19 | private LiteException(int code, string message, Exception? inner)
20 | : base(message, inner)
21 | {
22 | this.ErrorCode = code;
23 | }
24 | }
--------------------------------------------------------------------------------
/src/LiteDB/Utils/Extensions/DateExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal static class DateExtensions
4 | {
5 | ///
6 | /// Truncate DateTime in milliseconds
7 | ///
8 | public static DateTime Truncate(this DateTime dt)
9 | {
10 | if (dt == DateTime.MaxValue || dt == DateTime.MinValue)
11 | {
12 | return dt;
13 | }
14 |
15 | return new DateTime(dt.Year, dt.Month, dt.Day,
16 | dt.Hour, dt.Minute, dt.Second, dt.Millisecond,
17 | dt.Kind);
18 | }
19 |
20 | // https://github.com/dotnet/runtime/issues/65858#issuecomment-1050836263
21 | public static TimeSpan GetElapsedTime(long start)
22 | => new TimeSpan((long)((double)(Stopwatch.GetTimestamp() - start) * TICK_FREQUENCY));
23 |
24 | public static int MonthDifference(this DateTime startDate, DateTime endDate)
25 | {
26 | // https://stackoverflow.com/a/1526116/3286260
27 |
28 | int compMonth = (endDate.Month + endDate.Year * 12) - (startDate.Month + startDate.Year * 12);
29 | double daysInEndMonth = (endDate - endDate.AddMonths(1)).Days;
30 | double months = compMonth + (startDate.Day - endDate.Day) / daysInEndMonth;
31 |
32 | return Convert.ToInt32(Math.Truncate(months));
33 | }
34 |
35 | public static int YearDifference(this DateTime startDate, DateTime endDate)
36 | {
37 | // https://stackoverflow.com/a/28444291/3286260
38 |
39 | //Excel documentation says "COMPLETE calendar years in between dates"
40 | int years = endDate.Year - startDate.Year;
41 |
42 | if (startDate.Month == endDate.Month &&// if the start month and the end month are the same
43 | endDate.Day < startDate.Day)// BUT the end day is less than the start day
44 | {
45 | years--;
46 | }
47 | else if (endDate.Month < startDate.Month)// if the end month is less than the start month
48 | {
49 | years--;
50 | }
51 |
52 | return years;
53 | }
54 | }
--------------------------------------------------------------------------------
/src/LiteDB/Utils/Extensions/HashSetExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal static class HashSetExtensions
4 | {
5 | public static HashSet AddRange(this HashSet hash, IEnumerable items)
6 | {
7 | if (items is null) return hash;
8 |
9 | foreach(var item in items)
10 | {
11 | hash.Add(item);
12 | }
13 |
14 | return hash;
15 | }
16 | }
--------------------------------------------------------------------------------
/src/LiteDB/Utils/Extensions/IOExceptionExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal static class IOExceptionExtensions
4 | {
5 | private const int ERROR_SHARING_VIOLATION = 32;
6 | private const int ERROR_LOCK_VIOLATION = 33;
7 |
8 | ///
9 | /// Detect if exception is an Locked exception
10 | ///
11 | public static bool IsLocked(this IOException ex)
12 | {
13 | var errorCode = Marshal.GetHRForException(ex) & ((1 << 16) - 1);
14 |
15 | return
16 | errorCode == ERROR_SHARING_VIOLATION ||
17 | errorCode == ERROR_LOCK_VIOLATION;
18 | }
19 | }
--------------------------------------------------------------------------------
/src/LiteDB/Utils/Extensions/LinqExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal static class LinqExtensions
4 | {
5 | public static IEnumerable> Batch(this IEnumerable source, int batchSize)
6 | {
7 | using (var enumerator = source.GetEnumerator())
8 | {
9 | while (enumerator.MoveNext())
10 | {
11 | yield return YieldBatchElements(enumerator, batchSize - 1);
12 | }
13 | }
14 | }
15 |
16 | private static IEnumerable YieldBatchElements(IEnumerator source, int batchSize)
17 | {
18 | yield return source.Current;
19 |
20 | for (int i = 0; i < batchSize && source.MoveNext(); i++)
21 | {
22 | yield return source.Current;
23 | }
24 | }
25 |
26 | public static IEnumerable DistinctBy(this IEnumerable source,
27 | Func keySelector, IEqualityComparer comparer)
28 | {
29 | if (source == null) throw new ArgumentNullException(nameof(source));
30 | if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));
31 |
32 | return _();
33 |
34 | IEnumerable _()
35 | {
36 | var knownKeys = new HashSet(comparer);
37 |
38 | foreach (var element in source)
39 | {
40 | if (knownKeys.Add(keySelector(element)))
41 | {
42 | yield return element;
43 | }
44 | }
45 | }
46 | }
47 |
48 | ///
49 | /// Return same IEnumerable but indicate if item last item in enumerable
50 | ///
51 | public static IEnumerable> IsLast(this IEnumerable source) where T : class
52 | {
53 | T? last = default;
54 |
55 | foreach(var item in source)
56 | {
57 | if (last != default)
58 | {
59 | yield return new LastItem { Item = last, IsLast = false };
60 | }
61 |
62 | last = item;
63 | }
64 |
65 | if (last != null)
66 | {
67 | yield return new LastItem { Item = last, IsLast = true };
68 | }
69 | }
70 | }
71 |
72 | internal struct LastItem
73 | {
74 | public T Item { get; set; }
75 | public bool IsLast { get; set; }
76 | }
--------------------------------------------------------------------------------
/src/LiteDB/Utils/Extensions/MarshalExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal unsafe static class MarshalEx
4 | {
5 |
6 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
7 | public static void FillZero(byte* ptr, int length)
8 | {
9 | var span = new Span(ptr, length);
10 |
11 | span.Fill(0);
12 | }
13 |
14 | }
--------------------------------------------------------------------------------
/src/LiteDB/Utils/Interfaces/IIsEmpty.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal interface IIsEmpty
4 | {
5 | bool IsEmpty { get; }
6 | }
--------------------------------------------------------------------------------
/src/LiteDB/Utils/Randomizer.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// A singleton shared randomizer class
5 | ///
6 | internal static class Randomizer
7 | {
8 | private static readonly Random _random = new(RANDOMIZER_SEED);
9 |
10 | public static int Next()
11 | {
12 | #if DEBUG
13 | lock (_random)
14 | {
15 | return _random.Next();
16 | }
17 | #else
18 | return Random.Shared.Next();
19 | #endif
20 | }
21 |
22 | public static int Next(int minValue, int maxValue)
23 | {
24 | #if DEBUG
25 | lock (_random)
26 | {
27 | return _random.Next(minValue, maxValue);
28 | }
29 | #else
30 | return Random.Shared.Next(minValue, maxValue);
31 | #endif
32 | }
33 | }
--------------------------------------------------------------------------------
/src/LiteDB/Utils/SharedArray.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | ///
4 | /// A shared byte array to rent and return on dispose
5 | ///
6 | public readonly struct SharedArray : IDisposable
7 | {
8 | private readonly T[] _array;
9 | private readonly int _length;
10 |
11 | private SharedArray(T[] array, int length)
12 | {
13 | _array = array;
14 | _length = length;
15 | }
16 |
17 | public T this[int index]
18 | {
19 | get => _array[index];
20 | set => _array[index] = value;
21 | }
22 |
23 | public readonly Span AsSpan() => _array.AsSpan(0, _length);
24 |
25 | public readonly Span AsSpan(int start) => _array.AsSpan(start);
26 |
27 | public readonly Span AsSpan(int start, int length) => _array.AsSpan(start, length);
28 |
29 | public static SharedArray Rent(int length)
30 | {
31 | ENSURE(length < int.MaxValue, new { length });
32 |
33 | var array = ArrayPool.Shared.Rent(length);
34 |
35 | return new (array, length);
36 | }
37 |
38 | public void Dispose()
39 | {
40 | ArrayPool.Shared.Return(_array);
41 | }
42 |
43 | public override string ToString()
44 | {
45 | return Dump.Object(new { _length });
46 | }
47 | }
--------------------------------------------------------------------------------
/src/LiteDB/Utils/StringBuilderCache.cs:
--------------------------------------------------------------------------------
1 | namespace LiteDB;
2 |
3 | internal static class StringBuilderCache
4 | {
5 | private static StringBuilder? _cache;
6 |
7 | public static StringBuilder Acquire()
8 | {
9 | var sb = _cache;
10 |
11 | if (sb is not null)
12 | {
13 | _cache = null;
14 | sb.Clear();
15 | return sb;
16 | }
17 |
18 | return new StringBuilder();
19 | }
20 |
21 | public static string Release(StringBuilder sb)
22 | {
23 | var result = sb.ToString();
24 |
25 | _cache = sb;
26 |
27 | return result;
28 | }
29 | }
--------------------------------------------------------------------------------
/src/LiteDB/_Imports.cs:
--------------------------------------------------------------------------------
1 | global using System;
2 | global using System.Globalization;
3 | global using System.Collections;
4 | global using System.Collections.Generic;
5 | global using System.Collections.Concurrent;
6 | global using System.Text;
7 | global using System.Threading;
8 | global using System.Threading.Tasks;
9 | global using System.Linq;
10 | global using System.Linq.Expressions;
11 | global using System.IO;
12 | global using System.Text.RegularExpressions;
13 | global using System.Runtime.InteropServices;
14 | global using System.Runtime.CompilerServices;
15 | global using System.Security;
16 | global using System.Diagnostics;
17 | global using System.Diagnostics.CodeAnalysis;
18 | global using System.Security.Cryptography;
19 | global using System.Reflection;
20 | global using System.Buffers;
21 | global using System.Buffers.Binary;
22 |
23 | global using LiteDB.Engine;
24 | global using static LiteDB.Constants;
25 | global using static LiteDB.LiteException;
26 | global using static LiteDB.CodeContract;
27 | global using static LiteDB.Profiler;
28 |
29 | [assembly: InternalsVisibleTo("ConsoleApp1")]
30 | [assembly: InternalsVisibleTo("LiteDB.Tests")]
31 | [assembly: InternalsVisibleTo("LiteDB.Benchmark")]
32 | [assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
33 |
34 |
--------------------------------------------------------------------------------
/src/LiteDB/icon_64x64.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mbdavid/LiteDB-vNext/2941736fcf4e8efb23e8c8228a6d6532e361702a/src/LiteDB/icon_64x64.png
--------------------------------------------------------------------------------