com.backnd.database 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/Attributes/ColumnAttribute.cs +46 -0
  2. package/Attributes/ColumnAttribute.cs.meta +11 -0
  3. package/Attributes/PrimaryKeyAttribute.cs +12 -0
  4. package/Attributes/PrimaryKeyAttribute.cs.meta +11 -0
  5. package/Attributes/TableAttribute.cs +44 -0
  6. package/Attributes/TableAttribute.cs.meta +11 -0
  7. package/Attributes.meta +8 -0
  8. package/BACKND.Database.asmdef +14 -0
  9. package/BACKND.Database.asmdef.meta +7 -0
  10. package/BaseModel.cs +22 -0
  11. package/BaseModel.cs.meta +11 -0
  12. package/Client.cs +490 -0
  13. package/Client.cs.meta +11 -0
  14. package/Editor/BACKND.Database.Editor.asmdef +18 -0
  15. package/Editor/BACKND.Database.Editor.asmdef.meta +7 -0
  16. package/Editor/PackageAssetInstaller.cs +251 -0
  17. package/Editor/PackageAssetInstaller.cs.meta +11 -0
  18. package/Editor.meta +8 -0
  19. package/Exceptions/DatabaseException.cs +34 -0
  20. package/Exceptions/DatabaseException.cs.meta +11 -0
  21. package/Exceptions.meta +8 -0
  22. package/Internal/ExpressionAnalyzer.cs +433 -0
  23. package/Internal/ExpressionAnalyzer.cs.meta +11 -0
  24. package/Internal/QueryTypes.cs +61 -0
  25. package/Internal/QueryTypes.cs.meta +11 -0
  26. package/Internal/SqlBuilder.cs +375 -0
  27. package/Internal/SqlBuilder.cs.meta +11 -0
  28. package/Internal/ValueFormatter.cs +103 -0
  29. package/Internal/ValueFormatter.cs.meta +11 -0
  30. package/Internal.meta +8 -0
  31. package/Network/DatabaseExecutor.cs +171 -0
  32. package/Network/DatabaseExecutor.cs.meta +11 -0
  33. package/Network/DatabaseRequest.cs +16 -0
  34. package/Network/DatabaseRequest.cs.meta +11 -0
  35. package/Network/DatabaseResponse.cs +81 -0
  36. package/Network/DatabaseResponse.cs.meta +11 -0
  37. package/Network.meta +8 -0
  38. package/QueryBuilder.cs +1001 -0
  39. package/QueryBuilder.cs.meta +11 -0
  40. package/README.md +24 -0
  41. package/TheBackend~/Plugins/Android/Backend.aar +0 -0
  42. package/TheBackend~/Plugins/Backend.dll +0 -0
  43. package/TheBackend~/Plugins/Editor/TheBackendMultiSettingEditor.dll +0 -0
  44. package/TheBackend~/Plugins/Editor/TheBackendSettingEditor.dll +0 -0
  45. package/TheBackend~/Plugins/LitJSON.dll +0 -0
  46. package/TheBackend~/Plugins/Settings/TheBackendHashKeySettings.dll +0 -0
  47. package/TheBackend~/Plugins/Settings/TheBackendMultiSettings.dll +0 -0
  48. package/TheBackend~/Plugins/Settings/TheBackendSettings.dll +0 -0
  49. package/Tools/BTask.cs +905 -0
  50. package/Tools/BTask.cs.meta +11 -0
  51. package/Tools/DatabaseLoop.cs +110 -0
  52. package/Tools/DatabaseLoop.cs.meta +11 -0
  53. package/Tools/JsonHelper.cs +82 -0
  54. package/Tools/JsonHelper.cs.meta +11 -0
  55. package/Tools.meta +8 -0
  56. package/TransactionBuilder.cs +154 -0
  57. package/TransactionBuilder.cs.meta +11 -0
  58. package/TransactionQueryBuilder.cs +205 -0
  59. package/TransactionQueryBuilder.cs.meta +11 -0
  60. package/TransactionResult.cs +33 -0
  61. package/TransactionResult.cs.meta +11 -0
  62. package/package.json +20 -0
  63. package/package.json.meta +7 -0
