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:
Martin Kleusberg
2020-02-03 17:31:27 +01:00
parent cbced65931
commit d522de70db
10 changed files with 4450 additions and 3891 deletions

View File

@@ -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

View File

@@ -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"

View File

@@ -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);

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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