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,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 3d21f87f626064f58a45e4841d3be881
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,110 @@
1
+ using System;
2
+
3
+ using UnityEngine;
4
+ using UnityEngine.LowLevel;
5
+ using UnityEngine.PlayerLoop;
6
+
7
+ namespace BACKND.Database
8
+ {
9
+ public static class DatabaseLoop
10
+ {
11
+ internal enum AddMode { Beginning, End }
12
+
13
+ public static Action OnEarlyUpdate;
14
+ public static Action OnLateUpdate;
15
+
16
+ [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
17
+ static void ResetStatics()
18
+ {
19
+ OnEarlyUpdate = null;
20
+ OnLateUpdate = null;
21
+ }
22
+
23
+ internal static int FindPlayerLoopEntryIndex(PlayerLoopSystem.UpdateFunction function, PlayerLoopSystem playerLoop, Type playerLoopSystemType)
24
+ {
25
+ if (playerLoop.type == playerLoopSystemType)
26
+ return Array.FindIndex(playerLoop.subSystemList, (elem => elem.updateDelegate == function));
27
+
28
+ if (playerLoop.subSystemList != null)
29
+ {
30
+ for (int i = 0; i < playerLoop.subSystemList.Length; ++i)
31
+ {
32
+ int index = FindPlayerLoopEntryIndex(function, playerLoop.subSystemList[i], playerLoopSystemType);
33
+ if (index != -1) return index;
34
+ }
35
+ }
36
+ return -1;
37
+ }
38
+
39
+ internal static bool AddToPlayerLoop(PlayerLoopSystem.UpdateFunction function, Type ownerType, ref PlayerLoopSystem playerLoop, Type playerLoopSystemType, AddMode addMode)
40
+ {
41
+ if (playerLoop.type == playerLoopSystemType)
42
+ {
43
+ if (Array.FindIndex(playerLoop.subSystemList, (s => s.updateDelegate == function)) != -1)
44
+ {
45
+ return true;
46
+ }
47
+
48
+ int oldListLength = (playerLoop.subSystemList != null) ? playerLoop.subSystemList.Length : 0;
49
+ Array.Resize(ref playerLoop.subSystemList, oldListLength + 1);
50
+
51
+ PlayerLoopSystem system = new PlayerLoopSystem
52
+ {
53
+ type = ownerType,
54
+ updateDelegate = function
55
+ };
56
+
57
+ if (addMode == AddMode.Beginning)
58
+ {
59
+ Array.Copy(playerLoop.subSystemList, 0, playerLoop.subSystemList, 1, playerLoop.subSystemList.Length - 1);
60
+ playerLoop.subSystemList[0] = system;
61
+ }
62
+ else if (addMode == AddMode.End)
63
+ {
64
+ playerLoop.subSystemList[oldListLength] = system;
65
+ }
66
+
67
+ return true;
68
+ }
69
+
70
+ if (playerLoop.subSystemList != null)
71
+ {
72
+ for (int i = 0; i < playerLoop.subSystemList.Length; ++i)
73
+ {
74
+ if (AddToPlayerLoop(function, ownerType, ref playerLoop.subSystemList[i], playerLoopSystemType, addMode))
75
+ return true;
76
+ }
77
+ }
78
+ return false;
79
+ }
80
+
81
+ [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
82
+ static void RuntimeInitializeOnLoad()
83
+ {
84
+ PlayerLoopSystem playerLoop = PlayerLoop.GetCurrentPlayerLoop();
85
+
86
+ bool earlyAdded = AddToPlayerLoop(DatabaseEarlyUpdate, typeof(DatabaseLoop), ref playerLoop, typeof(EarlyUpdate), AddMode.End);
87
+ bool lateAdded = AddToPlayerLoop(DatabaseLateUpdate, typeof(DatabaseLoop), ref playerLoop, typeof(PreLateUpdate), AddMode.End);
88
+
89
+ PlayerLoop.SetPlayerLoop(playerLoop);
90
+
91
+ }
92
+
93
+ static void DatabaseEarlyUpdate()
94
+ {
95
+ if (!Application.isPlaying) return;
96
+
97
+ OnEarlyUpdate?.Invoke();
98
+ }
99
+
100
+ static void DatabaseLateUpdate()
101
+ {
102
+ if (!Application.isPlaying) return;
103
+
104
+ if (OnLateUpdate != null)
105
+ {
106
+ OnLateUpdate.Invoke();
107
+ }
108
+ }
109
+ }
110
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 18e80028fcc974189aeef46732102b2c
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,82 @@
1
+ using System;
2
+ using System.Globalization;
3
+
4
+ using Newtonsoft.Json;
5
+
6
+ namespace BACKND.Database
7
+ {
8
+ /// <summary>
9
+ /// JSON serialization helper for database operations
10
+ /// </summary>
11
+ public static class JsonHelper
12
+ {
13
+ /// <summary>
14
+ /// Deserialize object from JSON string to target type
15
+ /// </summary>
16
+ /// <typeparam name="T">Target type</typeparam>
17
+ /// <param name="value">Input object (string or other)</param>
18
+ /// <returns>Deserialized object of type T</returns>
19
+ public static T DeserializeObject<T>(object value)
20
+ {
21
+ if (value == null)
22
+ return default(T);
23
+
24
+ // If the value is already the correct type, return it directly
25
+ if (value is T directValue)
26
+ return directValue;
27
+
28
+ // Convert to string and deserialize
29
+ string jsonString = value.ToString();
30
+
31
+ try
32
+ {
33
+ return JsonConvert.DeserializeObject<T>(jsonString);
34
+ }
35
+ catch (Exception ex)
36
+ {
37
+ UnityEngine.Debug.LogWarning($"Failed to deserialize JSON for type {typeof(T).Name}: {ex.Message}");
38
+ return default(T);
39
+ }
40
+ }
41
+
42
+ /// <summary>
43
+ /// Serialize object to JSON string
44
+ /// </summary>
45
+ /// <param name="value">Object to serialize</param>
46
+ /// <returns>JSON string</returns>
47
+ public static string SerializeObject(object value)
48
+ {
49
+ if (value == null)
50
+ return null;
51
+
52
+ try
53
+ {
54
+ return JsonConvert.SerializeObject(value);
55
+ }
56
+ catch (Exception ex)
57
+ {
58
+ UnityEngine.Debug.LogWarning($"Failed to serialize object to JSON: {ex.Message}");
59
+ return value.ToString();
60
+ }
61
+ }
62
+
63
+ /// <summary>
64
+ /// Convert object to string, preserving ISO 8601 format for DateTime
65
+ /// </summary>
66
+ /// <param name="value">Input object</param>
67
+ /// <returns>String representation</returns>
68
+ public static string ConvertToString(object value)
69
+ {
70
+ if (value == null)
71
+ return null;
72
+
73
+ if (value is string strValue)
74
+ return strValue;
75
+
76
+ if (value is DateTime dateValue)
77
+ return dateValue.ToString("yyyy-MM-ddTHH:mm:ss.fffZ", CultureInfo.InvariantCulture);
78
+
79
+ return value.ToString();
80
+ }
81
+ }
82
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 2b15410c5d076458e962881b7285b8ed
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
package/Tools.meta ADDED
@@ -0,0 +1,8 @@
1
+ fileFormatVersion: 2
2
+ guid: 4ac9fdc25ea1e46a7896049e7636c264
3
+ folderAsset: yes
4
+ DefaultImporter:
5
+ externalObjects: {}
6
+ userData:
7
+ assetBundleName:
8
+ assetBundleVariant:
@@ -0,0 +1,154 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.Text;
4
+
5
+ using BACKND.Database.Network;
6
+
7
+ using Newtonsoft.Json;
8
+
9
+ namespace BACKND.Database
10
+ {
11
+ /// <summary>
12
+ /// 트랜잭션 빌더 - 여러 데이터베이스 작업을 원자적으로 실행
13
+ /// </summary>
14
+ public class TransactionBuilder
15
+ {
16
+ private readonly Client client;
17
+ private readonly List<string> statements = new();
18
+
19
+ /// <summary>
20
+ /// 현재 활성화된 TransactionQueryBuilder (암묵적 실행용)
21
+ /// </summary>
22
+ internal TransactionQueryBuilderBase activeQueryBuilder;
23
+
24
+ internal TransactionBuilder(Client client)
25
+ {
26
+ this.client = client;
27
+ }
28
+
29
+ /// <summary>
30
+ /// 특정 모델 타입에 대한 쿼리 빌더 시작
31
+ /// </summary>
32
+ public TransactionQueryBuilder<T> From<T>() where T : BaseModel, new()
33
+ {
34
+ // 이전에 활성화된 QueryBuilder가 있고 미완료 작업이 있으면 flush
35
+ FlushActiveQueryBuilder();
36
+
37
+ var queryBuilder = new TransactionQueryBuilder<T>(this, client);
38
+ activeQueryBuilder = queryBuilder;
39
+ return queryBuilder;
40
+ }
41
+
42
+ /// <summary>
43
+ /// 활성화된 QueryBuilder의 미완료 작업을 flush
44
+ /// </summary>
45
+ internal void FlushActiveQueryBuilder()
46
+ {
47
+ if (activeQueryBuilder != null && activeQueryBuilder.HasPendingSetClauses)
48
+ {
49
+ activeQueryBuilder.FlushPendingSetClauses();
50
+ }
51
+ activeQueryBuilder = null;
52
+ }
53
+
54
+ /// <summary>
55
+ /// SQL 문장 추가 (내부용)
56
+ /// </summary>
57
+ internal void AddStatement(string sql)
58
+ {
59
+ if (string.IsNullOrWhiteSpace(sql))
60
+ throw new ArgumentException("SQL statement cannot be empty");
61
+
62
+ statements.Add(sql);
63
+ }
64
+
65
+ /// <summary>
66
+ /// 현재 트랜잭션에 포함된 작업 수
67
+ /// </summary>
68
+ public int Count => statements.Count;
69
+
70
+ /// <summary>
71
+ /// 트랜잭션 실행
72
+ /// </summary>
73
+ public async BTask<TransactionResult> CommitAsync()
74
+ {
75
+ // 미완료 작업이 있으면 flush
76
+ FlushActiveQueryBuilder();
77
+
78
+ if (statements.Count == 0)
79
+ throw new InvalidOperationException("Transaction has no operations. Add at least one operation before committing.");
80
+
81
+ // DynamoDB TransactWriteItems 제한: 100개
82
+ const int maxOperations = 100;
83
+ if (statements.Count > maxOperations)
84
+ throw new InvalidOperationException($"Transaction exceeds maximum operations limit. Maximum: {maxOperations}, Current: {statements.Count}");
85
+
86
+ // 트랜잭션 SQL 생성
87
+ var sb = new StringBuilder();
88
+ sb.AppendLine("BEGIN;");
89
+ foreach (var statement in statements)
90
+ {
91
+ sb.Append(statement);
92
+ if (!statement.EndsWith(";"))
93
+ sb.Append(";");
94
+ sb.AppendLine();
95
+ }
96
+ sb.Append("COMMIT;");
97
+
98
+ var request = new DatabaseRequest
99
+ {
100
+ Query = sb.ToString(),
101
+ Parameters = new Dictionary<string, object>()
102
+ };
103
+
104
+ // user_uuid 파라미터 추가
105
+ if (client.UserUUID != null)
106
+ {
107
+ request.Parameters["@current_user_uuid"] = client.UserUUID;
108
+ }
109
+
110
+ var response = await client.ExecuteTransaction(request);
111
+
112
+ if (!response.Success)
113
+ {
114
+ return new TransactionResult
115
+ {
116
+ Success = false,
117
+ OperationCount = statements.Count,
118
+ Error = response.Error,
119
+ Message = "Transaction failed"
120
+ };
121
+ }
122
+
123
+ // 결과 파싱 시도
124
+ TransactionResult result;
125
+ try
126
+ {
127
+ result = JsonConvert.DeserializeObject<TransactionResult>(response.Result);
128
+ if (result == null)
129
+ {
130
+ result = new TransactionResult();
131
+ }
132
+ }
133
+ catch
134
+ {
135
+ result = new TransactionResult();
136
+ }
137
+
138
+ result.Success = true;
139
+ result.OperationCount = statements.Count;
140
+ result.Message = result.Message ?? "Transaction committed successfully";
141
+
142
+ return result;
143
+ }
144
+ }
145
+
146
+ /// <summary>
147
+ /// TransactionQueryBuilder 베이스 클래스 (암묵적 실행 지원용)
148
+ /// </summary>
149
+ public abstract class TransactionQueryBuilderBase
150
+ {
151
+ internal abstract bool HasPendingSetClauses { get; }
152
+ internal abstract void FlushPendingSetClauses();
153
+ }
154
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 15c18476cba204c66a8910780b6c3bf8
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,205 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.Linq;
4
+ using System.Linq.Expressions;
5
+
6
+ using BACKND.Database.Internal;
7
+
8
+ namespace BACKND.Database
9
+ {
10
+ /// <summary>
11
+ /// 트랜잭션용 쿼리 빌더
12
+ /// </summary>
13
+ public class TransactionQueryBuilder<T> : TransactionQueryBuilderBase where T : BaseModel, new()
14
+ {
15
+ private readonly TransactionBuilder transaction;
16
+ private readonly Client client;
17
+ private readonly T modelInstance;
18
+ private readonly ExpressionAnalyzer expressionAnalyzer;
19
+
20
+ private readonly List<WhereCondition> whereConditions = new();
21
+ private readonly List<SetClause> setClauses = new();
22
+ private bool isOfCurrentUser;
23
+
24
+ internal TransactionQueryBuilder(TransactionBuilder transaction, Client client)
25
+ {
26
+ this.transaction = transaction;
27
+ this.client = client;
28
+ this.modelInstance = new T();
29
+ this.expressionAnalyzer = new ExpressionAnalyzer(modelInstance);
30
+ }
31
+
32
+ internal override bool HasPendingSetClauses => setClauses.Count > 0;
33
+
34
+ internal override void FlushPendingSetClauses()
35
+ {
36
+ if (setClauses.Count == 0)
37
+ return;
38
+
39
+ if (whereConditions.Count == 0)
40
+ throw new InvalidOperationException("Inc/Dec requires a WHERE clause");
41
+
42
+ var whereClause = SqlBuilder.BuildWhereClause(
43
+ whereConditions,
44
+ isOfCurrentUser,
45
+ modelInstance.GetTableType());
46
+ var sql = SqlBuilder.BuildUpdateQueryFromSetClauses(
47
+ modelInstance.GetTableName(),
48
+ setClauses,
49
+ whereClause);
50
+
51
+ transaction.AddStatement(sql);
52
+ setClauses.Clear();
53
+ }
54
+
55
+ #region 조건 빌더 (체이닝 → 자신 반환)
56
+
57
+ /// <summary>
58
+ /// WHERE 조건 추가
59
+ /// </summary>
60
+ public TransactionQueryBuilder<T> Where(Expression<Func<T, bool>> predicate)
61
+ {
62
+ var condition = expressionAnalyzer.AnalyzeSingle(predicate, whereConditions);
63
+ if (condition != null)
64
+ {
65
+ whereConditions.Add(condition);
66
+ }
67
+ return this;
68
+ }
69
+
70
+ /// <summary>
71
+ /// 현재 사용자 데이터만 대상 (UserTable용)
72
+ /// </summary>
73
+ public TransactionQueryBuilder<T> OfCurrentUser()
74
+ {
75
+ isOfCurrentUser = true;
76
+ return this;
77
+ }
78
+
79
+ /// <summary>
80
+ /// 필드 값 증가
81
+ /// </summary>
82
+ public TransactionQueryBuilder<T> Inc<TField>(Expression<Func<T, TField>> selector, TField value)
83
+ {
84
+ var columnName = expressionAnalyzer.GetColumnNameFromKeySelector(selector);
85
+ ValidateNoDuplicateSetClause(columnName);
86
+ setClauses.Add(new SetClause
87
+ {
88
+ ColumnName = columnName,
89
+ Operator = "+",
90
+ Value = value
91
+ });
92
+ return this;
93
+ }
94
+
95
+ /// <summary>
96
+ /// 필드 값 감소
97
+ /// </summary>
98
+ public TransactionQueryBuilder<T> Dec<TField>(Expression<Func<T, TField>> selector, TField value)
99
+ {
100
+ var columnName = expressionAnalyzer.GetColumnNameFromKeySelector(selector);
101
+ ValidateNoDuplicateSetClause(columnName);
102
+ setClauses.Add(new SetClause
103
+ {
104
+ ColumnName = columnName,
105
+ Operator = "-",
106
+ Value = value
107
+ });
108
+ return this;
109
+ }
110
+
111
+ private void ValidateNoDuplicateSetClause(string columnName)
112
+ {
113
+ if (setClauses.Any(c => c.ColumnName == columnName))
114
+ throw new InvalidOperationException($"'{columnName}' is already being modified. Each field can only be modified once.");
115
+ }
116
+
117
+ #endregion
118
+
119
+ #region 실행 메서드 (SQL 추가 → TransactionBuilder 반환)
120
+
121
+ /// <summary>
122
+ /// INSERT 작업 추가
123
+ /// </summary>
124
+ public TransactionBuilder Insert(T model)
125
+ {
126
+ if (model == null)
127
+ throw new ArgumentNullException(nameof(model));
128
+
129
+ var sql = SqlBuilder.BuildInsertQuery(model, out _);
130
+ transaction.AddStatement(sql);
131
+
132
+ // 활성 QueryBuilder 해제
133
+ transaction.activeQueryBuilder = null;
134
+ return transaction;
135
+ }
136
+
137
+ /// <summary>
138
+ /// UPDATE 작업 추가
139
+ /// </summary>
140
+ public TransactionBuilder Update(T model)
141
+ {
142
+ if (model == null)
143
+ throw new ArgumentNullException(nameof(model));
144
+
145
+ // WHERE 절이 없으면 PrimaryKey로 자동 생성
146
+ if (whereConditions.Count == 0)
147
+ {
148
+ var pkConditions = SqlBuilder.BuildPrimaryKeyConditions(model);
149
+ whereConditions.AddRange(pkConditions);
150
+ }
151
+
152
+ var whereClause = SqlBuilder.BuildWhereClause(
153
+ whereConditions,
154
+ isOfCurrentUser,
155
+ modelInstance.GetTableType());
156
+ var sql = SqlBuilder.BuildUpdateQuery(model, whereClause, out _);
157
+ transaction.AddStatement(sql);
158
+
159
+ // 활성 QueryBuilder 해제
160
+ transaction.activeQueryBuilder = null;
161
+ return transaction;
162
+ }
163
+
164
+ /// <summary>
165
+ /// DELETE 작업 추가
166
+ /// </summary>
167
+ public TransactionBuilder Delete()
168
+ {
169
+ if (whereConditions.Count == 0)
170
+ throw new InvalidOperationException("Delete requires a WHERE clause");
171
+
172
+ var whereClause = SqlBuilder.BuildWhereClause(
173
+ whereConditions,
174
+ isOfCurrentUser,
175
+ modelInstance.GetTableType());
176
+ var sql = SqlBuilder.BuildDeleteQuery(modelInstance.GetTableName(), whereClause);
177
+ transaction.AddStatement(sql);
178
+
179
+ // 활성 QueryBuilder 해제
180
+ transaction.activeQueryBuilder = null;
181
+ return transaction;
182
+ }
183
+
184
+ #endregion
185
+
186
+ #region 암묵적 실행을 위한 From<U>()
187
+
188
+ /// <summary>
189
+ /// 다른 테이블로 전환 (현재 Inc/Dec 작업이 있으면 자동 flush)
190
+ /// </summary>
191
+ public TransactionQueryBuilder<U> From<U>() where U : BaseModel, new()
192
+ {
193
+ // 현재 Inc/Dec 작업이 있으면 자동으로 UPDATE SQL 생성
194
+ if (HasPendingSetClauses)
195
+ {
196
+ FlushPendingSetClauses();
197
+ }
198
+
199
+ // 새 QueryBuilder 생성
200
+ return transaction.From<U>();
201
+ }
202
+
203
+ #endregion
204
+ }
205
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 2eb969df47e9149488fe15818b3937f3
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,33 @@
1
+ namespace BACKND.Database
2
+ {
3
+ /// <summary>
4
+ /// 트랜잭션 실행 결과
5
+ /// </summary>
6
+ public class TransactionResult
7
+ {
8
+ /// <summary>
9
+ /// 트랜잭션 성공 여부
10
+ /// </summary>
11
+ public bool Success { get; set; }
12
+
13
+ /// <summary>
14
+ /// 트랜잭션에 포함된 작업 수
15
+ /// </summary>
16
+ public int OperationCount { get; set; }
17
+
18
+ /// <summary>
19
+ /// 결과 메시지
20
+ /// </summary>
21
+ public string Message { get; set; }
22
+
23
+ /// <summary>
24
+ /// 에러 메시지 (실패 시)
25
+ /// </summary>
26
+ public string Error { get; set; }
27
+
28
+ /// <summary>
29
+ /// 영향받은 총 행 수
30
+ /// </summary>
31
+ public int TotalAffectedRows { get; set; }
32
+ }
33
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 42d64baeef2e04aaaa813580bbf5ad1f
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
package/package.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "com.backnd.database",
3
+ "version": "0.0.1",
4
+ "displayName": "BACKND Database",
5
+ "description": "BACKND Database is a Unity SDK for seamless integration with BACKND cloud database services.\n\nEasily manage and synchronize game data such as player profiles, game states, and leaderboards across multiple platforms.\nIdeal for Unity developers looking to implement robust database solutions without complex backend setups.",
6
+ "unity": "2021.3",
7
+ "keywords": [
8
+ "backnd",
9
+ "unity",
10
+ "database",
11
+ "networking"
12
+ ],
13
+ "dependencies": {},
14
+ "category": "SDK",
15
+ "author": {
16
+ "name": "backnd",
17
+ "email": "help@thebackend.io",
18
+ "url": "https://backnd.com"
19
+ }
20
+ }