Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- module ccb.io.automap_table;
- import std.ascii,
- std.array,
- std.string;
- template MapTables(string sql, bool humanForm = true)
- {
- const string MapTables = mapTables(sql, humanForm);
- }
- private struct TableDef {
- string name;
- ColumnDef[] columns;
- }
- private struct ColumnDef {
- string name;
- string sqltype;
- string dtype;
- string width;
- bool unsigned;
- bool notNull;
- }
- private string mapTables(string sql, bool humanForm = true)
- {
- debug
- enum EOL = "\n";
- else
- enum EOL = "";
- // Clean up and return only CREATE statements
- auto creates = getCreateStatements(sql);
- // For each create, define a datastructure representing a single row
- string ret = "";
- foreach (st; creates)
- {
- auto table = parseCreateStatement(st);
- string structureName = humanForm ? camel(singular(table.name)) : table.name;
- ret ~= `class `~ structureName ~ ` {` ~ EOL;
- foreach (c; table.columns)
- ret ~= "\t"~ c.dtype ~ ` ` ~ c.name ~ `;` ~ EOL;
- ret ~= `}` ~ EOL;
- }
- return ret;
- }
- /// Filters a sql string for CREATE statements and returns as array
- private string[] getCreateStatements(string sql)
- {
- string[] ret;
- bool inQuote = false;
- bool inComment = false;
- bool inCreate = false;
- size_t parens = 0;
- size_t cs;
- bool matchCreate(size_t from)
- {
- static const mLen = "CREATE TABLE".length;
- return (sql.length > from + mLen &&
- sql[from..from + mLen] == "CREATE TABLE");
- }
- dchar prev;
- foreach (i, char c; sql)
- {
- if (inComment)
- {
- if (c == '/' && prev == '*')
- inComment = false;
- } else {
- if (c == '*' && prev == '/')
- inComment = true;
- else if (c == '`')
- inQuote = !inQuote;
- else if (c == '(' && inCreate && !inQuote)
- parens++;
- else if (c == ')' && inCreate && !inQuote) {
- if (--parens == 0)
- {
- ret ~= sql[cs..i+1]; // push new statement
- inCreate = false;
- }
- } else if (c == 'C' && !inQuote && matchCreate(i)) {
- cs = i;
- inCreate = true;
- }
- }
- prev = c;
- }
- return ret;
- }
- private TableDef parseCreateStatement(string sql)
- {
- TableDef ret;
- size_t pos,
- start;
- bool _isWhite(char c) { return c == ' ' || c == '\n'; }
- void advanceTo(char c) {
- while (c != sql[pos])
- { if (++pos == sql.length) break; }
- }
- void advancePast(char c) { advanceTo(c); pos++; }
- void consumeWhite() {
- while (_isWhite(sql[pos]))
- { if (++pos == sql.length) break; }
- }
- bool nextIs(string s) {
- return pos + s.length < sql.length &&
- sql[pos..pos + s.length] == s;
- }
- // skip along to the table name
- advancePast('`');
- start = pos;
- advanceTo('`');
- ret.name = sql[start..pos];
- // after the parens, the fields start
- advancePast('(');
- while (true)
- {
- ColumnDef curCol;
- consumeWhite();
- if (nextIs("PRIMARY") || nextIs("KEY"))
- {
- // ignore and quit for now
- break;
- }
- // first part is name in quotes
- start = ++pos; // advance past first quote
- advanceTo('`');
- curCol.name = sql[start..pos++];
- consumeWhite();
- // then the type
- start = pos;
- advanceTo(' ');
- auto typeEndPos = pos;
- // check for (M) or (M,D) syntax
- pos = start;
- advanceTo('(');
- if (pos < typeEndPos)
- {
- curCol.sqltype = sql[start..pos];
- start = ++pos;
- // check for comma
- advanceTo(',');
- if (pos < typeEndPos)
- curCol.width = sql[start..pos];
- else
- curCol.width = sql[start..typeEndPos-1];
- } else
- curCol.sqltype = sql[start..typeEndPos];
- pos = typeEndPos + 1;
- consumeWhite();
- // optionally an unsigned marker
- if (nextIs("unsigned"))
- {
- curCol.unsigned = true;
- pos += "unsigned".length;
- }
- // push the column
- curCol.dtype = getDType(curCol.sqltype, curCol.width, curCol.unsigned);
- ret.columns.length = ret.columns.length + 1;
- ret.columns[ret.columns.length - 1] = curCol;
- // next should be either a parens or a comma
- // (we'll skip over NOT NULL, DEFAULT NULL, etc.
- start = pos;
- advanceTo(',');
- auto commaPos = pos;
- pos = start;
- advanceTo(')');
- if (commaPos < pos)
- pos = commaPos;
- else
- break; // done parsing
- pos++;
- }
- return ret;
- }
- private string getDType(string sqltype, string width, bool unsigned = false)
- {
- string baseDType;
- string st = sqltype._toLower();
- bool isArray = false;
- switch (st) {
- case "binary": case "varbinary": case "tinyblob":
- case "blob": case "mediumblob": case "longblob":
- baseDType = "byte";
- isArray = true;
- break;
- case "char":
- baseDType = "char";
- isArray = true;
- break;
- case "varchar": case "tinytext":
- case "text": case "mediumtext": case "longtext":
- baseDType = "string";
- break;
- case "tinyint": baseDType = "byte"; break;
- case "smallint": baseDType = "short"; break;
- case "mediumint": baseDType = "int"; break;
- case "bigint": baseDType = "long"; break;
- default:
- baseDType = st;
- }
- if (unsigned)
- baseDType = "u"~baseDType;
- if (isArray)
- {
- baseDType ~= "[";
- if (width != "")
- baseDType ~= width;
- baseDType ~= "]";
- }
- return baseDType;
- }
- private string singular(string plural)
- {
- if (plural[$-1] == 's')
- return plural[0..$-1];
- return plural;
- }
- private string camel(string name)
- {
- auto parts = _split(name, '_');
- string ret;
- foreach (p; parts)
- ret ~= [cast(char)toUpper(p[0])].idup ~ p[1..$];
- return ret;
- }
- private string[] _split(string s, char splitOn)
- {
- string[] ret;
- size_t start;
- foreach (i, c; s)
- {
- if (c == splitOn)
- {
- if (start < i)
- {
- ret.length = ret.length + 1;
- ret[$-1] = s[start..i];
- }
- start = i + 1;
- }
- }
- if (start < s.length)
- {
- ret.length = ret.length + 1;
- ret[$-1] = s[start..$];
- }
- return ret;
- }
- private string _toLower(string s)
- {
- char[] ret = new char[](s.length);
- foreach (i, c; s)
- ret[i] = cast(char)toLower(c);
- return ret.idup;
- }
Add Comment
Please, Sign In to add comment