mirror of
https://github.com/sqlitebrowser/sqlitebrowser.git
synced 2026-01-19 18:40:13 -06:00
parser: Add support for parsing GENERATED ALWAYS AS columns
This adds support for parsing, storing, and generating GENERATED ALWAYS AS columns as added in SQLite 3.31.0. See issue #2107.
This commit is contained in:
@@ -24,7 +24,7 @@ void SqlUiLexer::setupAutoCompletion()
|
||||
keywordPatterns
|
||||
// Keywords
|
||||
<< "ABORT" << "ACTION" << "ADD" << "AFTER" << "ALL"
|
||||
<< "ALTER" << "ANALYZE" << "AND" << "AS" << "ASC"
|
||||
<< "ALTER" << "ALWAYS" << "ANALYZE" << "AND" << "AS" << "ASC"
|
||||
<< "ATTACH" << "AUTOINCREMENT" << "BEFORE" << "BEGIN" << "BETWEEN"
|
||||
<< "BY" << "CASCADE" << "CASE" << "CAST" << "CHECK"
|
||||
<< "COLLATE" << "COLUMN" << "COMMIT" << "CONFLICT" << "CONSTRAINT"
|
||||
@@ -33,7 +33,7 @@ void SqlUiLexer::setupAutoCompletion()
|
||||
<< "DESC" << "DETACH" << "DISTINCT" << "DO" << "DROP" << "EACH"
|
||||
<< "ELSE" << "END" << "ESCAPE" << "EXCEPT" << "EXCLUSIVE"
|
||||
<< "EXISTS" << "EXPLAIN" << "FAIL" << "FILTER" << "FOLLOWING" << "FOR" << "FOREIGN"
|
||||
<< "FROM" << "FULL" << "GLOB" << "GROUP" << "HAVING"
|
||||
<< "FROM" << "FULL" << "GENERATED" << "GLOB" << "GROUP" << "HAVING"
|
||||
<< "IF" << "IGNORE" << "IMMEDIATE" << "IN" << "INDEX"
|
||||
<< "INDEXED" << "INITIALLY" << "INNER" << "INSERT" << "INSTEAD"
|
||||
<< "INTERSECT" << "INTO" << "IS" << "ISNULL" << "JOIN"
|
||||
@@ -43,7 +43,7 @@ void SqlUiLexer::setupAutoCompletion()
|
||||
<< "OUTER" << "OVER" << "PARTITION" << "PLAN" << "PRAGMA" << "PRECEDING" << "PRIMARY" << "QUERY"
|
||||
<< "RAISE" << "RANGE" << "RECURSIVE" << "REFERENCES" << "REGEXP" << "REINDEX" << "RELEASE"
|
||||
<< "RENAME" << "REPLACE" << "RESTRICT" << "RIGHT" << "ROLLBACK"
|
||||
<< "ROWID" << "ROW" << "ROWS" << "SAVEPOINT" << "SELECT" << "SET" << "TABLE"
|
||||
<< "ROWID" << "ROW" << "ROWS" << "SAVEPOINT" << "SELECT" << "SET" << "STORED" << "TABLE"
|
||||
<< "TEMP" << "TEMPORARY" << "THEN" << "TO" << "TRANSACTION"
|
||||
<< "TRIGGER" << "UNBOUNDED" << "UNION" << "UNIQUE" << "UPDATE" << "USING"
|
||||
<< "VACUUM" << "VALUES" << "VIEW" << "VIRTUAL" << "WHEN"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -602,7 +602,7 @@ extern int yylex (yyscan_t yyscanner);
|
||||
#undef yyTABLES_NAME
|
||||
#endif
|
||||
|
||||
#line 235 "sqlite3_lexer.ll"
|
||||
#line 238 "sqlite3_lexer.ll"
|
||||
|
||||
|
||||
#line 609 "sqlite3_lexer.h"
|
||||
|
||||
@@ -115,6 +115,7 @@ WS [ \t\f]
|
||||
|
||||
"ABORT" return TOKEN(ABORT);
|
||||
"ACTION" return TOKEN(ACTION);
|
||||
"ALWAYS" return TOKEN(ALWAYS);
|
||||
"AS" return TOKEN(AS);
|
||||
"ASC" return TOKEN(ASC);
|
||||
"AUTOINCREMENT" return TOKEN(AUTOINCREMENT);
|
||||
@@ -144,6 +145,7 @@ WS [ \t\f]
|
||||
"FILTER" return TOKEN(FILTER);
|
||||
"FOLLOWING" return TOKEN(FOLLOWING);
|
||||
"FOREIGN" return TOKEN(FOREIGN);
|
||||
"GENERATED" return TOKEN(GENERATED);
|
||||
"GLOB" return TOKEN(GLOB);
|
||||
"IF" return TOKEN(IF);
|
||||
"IGNORE" return TOKEN(IGNORE);
|
||||
@@ -178,6 +180,7 @@ WS [ \t\f]
|
||||
"ROWS" return TOKEN(ROWS);
|
||||
"SELECT" return TOKEN(SELECT);
|
||||
"SET" return TOKEN(SET);
|
||||
"STORED" return TOKEN(STORED);
|
||||
"TABLE" return TOKEN(TABLE);
|
||||
"TEMP" return TOKEN(TEMP);
|
||||
"TEMPORARY" return TOKEN(TEMPORARY);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
// A Bison parser, made by GNU Bison 3.4.1.
|
||||
// A Bison parser, made by GNU Bison 3.5.1.
|
||||
|
||||
// Locations for Bison parsers in C++
|
||||
|
||||
// Copyright (C) 2002-2015, 2018-2019 Free Software Foundation, Inc.
|
||||
// Copyright (C) 2002-2015, 2018-2020 Free Software Foundation, Inc.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
@@ -38,7 +38,6 @@
|
||||
#ifndef YY_YY_SQLITE3_LOCATION_H_INCLUDED
|
||||
# define YY_YY_SQLITE3_LOCATION_H_INCLUDED
|
||||
|
||||
# include <algorithm> // std::max
|
||||
# include <iostream>
|
||||
# include <string>
|
||||
|
||||
@@ -56,16 +55,19 @@
|
||||
|
||||
#line 10 "sqlite3_parser.yy"
|
||||
namespace sqlb { namespace parser {
|
||||
#line 60 "sqlite3_location.h"
|
||||
#line 59 "sqlite3_location.h"
|
||||
|
||||
/// A point in a source file.
|
||||
class position
|
||||
{
|
||||
public:
|
||||
/// Type for line and column numbers.
|
||||
typedef int counter_type;
|
||||
|
||||
/// Construct a position.
|
||||
explicit position (std::string* f = YY_NULLPTR,
|
||||
unsigned l = 1u,
|
||||
unsigned c = 1u)
|
||||
counter_type l = 1,
|
||||
counter_type c = 1)
|
||||
: filename (f)
|
||||
, line (l)
|
||||
, column (c)
|
||||
@@ -74,8 +76,8 @@ namespace sqlb { namespace parser {
|
||||
|
||||
/// Initialization.
|
||||
void initialize (std::string* fn = YY_NULLPTR,
|
||||
unsigned l = 1u,
|
||||
unsigned c = 1u)
|
||||
counter_type l = 1,
|
||||
counter_type c = 1)
|
||||
{
|
||||
filename = fn;
|
||||
line = l;
|
||||
@@ -85,17 +87,17 @@ namespace sqlb { namespace parser {
|
||||
/** \name Line and Column related manipulators
|
||||
** \{ */
|
||||
/// (line related) Advance to the COUNT next lines.
|
||||
void lines (int count = 1)
|
||||
void lines (counter_type count = 1)
|
||||
{
|
||||
if (count)
|
||||
{
|
||||
column = 1u;
|
||||
column = 1;
|
||||
line = add_ (line, count, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// (column related) Advance to the COUNT next columns.
|
||||
void columns (int count = 1)
|
||||
void columns (counter_type count = 1)
|
||||
{
|
||||
column = add_ (column, count, 1);
|
||||
}
|
||||
@@ -104,22 +106,21 @@ namespace sqlb { namespace parser {
|
||||
/// File name to which this position refers.
|
||||
std::string* filename;
|
||||
/// Current line number.
|
||||
unsigned line;
|
||||
counter_type line;
|
||||
/// Current column number.
|
||||
unsigned column;
|
||||
counter_type column;
|
||||
|
||||
private:
|
||||
/// Compute max (min, lhs+rhs).
|
||||
static unsigned add_ (unsigned lhs, int rhs, int min)
|
||||
static counter_type add_ (counter_type lhs, counter_type rhs, counter_type min)
|
||||
{
|
||||
return static_cast<unsigned> (std::max (min,
|
||||
static_cast<int> (lhs) + rhs));
|
||||
return lhs + rhs < min ? min : lhs + rhs;
|
||||
}
|
||||
};
|
||||
|
||||
/// Add \a width columns, in place.
|
||||
inline position&
|
||||
operator+= (position& res, int width)
|
||||
operator+= (position& res, position::counter_type width)
|
||||
{
|
||||
res.columns (width);
|
||||
return res;
|
||||
@@ -127,21 +128,21 @@ namespace sqlb { namespace parser {
|
||||
|
||||
/// Add \a width columns.
|
||||
inline position
|
||||
operator+ (position res, int width)
|
||||
operator+ (position res, position::counter_type width)
|
||||
{
|
||||
return res += width;
|
||||
}
|
||||
|
||||
/// Subtract \a width columns, in place.
|
||||
inline position&
|
||||
operator-= (position& res, int width)
|
||||
operator-= (position& res, position::counter_type width)
|
||||
{
|
||||
return res += -width;
|
||||
}
|
||||
|
||||
/// Subtract \a width columns.
|
||||
inline position
|
||||
operator- (position res, int width)
|
||||
operator- (position res, position::counter_type width)
|
||||
{
|
||||
return res -= width;
|
||||
}
|
||||
@@ -181,6 +182,8 @@ namespace sqlb { namespace parser {
|
||||
class location
|
||||
{
|
||||
public:
|
||||
/// Type for line and column numbers.
|
||||
typedef position::counter_type counter_type;
|
||||
|
||||
/// Construct a location from \a b to \a e.
|
||||
location (const position& b, const position& e)
|
||||
@@ -196,8 +199,8 @@ namespace sqlb { namespace parser {
|
||||
|
||||
/// Construct a 0-width location in \a f, \a l, \a c.
|
||||
explicit location (std::string* f,
|
||||
unsigned l = 1u,
|
||||
unsigned c = 1u)
|
||||
counter_type l = 1,
|
||||
counter_type c = 1)
|
||||
: begin (f, l, c)
|
||||
, end (f, l, c)
|
||||
{}
|
||||
@@ -205,8 +208,8 @@ namespace sqlb { namespace parser {
|
||||
|
||||
/// Initialization.
|
||||
void initialize (std::string* f = YY_NULLPTR,
|
||||
unsigned l = 1u,
|
||||
unsigned c = 1u)
|
||||
counter_type l = 1,
|
||||
counter_type c = 1)
|
||||
{
|
||||
begin.initialize (f, l, c);
|
||||
end = begin;
|
||||
@@ -222,13 +225,13 @@ namespace sqlb { namespace parser {
|
||||
}
|
||||
|
||||
/// Extend the current location to the COUNT next columns.
|
||||
void columns (int count = 1)
|
||||
void columns (counter_type count = 1)
|
||||
{
|
||||
end += count;
|
||||
}
|
||||
|
||||
/// Extend the current location to the COUNT next lines.
|
||||
void lines (int count = 1)
|
||||
void lines (counter_type count = 1)
|
||||
{
|
||||
end.lines (count);
|
||||
}
|
||||
@@ -243,39 +246,45 @@ namespace sqlb { namespace parser {
|
||||
};
|
||||
|
||||
/// Join two locations, in place.
|
||||
inline location& operator+= (location& res, const location& end)
|
||||
inline location&
|
||||
operator+= (location& res, const location& end)
|
||||
{
|
||||
res.end = end.end;
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Join two locations.
|
||||
inline location operator+ (location res, const location& end)
|
||||
inline location
|
||||
operator+ (location res, const location& end)
|
||||
{
|
||||
return res += end;
|
||||
}
|
||||
|
||||
/// Add \a width columns to the end position, in place.
|
||||
inline location& operator+= (location& res, int width)
|
||||
inline location&
|
||||
operator+= (location& res, location::counter_type width)
|
||||
{
|
||||
res.columns (width);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Add \a width columns to the end position.
|
||||
inline location operator+ (location res, int width)
|
||||
inline location
|
||||
operator+ (location res, location::counter_type width)
|
||||
{
|
||||
return res += width;
|
||||
}
|
||||
|
||||
/// Subtract \a width columns to the end position, in place.
|
||||
inline location& operator-= (location& res, int width)
|
||||
inline location&
|
||||
operator-= (location& res, location::counter_type width)
|
||||
{
|
||||
return res += -width;
|
||||
}
|
||||
|
||||
/// Subtract \a width columns to the end position.
|
||||
inline location operator- (location res, int width)
|
||||
inline location
|
||||
operator- (location res, location::counter_type width)
|
||||
{
|
||||
return res -= width;
|
||||
}
|
||||
@@ -304,7 +313,8 @@ namespace sqlb { namespace parser {
|
||||
std::basic_ostream<YYChar>&
|
||||
operator<< (std::basic_ostream<YYChar>& ostr, const location& loc)
|
||||
{
|
||||
unsigned end_col = 0 < loc.end.column ? loc.end.column - 1 : 0;
|
||||
location::counter_type end_col
|
||||
= 0 < loc.end.column ? loc.end.column - 1 : 0;
|
||||
ostr << loc.begin;
|
||||
if (loc.end.filename
|
||||
&& (!loc.begin.filename
|
||||
@@ -319,6 +329,6 @@ namespace sqlb { namespace parser {
|
||||
|
||||
#line 10 "sqlite3_parser.yy"
|
||||
} } // sqlb::parser
|
||||
#line 323 "sqlite3_location.h"
|
||||
#line 333 "sqlite3_location.h"
|
||||
|
||||
#endif // !YY_YY_SQLITE3_LOCATION_H_INCLUDED
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -39,6 +39,7 @@
|
||||
if(is_table_constraint)
|
||||
table_constraint = other.table_constraint;
|
||||
text = other.text;
|
||||
generated_constraint = other.generated_constraint;
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -58,6 +59,7 @@
|
||||
Default,
|
||||
Collate,
|
||||
ForeignKey,
|
||||
Generated,
|
||||
};
|
||||
|
||||
ConstraintType type;
|
||||
@@ -66,6 +68,7 @@
|
||||
|
||||
sqlb::ConstraintPtr table_constraint;
|
||||
std::string text;
|
||||
sqlb::GeneratedColumnConstraint generated_constraint;
|
||||
};
|
||||
using ColumnConstraintInfoVector = std::vector<ColumnConstraintInfo>;
|
||||
|
||||
@@ -135,6 +138,7 @@
|
||||
|
||||
%token <std::string> ABORT "ABORT"
|
||||
%token <std::string> ACTION "ACTION"
|
||||
%token <std::string> ALWAYS "ALWAYS"
|
||||
%token <std::string> AND "AND"
|
||||
%token <std::string> AND_BETWEEN "AND BETWEEN"
|
||||
%token <std::string> AS "AS"
|
||||
@@ -167,6 +171,7 @@
|
||||
%token <std::string> FILTER "FILTER"
|
||||
%token <std::string> FOLLOWING "FOLLOWING"
|
||||
%token <std::string> FOREIGN "FOREIGN"
|
||||
%token <std::string> GENERATED "GENERATED"
|
||||
%token <std::string> GLOB "GLOB"
|
||||
%token <std::string> IF "IF"
|
||||
%token <std::string> IGNORE "IGNORE"
|
||||
@@ -201,6 +206,7 @@
|
||||
%token <std::string> ROWS "ROWS"
|
||||
%token <std::string> SELECT "SELECT"
|
||||
%token <std::string> SET "SET"
|
||||
%token <std::string> STORED "STORED"
|
||||
%token <std::string> TABLE "TABLE"
|
||||
%token <std::string> TEMP "TEMP"
|
||||
%token <std::string> TEMPORARY "TEMPORARY"
|
||||
@@ -261,6 +267,8 @@
|
||||
%type <sqlb::TablePtr> createvirtualtable_stmt
|
||||
|
||||
%type <std::string> optional_typename
|
||||
%type <std::string> optional_storage_identifier
|
||||
%type <bool> optional_always_generated
|
||||
%type <ColumnConstraintInfoVector> columnconstraint_list
|
||||
%type <ColumnConstraintInfo> columnconstraint
|
||||
%type <ColumndefData> columndef
|
||||
@@ -333,6 +341,7 @@ id:
|
||||
allowed_keywords_as_identifier:
|
||||
ABORT
|
||||
| ACTION
|
||||
| ALWAYS
|
||||
| ASC
|
||||
| CASCADE
|
||||
| CAST
|
||||
@@ -343,6 +352,7 @@ allowed_keywords_as_identifier:
|
||||
| FAIL
|
||||
| FILTER
|
||||
| FOLLOWING
|
||||
| GENERATED
|
||||
| GLOB
|
||||
| KEY
|
||||
| LIKE
|
||||
@@ -362,6 +372,7 @@ allowed_keywords_as_identifier:
|
||||
| ROLLBACK
|
||||
| ROWID
|
||||
| ROWS
|
||||
| STORED
|
||||
| TEMPORARY
|
||||
| TEMP
|
||||
| UNBOUNDED
|
||||
@@ -662,6 +673,17 @@ optional_typename:
|
||||
| type_name { $$ = $1; }
|
||||
;
|
||||
|
||||
optional_storage_identifier:
|
||||
%empty { $$ = "VIRTUAL"; }
|
||||
| STORED { $$ = "STORED"; }
|
||||
| VIRTUAL { $$ = "VIRTUAL"; }
|
||||
;
|
||||
|
||||
optional_always_generated:
|
||||
%empty { $$ = false; }
|
||||
| GENERATED ALWAYS { $$ = true; }
|
||||
;
|
||||
|
||||
columnconstraint:
|
||||
optional_constraintname PRIMARY KEY optional_sort_order optional_conflictclause {
|
||||
$$.type = ColumnConstraintInfo::PrimaryKey;
|
||||
@@ -757,6 +779,14 @@ columnconstraint:
|
||||
$$.table_constraint = sqlb::ConstraintPtr(fk);
|
||||
$$.fully_parsed = true;
|
||||
}
|
||||
| optional_constraintname optional_always_generated AS "(" expr ")" optional_storage_identifier { // TODO Solve shift/reduce conflict.
|
||||
$$.type = ColumnConstraintInfo::Generated;
|
||||
$$.is_table_constraint = false;
|
||||
$$.generated_constraint.setExpression($5);
|
||||
$$.generated_constraint.setStorage($7);
|
||||
$$.generated_constraint.setName($1);
|
||||
$$.fully_parsed = true;
|
||||
}
|
||||
;
|
||||
|
||||
columnconstraint_list:
|
||||
@@ -795,6 +825,20 @@ columndef:
|
||||
f.setDefaultValue(c.text);
|
||||
} else if(c.type == ColumnConstraintInfo::Collate) {
|
||||
f.setCollation(c.text);
|
||||
} else if(c.type == ColumnConstraintInfo::Generated) {
|
||||
f.setGenerated(c.generated_constraint);
|
||||
|
||||
// This is a hack which removes any "GENERATED ALWAYS" from the end of the type name.
|
||||
// As of now these are shifted to the type instead of reducing the type when seeing the GENERATED identifier.
|
||||
// TODO Remove this once the grammar is conflict free
|
||||
const std::string generated_always = "GENERATED ALWAYS";
|
||||
if(f.type().size() >= generated_always.size() && f.type().compare(f.type().size() - generated_always.size(), generated_always.size(), generated_always) == 0)
|
||||
{
|
||||
std::string type = f.type().substr(0, f.type().size()-generated_always.size());
|
||||
if(type.back() == ' ')
|
||||
type.pop_back();
|
||||
f.setType(type);
|
||||
}
|
||||
} else {
|
||||
fully_parsed = false;
|
||||
}
|
||||
|
||||
@@ -224,6 +224,16 @@ std::string CheckConstraint::toSql() const
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string GeneratedColumnConstraint::toSql() const
|
||||
{
|
||||
std::string result;
|
||||
if(!m_name.empty())
|
||||
result = "CONSTRAINT " + escapeIdentifier(m_name) + " ";
|
||||
result += "GENERATED ALWAYS AS (" + m_expression + ")" + " " + storage();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Field::operator==(const Field& rhs) const
|
||||
{
|
||||
if(m_name != rhs.m_name)
|
||||
@@ -257,6 +267,8 @@ std::string Field::toString(const std::string& indent, const std::string& sep) c
|
||||
str += " UNIQUE";
|
||||
if(!m_collation.empty())
|
||||
str += " COLLATE " + m_collation;
|
||||
if(!m_generated.expression().empty())
|
||||
str += " " + m_generated.toSql();
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
@@ -139,6 +139,7 @@ public:
|
||||
UniqueConstraintType,
|
||||
ForeignKeyConstraintType,
|
||||
CheckConstraintType,
|
||||
GeneratedColumnConstraintType,
|
||||
|
||||
NoType = 999,
|
||||
};
|
||||
@@ -270,6 +271,30 @@ private:
|
||||
std::string m_expression;
|
||||
};
|
||||
|
||||
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 override;
|
||||
|
||||
ConstraintTypes type() const override { return GeneratedColumnConstraintType; }
|
||||
|
||||
private:
|
||||
std::string m_expression;
|
||||
std::string m_storage;
|
||||
};
|
||||
|
||||
class Field
|
||||
{
|
||||
public:
|
||||
@@ -331,6 +356,10 @@ public:
|
||||
bool unique() const { return m_unique; }
|
||||
const std::string& collation() const { return m_collation; }
|
||||
|
||||
const GeneratedColumnConstraint& generated() const { return m_generated; }
|
||||
GeneratedColumnConstraint& generated() { return m_generated; }
|
||||
void setGenerated(const GeneratedColumnConstraint& gen) { m_generated = gen; }
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::string m_type;
|
||||
@@ -339,6 +368,7 @@ private:
|
||||
std::string m_defaultvalue;
|
||||
bool m_unique;
|
||||
std::string m_collation;
|
||||
GeneratedColumnConstraint m_generated;
|
||||
};
|
||||
|
||||
class Table : public Object
|
||||
|
||||
Reference in New Issue
Block a user