Andrew's Web Libraries (AWL)
AwlDBDialect.php
1 <?php
18 if ( !defined('E_USER_ERROR') ) define('E_USER_ERROR',256);
19 
33 class AwlDBDialect {
41  protected $dialect;
42 
46  protected $db;
47 
51  private $version;
52 
59  const HttpDateFormat = "'Dy, DD Mon IYYY HH24:MI:SS \"GMT\"'";
60 
64  const SqlDateFormat = "'YYYYMMDD\"T\"HH24MISS'";
65 
70  const SqlUTCFormat = "'YYYYMMDD\"T\"HH24MISS\"Z\"'";
71 
76  const SqlDurationFormat = "'\"PT\"HH24\"H\"MI\"M\"'";
77 
90  function __construct( $connection_string, $dbuser=null, $dbpass=null, $options=null ) {
91  if ( preg_match( '/^(pgsql):/', $connection_string, $matches ) ) {
92  $this->dialect = $matches[1];
93  }
94  else {
95  error_log("Unable to connect to database: ". $e->getMessage() );
96  trigger_error("Unsupported database connection '".$connection_string."'",E_USER_ERROR);
97  }
98  try {
99  $this->db = new PDO( $connection_string, $dbuser, $dbpass, $options );
100  } catch (PDOException $e) {
101  error_log("Unable to connect to database: ". $e->getMessage() );
102  if ( function_exists('trigger_error') )
103  trigger_error("PDO connection error '".$connection_string."': ".$e->getMessage(),E_USER_ERROR);
104  throw $e;
105  }
106  }
107 
108 
109 
113  function SetSearchPath( $search_path = null ) {
114  if ( !isset($this->dialect) ) {
115  trigger_error("Unsupported database dialect",E_USER_ERROR);
116  }
117 
118  switch ( $this->dialect ) {
119  case 'pgsql':
120  if ( $search_path == null ) $search_path = 'public';
121  $sql = "SET search_path TO " . $search_path;
122  $sth = $this->db->query($sql);
123  return $sql;
124  }
125  }
126 
127 
132  function GetVersion( ) {
133  if ( isset($this->version) ) return $this->version;
134  if ( !isset($this->dialect) ) {
135  trigger_error("Unsupported database dialect", E_USER_ERROR);
136  }
137 
138  $version = $this->dialect.':';
139 
140  switch ( $this->dialect ) {
141  case 'pgsql':
142  $sql = "SELECT version()";
143  if ( $sth = $this->db->query($sql) ) {
144  $row = $sth->fetch(PDO::FETCH_NUM);
145  $version .= preg_replace( '/^PostgreSQL (\d+\.\d+)\..*$/i', '$1', $row[0]);
146  }
147  break;
148  default:
149  return null;
150  }
151  $this->version = $version;
152  return $version;
153  }
154 
155 
161  function GetFields( $tablename_string ) {
162  if ( !isset($this->dialect) ) {
163  trigger_error("Unsupported database dialect", E_USER_ERROR);
164  }
165 
166  switch ( $this->dialect ) {
167  case 'pgsql':
168  list( $schema, $table ) = explode('.', $tablename_string, 2);
169  if ( empty($table) ) {
170  $table = $tablename_string;
171  $schema = null;
172  }
173 
174  $sql = 'SELECT f.attname AS fieldname, t.typname AS typename, f.atttypmod AS precision FROM pg_attribute f';
175  $sql .= ' JOIN pg_class c ON ( f.attrelid = c.oid )';
176  $sql .= ' JOIN pg_type t ON ( f.atttypid = t.oid )';
177  $sql .= ' JOIN pg_namespace ns ON ( c.relnamespace = ns.oid )';
178  $sql .= ' WHERE relname = '.$this->Quote($table,PDO::PARAM_STR).' AND attnum >= 0 ';
179  if ( isset($schema) ) $sql .= ' AND ns.nspname = '.$this->Quote($schema,PDO::PARAM_STR);
180  $sql .= ' ORDER BY f.attnum';
181  dbg_error_log($sql);
182  return $sql;
183  }
184  }
185 
186 
200  function TranslateSQL( $sql_string ) {
201  // Noop for the time being...
202  return $sql_string;
203  }
204 
205 
206 
214  function Quote( $value, $value_type = null ) {
215  if ( isset($value_type) && $value_type == 'identifier' ) {
216  if ( $this->dialect == 'mysql' ) {
218  $rv = '`' . str_replace('`', '\\`', $value ) . '`';
219  }
220  else {
221  $rv = '"' . str_replace('"', '\\"', $value ) . '"';
222  }
223  return $rv;
224  }
225 
226  if ( !isset($value_type) ) {
227  if ( !isset($value) ) $value_type = PDO::PARAM_NULL;
228  elseif ( is_bool($value) ) $value_type = PDO::PARAM_BOOL;
229  elseif ( is_float($value) ) $value_type = PDO::PARAM_INT;
230  elseif ( is_numeric($value)) {
231  if ( preg_match('{^(19|20)\d\d(0[1-9]|1[012])([012]\d|30|31)$}', $value) )
232  $value_type = PDO::PARAM_STR; // YYYYMMDD
233  elseif ( preg_match('{^0x}i', $value) )
234  $value_type = PDO::PARAM_STR; // Any hex numbers will need to be explicitly cast in SQL
235  elseif ( preg_match('{^[0-9+-]+e[0-9+-]+$}i', $value) )
236  $value_type = PDO::PARAM_STR; // 72e57650 could easily be a string and will need an explicit cast also
237  elseif ( preg_match('/^[01]{6,}$/i', $value) )
238  $value_type = PDO::PARAM_STR; // Binary numbers will need to be explicitly cast in SQL
239  else
240  $value_type = PDO::PARAM_INT;
241  }
242  else
243  $value_type = PDO::PARAM_STR;
244  }
245 
246  if ( is_string($value_type) ) {
247  switch( $value_type ) {
248  case 'null':
249  $value_type = PDO::PARAM_NULL;
250  break;
251  case 'integer':
252  case 'double' :
253  $value_type = PDO::PARAM_INT;
254  break;
255  case 'boolean':
256  $value_type = PDO::PARAM_BOOL;
257  break;
258  case 'string':
259  $value_type = PDO::PARAM_STR;
260  break;
261  }
262  }
263 
264  switch ( $value_type ) {
265  case PDO::PARAM_NULL:
266  $rv = 'NULL';
267  break;
268  case PDO::PARAM_INT:
269  $rv = $value;
270  break;
271  case PDO::PARAM_BOOL:
272  $rv = ($value ? 'TRUE' : 'FALSE');
273  break;
274  case PDO::PARAM_STR:
275  default:
282  $rv = "'".str_replace("'", "''", str_replace(':', '\\x3a', str_replace('\\', '\\x5c', $value)))."'";
283 
284  if ( $this->dialect == 'pgsql' && strpos( $rv, '\\' ) !== false ) {
289  $rv = 'E'.str_replace('?', '\\x3f', $rv);
290  }
291 
292  }
293 
294  return $rv;
295 
296  }
297 
298 
316  function ReplaceParameters() {
317  $argc = func_num_args();
318  $args = func_get_args();
319 
320  if ( is_array($args[0]) ) {
324  $args = $args[0];
325  $argc = count($args);
326  }
327  $qry = array_shift($args);
328 
329  if ( is_array($args[0]) ) {
330  $args = $args[0];
331  $argc = count($args);
332  }
333 
334  if ( ! isset($args[0]) ) return $this->ReplaceNamedParameters($qry,$args);
335 
340  $parts = explode( '?', $qry, $argc + 1 );
341  $querystring = $parts[0];
342  $z = count($parts);
343 
344  for( $i = 0; $i < $argc; $i++ ) {
345  $arg = $args[$i];
346  $querystring .= $this->Quote($arg); //parameter
347  $z = $i+1;
348  if ( isset($parts[$z]) ) $querystring .= $parts[$z];
349  }
350 
351  return $querystring;
352  }
353 
373  $argc = func_num_args();
374  $args = func_get_args();
375 
376  if ( is_array($args[0]) ) {
380  $args = $args[0];
381  $argc = count($args);
382  }
383  $querystring = array_shift($args);
384 
385  if ( is_array($args[0]) ) {
386  $args = $args[0];
387  $argc = count($args);
388  }
389 
390  foreach( $args AS $name => $value ) {
391  if ( substr($name, 0, 1) != ':' ) {
392  dbg_error_log( "ERROR", "AwlDBDialect: Named parameter '%s' does not begin with a colon.", $name);
393  }
394  $replacement = str_replace('$', '\\$', $this->Quote($value)); // No positional replacement in $replacement!
395  $querystring = preg_replace( '{\Q'.$name.'\E\b}s', $replacement, $querystring );
396  }
397 
398  return $querystring;
399  }
400 
401 }
__construct( $connection_string, $dbuser=null, $dbpass=null, $options=null)
SetSearchPath( $search_path=null)
const SqlUTCFormat
const SqlDateFormat
Quote( $value, $value_type=null)
GetFields( $tablename_string)
TranslateSQL( $sql_string)
const HttpDateFormat
const SqlDurationFormat