public ColumnDefinition(string columnName, IEnumerable <Token <SqliteToken> > typeDefinition, IEnumerable <ColumnConstraint> columnConstraints) { if (columnName.IsNullOrWhiteSpace()) { throw new ArgumentNullException(nameof(columnName)); } Name = columnName; TypeDefinition = typeDefinition?.ToList() ?? Enumerable.Empty <Token <SqliteToken> >(); var nullable = true; var autoIncrement = false; var collation = SqliteCollation.None; var defaultValue = new List <Token <SqliteToken> >(); PrimaryKey?primaryKey = null; UniqueKey? uniqueKey = null; var foreignKeys = new List <ForeignKey>(); var checks = new List <Check>(); var generatedDefinition = new List <Token <SqliteToken> >(); var generatedColumnType = SqliteGeneratedColumnType.None; columnConstraints = columnConstraints?.ToList() ?? Enumerable.Empty <ColumnConstraint>(); foreach (var constraint in columnConstraints) { switch (constraint.ConstraintType) { case ColumnConstraint.ColumnConstraintType.Check: if (constraint is ColumnConstraint.Check ck) { checks.Add(new Check(ck.Name, ck.Definition)); } break; case ColumnConstraint.ColumnConstraintType.Collation: if (constraint is ColumnConstraint.Collation col) { collation = col.CollationType; } break; case ColumnConstraint.ColumnConstraintType.Default: if (constraint is ColumnConstraint.DefaultConstraint def) { defaultValue.AddRange(def.DefaultValue); } break; case ColumnConstraint.ColumnConstraintType.ForeignKey: if (constraint is ColumnConstraint.ForeignKey fk) { foreignKeys.Add(new ForeignKey(fk.Name, Name, fk.ParentTable, fk.ParentColumnNames)); } break; case ColumnConstraint.ColumnConstraintType.Nullable: if (constraint is ColumnConstraint.Nullable nullableCons) { nullable = nullableCons.IsNullable; } break; case ColumnConstraint.ColumnConstraintType.PrimaryKey: if (constraint is ColumnConstraint.PrimaryKey pk) { autoIncrement = pk.AutoIncrement; primaryKey = new PrimaryKey(pk.Name, new IndexedColumn(Name).WithColumnOrder(pk.ColumnOrder).ToEnumerable()); } break; case ColumnConstraint.ColumnConstraintType.UniqueKey: if (constraint is ColumnConstraint.UniqueKey uk) { uniqueKey = new UniqueKey(uk.Name, Name); } break; case ColumnConstraint.ColumnConstraintType.GeneratedAlways: if (constraint is ColumnConstraint.GeneratedAlways generated) { generatedDefinition.AddRange(generated.Definition); generatedColumnType = generated.GeneratedColumnType; } break; } } if (primaryKey != null && uniqueKey != null) { uniqueKey = null; // prefer primary key to unique key } Nullable = nullable; IsAutoIncrement = autoIncrement; Collation = collation; DefaultValue = defaultValue; PrimaryKey = primaryKey != null ? Option <PrimaryKey> .Some(primaryKey) : Option <PrimaryKey> .None; UniqueKey = uniqueKey != null ? Option <UniqueKey> .Some(uniqueKey) : Option <UniqueKey> .None; ForeignKeys = foreignKeys; Checks = checks; GeneratedColumnDefinition = generatedDefinition; GeneratedColumnType = generatedColumnType; }
/// <summary> /// Parses the tokens into structured table definition. /// </summary> /// <param name="definition">The textual definition of the <c>CREATE TABLE</c> statement.</param> /// <param name="tokens">A collection of tokens from the table definition.</param> /// <returns>Parsed data for a <c>CREATE TABLE</c> definition.</returns> /// <exception cref="ArgumentNullException"><paramref name="tokens"/> is empty.</exception> public ParsedTableData ParseTokens(string definition, TokenList <SqliteToken> tokens) { if (definition.IsNullOrWhiteSpace()) { throw new ArgumentNullException(nameof(definition)); } if (tokens == default || tokens.Empty()) { throw new ArgumentNullException(nameof(tokens)); } var next = tokens.ConsumeToken(); var createTablePrefix = SqliteTokenParsers.CreateTablePrefix(next.Location); if (!createTablePrefix.HasValue) { throw new ArgumentException("Token list does not start with a CREATE TABLE statement.", nameof(tokens)); } next = createTablePrefix.Remainder.ConsumeToken(); var ifNotExists = SqliteTokenParsers.IfNotExistsClause(next.Location); if (ifNotExists.HasValue) { next = ifNotExists.Remainder.ConsumeToken(); } var tableName = SqliteTokenParsers.QualifiedName(next.Location); if (!tableName.HasValue) { throw new ArgumentException("Unable to determine the name of the table being parsed.", nameof(tokens)); } next = tableName.Remainder.ConsumeToken(); var isSelectBasedTable = !next.HasValue || next.Value.Kind != SqliteToken.LParen; // skipping because we cannot parse extra information from a select-based table if (isSelectBasedTable) { return(ParsedTableData.Empty(definition)); } next = next.Remainder.ConsumeToken(); // consume LParen var tableMembers = SqliteTokenParsers.TableMembers(next.Location); if (!tableMembers.HasValue) { var errMessage = "Unable to parse columns and/or constraints from the table definition. Error: " + tableMembers.ErrorMessage; throw new ArgumentException(errMessage, nameof(tokens)); } var columns = new List <Column>(); var primaryKey = Option <PrimaryKey> .None; var uniqueKeys = new List <UniqueKey>(); var foreignKeys = new List <ForeignKey>(); var checks = new List <Check>(); var parsedColumns = tableMembers.Value.SelectMany(m => m.Columns); var parsedConstraints = tableMembers.Value.SelectMany(m => m.Constraints); foreach (var parsedColumn in parsedColumns) { var column = new Column( parsedColumn.Name, parsedColumn.TypeDefinition, parsedColumn.Nullable, parsedColumn.IsAutoIncrement, parsedColumn.Collation, parsedColumn.DefaultValue, parsedColumn.GeneratedColumnDefinition, parsedColumn.GeneratedColumnType ); columns.Add(column); if (primaryKey.IsNone && parsedColumn.PrimaryKey.IsSome) { primaryKey = parsedColumn.PrimaryKey; } parsedColumn.UniqueKey.IfSome(uk => uniqueKeys.Add(uk)); foreignKeys.AddRange(parsedColumn.ForeignKeys); checks.AddRange(parsedColumn.Checks); } foreach (var parsedConstraint in parsedConstraints) { switch (parsedConstraint.ConstraintType) { case TableConstraint.TableConstraintType.PrimaryKey: if (parsedConstraint is TableConstraint.PrimaryKey pk) { primaryKey = new PrimaryKey(pk.Name, pk.Columns); } break; case TableConstraint.TableConstraintType.UniqueKey: if (parsedConstraint is TableConstraint.UniqueKey uk) { uniqueKeys.Add(new UniqueKey(uk.Name, uk.Columns)); } break; case TableConstraint.TableConstraintType.ForeignKey: if (parsedConstraint is TableConstraint.ForeignKey fk) { foreignKeys.Add(new ForeignKey(fk.Name, fk.Columns, fk.ParentTable, fk.ParentColumnNames)); } break; case TableConstraint.TableConstraintType.Check: if (parsedConstraint is TableConstraint.Check ck) { checks.Add(new Check(ck.Name, ck.Definition)); } break; } } return(new ParsedTableData( definition, columns, primaryKey, uniqueKeys, foreignKeys, checks )); }