@@ -0,0 +1,433 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.Linq;
4
+ using System.Linq.Expressions;
5
+
6
+ using UnityEngine;
7
+
8
+ namespace BACKND.Database.Internal
9
+ {
10
+ /// <summary>
11
+ /// Expression Tree를 분석하여 WhereCondition으로 변환하는 유틸리티
12
+ /// </summary>
13
+ public class ExpressionAnalyzer
14
+ {
15
+ private readonly BaseModel modelInstance;
16
+ private readonly List<WhereCondition> additionalConditions = new();
17
+
18
+ public ExpressionAnalyzer(BaseModel modelInstance)
19
+ {
20
+ this.modelInstance = modelInstance;
21
+ }
22
+
23
+ /// <summary>
24
+ /// Expression을 분석하여 WhereCondition 목록 반환
25
+ /// </summary>
26
+ public List<WhereCondition> Analyze<T>(Expression<Func<T, bool>> expression)
27
+ {
28
+ additionalConditions.Clear();
29
+
30
+ try
31
+ {
32
+ var result = AnalyzeBinaryExpression(expression.Body);
33
+ if (result == null && Application.platform == RuntimePlatform.WebGLPlayer)
34
+ {
35
+ Debug.LogWarning("Complex expression analysis failed. Consider using simpler conditions or multiple Where() calls.");
36
+ }
37
+
38
+ var conditions = new List<WhereCondition>(additionalConditions);
39
+ if (result != null)
40
+ {
41
+ conditions.Add(result);
42
+ }
43
+ return conditions;
44
+ }
45
+ catch (Exception ex)
46
+ {
47
+ Debug.LogWarning($"Expression analysis failed: {ex.Message}. " +
48
+ $"Platform: {Application.platform}. " +
49
+ "Falling back to runtime evaluation may not work. " +
50
+ "Consider using multiple Where() calls instead.");
51
+ return new List<WhereCondition>();
52
+ }
53
+ }
54
+
55
+ /// <summary>
56
+ /// 단일 Expression 분석 (QueryBuilder 호환용)
57
+ /// </summary>
58
+ public WhereCondition AnalyzeSingle<T>(Expression<Func<T, bool>> expression, List<WhereCondition> targetList)
59
+ {
60
+ additionalConditions.Clear();
61
+
62
+ try
63
+ {
64
+ var result = AnalyzeBinaryExpression(expression.Body);
65
+ if (result == null && Application.platform == RuntimePlatform.WebGLPlayer)
66
+ {
67
+ Debug.LogWarning("Complex expression analysis failed. Consider using simpler conditions or multiple Where() calls.");
68
+ }
69
+
70
+ // 추가 조건들을 target 리스트에 추가
71
+ foreach (var condition in additionalConditions)
72
+ {
73
+ targetList.Add(condition);
74
+ }
75
+
76
+ return result;
77
+ }
78
+ catch (Exception ex)
79
+ {
80
+ Debug.LogWarning($"Expression analysis failed: {ex.Message}. " +
81
+ $"Platform: {Application.platform}. " +
82
+ "Falling back to runtime evaluation may not work. " +
83
+ "Consider using multiple Where() calls instead.");
84
+ return null;
85
+ }
86
+ }
87
+
88
+ private WhereCondition AnalyzeBinaryExpression(Expression expression)
89
+ {
90
+ if (expression is BinaryExpression binaryExpr)
91
+ {
92
+ if (binaryExpr.NodeType == ExpressionType.AndAlso || binaryExpr.NodeType == ExpressionType.OrElse)
93
+ {
94
+ var leftCondition = AnalyzeBinaryExpression(binaryExpr.Left);
95
+ var rightCondition = AnalyzeBinaryExpression(binaryExpr.Right);
96
+
97
+ if (leftCondition != null && rightCondition != null)
98
+ {
99
+ if (binaryExpr.NodeType == ExpressionType.OrElse)
100
+ {
101
+ rightCondition.LogicalOperator = LogicalOperator.Or;
102
+ }
103
+
104
+ additionalConditions.Add(leftCondition);
105
+ return rightCondition;
106
+ }
107
+ }
108
+
109
+ if (IsComparisonOperator(binaryExpr.NodeType))
110
+ {
111
+ var columnName = GetColumnNameFromMemberExpression(binaryExpr.Left);
112
+ var value = GetConstantValue(binaryExpr.Right);
113
+ var op = GetCompareOperator(binaryExpr.NodeType);
114
+
115
+ return new WhereCondition
116
+ {
117
+ ColumnName = columnName,
118
+ Operator = op,
119
+ Value = value,
120
+ LogicalOperator = LogicalOperator.And
121
+ };
122
+ }
123
+
124
+ throw new NotSupportedException($"Binary operator {binaryExpr.NodeType} not yet supported");
125
+ }
126
+
127
+ if (expression is MethodCallExpression methodCall)
128
+ {
129
+ return AnalyzeMethodCall(methodCall);
130
+ }
131
+
132
+ if (expression is UnaryExpression unaryExpr)
133
+ {
134
+ if (unaryExpr.NodeType == ExpressionType.Not)
135
+ {
136
+ var innerCondition = AnalyzeBinaryExpression(unaryExpr.Operand);
137
+ if (innerCondition != null)
138
+ {
139
+ innerCondition.Operator = GetNegatedOperator(innerCondition.Operator);
140
+ return innerCondition;
141
+ }
142
+ }
143
+ }
144
+
145
+ throw new NotSupportedException($"Expression type {expression.NodeType} is not supported");
146
+ }
147
+
148
+ private WhereCondition AnalyzeMethodCall(MethodCallExpression methodCall)
149
+ {
150
+ if (methodCall.Method.Name == "Contains" && methodCall.Method.DeclaringType == typeof(string))
151
+ {
152
+ throw new NotSupportedException("String.Contains is not supported because server doesn't support LIKE operator. Use exact equality comparison instead.");
153
+ }
154
+
155
+ if (methodCall.Method.Name == "Contains" && methodCall.Object != null)
156
+ {
157
+ var columnName = GetColumnNameFromMemberExpression(methodCall.Arguments[0]);
158
+ var values = GetConstantValue(methodCall.Object);
159
+
160
+ return new WhereCondition
161
+ {
162
+ ColumnName = columnName,
163
+ Operator = CompareOperator.In,
164
+ Value = values,
165
+ LogicalOperator = LogicalOperator.And
166
+ };
167
+ }
168
+
169
+ throw new NotSupportedException($"Method {methodCall.Method.Name} is not supported");
170
+ }
171
+
172
+ /// <summary>
173
+ /// MemberExpression에서 컬럼명 추출
174
+ /// </summary>
175
+ public string GetColumnNameFromMemberExpression(Expression expression)
176
+ {
177
+ if (expression is MemberExpression memberExpr && memberExpr.Expression?.NodeType == ExpressionType.Parameter)
178
+ {
179
+ return modelInstance.GetColumnName(memberExpr.Member.Name);
180
+ }
181
+
182
+ throw new NotSupportedException("Only simple property access is supported (e.g., x => x.PropertyName)");
183
+ }
184
+
185
+ /// <summary>
186
+ /// KeySelector Expression에서 컬럼명 추출
187
+ /// </summary>
188
+ public string GetColumnNameFromKeySelector<T, TKey>(Expression<Func<T, TKey>> keySelector)
189
+ {
190
+ return GetColumnNameFromMemberExpression(keySelector.Body);
191
+ }
192
+
193
+ /// <summary>
194
+ /// Expression에서 상수 값 추출
195
+ /// </summary>
196
+ public static object GetConstantValue(Expression expression)
197
+ {
198
+ if (expression is ConstantExpression constantExpr)
199
+ {
200
+ return constantExpr.Value;
201
+ }
202
+
203
+ if (expression is MemberExpression memberExpr)
204
+ {
205
+ if (memberExpr.Expression is ConstantExpression containerExpr)
206
+ {
207
+ var container = containerExpr.Value;
208
+ if (container == null) return null;
209
+
210
+ if (memberExpr.Member is System.Reflection.FieldInfo field)
211
+ {
212
+ return field.GetValue(container);
213
+ }
214
+ if (memberExpr.Member is System.Reflection.PropertyInfo property)
215
+ {
216
+ return property.GetValue(container);
217
+ }
218
+ }
219
+
220
+ if (memberExpr.Expression == null && memberExpr.Member is System.Reflection.FieldInfo staticField)
221
+ {
222
+ return staticField.GetValue(null);
223
+ }
224
+ if (memberExpr.Expression == null && memberExpr.Member is System.Reflection.PropertyInfo staticProperty)
225
+ {
226
+ return staticProperty.GetValue(null);
227
+ }
228
+
229
+ if (memberExpr.Expression is MemberExpression nestedMember)
230
+ {
231
+ var parentValue = GetConstantValue(nestedMember);
232
+ if (parentValue == null) return null;
233
+
234
+ if (memberExpr.Member is System.Reflection.FieldInfo field)
235
+ {
236
+ return field.GetValue(parentValue);
237
+ }
238
+ if (memberExpr.Member is System.Reflection.PropertyInfo property)
239
+ {
240
+ return property.GetValue(parentValue);
241
+ }
242
+ }
243
+ }
244
+
245
+ if (expression is UnaryExpression unaryExpr)
246
+ {
247
+ if (unaryExpr.NodeType == ExpressionType.Convert || unaryExpr.NodeType == ExpressionType.ConvertChecked)
248
+ {
249
+ var innerValue = GetConstantValue(unaryExpr.Operand);
250
+ if (innerValue != null && innerValue.GetType().IsEnum)
251
+ {
252
+ return Convert.ToInt32(innerValue);
253
+ }
254
+ return innerValue;
255
+ }
256
+
257
+ if (unaryExpr.NodeType == ExpressionType.Quote)
258
+ {
259
+ return GetConstantValue(unaryExpr.Operand);
260
+ }
261
+ }
262
+
263
+ if (expression is MethodCallExpression methodCallExpr)
264
+ {
265
+ try
266
+ {
267
+ if (methodCallExpr.Object == null)
268
+ {
269
+ var args = methodCallExpr.Arguments.Select(GetConstantValue).ToArray();
270
+ return methodCallExpr.Method.Invoke(null, args);
271
+ }
272
+
273
+ var instance = GetConstantValue(methodCallExpr.Object);
274
+ if (instance != null)
275
+ {
276
+ var args = methodCallExpr.Arguments.Select(GetConstantValue).ToArray();
277
+ return methodCallExpr.Method.Invoke(instance, args);
278
+ }
279
+ }
280
+ catch (Exception ex)
281
+ {
282
+ throw new NotSupportedException(
283
+ $"Method call {methodCallExpr.Method.Name} cannot be evaluated at compile time. " +
284
+ $"Platform: {Application.platform}. Use local variables instead.", ex);
285
+ }
286
+ }
287
+
288
+ if (expression is NewExpression newExpr)
289
+ {
290
+ try
291
+ {
292
+ var args = newExpr.Arguments.Select(GetConstantValue).ToArray();
293
+ return Activator.CreateInstance(newExpr.Type, args);
294
+ }
295
+ catch (Exception ex)
296
+ {
297
+ throw new NotSupportedException(
298
+ $"Cannot create instance of {newExpr.Type.Name} on platform {Application.platform}. " +
299
+ "Use local variables instead.", ex);
300
+ }
301
+ }
302
+
303
+ if (expression is ConditionalExpression conditionalExpr)
304
+ {
305
+ var testValue = GetConstantValue(conditionalExpr.Test);
306
+ if (testValue is bool testBool)
307
+ {
308
+ return testBool
309
+ ? GetConstantValue(conditionalExpr.IfTrue)
310
+ : GetConstantValue(conditionalExpr.IfFalse);
311
+ }
312
+ }
313
+
314
+ if (expression is BinaryExpression binaryExpr && !IsComparisonOperator(binaryExpr.NodeType))
315
+ {
316
+ var left = GetConstantValue(binaryExpr.Left);
317
+ var right = GetConstantValue(binaryExpr.Right);
318
+
319
+ return binaryExpr.NodeType switch
320
+ {
321
+ ExpressionType.Add => AddValues(left, right),
322
+ ExpressionType.Subtract => SubtractValues(left, right),
323
+ ExpressionType.Multiply => MultiplyValues(left, right),
324
+ ExpressionType.Divide => DivideValues(left, right),
325
+ ExpressionType.Modulo => ModuloValues(left, right),
326
+ ExpressionType.Coalesce => left ?? right,
327
+ _ => throw new NotSupportedException($"Binary operator {binaryExpr.NodeType} not supported in constant evaluation")
328
+ };
329
+ }
330
+
331
+ throw new NotSupportedException(
332
+ $"Expression {expression.NodeType} cannot be evaluated at compile time. " +
333
+ "Use local variables: var id = 5; Where(x => x.Id == id)");
334
+ }
335
+
336
+ #region Helper Methods
337
+
338
+ public static bool IsComparisonOperator(ExpressionType nodeType)
339
+ {
340
+ return nodeType == ExpressionType.Equal ||
341
+ nodeType == ExpressionType.NotEqual ||
342
+ nodeType == ExpressionType.GreaterThan ||
343
+ nodeType == ExpressionType.GreaterThanOrEqual ||
344
+ nodeType == ExpressionType.LessThan ||
345
+ nodeType == ExpressionType.LessThanOrEqual;
346
+ }
347
+
348
+ public static CompareOperator GetCompareOperator(ExpressionType nodeType)
349
+ {
350
+ return nodeType switch
351
+ {
352
+ ExpressionType.Equal => CompareOperator.Equal,
353
+ ExpressionType.NotEqual => CompareOperator.NotEqual,
354
+ ExpressionType.GreaterThan => CompareOperator.GreaterThan,
355
+ ExpressionType.GreaterThanOrEqual => CompareOperator.GreaterThanOrEqual,
356
+ ExpressionType.LessThan => CompareOperator.LessThan,
357
+ ExpressionType.LessThanOrEqual => CompareOperator.LessThanOrEqual,
358
+ _ => throw new NotSupportedException($"Operation {nodeType} is not supported")
359
+ };
360
+ }
361
+
362
+ public static CompareOperator GetNegatedOperator(CompareOperator originalOperator)
363
+ {
364
+ return originalOperator switch
365
+ {
366
+ CompareOperator.Equal => CompareOperator.NotEqual,
367
+ CompareOperator.NotEqual => CompareOperator.Equal,
368
+ CompareOperator.GreaterThan => CompareOperator.LessThanOrEqual,
369
+ CompareOperator.GreaterThanOrEqual => CompareOperator.LessThan,
370
+ CompareOperator.LessThan => CompareOperator.GreaterThanOrEqual,
371
+ CompareOperator.LessThanOrEqual => CompareOperator.GreaterThan,
372
+ CompareOperator.IsNull => CompareOperator.IsNotNull,
373
+ CompareOperator.IsNotNull => CompareOperator.IsNull,
374
+ _ => throw new NotSupportedException($"Cannot negate operator {originalOperator}")
375
+ };
376
+ }
377
+
378
+ #endregion
379
+
380
+ #region Arithmetic Operations
381
+
382
+ public static object AddValues(object left, object right)
383
+ {
384
+ if (left is int l1 && right is int r1) return l1 + r1;
385
+ if (left is long l2 && right is long r2) return l2 + r2;
386
+ if (left is float l3 && right is float r3) return l3 + r3;
387
+ if (left is double l4 && right is double r4) return l4 + r4;
388
+ if (left is string l5 && right is string r5) return l5 + r5;
389
+ if (left is DateTime l6 && right is TimeSpan r6) return l6 + r6;
390
+ throw new NotSupportedException($"Cannot add {left?.GetType().Name} and {right?.GetType().Name}");
391
+ }
392
+
393
+ public static object SubtractValues(object left, object right)
394
+ {
395
+ if (left is int l1 && right is int r1) return l1 - r1;
396
+ if (left is long l2 && right is long r2) return l2 - r2;
397
+ if (left is float l3 && right is float r3) return l3 - r3;
398
+ if (left is double l4 && right is double r4) return l4 - r4;
399
+ if (left is DateTime l5 && right is TimeSpan r5) return l5 - r5;
400
+ if (left is DateTime l6 && right is DateTime r6) return l6 - r6;
401
+ throw new NotSupportedException($"Cannot subtract {right?.GetType().Name} from {left?.GetType().Name}");
402
+ }
403
+
404
+ public static object MultiplyValues(object left, object right)
405
+ {
406
+ if (left is int l1 && right is int r1) return l1 * r1;
407
+ if (left is long l2 && right is long r2) return l2 * r2;
408
+ if (left is float l3 && right is float r3) return l3 * r3;
409
+ if (left is double l4 && right is double r4) return l4 * r4;
410
+ throw new NotSupportedException($"Cannot multiply {left?.GetType().Name} and {right?.GetType().Name}");
411
+ }
412
+
413
+ public static object DivideValues(object left, object right)
414
+ {
415
+ if (left is int l1 && right is int r1) return l1 / r1;
416
+ if (left is long l2 && right is long r2) return l2 / r2;
417
+ if (left is float l3 && right is float r3) return l3 / r3;
418
+ if (left is double l4 && right is double r4) return l4 / r4;
419
+ throw new NotSupportedException($"Cannot divide {left?.GetType().Name} by {right?.GetType().Name}");
420
+ }
421
+
422
+ public static object ModuloValues(object left, object right)
423
+ {
424
+ if (left is int l1 && right is int r1) return l1 % r1;
425
+ if (left is long l2 && right is long r2) return l2 % r2;
426
+ if (left is float l3 && right is float r3) return l3 % r3;
427
+ if (left is double l4 && right is double r4) return l4 % r4;
428
+ throw new NotSupportedException($"Cannot get modulo of {left?.GetType().Name} and {right?.GetType().Name}");
429
+ }
430
+
431
+ #endregion
432
+ }
433
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 0a70d9cdee65742d29efce2c4d3d4d2d
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
@@ -0,0 +1,61 @@
1
+ namespace BACKND.Database.Internal
2
+ {
3
+ /// <summary>
4
+ /// SQL 비교 연산자
5
+ /// </summary>
6
+ public enum CompareOperator
7
+ {
8
+ Equal,
9
+ NotEqual,
10
+ GreaterThan,
11
+ GreaterThanOrEqual,
12
+ LessThan,
13
+ LessThanOrEqual,
14
+ Between,
15
+ In,
16
+ IsNull,
17
+ IsNotNull
18
+ }
19
+
20
+ /// <summary>
21
+ /// SQL 논리 연산자
22
+ /// </summary>
23
+ public enum LogicalOperator
24
+ {
25
+ And,
26
+ Or
27
+ }
28
+
29
+ /// <summary>
30
+ /// WHERE 절 조건 정보
31
+ /// </summary>
32
+ public class WhereCondition
33
+ {
34
+ public string ColumnName { get; set; }
35
+ public CompareOperator Operator { get; set; }
36
+ public object Value { get; set; }
37
+ public object SecondValue { get; set; } // BETWEEN 연산자용
38
+ public LogicalOperator LogicalOperator { get; set; }
39
+ public bool IsGroupStart { get; set; } // 그룹 시작: (
40
+ public bool IsGroupEnd { get; set; } // 그룹 종료: )
41
+ }
42
+
43
+ /// <summary>
44
+ /// ORDER BY 절 정보
45
+ /// </summary>
46
+ public class OrderByInfo
47
+ {
48
+ public string Column { get; set; }
49
+ public bool Descending { get; set; }
50
+ }
51
+
52
+ /// <summary>
53
+ /// SET 절 정보 (Inc/Dec 연산용)
54
+ /// </summary>
55
+ public class SetClause
56
+ {
57
+ public string ColumnName { get; set; }
58
+ public string Operator { get; set; } // "+" or "-"
59
+ public object Value { get; set; }
60
+ }
61
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 87681eec5be0d40b1b0087f44ddcc531
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant: