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

/system/schema/pgsql/connection.php

https://github.com/HabariMag/habarimag-old
PHP | 373 lines | 292 code | 20 blank | 61 comment | 58 complexity | b27f484b14a31d44ea4112b624170894 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 PGSQLConnection extends DatabaseConnection
  8. {
  9. /**
  10. * Extends default connection method. It will be useful in order to
  11. * allow accents and other DB-centric global commands.
  12. *
  13. * @param string $connect_string a PDO connection string
  14. * @param string $db_user the database user name
  15. * @param string $db_pass the database user password
  16. * @return boolean true on success, false on error
  17. */
  18. public function connect( $connect_string, $db_user, $db_pass )
  19. {
  20. // If something went wrong, we don't need to exec the specific commands.
  21. if ( !parent::connect( $connect_string, $db_user, $db_pass ) ) {
  22. return false;
  23. }
  24. return true;
  25. }
  26. /**
  27. * Database specific SQL translation function, loosely modelled on the
  28. * internationalization _t() function.
  29. * Call with a database independent SQL string and it will be translated
  30. * to a PostgreSQL specific SQL string.
  31. *
  32. * @param $sql database independent SQL
  33. * @return string translated SQL string
  34. */
  35. function sql_t( $sql )
  36. {
  37. $sql = preg_replace_callback( '%concat\(([^)]+?)\)%i', array( &$this, 'replace_concat' ), $sql );
  38. $sql = preg_replace( '%DATE_SUB\s*\(\s*NOW\(\s*\)\s*,\s*INTERVAL\s+([0-9]+)\s+DAY\s*\)%ims', 'NOW() - INTERVAL \'${1} DAYS\'', $sql );
  39. $sql = preg_replace( '%OPTIMIZE TABLE ([^ ]*)%i', 'VACUUM ${1};', $sql );
  40. //$sql= preg_replace( '%YEAR\s*\(\s*([^ ]*)\s*\)%ims', 'date_part(\'year\', ${1})', $sql );
  41. //$sql= preg_replace( '%MONTH\s*\(\s*([^ ]*)\s*\)%ims', 'date_part(\'month\', ${1})', $sql );
  42. //$sql= preg_replace( '%DAY\s*\(\s*([^ ]*)\s*\)%ims', 'date_part(\'day\', ${1})', $sql );
  43. $sql = preg_replace( '%YEAR\s*\(\s*FROM_UNIXTIME\s*\(\s*([^ ]*)\s*\)\s*\)%ims', 'date_part(\'year\', \'epoch\'::timestamptz + ${1} * \'1 second\'::interval)', $sql );
  44. $sql = preg_replace( '%MONTH\s*\(\s*FROM_UNIXTIME\s*\(\s*([^ ]*)\s*\)\s*\)%ims', 'date_part(\'month\', \'epoch\'::timestamptz + ${1} * \'1 second\'::interval)', $sql );
  45. $sql = preg_replace( '%DAY\s*\(\s*FROM_UNIXTIME\s*\(\s*([^ ]*)\s*\)\s*\)%ims', 'date_part(\'day\', \'epoch\'::timestamptz + ${1} * \'1 second\'::interval)', $sql );
  46. $sql = preg_replace('%LIKE\s+((\'|").*\2)%iUms', 'ILIKE \1', $sql);
  47. $sql = preg_replace( '%RAND\s*\(\s*\)%i', 'RANDOM()', $sql );
  48. return $sql;
  49. }
  50. /**
  51. * Replaces the MySQL CONCAT function with PostgreSQL-compatible statements
  52. * @todo needs work, kind of sucky conversion
  53. * @param array $matches Matches from the regex in sql_t()
  54. * @return string The replacement for the MySQL SQL
  55. * @see PGSQLConnection::sql_t()
  56. */
  57. function replace_concat( $matches )
  58. {
  59. $innards = explode( ',', $matches[1] );
  60. return implode( ' || ', $innards );
  61. }
  62. /**
  63. * automatic diffing function - used for determining required database upgrades
  64. * based on Owen Winkler's microwiki upgrade function
  65. *
  66. * @param queries array of create table and insert statements which constitute a fresh install
  67. * @param (optional) execute should the queries be executed against the database or just simulated. default = true
  68. * @param (optional) silent silent running with no messages printed? default = true
  69. * @param boolean $doinserts (optional) Execute all insert queries found, default=false
  70. * @return array list of updates made
  71. */
  72. function dbdelta( $queries, $execute = true, $silent = true, $doinserts = false )
  73. {
  74. if ( !is_array( $queries ) ) {
  75. $queries = explode( ';', $queries );
  76. if ( '' == $queries[count( $queries ) - 1] ) {
  77. array_pop( $queries );
  78. }
  79. }
  80. $cseqqueries = array();
  81. $cqueries = array();
  82. $alterseqqueries = array();
  83. $indexqueries = array();
  84. $iqueries = array();
  85. $for_update = array();
  86. $indices = array();
  87. foreach ( $queries as $qry ) {
  88. if ( preg_match( "|CREATE TABLE\s+(\w*)|", $qry, $matches ) ) {
  89. $cqueries[strtolower( $matches[1] )] = $qry;
  90. $for_update[$matches[1]] = 'Created table ' . $matches[1];
  91. }
  92. else if ( preg_match( "|CREATE (UNIQUE )?INDEX ([^ ]*) ON ([^ ]*)|", $qry, $matches ) ) {
  93. $indexqueries[strtolower( $matches[3] )] = $qry;
  94. }
  95. else if ( preg_match( "|CREATE DATABASE ([^ ]*)|", $qry, $matches ) ) {
  96. array_unshift( $cqueries, $qry );
  97. }
  98. else if ( preg_match( "|CREATE SEQUENCE ([^ ]*)|", $qry, $matches ) ) {
  99. $cseqqueries[strtolower( $matches[1] )] = $qry;
  100. }
  101. else if ( preg_match( "|ALTER SEQUENCE ([^ ]*)|", $qry, $matches ) ) {
  102. $alterseqqueries[] = $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 {
  111. // Unrecognized query type
  112. }
  113. }
  114. // Checking sequence
  115. if ( $seqnames = $this->get_results(
  116. "SELECT c.relname as name,
  117. CASE c.relkind WHEN 'r' THEN 'table' WHEN 'v' THEN 'view' WHEN 'i' THEN 'index' WHEN 'S' THEN 'sequence' WHEN 's' THEN 'special' END AS type
  118. FROM pg_catalog.pg_class c
  119. JOIN pg_catalog.pg_roles r ON r.oid = c.relowner
  120. LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
  121. WHERE c.relkind IN ('S','')
  122. AND n.nspname NOT IN ('pg_catalog', 'pg_toast')
  123. AND pg_catalog.pg_table_is_visible(c.oid)
  124. ORDER BY 1,2;" ) ) {
  125. foreach ( ( array ) $seqnames as $seqname ) {
  126. if ( array_key_exists( strtolower( $seqname->name ), $cseqqueries ) ) {
  127. unset( $cseqqueries[$seqname->name] );
  128. }
  129. }
  130. }
  131. if ( $tables = $this->get_column(
  132. "SELECT table_name
  133. FROM information_schema.tables
  134. WHERE table_type = 'BASE TABLE'
  135. AND table_schema NOT IN ('pg_catalog', 'information_schema');" ) ) {
  136. foreach ( $tables as $table ) {
  137. if ( array_key_exists( strtolower( $table ), $cqueries ) ) {
  138. unset( $cfields );
  139. $cfields = array();
  140. unset( $indices );
  141. $indices = array();
  142. preg_match( "|\((.*)\)|ms", $cqueries[strtolower( $table )], $match2 );
  143. $qryline = trim( $match2[1] );
  144. $flds = explode( "\n", $qryline );
  145. foreach ( $flds as $fld ) {
  146. preg_match( "|^([^ ]*)|", trim( $fld ), $fvals );
  147. $fieldname = $fvals[1];
  148. $validfield = true;
  149. switch ( strtolower( $fieldname ) ) {
  150. case '':
  151. case 'primary':
  152. case 'index':
  153. case 'fulltext':
  154. case 'unique':
  155. case 'key':
  156. $validfield = false;
  157. $indices[] = trim( trim( $fld ), ", \n" );
  158. break;
  159. }
  160. $fld = trim( $fld );
  161. if ( $validfield ) {
  162. $cfields[strtolower( $fieldname )] = trim( $fld, ", \n" );
  163. }
  164. }
  165. if ( isset( $indexqueries[$table] ) ) {
  166. preg_match( "|CREATE (UNIQUE )?INDEX ([^ ]*) ON ([^ ]*) \((.*)\)|ms", $indexqueries[$table], $matches );
  167. if ( $matches ) {
  168. $indices[] = ' (' . preg_replace( '/\s/ms', '', $matches[4] ) . ')';
  169. }
  170. }
  171. $tablefields = $this->get_results(
  172. "SELECT column_name AS field,
  173. data_type AS type,
  174. column_default AS default,
  175. is_nullable AS null,
  176. character_maximum_length AS length,
  177. numeric_precision
  178. FROM information_schema.columns
  179. WHERE table_name = '{$table}'
  180. ORDER BY ordinal_position;" );
  181. foreach ( ( array ) $tablefields as $tablefield ) {
  182. if ( array_key_exists( strtolower( $tablefield->field ), $cfields ) ) {
  183. preg_match( '/^(.*) (.*)( |$)/U', $cfields[strtolower( $tablefield->field )], $matches );
  184. $cfieldname = $matches[1];
  185. $cfieldtype = $matches[2];
  186. $fieldtype = $tablefield->type;
  187. if ( stripos( $fieldtype, 'character' ) === false ) {
  188. // do nothing
  189. }
  190. else {
  191. if ( stripos( $fieldtype, 'varying' ) > 0 ) {
  192. $fieldtype = 'varchar(' . $tablefield->length . ')';
  193. }
  194. else {
  195. $fieldtype = 'char(' . $tablefield->length . ')';
  196. }
  197. }
  198. if ( stripos( $fieldtype, 'timestamp' ) === false ) {
  199. // do nothing
  200. }
  201. else {
  202. $fieldtype = 'timestamp';
  203. }
  204. if ( strtolower( $fieldtype ) != strtolower( $cfieldtype ) ) {
  205. $cqueries[] = "ALTER TABLE {$table} ALTER COLUMN " . $cfieldname . " TYPE " . $cfieldtype;
  206. $for_update[$table.'.'.$tablefield->field] = "Changed type of {$table}.{$tablefield->field} from {$tablefield->type} to {$fieldtype}";
  207. }
  208. if ( preg_match( "| DEFAULT ([^ ]*)|i", $cfields[strtolower( $tablefield->field )], $matches ) ) {
  209. $default_value = $matches[1];
  210. if ( strpos( '::', $tablefield->default) === false ) {
  211. $tablefield_default = $tablefield->default;
  212. }
  213. else {
  214. preg_match( '|(.*)::|', $tablefield->default, $matches );
  215. $tablefield_default = $matches[1] . ( preg_match( '|^nextval|i', $matches[1] ) > 0 ? ')' : '' );
  216. }
  217. if ( $tablefield_default != $default_value ) {
  218. $cqueries[] = "ALTER TABLE {$table} ALTER COLUMN {$tablefield->field} SET DEFAULT {$default_value}";
  219. $for_update[$table.'.'.$tablefield->field] = "Changed default value of {$table}.{$tablefield->field} from {$tablefield->default} to {$default_value}";
  220. }
  221. }
  222. elseif ( strlen( $tablefield->default) > 0 ) {
  223. $cqueries[] = "ALTER TABLE {$table} ALTER COLUMN {$tablefield->field} DROP DEFAULT";
  224. $for_update[$table.'.'.$tablefield->field] = "Dropped default value of {$table}.{$tablefield->field}";
  225. }
  226. unset( $cfields[strtolower( $tablefield->field )] );
  227. }
  228. else {
  229. // This field exists in the table, but not in the creation queries?
  230. }
  231. }
  232. foreach ( $cfields as $fieldname => $fielddef ) {
  233. $cqueries[] = "ALTER TABLE {$table} ADD COLUMN $fielddef";
  234. $for_update[$table.'.'.$fieldname] = 'Added column '.$table.'.'.$fieldname;
  235. }
  236. $tableindices = $this->get_results(
  237. "SELECT c2.relname AS key_name,
  238. i.indisprimary AS is_primary,
  239. i.indisunique AS is_unique,
  240. i.indisclustered AS is_clustered,
  241. i.indisvalid AS is_valid,
  242. pg_catalog.pg_get_indexdef(i.indexrelid, 0, true) AS index_def,
  243. c2.reltablespace
  244. FROM pg_catalog.pg_class c, pg_catalog.pg_class c2,
  245. pg_catalog.pg_index i
  246. WHERE c.oid = (
  247. SELECT c.oid
  248. FROM pg_catalog.pg_class c
  249. LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
  250. WHERE c.relname = '{$table}'
  251. AND pg_catalog.pg_table_is_visible(c.oid)
  252. )
  253. AND c.oid = i.indrelid AND i.indexrelid = c2.oid
  254. ORDER BY i.indisprimary DESC, i.indisunique DESC,
  255. c2.relname;" );
  256. if ( $tableindices ) {
  257. unset( $index_ary );
  258. $index_ary = array();
  259. foreach ( ( array ) $tableindices as $tableindex ) {
  260. $keyname = $tableindex->key_name;
  261. preg_match( '/\((.*)\)/', $tableindex->index_def, $matches );
  262. $fieldnames = str_replace( '"', '', str_replace( ' ', '', $matches[1] ) );
  263. $index_ary[$keyname]['fieldnames'] = $fieldnames;
  264. $index_ary[$keyname]['unique'] = ( $tableindex->is_unique == true ) ? true : false;
  265. $index_ary[$keyname]['primary'] = ( $tableindex->is_primary == true ) ? true : false;
  266. }
  267. foreach ( $index_ary as $index_name => $index_data ) {
  268. $index_string = '';
  269. if ( $index_data['primary'] ) {
  270. $index_string .= 'PRIMARY KEY ';
  271. }
  272. else if ( $index_data['unique'] ) {
  273. $index_string .= 'UNIQUE ';
  274. }
  275. $index_columns = $index_data['fieldnames'];
  276. $index_string = rtrim( $index_string, ' ' );
  277. $index_string .= ' (' . $index_columns . ')';
  278. if ( !( ( $aindex = array_search( $index_string, $indices ) ) === false ) ) {
  279. unset( $indices[$aindex] );
  280. unset( $indexqueries[$table] );
  281. }
  282. else {
  283. if ( $index_data['unique'] ) {
  284. $cqueries[] = "ALTER TABLE {$table} DROP CONSTRAINT {$index_name}";
  285. }
  286. else {
  287. $cqueries[] = "DROP INDEX {$index_name}";
  288. }
  289. }
  290. }
  291. }
  292. foreach ( $indices as $index ) {
  293. $cqueries[] = "ALTER TABLE {$table} ADD $index";
  294. $for_update[$table . '.' . $fieldname] = 'Added index ' . $table . ' ' . $index;
  295. }
  296. unset( $cqueries[strtolower( $table )] );
  297. unset( $for_update[strtolower( $table )] );
  298. }
  299. else {
  300. }
  301. }
  302. }
  303. $allqueries = array_merge( $cseqqueries, $cqueries, $alterseqqueries );
  304. if ( $doinserts ) {
  305. $allqueries = array_merge( $allqueries, $iqueries );
  306. }
  307. foreach ( $indexqueries as $tablename => $indexquery ) {
  308. $allqueries = array_merge( $allqueries, ( array ) $indexquery );
  309. }
  310. if ( $execute ) {
  311. foreach ( $allqueries as $query ) {
  312. if ( !$this->exec( $query ) ) {
  313. $this->get_errors();
  314. return false;
  315. }
  316. }
  317. }
  318. if ( !$silent ) {
  319. if ( count( $for_update ) > 0 ) {
  320. echo "<ul>\n";
  321. foreach ( $for_update as $upgrade ) {
  322. echo "<li>{$upgrade}</li>\n";
  323. }
  324. echo "</ul>\n";
  325. }
  326. else {
  327. echo "<ul><li>" . _t('No Upgrades') . "</li></ul>";
  328. }
  329. }
  330. return $for_update;
  331. }
  332. /**
  333. * Run all of the upgrades slated for pre-dbdelta since the last database revision.
  334. *
  335. * @param integer $old_version The current version of the database that is being upgraded
  336. * @return boolean True on success
  337. */
  338. public function upgrade_pre( $old_version, $upgrade_path = '' )
  339. {
  340. return parent::upgrade( $old_version, dirname(__FILE__) . '/upgrades/pre');
  341. }
  342. /**
  343. * Run all of the upgrades slated for post-dbdelta since the last database revision.
  344. *
  345. * @param integer $old_version The current version of the database that is being upgraded
  346. * @return boolean True on success
  347. */
  348. public function upgrade_post( $old_version, $upgrade_path = '' )
  349. {
  350. return parent::upgrade( $old_version, dirname(__FILE__) . '/upgrades/post');
  351. }
  352. }
  353. ?>