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

/recess/recess/database/pdo/SqliteDataSourceProvider.class.php

http://github.com/recess/recess
PHP | 248 lines | 158 code | 38 blank | 52 comment | 32 complexity | 9ff931db1fbb5b24f15e3c27a7c730d1 MD5 | raw file
Possible License(s): MIT, GPL-2.0
  1. <?php
  2. Library::import('recess.database.pdo.IPdoDataSourceProvider');
  3. /**
  4. * Sqlite 3 Data Source Provider
  5. * @author Kris Jordan <krisjordan@gmail.com>
  6. */
  7. class SqliteDataSourceProvider implements IPdoDataSourceProvider {
  8. const SQLITE_TABLE_PREFIX = 'sqlite_';
  9. protected $pdo = null;
  10. /**
  11. * Initialize with a reference back to the PDO object.
  12. *
  13. * @param PDO $pdo
  14. */
  15. function init(PDO $pdo) {
  16. $this->pdo = $pdo;
  17. }
  18. /**
  19. * List the tables in a data source.
  20. * @return array(string) The tables in the data source ordered alphabetically.
  21. */
  22. function getTables() {
  23. $results = $this->pdo->query('SELECT tbl_name FROM sqlite_master WHERE type="table"');
  24. $tables = array();
  25. foreach($results as $result) {
  26. if(substr($result[0],0,strlen(self::SQLITE_TABLE_PREFIX)) != self::SQLITE_TABLE_PREFIX)
  27. $tables[] = $result[0];
  28. }
  29. sort($tables);
  30. return $tables;
  31. }
  32. /**
  33. * List the column names of a table alphabetically.
  34. * @param string $table Table whose columns to list.
  35. * @return array(string) Column names sorted alphabetically.
  36. */
  37. function getColumns($table) {
  38. $results = $this->pdo->query('PRAGMA table_info("' . $table . '");');
  39. $columns = array();
  40. foreach($results as $result) {
  41. $columns[] = $result['name'];
  42. }
  43. sort($columns);
  44. return $columns;
  45. }
  46. /**
  47. * Retrieve the a table's RecessTableDescriptor.
  48. *
  49. * @param string $table Name of table.
  50. * @return RecessTableDescriptor
  51. */
  52. function getTableDescriptor($table) {
  53. $results = $this->pdo->query('PRAGMA table_info("' . $table . '");');
  54. $tableSql = $this->pdo->query('SELECT sql FROM sqlite_master WHERE type="table" AND name = "' . addslashes($table) . '"')->fetch();
  55. $tableSql = $tableSql['sql'];
  56. $columns = array();
  57. $tableDescriptor = new RecessTableDescriptor();
  58. foreach($results as $result) {
  59. $tableDescriptor->addColumn(
  60. $result['name'],
  61. SqliteDataSourceProvider::getRecessType($result['type']),
  62. $result['notnull'] == 0 ? true : false,
  63. $result['pk'] == 1 ? true : false,
  64. $result['dflt_value'] == null ? '' : $result['dflt_value'],
  65. strpos( $tableSql,
  66. $result['name'] . ' INTEGER PRIMARY KEY AUTOINCREMENT'
  67. ) !== false
  68. ? array('autoincrement'=>true) : array()
  69. );
  70. }
  71. return $tableDescriptor;
  72. }
  73. /**
  74. * Sanity check and semantic sugar from higher level
  75. * representation of table pushed down to the RDBMS
  76. * representation of the table.
  77. *
  78. * @param string $table
  79. * @param RecessTableDescriptor $descriptor
  80. * @return RecessTableDescriptor
  81. */
  82. function cascadeTableDescriptor($table, RecessTableDescriptor $descriptor) {
  83. $sourceDescriptor = $this->getTableDescriptor($table);
  84. if(!$sourceDescriptor->tableExists) {
  85. $descriptor->tableExists = false;
  86. return $descriptor;
  87. }
  88. $sourceColumns = $sourceDescriptor->getColumns();
  89. $errors = array();
  90. foreach($descriptor->getColumns() as $column) {
  91. if(isset($sourceColumns[$column->name])) {
  92. if($column->isPrimaryKey && !$sourceColumns[$column->name]->isPrimaryKey) {
  93. $errors[] = 'Column "' . $column->name . '" is not the primary key in table ' . $table . '.';
  94. }
  95. $sourceColumns[$column->name]->type = $column->type;
  96. } else {
  97. $errors[] = 'Column "' . $column->name . '" does not exist in table ' . $table . '.';
  98. }
  99. }
  100. if(!empty($errors)) {
  101. throw new RecessException(implode(' ', $errors), get_defined_vars());
  102. } else {
  103. return $sourceDescriptor;
  104. }
  105. }
  106. static function getRecessType($sqliteType) {
  107. $recessType = $sqliteType;
  108. if($recessType == 'DATETIME') {
  109. $recessType = 'DateTime';
  110. } else {
  111. $recessType = ucfirst(strtolower($recessType));
  112. }
  113. if(!in_array($recessType, RecessType::all())) {
  114. $recessType = RecessType::TEXT;
  115. }
  116. return $recessType;
  117. }
  118. /**
  119. * Drop a table from SQLite database.
  120. *
  121. * @param string $table Name of table.
  122. */
  123. function dropTable($table) {
  124. return $this->pdo->exec('DROP TABLE ' . $table);
  125. }
  126. /**
  127. * Empty a table from SQLite database.
  128. *
  129. * @param string $table Name of table.
  130. */
  131. function emptyTable($table) {
  132. return $this->pdo->exec('DELETE FROM ' . $table);
  133. }
  134. /**
  135. * Given a Table Definition, return the CREATE TABLE SQL statement
  136. * in the Sqlite's syntax.
  137. *
  138. * @param RecessTableDescriptor $tableDescriptor
  139. */
  140. function createTableSql(RecessTableDescriptor $definition) {
  141. $sql = 'CREATE TABLE ' . $definition->name;
  142. $columnSql = null;
  143. foreach($definition->getColumns() as $column) {
  144. if(isset($columnSql)) { $columnSql .= ', '; }
  145. $columnSql .= "\n\t" . $column->name . ' ' . strtoupper($column->type);
  146. if($column->isPrimaryKey) {
  147. $columnSql .= ' PRIMARY KEY';
  148. if(isset($column->options['autoincrement'])) {
  149. $columnSql .= ' AUTOINCREMENT';
  150. }
  151. }
  152. }
  153. $columnSql .= "\n";
  154. return $sql . ' (' . $columnSql . ')';
  155. }
  156. function fetchAll(PDOStatement $statement) {
  157. try {
  158. $columnCount = $statement->columnCount();
  159. $manualFetch = false;
  160. $booleanColumns = array();
  161. for($i = 0 ; $i < $columnCount; $i++) {
  162. $meta = $statement->getColumnMeta($i);
  163. if(isset($meta['sqlite:decl_type']) && $meta['sqlite:decl_type'] == 'BOOLEAN') {
  164. $manualFetch = true;
  165. $booleanColumns[] = $meta['name'];
  166. }
  167. }
  168. } catch(PDOException $e) {
  169. return $statement->fetchAll();
  170. }
  171. if(!$manualFetch) {
  172. return $statement->fetchAll();
  173. } else {
  174. $results = array();
  175. while($result = $statement->fetch()) {
  176. foreach($booleanColumns as $column) {
  177. $result->$column = $result->$column == 1;
  178. }
  179. $results[] = $result;
  180. }
  181. return $results;
  182. }
  183. }
  184. function getStatementForBuilder(SqlBuilder $builder, $action, PdoDataSource $source) {
  185. $sql = $builder->$action();
  186. $statement = $source->prepare($sql);
  187. $arguments = $builder->getPdoArguments();
  188. foreach($arguments as &$argument) {
  189. // Begin workaround for PDO's poor numeric binding
  190. $param = $argument->getQueryParameter();
  191. if(is_numeric($param)) { continue; }
  192. if(is_string($param) && strlen($param) > 0 && substr($param,0,1) !== ':') { continue; }
  193. // End Workaround
  194. // Ignore parameters that aren't used in this $action (i.e. assignments in select)
  195. if(''===$param || strpos($sql, $param) === false) { continue; }
  196. $statement->bindValue($param, $argument->value);
  197. }
  198. return $statement;
  199. }
  200. function executeSqlBuilder(SqlBuilder $builder, $action, PdoDataSource $source) {
  201. return $this->getStatementForBuilder($builder, $action, $source)->execute();
  202. }
  203. }
  204. class SqliteType {
  205. const Text = 'TEXT';
  206. const Integer = 'INTEGER';
  207. const Real = 'REAL';
  208. const Blob = 'BLOB';
  209. }
  210. ?>