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,46 @@
1
+ using System;
2
+
3
+ namespace BACKND.Database
4
+ {
5
+ public enum DatabaseType
6
+ {
7
+ None,
8
+ Int32,
9
+ UInt32,
10
+ Int64,
11
+ UInt64,
12
+ Float,
13
+ Bool,
14
+ String,
15
+ DateTime,
16
+ UUID,
17
+ Json
18
+ }
19
+
20
+ [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
21
+ public sealed class ColumnAttribute : Attribute
22
+ {
23
+ public string ColumnName { get; } = string.Empty;
24
+ public DatabaseType DataType { get; } = DatabaseType.None;
25
+ public bool NotNull { get; set; } = false;
26
+ public string DefaultValue { get; set; } = string.Empty;
27
+
28
+ public ColumnAttribute() { }
29
+
30
+ public ColumnAttribute(string columnName)
31
+ {
32
+ ColumnName = columnName;
33
+ }
34
+
35
+ public ColumnAttribute(DatabaseType dataType)
36
+ {
37
+ DataType = dataType;
38
+ }
39
+
40
+ public ColumnAttribute(string columnName, DatabaseType dataType)
41
+ {
42
+ ColumnName = columnName;
43
+ DataType = dataType;
44
+ }
45
+ }
46
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 92c1e0aaf13c247ae86812b38c98a20a
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,12 @@
1
+ using System;
2
+
3
+ namespace BACKND.Database
4
+ {
5
+ [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
6
+ public class PrimaryKeyAttribute : Attribute
7
+ {
8
+ public bool AutoIncrement { get; set; } = false;
9
+
10
+ public PrimaryKeyAttribute() { }
11
+ }
12
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 3e138edf14b7c434382f5b31272b5b43
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,44 @@
1
+ using System;
2
+
3
+ namespace BACKND.Database
4
+ {
5
+ public enum TableType
6
+ {
7
+ UserTable = 1,
8
+ FlexibleTable = 2
9
+ }
10
+
11
+ public enum TablePermission
12
+ {
13
+ SELF,
14
+ OTHERS
15
+ }
16
+
17
+ [AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
18
+ public class TableAttribute : Attribute
19
+ {
20
+ public string TableName { get; } = string.Empty;
21
+ public TableType TableType { get; } = TableType.FlexibleTable;
22
+ public bool ClientAccess { get; set; } = true;
23
+ public TablePermission[] ReadPermissions { get; set; } = { TablePermission.SELF, TablePermission.OTHERS };
24
+ public TablePermission[] WritePermissions { get; set; } = { TablePermission.SELF, TablePermission.OTHERS };
25
+
26
+ public TableAttribute() { }
27
+
28
+ public TableAttribute(string tableName)
29
+ {
30
+ TableName = tableName;
31
+ }
32
+
33
+ public TableAttribute(TableType tableType)
34
+ {
35
+ TableType = tableType;
36
+ }
37
+
38
+ public TableAttribute(string tableName, TableType tableType)
39
+ {
40
+ TableName = tableName;
41
+ TableType = tableType;
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: ea504502999d84fb1829f564cb040e94
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,8 @@
1
+ fileFormatVersion: 2
2
+ guid: 6ad078a1d58a84908863eb1b016604b0
3
+ folderAsset: yes
4
+ DefaultImporter:
5
+ externalObjects: {}
6
+ userData:
7
+ assetBundleName:
8
+ assetBundleVariant:
@@ -0,0 +1,14 @@
1
+ {
2
+ "name": "BACKND.Database",
3
+ "rootNamespace": "BACKND.Database",
4
+ "references": [],
5
+ "includePlatforms": [],
6
+ "excludePlatforms": [],
7
+ "allowUnsafeCode": false,
8
+ "overrideReferences": false,
9
+ "precompiledReferences": [],
10
+ "autoReferenced": true,
11
+ "defineConstraints": [],
12
+ "versionDefines": [],
13
+ "noEngineReferences": false
14
+ }
@@ -0,0 +1,7 @@
1
+ fileFormatVersion: 2
2
+ guid: cc8137176009d4057ba08d3ae1d0f9c1
3
+ AssemblyDefinitionImporter:
4
+ externalObjects: {}
5
+ userData:
6
+ assetBundleName:
7
+ assetBundleVariant:
package/BaseModel.cs ADDED
@@ -0,0 +1,22 @@
1
+ namespace BACKND.Database
2
+ {
3
+ public abstract class BaseModel
4
+ {
5
+ public virtual string GetTableName() { return string.Empty; }
6
+ public virtual TableType GetTableType() { return TableType.FlexibleTable; }
7
+ public virtual bool GetClientAccess() { return false; }
8
+ public virtual string[] GetReadPermissions() { return new string[] { "SELF", "OTHERS" }; }
9
+ public virtual string[] GetWritePermissions() { return new string[] { "SELF", "OTHERS" }; }
10
+ public virtual string[] GetPrimaryKeyColumnNames() { return new string[0]; }
11
+ public virtual string GetAutoIncrementColumnName() { return string.Empty; }
12
+ public virtual string GetPrimaryKey() { return string.Empty; }
13
+ public virtual string GetColumnList() { return string.Empty; }
14
+ public virtual string GetColumnDataType(string columnName) { return string.Empty; }
15
+ public virtual bool IsColumnNullable(string columnName) { return false; }
16
+ public virtual bool IsPropertyNullableType(string columnName) { return false; }
17
+ public virtual string GetColumnDefaultValue(string columnName) { return string.Empty; }
18
+ public virtual string GetColumnName(string propertyName) { return string.Empty; }
19
+ public virtual object GetValue(string columnName) { return null; }
20
+ public virtual void SetValue(string columnName, object value) { }
21
+ }
22
+ }
@@ -0,0 +1,11 @@
1
+ fileFormatVersion: 2
2
+ guid: 17e36e6d7a79e4d2d9f2f90850ec507a
3
+ MonoImporter:
4
+ externalObjects: {}
5
+ serializedVersion: 2
6
+ defaultReferences: []
7
+ executionOrder: 0
8
+ icon: {instanceID: 0}
9
+ userData:
10
+ assetBundleName:
11
+ assetBundleVariant:
package/Client.cs ADDED
@@ -0,0 +1,490 @@
1
+ using System;
2
+ using System.Collections.Generic;
3
+ using System.Linq;
4
+ using System.Text;
5
+ using System.Threading;
6
+
7
+ using BACKND.Database.Exceptions;
8
+ using BACKND.Database.Network;
9
+
10
+ namespace BACKND.Database
11
+ {
12
+ public class Client
13
+ {
14
+ public string UserUUID => headers.ContainsKey("x-gamerid") ? headers["x-gamerid"] : null;
15
+
16
+ private bool initialized = false;
17
+
18
+ private readonly Dictionary<string, string> headers = new Dictionary<string, string>();
19
+
20
+ private readonly Dictionary<Type, bool> createdTables = new Dictionary<Type, bool>();
21
+
22
+ private readonly Queue<QueuedRequest> requestQueue = new Queue<QueuedRequest>();
23
+ private readonly Queue<QueuedRequest> highPriorityQueue = new Queue<QueuedRequest>();
24
+ private readonly object queueLock = new object();
25
+ private bool isProcessingQueue = false;
26
+ private readonly CancellationTokenSource queueCancellationSource;
27
+ private readonly float requestDelay = 0.1f;
28
+
29
+ public Client(string uuid)
30
+ {
31
+ headers["database_uuid"] = uuid;
32
+
33
+ this.queueCancellationSource = new CancellationTokenSource();
34
+ }
35
+
36
+ public async BTask Initialize()
37
+ {
38
+ if (initialized)
39
+ {
40
+ return;
41
+ }
42
+
43
+ var userInfoResult = await GetUserInfoAsync();
44
+ if (!userInfoResult.IsSuccess())
45
+ {
46
+ UnityEngine.Debug.LogError("Failed to Backnd Service Initialization or Login Process - " + userInfoResult.ToString());
47
+ return;
48
+ }
49
+
50
+ var json = Newtonsoft.Json.Linq.JObject.Parse(userInfoResult.ReturnValue);
51
+ headers["x-gamerid"] = json["row"]["gamerId"].ToString();
52
+
53
+ var settings = BackEnd.Backend.GetBackndChatSettings();
54
+ foreach (var header in settings)
55
+ {
56
+ headers[header.Key] = header.Value;
57
+ }
58
+
59
+ //headers["x-gamerid"] = "40a1f3f0-19a1-11f0-9ee9-c1c83fc196eb";
60
+
61
+ StartQueueProcessing();
62
+
63
+ initialized = true;
64
+
65
+ await BTask.CompletedTask;
66
+ }
67
+
68
+ private BTask<BackEnd.BackendReturnObject> GetUserInfoAsync()
69
+ {
70
+ var tcs = new BTaskCompletionSource<BackEnd.BackendReturnObject>();
71
+
72
+ BackEnd.Backend.BMember.GetUserInfoV2(result =>
73
+ {
74
+ tcs.SetResult(result);
75
+ });
76
+
77
+ return tcs.Task;
78
+ }
79
+
80
+ #region Queue Management
81
+
82
+ private void StartQueueProcessing()
83
+ {
84
+ if (isProcessingQueue) return;
85
+
86
+ isProcessingQueue = true;
87
+ ProcessQueue();
88
+ }
89
+
90
+ private async void ProcessQueue()
91
+ {
92
+ while (!queueCancellationSource.Token.IsCancellationRequested)
93
+ {
94
+ QueuedRequest request = null;
95
+
96
+ lock (queueLock)
97
+ {
98
+ if (highPriorityQueue.Count > 0)
99
+ {
100
+ request = highPriorityQueue.Dequeue();
101
+ }
102
+ else if (requestQueue.Count > 0)
103
+ {
104
+ request = requestQueue.Dequeue();
105
+ }
106
+ }
107
+
108
+ if (request != null)
109
+ {
110
+ await ProcessRequest(request);
111
+
112
+ if (requestDelay > 0)
113
+ {
114
+ await BTask.Delay(requestDelay);
115
+ }
116
+ }
117
+ else
118
+ {
119
+ await BTask.Yield();
120
+ }
121
+ }
122
+
123
+ isProcessingQueue = false;
124
+ }
125
+
126
+ private async BTask ProcessRequest(QueuedRequest queuedRequest)
127
+ {
128
+ try
129
+ {
130
+ var response = await DatabaseExecutor.Execute(queuedRequest.Request, headers, queuedRequest.CancellationToken);
131
+ queuedRequest.TaskCompletionSource.SetResult(response);
132
+ }
133
+ catch (Exception ex)
134
+ {
135
+ queuedRequest.TaskCompletionSource.SetException(ex);
136
+ }
137
+ }
138
+
139
+ private BTask<Response> EnqueueRequest(DatabaseRequest request, bool highPriority = false, CancellationToken cancellationToken = default)
140
+ {
141
+ if (!initialized)
142
+ {
143
+ throw new InvalidOperationException("[Database.Client] Client not initialized. Call Initialize() first.");
144
+ }
145
+
146
+ var queuedRequest = new QueuedRequest
147
+ {
148
+ Request = request,
149
+ TaskCompletionSource = new BTaskCompletionSource<Response>(),
150
+ CancellationToken = cancellationToken,
151
+ Timestamp = DateTime.Now
152
+ };
153
+
154
+ lock (queueLock)
155
+ {
156
+ if (highPriority)
157
+ {
158
+ highPriorityQueue.Enqueue(queuedRequest);
159
+ }
160
+ else
161
+ {
162
+ requestQueue.Enqueue(queuedRequest);
163
+ }
164
+ }
165
+
166
+ return queuedRequest.TaskCompletionSource.Task;
167
+ }
168
+
169
+ #endregion
170
+
171
+ #region Table Operations
172
+
173
+ public async BTask CreateTable<T>() where T : BaseModel, new()
174
+ {
175
+ var type = typeof(T);
176
+ if (createdTables.ContainsKey(type))
177
+ {
178
+ return;
179
+ }
180
+
181
+ var query = BuildCreateTableQuery<T>();
182
+
183
+ var request = new DatabaseRequest { Query = query };
184
+
185
+ var response = await EnqueueRequest(request, highPriority: true);
186
+
187
+ if (response.Success)
188
+ {
189
+ createdTables[type] = true;
190
+ }
191
+ else
192
+ {
193
+ throw new DatabaseException($"Failed to create table: {response.Error}");
194
+ }
195
+ }
196
+
197
+ public async BTask DropTable<T>() where T : BaseModel, new()
198
+ {
199
+ var type = typeof(T);
200
+ var instance = new T();
201
+ var query = $"DROP TABLE {instance.GetTableName()}";
202
+
203
+ var request = new DatabaseRequest { Query = query };
204
+
205
+ var response = await EnqueueRequest(request, highPriority: true);
206
+
207
+ if (response.Success)
208
+ {
209
+ createdTables.Remove(type);
210
+ }
211
+ else
212
+ {
213
+ throw new DatabaseException($"Failed to drop table: {response.Error}");
214
+ }
215
+ }
216
+
217
+ public async BTask AlterTable<T>(string alterStatement) where T : BaseModel, new()
218
+ {
219
+ var instance = new T();
220
+ var query = $"ALTER TABLE {instance.GetTableName()} {alterStatement}";
221
+
222
+ var request = new DatabaseRequest { Query = query };
223
+
224
+ var response = await EnqueueRequest(request, highPriority: true);
225
+
226
+ if (!response.Success)
227
+ {
228
+ throw new DatabaseException($"Failed to alter table: {response.Error}");
229
+ }
230
+ }
231
+
232
+ #endregion
233
+
234
+ #region Index Operations
235
+
236
+ public async BTask CreateIndex<T>(string indexName, params string[] columns) where T : BaseModel, new()
237
+ {
238
+ var instance = new T();
239
+ var query = $"CREATE INDEX {indexName} ON {instance.GetTableName()} ({string.Join(", ", columns)})";
240
+
241
+ var request = new DatabaseRequest { Query = query };
242
+
243
+ var response = await EnqueueRequest(request, highPriority: true);
244
+
245
+ if (!response.Success)
246
+ {
247
+ throw new DatabaseException($"Failed to create index: {response.Error}");
248
+ }
249
+ }
250
+
251
+ public async BTask DropIndex<T>(string indexName) where T : BaseModel, new()
252
+ {
253
+ var instance = new T();
254
+ var query = $"DROP INDEX {indexName} ON {instance.GetTableName()}";
255
+
256
+ var request = new DatabaseRequest { Query = query };
257
+
258
+ var response = await EnqueueRequest(request, highPriority: true);
259
+
260
+ if (!response.Success)
261
+ {
262
+ throw new DatabaseException($"Failed to drop index: {response.Error}");
263
+ }
264
+ }
265
+
266
+ #endregion
267
+
268
+ #region Query Operations
269
+
270
+ public QueryBuilder<T> From<T>() where T : BaseModel, new()
271
+ {
272
+ if (!initialized)
273
+ {
274
+ throw new InvalidOperationException("[Database.Client] Client not initialized. Call Initialize() first.");
275
+ }
276
+
277
+ return new QueryBuilder<T>(this);
278
+ }
279
+
280
+ public QueryBuilder<T> Query<T>() where T : BaseModel, new()
281
+ {
282
+ if (!initialized)
283
+ {
284
+ throw new InvalidOperationException("[Database.Client] Client not initialized. Call Initialize() first.");
285
+ }
286
+
287
+ return new QueryBuilder<T>(this);
288
+ }
289
+
290
+ public async BTask<Response> ExecuteRawQuery(string query, CancellationToken cancellationToken = default)
291
+ {
292
+ var request = new DatabaseRequest { Query = query };
293
+ return await EnqueueRequest(request, highPriority: false, cancellationToken);
294
+ }
295
+
296
+ internal async BTask<Response> ExecuteQuery(DatabaseRequest request, CancellationToken cancellationToken = default)
297
+ {
298
+ return await EnqueueRequest(request, highPriority: false, cancellationToken);
299
+ }
300
+
301
+ internal async BTask<Response> ExecuteMutation(DatabaseRequest request, CancellationToken cancellationToken = default)
302
+ {
303
+ return await EnqueueRequest(request, highPriority: true, cancellationToken);
304
+ }
305
+
306
+ /// <summary>
307
+ /// 트랜잭션 빌더 생성
308
+ /// </summary>
309
+ public TransactionBuilder Transaction()
310
+ {
311
+ if (!initialized)
312
+ {
313
+ throw new InvalidOperationException("[Database.Client] Client not initialized. Call Initialize() first.");
314
+ }
315
+
316
+ return new TransactionBuilder(this);
317
+ }
318
+
319
+ /// <summary>
320
+ /// 트랜잭션 실행 (내부용)
321
+ /// </summary>
322
+ internal async BTask<Response> ExecuteTransaction(DatabaseRequest request, CancellationToken cancellationToken = default)
323
+ {
324
+ return await EnqueueRequest(request, highPriority: true, cancellationToken);
325
+ }
326
+
327
+ #endregion
328
+
329
+ #region Cleanup
330
+
331
+ public void Dispose()
332
+ {
333
+ queueCancellationSource?.Cancel();
334
+ queueCancellationSource?.Dispose();
335
+
336
+ lock (queueLock)
337
+ {
338
+ requestQueue.Clear();
339
+ highPriorityQueue.Clear();
340
+ }
341
+
342
+ initialized = false;
343
+ }
344
+
345
+ #endregion
346
+
347
+ #region Query Generation Helper Methods
348
+
349
+ private string BuildCreateTableQuery<T>() where T : BaseModel, new()
350
+ {
351
+ var instance = new T();
352
+ var tableName = instance.GetTableName();
353
+ var columnList = instance.GetColumnList();
354
+
355
+ if (string.IsNullOrEmpty(tableName) || string.IsNullOrEmpty(columnList))
356
+ {
357
+ throw new InvalidOperationException($"Model class {typeof(T).Name} has not been processed by DatabaseWeaver. Table name or column information is missing.");
358
+ }
359
+
360
+ var sb = new StringBuilder();
361
+ sb.Append($"CREATE TABLE {tableName} (");
362
+
363
+ var columns = columnList.Split(',').Select(c => c.Trim()).ToArray();
364
+ var columnDefs = new List<string>();
365
+
366
+ foreach (var column in columns)
367
+ {
368
+ var dataType = instance.GetColumnDataType(column);
369
+ var nullable = instance.IsColumnNullable(column);
370
+ var defaultValue = instance.GetColumnDefaultValue(column);
371
+ var isPrimary = instance.GetPrimaryKeyColumnNames().Any(pk => pk.Equals(column, StringComparison.OrdinalIgnoreCase));
372
+
373
+ var columnDef = $"{column} {dataType}";
374
+
375
+ if (isPrimary)
376
+ {
377
+ columnDef += " PRIMARY KEY";
378
+ if (column.Equals(instance.GetAutoIncrementColumnName(), StringComparison.OrdinalIgnoreCase))
379
+ {
380
+ columnDef += " AUTO_INCREMENT";
381
+ }
382
+ }
383
+ else if (!nullable)
384
+ {
385
+ columnDef += " NOT NULL";
386
+ }
387
+
388
+ if (!string.IsNullOrEmpty(defaultValue) && !isPrimary)
389
+ {
390
+ columnDef += $" DEFAULT {FormatDefaultValue(defaultValue, dataType)}";
391
+ }
392
+
393
+ columnDefs.Add(columnDef);
394
+ }
395
+
396
+ sb.Append(string.Join(", ", columnDefs));
397
+
398
+ var tableType = instance.GetTableType();
399
+ var tableTypeClause = tableType == TableType.UserTable ? "USERTABLE" : "FLEXIBLETABLE";
400
+ var readPermissions = string.Join(", ", instance.GetReadPermissions());
401
+ var writePermissions = string.Join(", ", instance.GetWritePermissions());
402
+
403
+ sb.Append($", {tableTypeClause} (");
404
+ sb.Append($"CLIENT_ACCESS = {instance.GetClientAccess().ToString().ToLower()}, ");
405
+ sb.Append($"READ = ({readPermissions}), ");
406
+ sb.Append($"WRITE = ({writePermissions})");
407
+ sb.Append("))");
408
+
409
+ return sb.ToString();
410
+ }
411
+
412
+ private static readonly HashSet<string> SqlFunctionsAndKeywords = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
413
+ {
414
+ "NOW()", "CURRENT_TIMESTAMP", "CURRENT_TIMESTAMP()", "UUID()",
415
+ "CURRENT_DATE", "CURRENT_DATE()", "CURRENT_TIME", "CURRENT_TIME()",
416
+ "NULL"
417
+ };
418
+
419
+ private static string FormatDefaultValue(string defaultValue, string dataType)
420
+ {
421
+ if (string.IsNullOrWhiteSpace(defaultValue))
422
+ return defaultValue;
423
+
424
+ var trimmed = defaultValue.Trim();
425
+
426
+ // SQL 함수 및 키워드(NULL)는 그대로 반환
427
+ if (SqlFunctionsAndKeywords.Contains(trimmed))
428
+ return trimmed;
429
+
430
+ // 이미 따옴표로 감싸져 있으면 그대로 반환
431
+ if ((trimmed.StartsWith("'") && trimmed.EndsWith("'")) ||
432
+ (trimmed.StartsWith("\"") && trimmed.EndsWith("\"")))
433
+ {
434
+ return trimmed;
435
+ }
436
+
437
+ // 함수와 NULL을 제외한 모든 값은 따옴표로 감싸기
438
+ return $"'{trimmed.Replace("'", "''")}'";
439
+ }
440
+
441
+ #endregion
442
+ }
443
+
444
+ internal class QueuedRequest
445
+ {
446
+ public DatabaseRequest Request { get; set; }
447
+ public BTaskCompletionSource<Response> TaskCompletionSource { get; set; }
448
+ public CancellationToken CancellationToken { get; set; }
449
+ public DateTime Timestamp { get; set; }
450
+ }
451
+
452
+ public class BTaskCompletionSource<T>
453
+ {
454
+ private BTask<T> task;
455
+ private bool completed = false;
456
+ private readonly object lockObj = new object();
457
+
458
+ public BTaskCompletionSource()
459
+ {
460
+ task = new BTask<T>();
461
+ }
462
+
463
+ public BTask<T> Task => task;
464
+
465
+ public void SetResult(T result)
466
+ {
467
+ lock (lockObj)
468
+ {
469
+ if (completed) return;
470
+ completed = true;
471
+ task.SetResult(result);
472
+ }
473
+ }
474
+
475
+ public void SetException(Exception exception)
476
+ {
477
+ lock (lockObj)
478
+ {
479
+ if (completed) return;
480
+ completed = true;
481
+ task.SetException(exception);
482
+ }
483
+ }
484
+
485
+ public void SetCanceled()
486
+ {
487
+ SetException(new OperationCanceledException());
488
+ }
489
+ }
490
+ }