PageRenderTime 40ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/generator/lib/reverse/mssql/MssqlSchemaParser.php

http://github.com/propelorm/Propel
PHP | 251 lines | 162 code | 29 blank | 60 comment | 13 complexity | 9a44c5681ff5f4e207e308e0df8b8bf3 MD5 | raw file
  1. <?php
  2. /**
  3. * This file is part of the Propel package.
  4. * For the full copyright and license information, please view the LICENSE
  5. * file that was distributed with this source code.
  6. *
  7. * @license MIT License
  8. */
  9. require_once dirname(__FILE__) . '/../BaseSchemaParser.php';
  10. /**
  11. * Microsoft SQL Server database schema parser.
  12. *
  13. * @author Hans Lellelid <hans@xmpl.org>
  14. * @version $Revision$
  15. * @package propel.generator.reverse.mssql
  16. */
  17. class MssqlSchemaParser extends BaseSchemaParser
  18. {
  19. /**
  20. * Map MSSQL native types to Propel types.
  21. *
  22. * @var array
  23. */
  24. private static $mssqlTypeMap = array(
  25. "binary" => PropelTypes::BINARY,
  26. "bit" => PropelTypes::BOOLEAN,
  27. "char" => PropelTypes::CHAR,
  28. "datetime" => PropelTypes::TIMESTAMP,
  29. "decimal() identity" => PropelTypes::DECIMAL,
  30. "decimal" => PropelTypes::DECIMAL,
  31. "image" => PropelTypes::LONGVARBINARY,
  32. "int" => PropelTypes::INTEGER,
  33. "int identity" => PropelTypes::INTEGER,
  34. "integer" => PropelTypes::INTEGER,
  35. "money" => PropelTypes::DECIMAL,
  36. "nchar" => PropelTypes::CHAR,
  37. "ntext" => PropelTypes::LONGVARCHAR,
  38. "numeric() identity" => PropelTypes::NUMERIC,
  39. "numeric" => PropelTypes::NUMERIC,
  40. "nvarchar" => PropelTypes::VARCHAR,
  41. "real" => PropelTypes::REAL,
  42. "float" => PropelTypes::FLOAT,
  43. "smalldatetime" => PropelTypes::TIMESTAMP,
  44. "smallint" => PropelTypes::SMALLINT,
  45. "smallint identity" => PropelTypes::SMALLINT,
  46. "smallmoney" => PropelTypes::DECIMAL,
  47. "sysname" => PropelTypes::VARCHAR,
  48. "text" => PropelTypes::LONGVARCHAR,
  49. "timestamp" => PropelTypes::BINARY,
  50. "tinyint identity" => PropelTypes::TINYINT,
  51. "tinyint" => PropelTypes::TINYINT,
  52. "uniqueidentifier" => PropelTypes::CHAR,
  53. "varbinary" => PropelTypes::VARBINARY,
  54. "varbinary(max)" => PropelTypes::CLOB,
  55. "varchar" => PropelTypes::VARCHAR,
  56. "varchar(max)" => PropelTypes::CLOB,
  57. // SQL Server 2000 only
  58. "bigint identity" => PropelTypes::BIGINT,
  59. "bigint" => PropelTypes::BIGINT,
  60. "sql_variant" => PropelTypes::VARCHAR,
  61. );
  62. /**
  63. * @see BaseSchemaParser::getTypeMapping()
  64. */
  65. protected function getTypeMapping()
  66. {
  67. return self::$mssqlTypeMap;
  68. }
  69. /**
  70. *
  71. */
  72. public function parse(Database $database, Task $task = null)
  73. {
  74. $stmt = $this->dbh->query("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME <> 'dtproperties'");
  75. // First load the tables (important that this happen before filling out details of tables)
  76. $tables = array();
  77. while ($row = $stmt->fetch(PDO::FETCH_NUM)) {
  78. $name = $this->cleanDelimitedIdentifiers($row[0]);
  79. if ($name == $this->getMigrationTable()) {
  80. continue;
  81. }
  82. $table = new Table($name);
  83. $table->setIdMethod($database->getDefaultIdMethod());
  84. $database->addTable($table);
  85. $tables[] = $table;
  86. }
  87. // Now populate only columns.
  88. foreach ($tables as $table) {
  89. $this->addColumns($table);
  90. }
  91. // Now add indexes and constraints.
  92. foreach ($tables as $table) {
  93. $this->addForeignKeys($table);
  94. $this->addIndexes($table);
  95. $this->addPrimaryKey($table);
  96. }
  97. return count($tables);
  98. }
  99. /**
  100. * Adds Columns to the specified table.
  101. *
  102. * @param Table $table The Table model class to add columns to.
  103. */
  104. protected function addColumns(Table $table)
  105. {
  106. $stmt = $this->dbh->query("sp_columns '" . $table->getName() . "'");
  107. while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
  108. $name = $this->cleanDelimitedIdentifiers($row['COLUMN_NAME']);
  109. $type = $row['TYPE_NAME'];
  110. $size = $row['LENGTH'];
  111. $is_nullable = $row['NULLABLE'];
  112. $default = $row['COLUMN_DEF'];
  113. $precision = $row['PRECISION'];
  114. $scale = $row['SCALE'];
  115. $autoincrement = false;
  116. if (strtolower($type) == "int identity") {
  117. $autoincrement = true;
  118. }
  119. $propelType = $this->getMappedPropelType($type);
  120. if (!$propelType) {
  121. $propelType = Column::DEFAULT_TYPE;
  122. $this->warn("Column [" . $table->getName() . "." . $name . "] has a column type (" . $type . ") that Propel does not support.");
  123. }
  124. $column = new Column($name);
  125. $column->setTable($table);
  126. $column->setDomainForType($propelType);
  127. // We may want to provide an option to include this:
  128. // $column->getDomain()->replaceSqlType($type);
  129. $column->getDomain()->replaceSize($size);
  130. $column->getDomain()->replaceScale($scale);
  131. if ($default !== null) {
  132. $column->getDomain()->setDefaultValue(new ColumnDefaultValue($default, ColumnDefaultValue::TYPE_VALUE));
  133. }
  134. $column->setAutoIncrement($autoincrement);
  135. $column->setNotNull(!$is_nullable);
  136. $table->addColumn($column);
  137. }
  138. }
  139. /**
  140. * Load foreign keys for this table.
  141. */
  142. protected function addForeignKeys(Table $table)
  143. {
  144. $database = $table->getDatabase();
  145. $stmt = $this->dbh->query("SELECT ccu1.TABLE_NAME, ccu1.COLUMN_NAME, ccu2.TABLE_NAME AS FK_TABLE_NAME, ccu2.COLUMN_NAME AS FK_COLUMN_NAME
  146. FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE ccu1 INNER JOIN
  147. INFORMATION_SCHEMA.TABLE_CONSTRAINTS tc1 ON tc1.CONSTRAINT_NAME = ccu1.CONSTRAINT_NAME AND
  148. CONSTRAINT_TYPE = 'Foreign Key' INNER JOIN
  149. INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS rc1 ON rc1.CONSTRAINT_NAME = tc1.CONSTRAINT_NAME INNER JOIN
  150. INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE ccu2 ON ccu2.CONSTRAINT_NAME = rc1.UNIQUE_CONSTRAINT_NAME
  151. WHERE (ccu1.table_name = '" . $table->getName() . "')");
  152. $foreignKeys = array(); // local store to avoid duplicates
  153. while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
  154. $lcol = $this->cleanDelimitedIdentifiers($row['COLUMN_NAME']);
  155. $ftbl = $this->cleanDelimitedIdentifiers($row['FK_TABLE_NAME']);
  156. $fcol = $this->cleanDelimitedIdentifiers($row['FK_COLUMN_NAME']);
  157. $foreignTable = $database->getTable($ftbl);
  158. $foreignColumn = $foreignTable->getColumn($fcol);
  159. $localColumn = $table->getColumn($lcol);
  160. if (!isset($foreignKeys[$name])) {
  161. $fk = new ForeignKey($name);
  162. $fk->setForeignTableCommonName($foreignTable->getCommonName());
  163. $fk->setForeignSchemaName($foreignTable->getSchema());
  164. //$fk->setOnDelete($fkactions['ON DELETE']);
  165. //$fk->setOnUpdate($fkactions['ON UPDATE']);
  166. $table->addForeignKey($fk);
  167. $foreignKeys[$name] = $fk;
  168. }
  169. $foreignKeys[$name]->addReference($localColumn, $foreignColumn);
  170. }
  171. }
  172. /**
  173. * Load indexes for this table
  174. */
  175. protected function addIndexes(Table $table)
  176. {
  177. $stmt = $this->dbh->query("sp_indexes_rowset '" . $table->getName() . "'");
  178. $indexes = array();
  179. while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
  180. $colName = $this->cleanDelimitedIdentifiers($row["COLUMN_NAME"]);
  181. $name = $this->cleanDelimitedIdentifiers($row['INDEX_NAME']);
  182. // FIXME -- Add UNIQUE support
  183. if (!isset($indexes[$name])) {
  184. $indexes[$name] = new Index($name);
  185. $table->addIndex($indexes[$name]);
  186. }
  187. $indexes[$name]->addColumn($table->getColumn($colName));
  188. }
  189. }
  190. /**
  191. * Loads the primary key for this table.
  192. */
  193. protected function addPrimaryKey(Table $table)
  194. {
  195. $stmt = $this->dbh->query("SELECT COLUMN_NAME
  196. FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS
  197. INNER JOIN INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE ON
  198. INFORMATION_SCHEMA.TABLE_CONSTRAINTS.CONSTRAINT_NAME = INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE.constraint_name
  199. WHERE (INFORMATION_SCHEMA.TABLE_CONSTRAINTS.CONSTRAINT_TYPE = 'PRIMARY KEY') AND
  200. (INFORMATION_SCHEMA.TABLE_CONSTRAINTS.TABLE_NAME = '" . $table->getName() . "')");
  201. // Loop through the returned results, grouping the same key_name together
  202. // adding each column for that key.
  203. while ($row = $stmt->fetch(PDO::FETCH_NUM)) {
  204. $name = $this->cleanDelimitedIdentifiers($row[0]);
  205. $table->getColumn($name)->setPrimaryKey(true);
  206. }
  207. }
  208. /**
  209. * according to the identifier definition, we have to clean simple quote (') around the identifier name
  210. * returns by mssql
  211. *
  212. * @see http://msdn.microsoft.com/library/ms175874.aspx
  213. *
  214. * @param string $identifier
  215. *
  216. * @return string
  217. */
  218. protected function cleanDelimitedIdentifiers($identifier)
  219. {
  220. return preg_replace('/^\'(.*)\'$/U', '$1', $identifier);
  221. }
  222. }