Pelzini

This is the code documentation for the Pelzini project

source of /processor/sqlite_outputter.php

Contains the SqliteOutputter class
  1. <?php
  2. /*
  3. Copyright 2008 Josh Heidenreich
  4.  
  5. This file is part of Pelzini.
  6.  
  7. Pelzini is free software: you can redistribute it and/or modify
  8. it under the terms of the GNU General Public License as published by
  9. the Free Software Foundation, either version 3 of the License, or
  10. (at your option) any later version.
  11.  
  12. Pelzini is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. GNU General Public License for more details.
  16.  
  17. You should have received a copy of the GNU General Public License
  18. along with Pelzini. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20.  
  21.  
  22. /**
  23.  * Contains the {@link SqliteOutputter} class
  24.  *
  25.  * @package Outputters
  26.  * @author Josh
  27.  * @since 0.2
  28.  **/
  29.  
  30. /**
  31.  * Outputs the tree to a SQLite database
  32.  **/
  33. class SqliteOutputter extends DatabaseOutputter {
  34. private $filename;
  35. private $db;
  36.  
  37. /**
  38.   * Connects to the db
  39.   */
  40. public function __construct($filename)
  41. {
  42. $this->filename = $filename;
  43. }
  44.  
  45.  
  46. /**
  47.   * Closes connection to the db
  48.   */
  49. public function __destruct()
  50. {
  51. if ($this->db) sqlite_close($this->db);
  52. }
  53.  
  54.  
  55.  
  56. /**
  57.   * Connects to the MySQL database
  58.   **/
  59. protected function connect()
  60. {
  61. $this->db = @sqlite_open($this->filename);
  62. if ($this->db == false) return false;
  63. return true;
  64. }
  65.  
  66.  
  67. /**
  68.   * Executes a MySQL query
  69.   */
  70. protected function query($query)
  71. {
  72. $query = str_ireplace('TRUNCATE', 'DELETE FROM', $query);
  73.  
  74. $return = sqlite_query($query, $this->db, SQLITE_BOTH, $error);
  75. if ($return === false) {
  76. echo "<p>Error in query <em>{$query}</em>. SQLite reported the following: <em>{$error}</em></p>";
  77. }
  78. return $return;
  79. }
  80.  
  81.  
  82. /**
  83.   * Safens some input
  84.   * @param string $input The input to safen
  85.   **/
  86. protected function sql_safen($input)
  87. {
  88. if ($input === null) {
  89. return 'NULL';
  90. } else if (is_integer($input)) {
  91. return $input;
  92. } else {
  93. return "'" . sqlite_escape_string($input) . "'";
  94. }
  95. }
  96.  
  97.  
  98. /**
  99.   * Fetches a row from the database (numerical)
  100.   **/
  101. protected function fetch_row($res)
  102. {
  103. return sqlite_fetch_array($res, SQLITE_NUM);
  104. }
  105.  
  106.  
  107. /**
  108.   * Fetches a row from the database (assoc)
  109.   **/
  110. protected function fetch_assoc($res)
  111. {
  112. return sqlite_fetch_array($res, SQLITE_ASSOC);
  113. }
  114.  
  115.  
  116. /**
  117.   * Returns the number of rows affected in the last query
  118.   * SQLite has no affected rows system. Assume if there is a result, that rows were affected.
  119.   **/
  120. protected function affected_rows($res)
  121. {
  122. if ($res) return 1;
  123. return 0;
  124. }
  125.  
  126.  
  127. /**
  128.   * Returns the autogenerated id created in the last query
  129.   **/
  130. protected function insert_id()
  131. {
  132. return sqlite_last_insert_rowid($this->db);
  133. }
  134.  
  135.  
  136. /**
  137.   * SQLite doesn't support multiple insert, so run each insert as a separate query
  138.   **/
  139. protected function do_multiple_insert($table, $data)
  140. {
  141. foreach ($data as $row) {
  142. $this->do_insert($table, $row);
  143. }
  144. }
  145.  
  146.  
  147. /**
  148.   * Returns an array of the tables in this database
  149.   **/
  150. protected function get_table_list()
  151. {
  152. $q = "SELECT name FROM sqlite_master
  153. WHERE type='table'
  154. ORDER BY name";
  155. $res = $this->query ($q);
  156.  
  157. $tables = array();
  158. while ($row = $this->fetch_row($res)) {
  159. $tables[] = $row[0];
  160. }
  161.  
  162. return $tables;
  163. }
  164.  
  165.  
  166. /**
  167.   * Converts an internal type into the database-specific SQL type.
  168.   * The defined internal types are:
  169.   * - serial: a number that automatically increments whenever a record is added
  170.   * - smallnum: a small number. needs to be able to hold at least 32,767 possible values (e.g. a 16-bit signed integer)
  171.   * - largenum: a large number. needs to be the same size or larger than a serial type
  172.   * - string: a character field long enough to hold identifiers of objects (e.g. function names)
  173.   * - text: a field that can hold arbitary pieces of text larger than 65536 chars in length.
  174.   *
  175.   * @param string $internal_type_name The internal type name.
  176.   * @return string The name used by the SQL database.
  177.   **/
  178. protected function get_sql_type($internal_type_name)
  179. {
  180. switch ($internal_type_name) {
  181. case 'serial': return 'INTEGER';
  182. case 'smallnum': return 'INTEGER';
  183. case 'largenum': return 'INTEGER';
  184. case 'string': return 'TEXT';
  185. case 'text': return 'TEXT';
  186. default:
  187. throw new Exception ("Undefined type '{$internal_type_name}' specified");
  188. break;
  189. }
  190. }
  191.  
  192.  
  193. /**
  194.   * Updates the database layout to match the layout file
  195.   * NOTE: currently only supports column and table adding and updating, not removal.
  196.   *
  197.   * @param string $layout_filename The name of the layout file to match
  198.   **/
  199. public function check_layout($layout_filename)
  200. {
  201. // Unfortunately, SQLite is a bit light-on when it comes to getting information
  202. // back about tables and columns
  203. // Thus, instead of doing a re-sync, we'll just try to wipe the db instead.
  204.  
  205. if ($this->db) sqlite_close($this->db);
  206.  
  207. $fp = @fopen($this->filename, 'wb');
  208. if ($fp) {
  209. ftruncate($fp, 0);
  210. fclose($fp);
  211. }
  212.  
  213. $this->db = @sqlite_open($this->filename);
  214. if ($this->db == false) {
  215. throw new Exception('Failed to reconnect to db');
  216. }
  217.  
  218. parent::check_layout($layout_filename);
  219. }
  220.  
  221. /**
  222.   * Should return a multi-dimentional array of the column details
  223.   * Format:
  224.   * Array [
  225.   * [0] => Array [
  226.   * 'Field' => field name
  227.   * 'Type' => field type, (e.g. 'serial', 'smallnum' or 'identifier')
  228.   * 'NotNull' => nullable?, (true or false)
  229.   * 'Key' => indexed?, ('PRI' for primary key)
  230.   * ]
  231.   * [1] => ...
  232.   * [n] => ...
  233.   **/
  234. protected function get_column_details($table_name)
  235. {
  236. //$q = "SELECT sql FROM sqlite_master WHERE type='table' AND name='{$table_name}'"
  237. //$res = $this->query ($q);
  238.  
  239. // TODO: parse CREATE TABLE statement.
  240. // This is going to be painful I think
  241. return;
  242.  
  243. $columns = array();
  244. while ($row = $this->fetch_assoc($res)) {
  245. if ($row['Null'] == 'YES') {
  246. $row['NotNull'] = false;
  247. } else {
  248. $row['NotNull'] = true;
  249. }
  250.  
  251. // Remap the SQL types back to Pelzini type
  252. $row['Type'] = preg_replace('/\(.+\)/', '', $row['Type']);
  253. $row['Type'] = strtolower($row['Type']);
  254. switch ($row['Type']) {
  255. case 'smallint unsigned': $row['Type'] = 'smallnum'; break;
  256. case 'smallint': $row['Type'] = 'smallnum'; break;
  257. case 'int unsigned': $row['Type'] = 'largenum'; break;
  258. case 'int': $row['Type'] = 'largenum'; break;
  259. case 'varchar': $row['Type'] = 'string'; break;
  260. case 'mediumtext': $row['Type'] = 'text'; break;
  261. }
  262.  
  263. // SERIAL takes a touch more thinking
  264. if (strcasecmp('bigint unsigned', $row['Type']) == 0
  265. and $row['NotNull']
  266. and stripos('auto_increment', $row['Extra']) !== false) {
  267. $row['Type'] = 'serial';
  268. }
  269.  
  270. unset ($row['Extra'], $row['Default']);
  271. $columns[] = $row;
  272. }
  273.  
  274. return $columns;
  275. }
  276.  
  277.  
  278.  
  279. /**
  280.   * Should return a multi-dimentional array of the index details
  281.   * Format:
  282.   * Array [
  283.   * [0] => Array [
  284.   * 'Fields' => array of field names
  285.   * ]
  286.   * [1] => ...
  287.   * [n] => ...
  288.   **/
  289. protected function get_index_details($table_name)
  290. {
  291. return array();
  292. }
  293.  
  294.  
  295. /**
  296.   * Gets the query that alters a column to match the new SQL definition
  297.   **/
  298. protected function get_alter_column_query($table, $column_name, $new_type, $not_null)
  299. {
  300. $new_type = $this->get_sql_type($new_type);
  301.  
  302. $q = "ALTER TABLE {$table} MODIFY COLUMN {$column_name} {$new_type}";
  303. return $q;
  304. }
  305.  
  306.  
  307. /**
  308.   * Creates a table
  309.   **/
  310. protected function create_table($table_name, $dest_table)
  311. {
  312. $q = "CREATE TABLE {$table_name} (\n";
  313. foreach ($dest_table['Columns'] as $col_name => $col_def) {
  314. $dest_sql = $this->get_sql_type($col_def['Type']);
  315. if ($col_def['NotNull']) $dest_sql .= ' not null';
  316.  
  317. $q .= " {$col_name} {$dest_sql},\n";
  318. }
  319. $q .= " PRIMARY KEY ({$dest_table['PK']})\n";
  320. $q .= ")";
  321. echo "<b>Query:\n{$q}</b>\n";
  322.  
  323. $res = $this->query ($q);
  324. if ($res) echo 'Affected rows: ', $this->affected_rows($res), "\n";
  325. }
  326.  
  327.  
  328. /**
  329.   * The database engine should start a transaction. If transactions are not supported, it should do nothing.
  330.   **/
  331. protected function start_transaction()
  332. {
  333. $this->query ('BEGIN TRANSACTION');
  334. }
  335.  
  336.  
  337. /**
  338.   * The database engine should commit a transaction. If transactions are not supported, it should do nothing.
  339.   **/
  340. protected function commit_transaction()
  341. {
  342. $this->query ('COMMIT TRANSACTION');
  343. }
  344.  
  345.  
  346. /**
  347.   * The database engine should rollback a transaction. If transactions are not supported, it should do nothing.
  348.   **/
  349. protected function rollback_transaction()
  350. {
  351. $this->query ('ROLLBACK TRANSACTION');
  352. }
  353.  
  354.  
  355. }
  356.  
  357.  
  358. ?>
  359.