Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ sql3string *sql3column_check_expr (sql3column *column);
sql3string *sql3column_default_expr (sql3column *column);
sql3string *sql3column_collate_name (sql3column *column);
sql3foreignkey *sql3column_foreignkey_clause (sql3column *column);
sql3string *sql3column_generated_expr (sql3column* column);
sql3gen_type sql3column_generated_type (sql3column* column);

// Foreign key
sql3string *sql3foreignkey_table (sql3foreignkey *fk);
Expand Down
1 change: 1 addition & 0 deletions debug/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ int main (void) {
, test -- test 123\n\
INTEGER, UNIQUE (flags /* Hello World*/, test) -- This is another table comment\n\
);");
test_case("CREATE TABLE Sales(Price INT, Qty INT, Total INT GENERATED ALWAYS AS (Price*Qty) VIRTUAL, Item TEXT)");

// https://www.sqlite.org/lang_altertable.html
test_case("ALTER TABLE foo RENAME TO bar");
Expand Down
16 changes: 16 additions & 0 deletions debug/sql3parse_debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ static const char *sql3constraint_type_str (sql3constraint_type type) {
}
}

static const char *sql3generated_type_str (sql3gen_type type) {
switch (type) {
case SQL3GENTYPE_NONE: return "NONE";
case SQL3GENTYPE_STORED: return "STORED";
case SQL3GENTYPE_VIRTUAL: return "VIRTUAL";
}
}

//MARK: - Dump Code -

