Skip to content

Commit

Permalink
Merge pull request #334 from mk3008/331-supports-parsing-of-create-ta…
Browse files Browse the repository at this point in the history
…ble-statement

331 supports parsing of create table statement
  • Loading branch information
mk3008 authored Feb 17, 2024
2 parents 47e5d2a + 6e4030f commit 6e882d1
Show file tree
Hide file tree
Showing 23 changed files with 1,061 additions and 151 deletions.
92 changes: 92 additions & 0 deletions src/Carbunql/Analysis/CreateTableQueryParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
using Carbunql.Analysis.Parser;
using Carbunql.Extensions;
using System.Data;

namespace Carbunql.Analysis;

public static class CreateTableQueryParser
{
public static CreateTableQuery Parse(string text)
{
var r = new SqlTokenReader(text);
var q = Parse(r);

if (!r.Peek().IsEndToken())
{
throw new NotSupportedException($"Parsing terminated despite the presence of unparsed tokens.(token:'{r.Peek()}')");
}

return q;
}

static IEnumerable<string> ConstraintTokens => new[] { "primary key", "unique", "foreign key", "check", "not null", "constraint" };

public static CreateTableQuery Parse(ITokenReader r)
{
var t = ParseAsCreateTableCommand(r);

var token = r.Peek();
if (token.IsEqualNoCase("as"))
{
r.Read("as");
t.Query = SelectQueryParser.Parse(r);
return t;
}
else if (token == "(")
{
r.Read("(");
do
{
r.ReadOrDefault(",");
token = r.Peek();
if (token.IsEqualNoCase(ConstraintTokens))
{
var c = ConstraintParser.Parse(r);
t.Constraints.Add(c);
}
else
{
var c = ColumnDefinitionParser.Parse(r);
t.Columns.Add(c);
}
} while (r.Peek() == ",");
r.Read(")");
}

return t;
}

private static CreateTableQuery ParseAsCreateTableCommand(ITokenReader r)
{
var isTemporary = false;
var token = r.Read();
if (token.IsEqualNoCase("create temporary table"))
{
isTemporary = true;
}
else if (token.IsEqualNoCase("create table"))
{
isTemporary = false;
}
else
{
throw new NotSupportedException($"Token:{token}");
}

token = r.Read();
var schema = string.Empty;
string? table;
if (r.Peek() == ".")
{
r.Read(".");
schema = token;
table = r.Read();
}
else
{
table = token;
}

return new CreateTableQuery(schema, table) { IsTemporary = isTemporary };
}
}
27 changes: 27 additions & 0 deletions src/Carbunql/Analysis/Parser/ArrayParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace Carbunql.Analysis.Parser;

public class ArrayParser
{
public static List<string> Parse(string text)
{
var r = new SqlTokenReader(text);
var q = Parse(r);
return q;
}

public static List<string> Parse(ITokenReader r)
{
var lst = new List<string>();

r.Read("(");
do
{
r.ReadOrDefault(",");
lst.Add(r.Read());
} while (r.Peek() != ")");

r.Read(")");

return lst;
}
}
100 changes: 100 additions & 0 deletions src/Carbunql/Analysis/Parser/ColumnDefinitionParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
using Carbunql.Definitions;
using Carbunql.Extensions;

namespace Carbunql.Analysis.Parser;

