forked from sqlitebrowser/sqlitebrowser
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsqlitetypes.h
More file actions
583 lines (465 loc) · 18 KB
/
Copy pathsqlitetypes.h
File metadata and controls
583 lines (465 loc) · 18 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
#pragma once
#ifndef SQLITETYPES_H
#define SQLITETYPES_H
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
#include <cctype>
#include <set>
template<typename C, typename E>
bool contains(const C& container, E element)
{
return std::find(container.begin(), container.end(), 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.
if(a.length() != b.length())
return false;
return std::equal(a.begin(), a.end(), b.begin(), [](unsigned char c1, unsigned char c2) {
return std::tolower(c1) == std::tolower(c2);
});
// TODO Replace the entire code above by the following once we have enabled C++14 support
/*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>;
StringVector escapeIdentifier(StringVector ids);
std::string joinStringVector(const StringVector& vec, const std::string& delim);
class Object;
class Table;
class Index;
class View;
class Trigger;
class Field;
class Constraint;
class IndexedColumn;
struct FieldInfo;
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 ConstraintPtr = std::shared_ptr<Constraint>;
using FieldVector = std::vector<Field>;
using IndexedColumnVector = std::vector<IndexedColumn>;
using ConstraintSet = std::set<ConstraintPtr>;
using FieldInfoList = std::vector<FieldInfo>;
struct FieldInfo
{
FieldInfo(const std::string& name_, const std::string& type_, const std::string& sql_)
: name(name_), type(type_), sql(sql_)
{}
std::string name;
std::string type;
std::string sql;
};
class Object
{
public:
enum Types
{
Table,
Index,
View,
Trigger
};
explicit Object(const std::string& name): m_name(name), m_fullyParsed(false) {}
virtual ~Object() = default;
bool operator==(const Object& rhs) const;
virtual Types type() const = 0;
static std::string typeToString(Types type);
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; }
virtual std::string baseTable() const { return std::string(); }
void setFullyParsed(bool fully_parsed) { m_fullyParsed = fully_parsed; }
bool fullyParsed() const { return m_fullyParsed; }
virtual FieldInfoList fieldInformation() const { return FieldInfoList(); }
/**
* @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:
enum ConstraintTypes
{
PrimaryKeyConstraintType,
UniqueConstraintType,
ForeignKeyConstraintType,
CheckConstraintType,
NoType = 999,
};
explicit Constraint(const StringVector& columns = {}, const std::string& name = std::string())
: column_list(columns),
m_name(name)
{
}
virtual ~Constraint() = default;
static ConstraintPtr makeConstraint(ConstraintTypes type);
virtual ConstraintTypes type() const = 0;
void setName(const std::string& name) { m_name = name; }
const std::string& name() const { return m_name; }
StringVector columnList() const { return column_list; }
virtual void setColumnList(const StringVector& list) { column_list = list; }
virtual void addToColumnList(const std::string& key) { column_list.push_back(key); }
virtual void replaceInColumnList(const std::string& from, const std::string& to);
virtual void removeFromColumnList(const std::string& key);
virtual std::string toSql() const = 0;
protected:
StringVector column_list;
std::string m_name;
};
class ForeignKeyClause : public Constraint
{
public:
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)
{
}
bool isSet() const;
std::string toString() const;
void setFromString(const std::string& fk);
void setTable(const std::string& table) { m_override.clear(); 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 override;
ConstraintTypes type() const override { return ForeignKeyConstraintType; }
private:
std::string m_table;
StringVector m_columns;
std::string m_constraint;
std::string m_override;
};
class UniqueConstraint : public Constraint
{
public:
explicit UniqueConstraint(const IndexedColumnVector& columns = {});
explicit UniqueConstraint(const StringVector& columns);
void setConflictAction(const std::string& conflict) { m_conflictAction = conflict; }
const std::string& conflictAction() const { return m_conflictAction; }
// We override these because we maintain our own copy of the column_list variable in m_columns.
// This needs to be done because in a unique constraint we can add expressions, sort order, etc. to the
// list of columns.
void setColumnList(const StringVector& list) override;
void addToColumnList(const std::string& key) override;
void replaceInColumnList(const std::string& from, const std::string& to) override;
void removeFromColumnList(const std::string& key) override;
std::string toSql() const override;
ConstraintTypes type() const override { return UniqueConstraintType; }
protected:
IndexedColumnVector m_columns;
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:
explicit PrimaryKeyConstraint(const IndexedColumnVector& columns = {});
explicit PrimaryKeyConstraint(const StringVector& columns);
void setAutoIncrement(bool ai) { m_auto_increment = ai; }
bool autoIncrement() const { return m_auto_increment; }
std::string toSql() const override;
ConstraintTypes type() const override { return PrimaryKeyConstraintType; }
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 override;
ConstraintTypes type() const override { return CheckConstraintType; }
private:
std::string m_expression;
};
class Field
{
public:
Field()
: m_notnull(false),
m_unique(false)
{}
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)
, m_check(check)
, m_defaultvalue(defaultvalue)
, m_unique(unique)
, m_collation(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(bool notnull = true) { m_notnull = notnull; }
void setCheck(const std::string& check) { m_check = check; }
void setDefaultValue(const std::string& defaultvalue) { m_defaultvalue = defaultvalue; }
void setUnique(bool u) { m_unique = u; }
void setCollation(const std::string& collation) { m_collation = collation; }
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; }
const std::string& check() const { return m_check; }
const std::string& defaultValue() const { return m_defaultvalue; }
bool unique() const { return m_unique; }
const std::string& collation() const { return m_collation; }
private:
std::string m_name;
std::string m_type;
bool m_notnull;
std::string m_check;
std::string m_defaultvalue;
bool m_unique;
std::string m_collation;
};
class Table : public Object
{
public:
explicit Table(const std::string& name): Object(name), m_withoutRowid(false) {}
explicit Table(const Table& table);
Table& operator=(const Table& rhs);
bool operator==(const Table& rhs) const;
Types type() const override { return Object::Table; }
FieldVector fields;
using field_type = Field;
using field_iterator = FieldVector::iterator;
/**
* @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_withoutRowid = without_rowid; }
bool withoutRowidTable() const { return m_withoutRowid; }
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(); }
FieldInfoList fieldInformation() const override;
void addConstraint(ConstraintPtr constraint);
void setConstraint(ConstraintPtr constraint);
void removeConstraint(ConstraintPtr constraint);
void removeConstraints(const StringVector& vStrFields = StringVector(), Constraint::ConstraintTypes type = Constraint::NoType);
ConstraintPtr constraint(const StringVector& vStrFields = StringVector(), Constraint::ConstraintTypes type = Constraint::NoType) const; //! Only returns the first constraint, if any
std::vector<ConstraintPtr> constraints(const StringVector& vStrFields = StringVector(), Constraint::ConstraintTypes type = Constraint::NoType) const;
ConstraintSet allConstraints() const { return m_constraints; }
void setConstraints(const ConstraintSet& constraints);
void replaceConstraint(ConstraintPtr from, ConstraintPtr to);
std::shared_ptr<PrimaryKeyConstraint> primaryKey();
void removeKeyFromAllConstraints(const std::string& key);
void renameKeyInAllConstraints(const std::string& key, const std::string& to);
/**
* @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:
bool m_withoutRowid;
ConstraintSet m_constraints;
std::string m_virtual;
};
class IndexedColumn
{
public:
IndexedColumn() {}
IndexedColumn(const std::string& name, bool expr, const std::string& order = std::string())
: m_name(name),
m_isExpression(expr),
m_order(order)
{
}
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);
Types type() const override { return Object::Index; }
IndexedColumnVector fields;
using field_type = IndexedColumn;
using field_iterator = IndexedColumnVector::iterator;
std::string baseTable() const override { return m_table; }
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);
FieldInfoList fieldInformation() const override;
private:
StringVector columnSqlList() const;
bool m_unique;
std::string m_table;
std::string m_whereExpr;
};
class View : public Object
{
public:
explicit View(const std::string& name): Object(name) {}
Types type() const override { return Object::View; }
FieldVector fields;
std::string sql(const std::string& /*schema*/ = "main", bool /*ifNotExists*/ = false) const override
{ /* TODO */ return m_originalSql; }
static ViewPtr parseSQL(const std::string& sSQL);
StringVector fieldNames() const;
FieldInfoList fieldInformation() const override;
};
class Trigger : public Object
{
public:
explicit Trigger(const std::string& name): Object(name) {}
Types type() const override { return Object::Trigger; }
std::string sql(const std::string& /*schema*/ = "main", bool /*ifNotExists*/ = false) const override
{ /* TODO */ return m_originalSql; }
static TriggerPtr parseSQL(const std::string& sSQL);
std::string baseTable() const override { return m_table; }
void setTable(const std::string& table) { m_table = table; }
std::string table() const { return m_table; }
private:
std::string m_table;
};
/**
* @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);
}
} //namespace sqlb
#endif