PageRenderTime 43ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/system/schema/sqlite/connection.php

https://github.com/HabariMag/habarimag-old
PHP | 234 lines | 142 code | 20 blank | 72 comment | 26 complexity | 33794720554874949f6b42ddefa02a13 MD5 | raw file
Possible License(s): Apache-2.0
  1. <?php
  2. /**
  3. * Habari database specific connection class
  4. *
  5. * @package Habari
  6. */
  7. class SQLiteConnection extends DatabaseConnection
  8. {
  9. /**
  10. * database specific SQL translation function, loosely modelled on the
  11. * internationalization _t() function
  12. * Call with a database independent SQL string and it will be translated
  13. * to a SQLite specific SQL string
  14. *
  15. * @param sql database independent SQL
  16. * @return string translated SQL string
  17. */
  18. function sql_t( $sql )
  19. {
  20. $sql = preg_replace_callback( '%concat\(([^)]+?)\)%i', array( &$this, 'replace_concat' ), $sql );
  21. $sql = preg_replace( '%DATE_SUB\s*\(\s*NOW\(\s*\)\s*,\s*INTERVAL\s+([0-9]+)\s+DAY\s*\)%ims', 'date(\'now\', \'-${1} days\')', $sql );
  22. $sql = preg_replace( '%OPTIMIZE TABLE ([^ ]*)%i', 'VACUUM;', $sql );
  23. //$sql= preg_replace( '%YEAR\s*\(\s*([^ ]*)\s*\)%ims', 'strftime(\'%Y\', ${1})', $sql );
  24. //$sql= preg_replace( '%MONTH\s*\(\s*([^ ]*)\s*\)%ims', 'strftime(\'%m\', ${1})', $sql );
  25. //$sql= preg_replace( '%DAY\s*\(\s*([^ ]*)\s*\)%ims', 'strftime(\'%d\', ${1})', $sql );
  26. $sql = preg_replace( '%YEAR\s*\(\s*FROM_UNIXTIME\s*\(\s*([^ ]*)\s*\)\s*\)%ims', 'strftime(\'%Y\', ${1}, \'unixepoch\')', $sql );
  27. $sql = preg_replace( '%MONTH\s*\(\s*FROM_UNIXTIME\s*\(\s*([^ ]*)\s*\)\s*\)%ims', 'strftime(\'%m\', ${1}, \'unixepoch\')', $sql );
  28. $sql = preg_replace( '%DAY\s*\(\s*FROM_UNIXTIME\s*\(\s*([^ ]*)\s*\)\s*\)%ims', 'strftime(\'%d\', ${1}, \'unixepoch\')', $sql );
  29. $sql = preg_replace( '%TRUNCATE \s*([^ ]*)%i', 'DELETE FROM ${1}', $sql );
  30. $sql = preg_replace( '%RAND\s*\(\s*\)%i', 'RANDOM()', $sql );
  31. return $sql;
  32. }
  33. /**
  34. * Replaces the MySQL CONCAT function with SQLite-compatible statements
  35. * @todo needs work, kind of sucky conversion
  36. * @param array $matches Matches from the regex in sql_t()
  37. * @return string The replacement for the MySQL SQL
  38. * @see SQLiteConnection::sql_t()
  39. */
  40. function replace_concat( $matches )
  41. {
  42. $innards = explode( ',', $matches[1] );
  43. return implode( ' || ', $innards );
  44. }
  45. /**
  46. * Connect to SQLite
  47. * Overrides the DatabaseConnection to return false if the SQLite file doesn't exist.
  48. *
  49. * @param connection_string string a PDO connection string
  50. * @param db_user string the database user name
  51. * @param db_pass string the database user password
  52. * @return boolean True if connection succeeded, false if not.
  53. */
  54. public function connect( $connect_string, $db_user, $db_pass )
  55. {
  56. list( $type, $file )= explode( ':', $connect_string, 2 );
  57. if ( $file == basename( $file ) ) {
  58. if ( file_exists( HABARI_PATH . '/' . $file ) ) {
  59. $file = HABARI_PATH . '/' . $file;
  60. }
  61. else {
  62. $file = HABARI_PATH . '/' . Site::get_path( 'user', true ) . $file;
  63. }
  64. $connect_string = implode( ':', array( $type, $file ) );
  65. }
  66. if ( file_exists( $file ) && !is_writable( $file ) ) {
  67. die( _t( 'Database file "%s" must be writable.', array($file) ) );
  68. }
  69. $conn = parent::connect( $connect_string, $db_user, $db_pass );
  70. $this->exec( 'PRAGMA synchronous = OFF' );
  71. return $conn;
  72. }
  73. /**
  74. * automatic diffing function - used for determining required database upgrades
  75. * based on Owen Winkler's microwiki upgrade function
  76. *
  77. * @param queries array of create table and insert statements which constitute a fresh install
  78. * @param (optional) execute should the queries be executed against the database or just simulated. default = true
  79. * @param (optional) silent silent running with no messages printed? default = true
  80. * @return string translated SQL string
  81. */
  82. function dbdelta( $queries, $execute = true, $silent = true, $doinserts = false )
  83. {
  84. if ( !is_array( $queries ) ) {
  85. $queries = explode( ';', $queries );
  86. if ( '' == $queries[count( $queries ) - 1] ) {
  87. array_pop( $queries );
  88. }
  89. }
  90. $cqueries = array();
  91. $indexqueries = array();
  92. $iqueries = array();
  93. $pqueries = array();
  94. $for_update = array();
  95. $allqueries = array();
  96. foreach ( $queries as $qry ) {
  97. if ( preg_match( "|CREATE TABLE ([^ ]*)|", $qry, $matches ) ) {
  98. $cqueries[strtolower( $matches[1] )] = $qry;
  99. $for_update[$matches[1]] = 'Created table '.$matches[1];
  100. }
  101. else if ( preg_match( "|CREATE (UNIQUE )?INDEX ([^ ]*)|", $qry, $matches ) ) {
  102. $indexqueries[] = $qry;
  103. }
  104. else if ( preg_match( "|INSERT INTO ([^ ]*)|", $qry, $matches ) ) {
  105. $iqueries[] = $qry;
  106. }
  107. else if ( preg_match( "|UPDATE ([^ ]*)|", $qry, $matches ) ) {
  108. $iqueries[] = $qry;
  109. }
  110. else if ( preg_match ( "|PRAGMA ([^ ]*)|", $qry, $matches ) ) {
  111. $pqueries[] = $qry;
  112. }
  113. else {
  114. // Unrecognized query type
  115. }
  116. }
  117. // Merge the queries into allqueries; pragmas MUST go first
  118. $allqueries = array_merge($pqueries);
  119. $tables = $this->get_column( "SELECT name FROM sqlite_master WHERE type = 'table';" );
  120. foreach ( $cqueries as $tablename => $query ) {
  121. if ( in_array( $tablename, $tables ) ) {
  122. $sql = $this->get_value( "SELECT sql FROM sqlite_master WHERE type = 'table' AND name='" . $tablename . "';" );
  123. $sql = preg_replace( '%\s+%', ' ', $sql ) . ';';
  124. $query = preg_replace( '%\s+%', ' ', $query );
  125. if ( $sql != $query ) {
  126. $this->query("ALTER TABLE {$tablename} RENAME TO {$tablename}__temp;");
  127. $this->query($query);
  128. $new_fields_temp = $this->get_results( "pragma table_info({$tablename});" );
  129. $new_fields = array();
  130. foreach ( $new_fields_temp as $field ) {
  131. $new_fields[$field->name] = $field;
  132. }
  133. $old_fields = $this->get_results( "pragma table_info({$tablename}__temp);" );
  134. $new_field_names = array_map(array($this, 'filter_fieldnames'), $new_fields);
  135. $old_field_names = array_map(array($this, 'filter_fieldnames'), $old_fields);
  136. $used_field_names = array_intersect($new_field_names, $old_field_names);
  137. $used_field_names = implode(',', $used_field_names);
  138. $needed_fields = array_diff($new_field_names, $old_field_names);
  139. foreach ( $needed_fields as $needed_field_name ) {
  140. $used_field_names .= ",'" . $new_fields[$needed_field_name]->dflt_value . "' as " . $needed_field_name;
  141. }
  142. $this->query("INSERT INTO {$tablename} SELECT {$used_field_names} FROM {$tablename}__temp;");
  143. $this->query("DROP TABLE {$tablename}__temp;");
  144. }
  145. }
  146. else {
  147. $allqueries[] = $query;
  148. }
  149. }
  150. // Drop all indices that we created, they'll get recreated by indexqueries below.
  151. // The other option would be to loop through the indices, comparing with indexqueries, and only drop the ones that have changed.
  152. $indices = DB::get_column( "SELECT name FROM sqlite_master WHERE type='index' AND name NOT LIKE 'sqlite_autoindex_%'" );
  153. foreach ( $indices as $name ) {
  154. DB::exec( 'DROP INDEX ' . $name );
  155. }
  156. $allqueries = array_merge( $allqueries, $indexqueries );
  157. if ( $doinserts ) {
  158. $allqueries = array_merge( $allqueries, $iqueries );
  159. }
  160. if ( $execute ) {
  161. DB::exec( 'PRAGMA cache_size=4000' );
  162. foreach ( $allqueries as $query ) {
  163. if ( !$this->query( $query ) ) {
  164. $this->get_errors();
  165. return false;
  166. }
  167. }
  168. }
  169. return $allqueries;
  170. }
  171. /**
  172. * Execute a stored procedure
  173. *
  174. * @param procedure name of the stored procedure
  175. * @param args arguments for the procedure
  176. * @return mixed whatever the procedure returns...
  177. * Not supported with SQLite
  178. */
  179. public function execute_procedure( $procedure, $args = array() )
  180. {
  181. die( _t( 'not yet supported on SQLite' ) );
  182. }
  183. /**
  184. * Run all of the upgrades slated for pre-dbdelta since the last database revision.
  185. *
  186. * @param integer $old_version The current version of the database that is being upgraded
  187. * @return boolean True on success
  188. */
  189. public function upgrade_pre( $old_version, $upgrade_path = '' )
  190. {
  191. return parent::upgrade( $old_version, dirname(__FILE__) . '/upgrades/pre');
  192. }
  193. /**
  194. * Run all of the upgrades slated for post-dbdelta since the last database revision.
  195. *
  196. * @param integer $old_version The current version of the database that is being upgraded
  197. * @return boolean True on success
  198. */
  199. public function upgrade_post( $old_version, $upgrade_path = '' )
  200. {
  201. return parent::upgrade( $old_version, dirname(__FILE__) . '/upgrades/post');
  202. }
  203. /**
  204. * Filter out the fieldnames from whole pragma rows
  205. *
  206. * @param StdClass $row A row result from a SQLite PRAGMA table_info query
  207. * @return string The name of the associated field
  208. */
  209. protected function filter_fieldnames($row)
  210. {
  211. return $row->name;
  212. }
  213. }
  214. ?>