JNewt

D ORM (hacky)

Oct 17th, 2011
112
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
D 5.92 KB | None | 0 0
  1.  
  2. module ccb.io.automap_table;
  3. import std.ascii,
  4.     std.array,
  5.     std.string;
  6.  
  7. template MapTables(string sql, bool humanForm = true)
  8. {
  9.     const string MapTables = mapTables(sql, humanForm);
  10. }
  11.  
  12.  
  13. private struct TableDef {
  14.     string name;
  15.     ColumnDef[] columns;
  16. }
  17.  
  18. private struct ColumnDef {
  19.     string name;
  20.     string sqltype;
  21.     string dtype;
  22.     string width;
  23.     bool unsigned;
  24.     bool notNull;
  25. }
  26.  
  27. private string mapTables(string sql, bool humanForm = true)
  28. {
  29.     debug
  30.         enum EOL = "\n";
  31.     else
  32.         enum EOL = "";
  33.  
  34.     // Clean up and return only CREATE statements
  35.     auto creates = getCreateStatements(sql);
  36.  
  37.     // For each create, define a datastructure representing a single row
  38.     string ret = "";
  39.     foreach (st; creates)
  40.     {
  41.         auto table = parseCreateStatement(st);
  42.         string structureName = humanForm ? camel(singular(table.name)) : table.name;
  43.  
  44.         ret ~= `class `~ structureName ~ ` {` ~ EOL;
  45.         foreach (c; table.columns)
  46.             ret ~= "\t"~ c.dtype ~ ` ` ~ c.name ~ `;` ~ EOL;
  47.         ret ~= `}` ~ EOL;
  48.     }
  49.     return ret;
  50. }
  51.  
  52. /// Filters a sql string for CREATE statements and returns as array
  53. private string[] getCreateStatements(string sql)
  54. {
  55.     string[] ret;
  56.     bool inQuote = false;
  57.     bool inComment = false;
  58.     bool inCreate = false;
  59.     size_t parens = 0;
  60.  
  61.     size_t cs;
  62.  
  63.     bool matchCreate(size_t from)
  64.     {
  65.         static const mLen = "CREATE TABLE".length;
  66.         return (sql.length > from + mLen &&
  67.                 sql[from..from + mLen] == "CREATE TABLE");
  68.     }
  69.  
  70.     dchar prev;
  71.     foreach (i, char c; sql)
  72.     {
  73.         if (inComment)
  74.         {
  75.             if (c == '/' && prev == '*')
  76.                 inComment = false;
  77.         } else {
  78.             if (c == '*' && prev == '/')
  79.                 inComment = true;
  80.             else if (c == '`')
  81.                 inQuote = !inQuote;
  82.             else if (c == '(' && inCreate && !inQuote)
  83.                 parens++;
  84.             else if (c == ')' && inCreate && !inQuote) {
  85.                 if (--parens == 0)
  86.                 {
  87.                     ret ~= sql[cs..i+1];    // push new statement
  88.                     inCreate = false;
  89.                 }
  90.             } else if (c == 'C' && !inQuote && matchCreate(i)) {
  91.                 cs = i;
  92.                 inCreate = true;
  93.             }
  94.         }
  95.        
  96.         prev = c;
  97.     }
  98.     return ret;
  99. }
  100.  
  101. private TableDef parseCreateStatement(string sql)
  102. {
  103.     TableDef ret;
  104.  
  105.     size_t  pos,
  106.             start;
  107.  
  108.     bool _isWhite(char c) { return c == ' ' || c == '\n'; }
  109.     void advanceTo(char c) {
  110.         while (c != sql[pos])
  111.         { if (++pos == sql.length) break; }
  112.     }
  113.     void advancePast(char c) { advanceTo(c); pos++; }
  114.     void consumeWhite() {
  115.         while (_isWhite(sql[pos]))
  116.         { if (++pos == sql.length) break; }
  117.     }
  118.     bool nextIs(string s) {
  119.         return pos + s.length < sql.length &&
  120.                 sql[pos..pos + s.length] == s;
  121.     }
  122.  
  123.     // skip along to the table name
  124.     advancePast('`');
  125.     start = pos;
  126.     advanceTo('`');
  127.     ret.name = sql[start..pos];
  128.  
  129.     // after the parens, the fields start
  130.     advancePast('(');
  131.  
  132.     while (true)
  133.     {
  134.         ColumnDef curCol;
  135.  
  136.         consumeWhite();
  137.  
  138.         if (nextIs("PRIMARY") || nextIs("KEY"))
  139.         {
  140.             // ignore and quit for now
  141.             break;
  142.         }
  143.  
  144.         // first part is name in quotes
  145.         start = ++pos;  // advance past first quote
  146.         advanceTo('`');
  147.         curCol.name = sql[start..pos++];
  148.  
  149.         consumeWhite();
  150.  
  151.         // then the type
  152.         start = pos;
  153.         advanceTo(' ');
  154.         auto typeEndPos = pos;
  155.  
  156.         // check for (M) or (M,D) syntax
  157.         pos = start;
  158.         advanceTo('(');
  159.         if (pos < typeEndPos)
  160.         {
  161.             curCol.sqltype = sql[start..pos];
  162.  
  163.             start = ++pos;
  164.  
  165.             // check for comma
  166.             advanceTo(',');
  167.             if (pos < typeEndPos)
  168.                 curCol.width = sql[start..pos];
  169.             else
  170.                 curCol.width = sql[start..typeEndPos-1];
  171.  
  172.         } else
  173.             curCol.sqltype = sql[start..typeEndPos];
  174.         pos = typeEndPos + 1;
  175.  
  176.         consumeWhite();
  177.        
  178.         // optionally an unsigned marker
  179.         if (nextIs("unsigned"))
  180.         {
  181.             curCol.unsigned = true;
  182.             pos += "unsigned".length;
  183.         }
  184.        
  185.         // push the column
  186.         curCol.dtype = getDType(curCol.sqltype, curCol.width, curCol.unsigned);
  187.         ret.columns.length = ret.columns.length + 1;
  188.         ret.columns[ret.columns.length - 1] = curCol;
  189.  
  190.         // next should be either a parens or a comma
  191.         // (we'll skip over NOT NULL, DEFAULT NULL, etc.
  192.         start = pos;
  193.         advanceTo(',');
  194.         auto commaPos = pos;
  195.         pos = start;
  196.         advanceTo(')');
  197.  
  198.         if (commaPos < pos)
  199.             pos = commaPos;
  200.         else
  201.             break;  // done parsing
  202.  
  203.         pos++;
  204.     }  
  205.  
  206.     return ret;
  207. }
  208.  
  209. private string getDType(string sqltype, string width, bool unsigned = false)
  210. {
  211.     string baseDType;
  212.     string st = sqltype._toLower();
  213.     bool isArray = false;
  214.  
  215.     switch (st) {
  216.         case "binary": case "varbinary": case "tinyblob":
  217.         case "blob": case "mediumblob": case "longblob":
  218.             baseDType = "byte";
  219.             isArray = true;
  220.             break;
  221.  
  222.         case "char":
  223.             baseDType = "char";
  224.             isArray = true;
  225.             break;
  226.  
  227.         case "varchar": case "tinytext":
  228.         case "text": case "mediumtext": case "longtext":
  229.             baseDType = "string";
  230.             break;
  231.  
  232.         case "tinyint": baseDType = "byte"; break;
  233.         case "smallint": baseDType = "short"; break;
  234.         case "mediumint": baseDType = "int"; break;
  235.         case "bigint": baseDType = "long"; break;
  236.  
  237.         default:
  238.             baseDType = st;
  239.     }
  240.  
  241.     if (unsigned)
  242.         baseDType = "u"~baseDType;
  243.    
  244.     if (isArray)
  245.     {
  246.         baseDType ~= "[";
  247.         if (width != "")
  248.             baseDType ~= width;
  249.         baseDType ~= "]";
  250.     }
  251.     return baseDType;
  252. }
  253.  
  254. private string singular(string plural)
  255. {
  256.     if (plural[$-1] == 's')
  257.         return plural[0..$-1];
  258.     return plural;
  259. }
  260.  
  261. private string camel(string name)
  262. {
  263.     auto parts = _split(name, '_');
  264.     string ret;
  265.     foreach (p; parts)
  266.         ret ~= [cast(char)toUpper(p[0])].idup ~ p[1..$];
  267.     return ret;
  268. }
  269.  
  270. private string[] _split(string s, char splitOn)
  271. {
  272.     string[] ret;
  273.     size_t start;
  274.     foreach (i, c; s)
  275.     {
  276.         if (c == splitOn)
  277.         {
  278.             if (start < i)
  279.             {
  280.                 ret.length = ret.length + 1;
  281.                 ret[$-1] = s[start..i];
  282.             }
  283.             start = i + 1;
  284.         }
  285.     }
  286.  
  287.     if (start < s.length)
  288.     {
  289.         ret.length = ret.length + 1;
  290.         ret[$-1] = s[start..$];
  291.     }
  292.     return ret;
  293. }
  294.  
  295. private string _toLower(string s)
  296. {
  297.     char[] ret = new char[](s.length);
  298.     foreach (i, c; s)
  299.         ret[i] = cast(char)toLower(c);
  300.     return ret.idup;
  301. }    
  302.  
Add Comment
Please, Sign In to add comment