-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
Expand file tree
/
Copy pathsqlitetypes.h
More file actions
636 lines (514 loc) · 21.5 KB
/
sqlitetypes.h
File metadata and controls
636 lines (514 loc) · 21.5 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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
#pragma once
#ifndef SQLITETYPES_H
#define SQLITETYPES_H
#include <algorithm>
#include <bitset>
#include <cctype>
#include <map>
#include <memory>
#include <set>
#include <string>
#include <vector>
template<typename C, typename E>
bool contains(const C& container, E element)
{
return std::find(container.begin(), container.end(), element) != container.end();
}
template<typename T1, typename T2, typename E>
bool contains(const std::map<T1, T2>& container, E element)
{
return container.find(element) != container.end();
}
template<typename T>
bool compare_ci(const T& a, const T& b)
{
// Note: This function does not have to be (actually it must not be) fully UTF-8 aware because SQLite itself is not either.
return std::equal(a.begin(), a.end(), b.begin(), b.end(), [](unsigned char c1, unsigned char c2) {
return std::tolower(c1) == std::tolower(c2);
});
}
template<typename T>
bool compare_ci(const T& a, const char* b)
{
return compare_ci(a, std::string(b));
}
inline bool starts_with_ci(const std::string& str, const std::string& with)
{
if(str.size() < with.size())
return false;
else
return compare_ci(str.substr(0, with.size()), with);
}
namespace sqlb {
using StringVector = std::vector<std::string>;
class Object;
class Table;
class Index;
class View;
class Trigger;
class Field;
class Constraint;
class IndexedColumn;
using ObjectPtr = std::shared_ptr<Object>;
using TablePtr = std::shared_ptr<Table>;
using IndexPtr = std::shared_ptr<Index>;
using ViewPtr = std::shared_ptr<View>;
using TriggerPtr = std::shared_ptr<Trigger>;
using FieldVector = std::vector<Field>;
using IndexedColumnVector = std::vector<IndexedColumn>;
StringVector escapeIdentifier(StringVector ids);
std::string joinStringVector(const StringVector& vec, const std::string& delim);
std::string joinStringVector(const IndexedColumnVector& vec, const std::string& delim);
class Object
{
public:
explicit Object(const std::string& name): m_name(name), m_fullyParsed(false) {}
virtual ~Object() = default;
bool operator==(const Object& rhs) const;
void setName(const std::string& name) { m_name = name; }
const std::string& name() const { return m_name; }
void setOriginalSql(const std::string& original_sql) { m_originalSql = original_sql; }
std::string originalSql() const { return m_originalSql; }
void setFullyParsed(bool fully_parsed) { m_fullyParsed = fully_parsed; }
bool fullyParsed() const { return m_fullyParsed; }
/**
* @brief Returns the CREATE statement for this object
* @param schema The schema name of the object
* @param ifNotExists If set to true the "IF NOT EXISTS" qualifier will be added to the create statement
* @return A std::string with the CREATE statement.
*/
virtual std::string sql(const std::string& schema = std::string("main"), bool ifNotExists = false) const = 0;
protected:
std::string m_name;
std::string m_originalSql;
bool m_fullyParsed;
};
class Constraint
{
public:
void setName(const std::string& name) { m_name = name; }
const std::string& name() const { return m_name; }
protected:
std::string m_name;
};
class ForeignKeyClause : public Constraint
{
public:
explicit ForeignKeyClause(const std::string& table = std::string(), const StringVector& columns = {}, const std::string& constraint = std::string())
: m_table(table),
m_columns(columns),
m_constraint(constraint)
{
}
std::string toString() const;
void setTable(const std::string& table) { m_table = table; }
const std::string& table() const { return m_table; }
void setColumns(const StringVector& columns) { m_columns = columns; }
const StringVector& columns() const { return m_columns; }
void setConstraint(const std::string& constraint) { m_constraint = constraint; }
const std::string& constraint() const { return m_constraint; }
std::string toSql(const StringVector& columns) const;
private:
std::string m_table;
StringVector m_columns;
std::string m_constraint;
};
class UniqueConstraint : public Constraint
{
public:
virtual ~UniqueConstraint() = default;
void setConflictAction(const std::string& conflict) { m_conflictAction = conflict; }
const std::string& conflictAction() const { return m_conflictAction; }
virtual std::string toSql(const IndexedColumnVector& columns) const;
virtual bool isPrimaryKey() const { return false; }
protected:
std::string m_conflictAction;
};
class NotNullConstraint : public Constraint
{
public:
void setConflictAction(const std::string& conflict) { m_conflictAction = conflict; }
const std::string& conflictAction() const { return m_conflictAction; }
std::string toSql() const;
protected:
std::string m_conflictAction;
};
class PrimaryKeyConstraint : public UniqueConstraint
{
// Primary keys are a sort of unique constraint for us. This matches quite nicely as both can have a conflict action
// and both need to maintain a copy of the column list with sort order information etc.
public:
PrimaryKeyConstraint()
: m_auto_increment(false)
{}
void setAutoIncrement(bool ai) { m_auto_increment = ai; }
bool autoIncrement() const { return m_auto_increment; }
bool isPrimaryKey() const override { return true; }
std::string toSql(const IndexedColumnVector& columns) const override;
private:
bool m_auto_increment;
};
class CheckConstraint : public Constraint
{
public:
explicit CheckConstraint(const std::string& expr = std::string())
: m_expression(expr)
{
}
void setExpression(const std::string& expr) { m_expression = expr; }
const std::string& expression() const { return m_expression; }
std::string toSql() const;
private:
std::string m_expression;
};
class DefaultConstraint : public Constraint
{
public:
explicit DefaultConstraint(const std::string& value = std::string())
: m_value(value)
{
}
void setValue(const std::string& value) { m_value = value; }
const std::string& value() const { return m_value; }
std::string toSql() const;
private:
std::string m_value;
};
class CollateConstraint : public Constraint
{
public:
explicit CollateConstraint(const std::string& collation)
: m_collation(collation)
{
}
void setCollation(const std::string& collation) { m_collation = collation; }
const std::string& collation() const { return m_collation; }
std::string toSql() const;
private:
std::string m_collation;
};
class GeneratedColumnConstraint : public Constraint
{
public:
explicit GeneratedColumnConstraint(const std::string& expr = std::string(), const std::string& storage = "VIRTUAL")
: m_expression(expr),
m_storage(storage)
{
}
void setExpression(const std::string& expr) { m_expression = expr; }
const std::string& expression() const { return m_expression; }
void setStorage(const std::string& storage) { m_storage = storage; }
std::string storage() const { return m_storage.empty() ? "VIRTUAL" : m_storage; }
std::string toSql() const;
private:
std::string m_expression;
std::string m_storage;
};
class Field
{
public:
Field()
{}
Field(const std::string& name,
const std::string& type,
bool notnull = false,
const std::string& defaultvalue = std::string(),
const std::string& check = std::string(),
bool unique = false,
const std::string& collation = std::string())
: m_name(name)
, m_type(type)
, m_notnull(notnull ? std::make_shared<NotNullConstraint>() : nullptr)
, m_check(check.empty() ? nullptr : std::make_shared<CheckConstraint>(check))
, m_defaultvalue(defaultvalue.empty() ? nullptr : std::make_shared<DefaultConstraint>(defaultvalue))
, m_unique(unique ? std::make_shared<UniqueConstraint>() : nullptr)
, m_collation(collation.empty() ? nullptr : std::make_shared<CollateConstraint>(collation))
{}
bool operator==(const Field& rhs) const;
std::string toString(const std::string& indent = "\t", const std::string& sep = "\t") const;
void setName(const std::string& name) { m_name = name; }
void setType(const std::string& type) { m_type = type; }
void setNotNull(std::shared_ptr<NotNullConstraint> notnull) { m_notnull = notnull; }
void setNotNull(bool notnull = true) { if(notnull) m_notnull = std::make_shared<NotNullConstraint>(); else m_notnull.reset(); }
void setCheck(std::shared_ptr<CheckConstraint> check) { m_check = check; }
void setCheck(const std::string& check) { if(!check.empty()) m_check = std::make_shared<CheckConstraint>(check); else m_check.reset(); }
void setDefaultValue(std::shared_ptr<DefaultConstraint> defaultvalue) { m_defaultvalue = defaultvalue; }
void setDefaultValue(const std::string& value) { if(!value.empty()) m_defaultvalue = std::make_shared<DefaultConstraint>(value); else m_defaultvalue.reset(); }
void setUnique(std::shared_ptr<UniqueConstraint> u) { m_unique = u; }
void setUnique(bool u) { if(u) m_unique = std::make_shared<UniqueConstraint>(); else m_unique.reset(); }
void setCollation(std::shared_ptr<CollateConstraint> c) { m_collation = c; }
void setCollation(const std::string& collation) { if(!collation.empty()) m_collation = std::make_shared<CollateConstraint>(collation); else m_collation.reset(); }
bool isText() const;
bool isInteger() const;
bool isBlob() const;
bool isReal() const;
bool isNumeric() const;
// Type affinity of the column according to SQLite3 rules.
// The Affinity enum values match the SQLITE_INTEGER, SQLITE_FLOAT, SQLITE_BLOB, and SQLITE_TEXT constants
enum Affinity
{
IntegerAffinity = 1,
FloatAffinity = 2,
TextAffinity = 3,
BlobAffinity = 4,
};
Affinity affinity() const;
const std::string& name() const { return m_name; }
const std::string& type() const { return m_type; }
bool notnull() const { return m_notnull ? true : false; }
std::string check() const { return m_check ? m_check->expression() : std::string{}; }
std::string defaultValue() const { return m_defaultvalue ? m_defaultvalue->value() : std::string{}; }
bool unique() const { return m_unique ? true : false; }
std::string collation() const { return m_collation ? m_collation->collation() : std::string{}; }
const std::shared_ptr<GeneratedColumnConstraint> generated() const { return m_generated; }
std::shared_ptr<GeneratedColumnConstraint> generated() { return m_generated; }
void setGenerated(std::shared_ptr<GeneratedColumnConstraint> gen) { m_generated = gen; }
private:
std::string m_name;
std::string m_type;
std::shared_ptr<NotNullConstraint> m_notnull;
std::shared_ptr<CheckConstraint> m_check;
std::shared_ptr<DefaultConstraint> m_defaultvalue;
std::shared_ptr<UniqueConstraint> m_unique;
std::shared_ptr<CollateConstraint> m_collation;
std::shared_ptr<GeneratedColumnConstraint> m_generated;
};
class Table : public Object
{
public:
explicit Table(const std::string& name): Object(name) {}
explicit Table(const Table& table);
Table& operator=(const Table& rhs);
bool operator==(const Table& rhs) const;
virtual bool isView() const { return false; }
FieldVector fields;
using field_type = Field;
using field_iterator = FieldVector::iterator;
enum Options
{
WithoutRowid,
Strict,
NumOptions
};
/**
* @brief Returns the CREATE TABLE statement for this table object
* @return A std::string with the CREATE TABLE object.
*/
std::string sql(const std::string& schema = "main", bool ifNotExists = false) const override;
StringVector fieldNames() const;
StringVector rowidColumns() const;
void setWithoutRowidTable(bool without_rowid) { m_options.set(WithoutRowid, without_rowid); }
bool withoutRowidTable() const { return m_options.test(WithoutRowid); }
void setStrict(bool strict) { m_options.set(Strict, strict); }
bool isStrict() const { return m_options.test(Strict); }
void setVirtualUsing(const std::string& virt_using) { m_virtual = virt_using; }
const std::string& virtualUsing() const { return m_virtual; }
bool isVirtual() const { return !m_virtual.empty(); }
void addConstraint(const IndexedColumnVector& columns, std::shared_ptr<PrimaryKeyConstraint> constraint) { m_indexConstraints.insert(std::make_pair(columns, constraint)); }
void addConstraint(const IndexedColumnVector& columns, std::shared_ptr<UniqueConstraint> constraint) { m_indexConstraints.insert(std::make_pair(columns, constraint)); }
void addConstraint(const StringVector& columns, std::shared_ptr<ForeignKeyClause> constraint) { m_foreignKeys.insert(std::make_pair(columns, constraint)); }
void addConstraint(std::shared_ptr<CheckConstraint> constraint) { m_checkConstraints.push_back(constraint); }
void removeConstraint(std::shared_ptr<UniqueConstraint> constraint);
void removeConstraint(std::shared_ptr<ForeignKeyClause> constraint);
void removeConstraint(std::shared_ptr<CheckConstraint> constraint);
void addKeyToConstraint(std::shared_ptr<UniqueConstraint> constraint, const std::string& key);
void removeKeyFromConstraint(std::shared_ptr<UniqueConstraint> constraint, const std::string& key);
void removeKeyFromAllConstraints(const std::string& key);
void renameKeyInAllConstraints(const std::string& key, const std::string& to);
// These three functions are helper functions which we need for some templated code. As soon as we
// switch to C++17 we should be able to rewrite that code so we can get rid of these.
void addConstraint(const StringVector& columns, std::shared_ptr<PrimaryKeyConstraint> constraint);
void addConstraint(const StringVector& columns, std::shared_ptr<UniqueConstraint> constraint);
void addConstraint(const IndexedColumnVector& columns, std::shared_ptr<ForeignKeyClause> constraint);
std::shared_ptr<PrimaryKeyConstraint> primaryKey() const;
IndexedColumnVector primaryKeyColumns() const;
std::multimap<IndexedColumnVector, std::shared_ptr<UniqueConstraint>> indexConstraints() const { return m_indexConstraints; }
std::multimap<StringVector, std::shared_ptr<ForeignKeyClause>> foreignKeys() const { return m_foreignKeys; }
std::shared_ptr<ForeignKeyClause> foreignKey(const StringVector& columns) const; //! Only returns the first foreign key, if any
std::vector<std::shared_ptr<CheckConstraint>> checkConstraints() const { return m_checkConstraints; }
/**
* @brief parseSQL Parses the create Table statement in sSQL.
* @param sSQL The create table statement.
* @return The table object. The table object may be empty if parsing failed.
*/
static TablePtr parseSQL(const std::string& sSQL);
private:
StringVector fieldList() const;
private:
std::multimap<IndexedColumnVector, std::shared_ptr<UniqueConstraint>> m_indexConstraints; // This stores both UNIQUE and PRIMARY KEY constraints
std::multimap<StringVector, std::shared_ptr<ForeignKeyClause>> m_foreignKeys;
std::vector<std::shared_ptr<CheckConstraint>> m_checkConstraints;
std::string m_virtual;
std::bitset<NumOptions> m_options;
};
class IndexedColumn
{
public:
IndexedColumn()
: m_isExpression(false)
{
}
IndexedColumn(const std::string& name, bool expr, const std::string& order = std::string())
: m_name(name),
m_isExpression(expr),
m_order(order)
{
}
bool operator==(const std::string& name) const { return !m_isExpression && m_name == name; }
bool operator!=(const std::string& name) const { return !m_isExpression && m_name != name; }
bool operator==(const IndexedColumn& other) const { return m_name == other.m_name && m_isExpression == other.m_isExpression && m_order == other.m_order; }
bool operator<(const IndexedColumn& other) const { return m_name < other.m_name; }
void setName(const std::string& name) { m_name = name; }
const std::string& name() const { return m_name; }
void setExpression(bool expr) { m_isExpression = expr; }
bool expression() const { return m_isExpression; }
void setOrder(const std::string& order) { m_order = order; }
std::string order() const { return m_order; }
std::string toString(const std::string& indent = "\t", const std::string& sep = "\t") const;
private:
std::string m_name;
bool m_isExpression;
std::string m_order;
};
class Index : public Object
{
public:
explicit Index(const std::string& name): Object(name), m_unique(false) {}
Index& operator=(const Index& rhs);
IndexedColumnVector fields;
using field_type = IndexedColumn;
using field_iterator = IndexedColumnVector::iterator;
void setUnique(bool unique) { m_unique = unique; }
bool unique() const { return m_unique; }
void setTable(const std::string& table) { m_table = table; }
const std::string& table() const { return m_table; }
void setWhereExpr(const std::string& expr) { m_whereExpr = expr; }
const std::string& whereExpr() const { return m_whereExpr; }
/**
* @brief Returns the CREATE INDEX statement for this index object
* @return A std::string with the CREATE INDEX object.
*/
std::string sql(const std::string& schema = "main", bool ifNotExists = false) const override;
/**
* @brief parseSQL Parses the CREATE INDEX statement in sSQL.
* @param sSQL The create index statement.
* @return The index object. The index object may be empty if the parsing failed.
*/
static IndexPtr parseSQL(const std::string& sSQL);
private:
StringVector columnSqlList() const;
bool m_unique;
std::string m_table;
std::string m_whereExpr;
};
class View : public Table
{
public:
explicit View(const std::string& name): Table(name) {}
virtual bool isView() const override { return true; }
std::string sql(const std::string& /*schema*/ = "main", bool /*ifNotExists*/ = false) const override
{ /* TODO */ return m_originalSql; }
static ViewPtr parseSQL(const std::string& sSQL);
};
class Trigger : public Object
{
public:
explicit Trigger(const std::string& name): Object(name) {}
std::string sql(const std::string& /*schema*/ = "main", bool /*ifNotExists*/ = false) const override
{ /* TODO */ return m_originalSql; }
static TriggerPtr parseSQL(const std::string& sSQL);
void setTable(const std::string& table) { m_table = table; }
std::string table() const { return m_table; }
private:
std::string m_table;
};
/**
* @brief Return the name of the base table of the given object. For indices and triggers that is the table which the object is related to.
*/
template<typename T>
std::string getBaseTable(std::shared_ptr<T> /*object*/)
{
return std::string{};
}
template<>
std::string getBaseTable<Index>(IndexPtr object);
template<>
std::string getBaseTable<Trigger>(TriggerPtr object);
/**
* @brief findField Finds a field in the database object and returns an iterator to it.
* @param object
* @param name
* @return The iterator pointing to the field in the field container of the object if the field was found.
* object.fields.end() if the field couldn't be found.
*/
template<typename T>
typename T::field_iterator findField(T* object, const std::string& name)
{
return std::find_if(object->fields.begin(), object->fields.end(), [&name](const typename T::field_type& f) {
return compare_ci(name, f.name());
});
}
template<typename T>
typename T::field_iterator findField(const T* object, const std::string& name)
{
return findField(const_cast<T*>(object), name);
}
template<typename T>
typename std::remove_reference<T>::type::field_iterator findField(std::shared_ptr<T> object, const std::string& name)
{
return findField(object.get(), name);
}
template<typename T>
typename std::remove_reference<T>::type::field_iterator findField(T& object, const std::string& name)
{
return findField(&object, name);
}
template<typename T> struct is_shared_ptr : std::false_type {};
template<typename T> struct is_shared_ptr<std::shared_ptr<T>> : std::true_type {};
/**
* @brief removeField Finds and removes a field in the database object
* @param object
* @param name
* @return true if sucessful, otherwise false
*/
template<typename T>
bool removeField(T* object, const std::string& name)
{
auto index = findField(object, name);
if(index != object->fields.end())
{
object->fields.erase(index);
return true;
}
return false;
}
template<typename T, typename = typename std::enable_if<is_shared_ptr<T>::value>::type>
bool removeField(T object, const std::string& name)
{
return removeField(object.get(), name);
}
template<typename T, typename = typename std::enable_if<!is_shared_ptr<T>::value>::type>
bool removeField(T& object, const std::string& name)
{
return removeField(&object, name);
}
/**
* @brief getFieldNumber returns the number of the field with the given name in an object. This is supposed to be a temporary helper function only.
* @param object
* @param name
* @return number of the field
*
* TODO Remove this function. Whereever it is used we make the assumption that the queried columns are exactly equal to the columns of the table or view.
* For more complex queries this is not true and in fact it already is a dubious assumption because we also select the rowid column.
*/
inline size_t getFieldNumber(TablePtr object, const std::string& name)
{
for(size_t i=0;i<object->fields.size();i++)
{
if(object->fields[i].name() == name)
return i;
}
return 0;
}
} //namespace sqlb
#endif