public static class ColumnDefinitionParser
{
public static ColumnDefinition Parse(string text)
{
var r = new SqlTokenReader(text);
var q = Parse(r);
return q;
}

private static IEnumerable<string> PostgresAutoNumberTypes => new[] { "bigserial", "serial", "serial8", "serial4" };

private static IEnumerable<string> GeneraetedIdentitySyntaxs => new[] { "generated always as identity", "generated by default as identity" };


public static ColumnDefinition Parse(ITokenReader r)
{
var columnName = r.Read();
var columnType = ValueParser.Parse(r);

var c = new ColumnDefinition(columnName, columnType);

if (columnType.ToText().IsEqualNoCase(PostgresAutoNumberTypes))
{
//for Postgres
c.IsAutoNumber = true;
}

var token = r.Peek();
if (token.IsEqualNoCase("auto_increment"))
{
//for MySql
c.AutoNumberDefinition = ValueParser.Parse(r);
c.IsAutoNumber = true;
token = r.Peek();
}

if (token.IsEqualNoCase("identity"))
{
//for SQLServer
c.AutoNumberDefinition = ValueParser.Parse(r);
c.IsAutoNumber = true;
token = r.Peek();
}

if (token.IsEqualNoCase(GeneraetedIdentitySyntaxs))
{
//Generated Indentity
c.AutoNumberDefinition = ValueParser.Parse(r);
c.IsAutoNumber = true;
token = r.Peek();
}

if (token.IsEqualNoCase("not null"))
{
r.Read();
c.IsNullable = false;
token = r.Peek();
}
else
{
c.IsNullable = true;
}

if (token.IsEqualNoCase("default"))
{
r.Read();
c.DefaultValueDefinition = ValueParser.Parse(r);
token = r.Peek();
}

if (token.IsEqualNoCase("primary key"))
{
r.Read();
c.IsPrimaryKey = true;
token = r.Peek();
}

if (token.IsEqualNoCase("unique"))
{
r.Read();
c.IsUniqueKey = true;
token = r.Peek();
}

if (token.IsEqualNoCase("check"))
{
r.Read();
c.CheckDefinition = ValueParser.Parse(r);
token = r.Peek();
}

if (token == "," || token == ")") return c;
throw new NotSupportedException($"Token : {token}");
}
}
78 changes: 78 additions & 0 deletions src/Carbunql/Analysis/Parser/ConstraintParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
using Carbunql.Analysis.Parser;
using Carbunql.Definitions;
using Carbunql.Extensions;

namespace Carbunql.Analysis;

public static class ConstraintParser
{
public static IConstraint Parse(string text)
{
var r = new SqlTokenReader(text);
var q = Parse(r);
return q;
}

public static IConstraint Parse(ITokenReader r)
{
var token = r.Read();
var name = string.Empty;
if (token.IsEqualNoCase("constraint"))
{
name = r.Read();
token = r.Read();
}

if (token.IsEqualNoCase("primary key"))
{
var columns = ArrayParser.Parse(r);
return new PrimaryKeyConstraint()
{
ConstraintName = name,
ColumnNames = columns
};
}

if (token.IsEqualNoCase("unique"))
{
var columns = ArrayParser.Parse(r);
return new UniqueConstraint()
{
ConstraintName = name,
ColumnNames = columns
};
}

if (token.IsEqualNoCase("check"))
{
var val = ValueParser.Parse(r);
return new CheckConstraint()
{
Value = val
};
}

if (token.IsEqualNoCase("not null"))
{
return new NotNullConstraint()
{
ConstraintName = name,
ColumnName = r.Read()
};
}

if (token.IsEqualNoCase("foreign key"))
{
var columns = ArrayParser.Parse(r);
var reference = ReferenceParser.Parse(r);
return new ForeignKeyConstraint()
{
ConstraintName = name,
ColumnNames = columns,
Reference = reference
};
}

throw new NotSupportedException();
}
}
23 changes: 23 additions & 0 deletions src/Carbunql/Analysis/Parser/ReferenceParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Carbunql.Analysis.Parser;
using Carbunql.Definitions;

namespace Carbunql.Analysis;

public static class ReferenceParser
{
public static ReferenceDefinition Parse(string text)
{
var r = new SqlTokenReader(text);
var q = Parse(r);
return q;
}

public static ReferenceDefinition Parse(ITokenReader r)
{
var token = r.Read("references");
var table = r.Read();
var columns = ArrayParser.Parse(r);

return new ReferenceDefinition(table, columns);
}
}
Loading

0 comments on commit 6e882d1

Please sign in to comment.