-
Notifications
You must be signed in to change notification settings - Fork 7
Expand file tree
/
Copy pathCompiledTable.cs
More file actions
149 lines (122 loc) · 4.78 KB
/
CompiledTable.cs
File metadata and controls
149 lines (122 loc) · 4.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
namespace PowerSync.Common.DB.Schema;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
class CompiledTable
{
public static readonly Regex InvalidSQLCharacters = new Regex(@"[""'%,.#\s\[\]]", RegexOptions.Compiled);
public string Name { get; init; } = null!;
protected TableOptions Options { get; init; } = null!;
public IReadOnlyDictionary<string, ColumnType> Columns { get; init; }
public IReadOnlyDictionary<string, List<string>> Indexes { get; init; }
private readonly ColumnJSON[] ColumnsJSON;
private readonly IndexJSON[] IndexesJSON;
public CompiledTable(string name, Dictionary<string, ColumnType> columns, TableOptions options)
{
Name = name;
Options = options;
Columns = columns;
ColumnsJSON =
columns
.Select(kvp => new ColumnJSON(new ColumnJSONOptions(kvp.Key, kvp.Value)))
.ToArray();
IndexesJSON =
(Options?.Indexes ?? [])
.Select(kvp =>
new IndexJSON(new IndexJSONOptions(
kvp.Key,
kvp.Value.Select(name =>
new IndexedColumnJSON(new IndexedColumnJSONOptions(
name.Replace("-", ""), !name.StartsWith("-")))
).ToArray()
))
)
.ToArray();
Indexes = Options?.Indexes ?? [];
}
public void Validate()
{
if (string.IsNullOrWhiteSpace(Name))
{
throw new Exception($"Table name is required.");
}
if (InvalidSQLCharacters.IsMatch(Name))
{
throw new Exception($"Invalid characters in table name: {Name}");
}
if (!string.IsNullOrWhiteSpace(Options.ViewName) && InvalidSQLCharacters.IsMatch(Options.ViewName!))
{
throw new Exception($"Invalid characters in view name: {Options.ViewName}");
}
if (Columns.Count > Table.MAX_AMOUNT_OF_COLUMNS)
{
throw new Exception(
$"Table has too many columns. The maximum number of columns is {Table.MAX_AMOUNT_OF_COLUMNS}.");
}
if (Options.TrackMetadata && Options.LocalOnly)
{
throw new Exception("Can't include metadata for local-only tables.");
}
if (Options.TrackPreviousValues != null && Options.LocalOnly)
{
throw new Exception("Can't include old values for local-only tables.");
}
var columnNames = new HashSet<string> { "id" };
foreach (var kvp in Columns)
{
string columnName = kvp.Key;
ColumnType columnType = kvp.Value;
if (columnName == "id")
{
throw new Exception("An id column is automatically added, custom id columns are not supported");
}
if (columnType == ColumnType.Inferred)
{
throw new Exception($"Invalid ColumnType for {kvp.Key}: ColumnType.Inferred. ColumnType.Inferred is only supported when using the schema attribute syntax for defining tables.");
}
if (InvalidSQLCharacters.IsMatch(columnName))
{
throw new Exception($"Invalid characters in column name: {columnName}");
}
columnNames.Add(columnName);
}
foreach (var index in IndexesJSON)
{
var indexName = index.Name;
var indexColumns = index.Columns;
if (InvalidSQLCharacters.IsMatch(indexName))
{
throw new Exception($"Invalid characters in index name: {indexName}");
}
foreach (var indexColumn in indexColumns)
{
if (!columnNames.Contains(indexColumn.Name))
{
throw new Exception($"Column {indexColumn.Name} not found for index {indexName}");
}
}
}
}
public object ToJSONObject()
{
var trackPrevious = Options.TrackPreviousValues;
return new
{
name = Name,
view_name = Options.ViewName ?? Name,
local_only = Options.LocalOnly,
insert_only = Options.InsertOnly,
columns = ColumnsJSON.Select(c => c.ToJSONObject()).ToList(),
indexes = IndexesJSON.Select(i => i.ToJSONObject(this)).ToList(),
include_metadata = Options.TrackMetadata,
ignore_empty_update = Options.IgnoreEmptyUpdates,
include_old = (object)(trackPrevious switch
{
null => false,
{ Columns: null } => true,
{ Columns: var cols } => cols
}),
include_old_only_when_changed = trackPrevious?.OnlyWhenChanged ?? false
};
}
}