static void sql3string_dump (sql3string *ptr, const char *label) {
Expand Down Expand Up @@ -148,6 +156,10 @@ static void sql3column_dump (sql3column *column) {
clause = sql3column_unique_conflictclause(column);
if (clause != SQL3CONFLICT_NONE)
printf("Unique Conflict Cause: %s\n", sql3conflict_clause_str(clause));

sql3gen_type gen_type = sql3column_generated_type(column);
if (gen_type != SQL3GENTYPE_NONE)
printf("Generated Type: %s\n", sql3generated_type_str(gen_type));

// check expr
ptr = sql3column_check_expr(column);
Expand All @@ -164,6 +176,10 @@ static void sql3column_dump (sql3column *column) {
// foreign key
sql3foreignkey *fk = sql3column_foreignkey_clause(column);
sql3foreignkey_dump(fk);

// generated expr
ptr = sql3column_generated_expr(column);
sql3string_dump(ptr, "Generated Expression");
}

static void sql3tableconstraint_dump (sql3tableconstraint *constraint) {
Expand Down
51 changes: 49 additions & 2 deletions sql3parse_table.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ typedef enum {
// constraints
TOK_CONSTRAINT, TOK_PRIMARY, TOK_KEY, TOK_UNIQUE, TOK_CHECK, TOK_FOREIGN,
TOK_ON, TOK_CONFLICT, TOK_ROLLBACK, TOK_ABORT, TOK_FAIL, TOK_IGNORE, TOK_REPLACE,
TOK_COLLATE, TOK_ASC, TOK_DESC, TOK_AUTOINCREMENT,
TOK_COLLATE, TOK_ASC, TOK_DESC, TOK_AUTOINCREMENT, TOK_GENERATED, TOK_ALWAYS,
TOK_STORED, TOK_VIRTUAL,

// foreign key clause
TOK_REFERENCES, TOK_DELETE, TOK_UPDATE, TOK_SET, TOK_NULL, TOK_DEFAULT, TOK_CASCADE,
Expand Down Expand Up @@ -70,6 +71,8 @@ struct sql3column {
sql3string default_expr; // default expression (can be NULL)
sql3string collate_name; // collate name (can be NULL)
sql3foreignkey *foreignkey_clause; // foreign key clause (can be NULL)
sql3string generated_expr; // generated expression (can be NULL)
sql3gen_type generated_type; // generated type
};

struct sql3tableconstraint {
Expand Down Expand Up @@ -224,7 +227,8 @@ static bool symbol_is_punctuation (sql3char c) {

static bool token_is_column_constraint (sql3token_t t) {
return ((t == TOK_CONSTRAINT) || (t == TOK_PRIMARY) || (t == TOK_NOT) || (t == TOK_UNIQUE) ||
(t == TOK_CHECK) || (t == TOK_DEFAULT) || (t == TOK_COLLATE) || (t == TOK_REFERENCES));
(t == TOK_CHECK) || (t == TOK_DEFAULT) || (t == TOK_COLLATE) || (t == TOK_REFERENCES) ||
(t == TOK_GENERATED) || (t == TOK_AS));
}

static bool token_is_table_constraint (sql3token_t t) {
Expand Down Expand Up @@ -282,6 +286,8 @@ static sql3token_t sql3lexer_keyword (const char *ptr, size_t length) {
if (str_nocasencmp(ptr, "strict", length) == 0) return TOK_STRICT;
if (str_nocasencmp(ptr, "rename", length) == 0) return TOK_RENAME;
if (str_nocasencmp(ptr, "column", length) == 0) return TOK_COLUMN;
if (str_nocasencmp(ptr, "always", length) == 0) return TOK_ALWAYS;
if (str_nocasencmp(ptr, "stored", length) == 0) return TOK_STORED;
break;

case 7:
Expand All @@ -292,6 +298,7 @@ static sql3token_t sql3lexer_keyword (const char *ptr, size_t length) {
if (str_nocasencmp(ptr, "replace", length) == 0) return TOK_REPLACE;
if (str_nocasencmp(ptr, "cascade", length) == 0) return TOK_CASCADE;
if (str_nocasencmp(ptr, "foreign", length) == 0) return TOK_FOREIGN;
if (str_nocasencmp(ptr, "virtual", length) == 0) return TOK_VIRTUAL;
break;

case 8:
Expand All @@ -305,6 +312,7 @@ static sql3token_t sql3lexer_keyword (const char *ptr, size_t length) {
if (str_nocasencmp(ptr, "temporary", length) == 0) return TOK_TEMP;
if (str_nocasencmp(ptr, "initially", length) == 0) return TOK_INITIALLY;
if (str_nocasencmp(ptr, "immediate", length) == 0) return TOK_IMMEDIATE;
if (str_nocasencmp(ptr, "generated", length) == 0) return TOK_GENERATED;
break;

case 10:
Expand Down Expand Up @@ -503,6 +511,19 @@ static sql3error_code sql3parse_optionalconflitclause (sql3state *state, sql3con
return SQL3ERROR_NONE;
}

static sql3error_code sql3parse_optionalgentype(sql3state* state, sql3gen_type* type) {
sql3token_t token = sql3lexer_peek(state);
*type = SQL3GENTYPE_NONE;

if ((token == TOK_STORED) || (token == TOK_VIRTUAL)) {
sql3lexer_next(state); // consume token
if (token == TOK_STORED) *type = SQL3GENTYPE_STORED;
else *type = SQL3GENTYPE_VIRTUAL;
}

return SQL3ERROR_NONE;
}

static void sql3free_foreignkey(sql3foreignkey *fk) {
if (!fk) return;
// shallow slices only; nothing to free inside currently
Expand Down Expand Up @@ -953,6 +974,23 @@ static sql3error_code sql3parse_column_constraints (sql3state *state, sql3column
if (!fk) return SQL3ERROR_SYNTAX;
column->foreignkey_clause = fk;
} break;

case TOK_GENERATED:
case TOK_AS: {
if (token == TOK_GENERATED) {
token = sql3lexer_next(state);
// 'column-constraint' syntax indicates that TOK_ALWAYS is
// mandatory but SQLite accepts the statement without it.
if (token == TOK_ALWAYS) token = sql3lexer_next(state);
if (token != TOK_AS) return SQL3ERROR_SYNTAX;
}
token = sql3lexer_peek(state);
if (token != TOK_OPEN_PARENTHESIS) return SQL3ERROR_SYNTAX;

// expressions are extracted (not fully parsed) in this version
column->generated_expr = sql3parse_expression(state);
if (sql3parse_optionalgentype(state, &column->generated_type) != SQL3ERROR_NONE) return SQL3ERROR_SYNTAX;
} break;

default:
return SQL3ERROR_SYNTAX;
Expand Down Expand Up @@ -1505,6 +1543,15 @@ sql3foreignkey *sql3column_foreignkey_clause (sql3column *column) {
return column->foreignkey_clause;
}

sql3string* sql3column_generated_expr(sql3column* column) {
CHECK_STR(column->generated_expr);
return &column->generated_expr;
}

sql3gen_type sql3column_generated_type(sql3column* column) {
return column->generated_type;
}

// MARK: - Public Foreign Key Functions -

sql3string *sql3foreignkey_table (sql3foreignkey *fk) {
Expand Down
8 changes: 8 additions & 0 deletions sql3parse_table.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ typedef enum {
SQL3ALTER_DROP_COLUMN
} sql3statement_type;

typedef enum {
SQL3GENTYPE_NONE,
SQL3GENTYPE_STORED,
SQL3GENTYPE_VIRTUAL
} sql3gen_type;

// Main http://www.sqlite.org/lang_createtable.html
sql3table *sql3parse_table (const char *sql, size_t length, sql3error_code *error);

Expand Down Expand Up @@ -147,6 +153,8 @@ sql3string *sql3column_check_expr (sql3column *column);
sql3string *sql3column_default_expr (sql3column *column);
sql3string *sql3column_collate_name (sql3column *column);
sql3foreignkey *sql3column_foreignkey_clause (sql3column *column);
sql3string* sql3column_generated_expr(sql3column* column);
sql3gen_type sql3column_generated_type(sql3column* column);

// Foreign Key
sql3string *sql3foreignkey_table (sql3foreignkey *fk);
Expand Down