Subversion-Projekte lars-tiefland.ci

Revision

Revision 2107 | Revision 2254 | Zur aktuellen Revision | Details | Vergleich mit vorheriger | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
68 lars 1
<?php
2
/**
3
 * CodeIgniter
4
 *
5
 * An open source application development framework for PHP
6
 *
7
 * This content is released under the MIT License (MIT)
8
 *
2242 lars 9
 * Copyright (c) 2014 - 2018, British Columbia Institute of Technology
68 lars 10
 *
11
 * Permission is hereby granted, free of charge, to any person obtaining a copy
12
 * of this software and associated documentation files (the "Software"), to deal
13
 * in the Software without restriction, including without limitation the rights
14
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
 * copies of the Software, and to permit persons to whom the Software is
16
 * furnished to do so, subject to the following conditions:
17
 *
18
 * The above copyright notice and this permission notice shall be included in
19
 * all copies or substantial portions of the Software.
20
 *
21
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
 * THE SOFTWARE.
28
 *
29
 * @package	CodeIgniter
30
 * @author	EllisLab Dev Team
31
 * @copyright	Copyright (c) 2008 - 2014, EllisLab, Inc. (https://ellislab.com/)
2242 lars 32
 * @copyright	Copyright (c) 2014 - 2018, British Columbia Institute of Technology (http://bcit.ca/)
68 lars 33
 * @license	http://opensource.org/licenses/MIT	MIT License
34
 * @link	https://codeigniter.com
35
 * @since	Version 1.3.0
36
 * @filesource
37
 */
38
defined('BASEPATH') OR exit('No direct script access allowed');
39
 
40
/**
41
 * Postgre Database Adapter Class
42
 *
43
 * Note: _DB is an extender class that the app controller
44
 * creates dynamically based on whether the query builder
45
 * class is being used or not.
46
 *
47
 * @package		CodeIgniter
48
 * @subpackage	Drivers
49
 * @category	Database
50
 * @author		EllisLab Dev Team
51
 * @link		https://codeigniter.com/user_guide/database/
52
 */
53
class CI_DB_postgre_driver extends CI_DB {
54
 
55
	/**
56
	 * Database driver
57
	 *
58
	 * @var	string
59
	 */
60
	public $dbdriver = 'postgre';
61
 
62
	/**
63
	 * Database schema
64
	 *
65
	 * @var	string
66
	 */
67
	public $schema = 'public';
68
 
69
	// --------------------------------------------------------------------
70
 
71
	/**
72
	 * ORDER BY random keyword
73
	 *
74
	 * @var	array
75
	 */
76
	protected $_random_keyword = array('RANDOM()', 'RANDOM()');
77
 
78
	// --------------------------------------------------------------------
79
 
80
	/**
81
	 * Class constructor
82
	 *
83
	 * Creates a DSN string to be used for db_connect() and db_pconnect()
84
	 *
85
	 * @param	array	$params
86
	 * @return	void
87
	 */
88
	public function __construct($params)
89
	{
90
		parent::__construct($params);
91
 
92
		if ( ! empty($this->dsn))
93
		{
94
			return;
95
		}
96
 
97
		$this->dsn === '' OR $this->dsn = '';
98
 
99
		if (strpos($this->hostname, '/') !== FALSE)
100
		{
101
			// If UNIX sockets are used, we shouldn't set a port
102
			$this->port = '';
103
		}
104
 
105
		$this->hostname === '' OR $this->dsn = 'host='.$this->hostname.' ';
106
 
107
		if ( ! empty($this->port) && ctype_digit($this->port))
108
		{
109
			$this->dsn .= 'port='.$this->port.' ';
110
		}
111
 
112
		if ($this->username !== '')
113
		{
114
			$this->dsn .= 'user='.$this->username.' ';
115
 
116
			/* An empty password is valid!
117
			 *
118
			 * $db['password'] = NULL must be done in order to ignore it.
119
			 */
120
			$this->password === NULL OR $this->dsn .= "password='".$this->password."' ";
121
		}
122
 
123
		$this->database === '' OR $this->dsn .= 'dbname='.$this->database.' ';
124
 
125
		/* We don't have these options as elements in our standard configuration
126
		 * array, but they might be set by parse_url() if the configuration was
127
		 * provided via string. Example:
128
		 *
129
		 * postgre://username:password@localhost:5432/database?connect_timeout=5&sslmode=1
130
		 */
131
		foreach (array('connect_timeout', 'options', 'sslmode', 'service') as $key)
132
		{
2107 lars 133
			if (isset($this->$key) && is_string($this->$key) && $this->$key !== '')
68 lars 134
			{
2107 lars 135
				$this->dsn .= $key."='".$this->$key."' ";
68 lars 136
			}
137
		}
138
 
139
		$this->dsn = rtrim($this->dsn);
140
	}
141
 
142
	// --------------------------------------------------------------------
143
 
144
	/**
145
	 * Database connection
146
	 *
147
	 * @param	bool	$persistent
148
	 * @return	resource
149
	 */
150
	public function db_connect($persistent = FALSE)
151
	{
152
		$this->conn_id = ($persistent === TRUE)
153
			? pg_pconnect($this->dsn)
154
			: pg_connect($this->dsn);
155
 
156
		if ($this->conn_id !== FALSE)
157
		{
158
			if ($persistent === TRUE
159
				&& pg_connection_status($this->conn_id) === PGSQL_CONNECTION_BAD
160
				&& pg_ping($this->conn_id) === FALSE
161
			)
162
			{
163
				return FALSE;
164
			}
165
 
166
			empty($this->schema) OR $this->simple_query('SET search_path TO '.$this->schema.',public');
167
		}
168
 
169
		return $this->conn_id;
170
	}
171
 
172
	// --------------------------------------------------------------------
173
 
174
	/**
175
	 * Reconnect
176
	 *
177
	 * Keep / reestablish the db connection if no queries have been
178
	 * sent for a length of time exceeding the server's idle timeout
179
	 *
180
	 * @return	void
181
	 */
182
	public function reconnect()
183
	{
184
		if (pg_ping($this->conn_id) === FALSE)
185
		{
186
			$this->conn_id = FALSE;
187
		}
188
	}
189
 
190
	// --------------------------------------------------------------------
191
 
192
	/**
193
	 * Set client character set
194
	 *
195
	 * @param	string	$charset
196
	 * @return	bool
197
	 */
198
	protected function _db_set_charset($charset)
199
	{
200
		return (pg_set_client_encoding($this->conn_id, $charset) === 0);
201
	}
202
 
203
	// --------------------------------------------------------------------
204
 
205
	/**
206
	 * Database version number
207
	 *
208
	 * @return	string
209
	 */
210
	public function version()
211
	{
212
		if (isset($this->data_cache['version']))
213
		{
214
			return $this->data_cache['version'];
215
		}
216
 
217
		if ( ! $this->conn_id OR ($pg_version = pg_version($this->conn_id)) === FALSE)
218
		{
219
			return FALSE;
220
		}
221
 
222
		/* If PHP was compiled with PostgreSQL lib versions earlier
223
		 * than 7.4, pg_version() won't return the server version
224
		 * and so we'll have to fall back to running a query in
225
		 * order to get it.
226
		 */
2242 lars 227
		return (isset($pg_version['server']) && preg_match('#^(\d+\.\d+)#', $pg_version['server'], $match))
228
			? $this->data_cache['version'] = $match[1]
68 lars 229
			: parent::version();
230
	}
231
 
232
	// --------------------------------------------------------------------
233
 
234
	/**
235
	 * Execute the query
236
	 *
237
	 * @param	string	$sql	an SQL query
238
	 * @return	resource
239
	 */
240
	protected function _execute($sql)
241
	{
242
		return pg_query($this->conn_id, $sql);
243
	}
244
 
245
	// --------------------------------------------------------------------
246
 
247
	/**
248
	 * Begin Transaction
249
	 *
250
	 * @return	bool
251
	 */
252
	protected function _trans_begin()
253
	{
254
		return (bool) pg_query($this->conn_id, 'BEGIN');
255
	}
256
 
257
	// --------------------------------------------------------------------
258
 
259
	/**
260
	 * Commit Transaction
261
	 *
262
	 * @return	bool
263
	 */
264
	protected function _trans_commit()
265
	{
266
		return (bool) pg_query($this->conn_id, 'COMMIT');
267
	}
268
 
269
	// --------------------------------------------------------------------
270
 
271
	/**
272
	 * Rollback Transaction
273
	 *
274
	 * @return	bool
275
	 */
276
	protected function _trans_rollback()
277
	{
278
		return (bool) pg_query($this->conn_id, 'ROLLBACK');
279
	}
280
 
281
	// --------------------------------------------------------------------
282
 
283
	/**
284
	 * Determines if a query is a "write" type.
285
	 *
286
	 * @param	string	An SQL query string
287
	 * @return	bool
288
	 */
289
	public function is_write_type($sql)
290
	{
1257 lars 291
		if (preg_match('#^(INSERT|UPDATE).*RETURNING\s.+(\,\s?.+)*$#is', $sql))
68 lars 292
		{
293
			return FALSE;
294
		}
295
 
296
		return parent::is_write_type($sql);
297
	}
298
 
299
	// --------------------------------------------------------------------
300
 
301
	/**
2107 lars 302
	 * Platform-dependent string escape
68 lars 303
	 *
304
	 * @param	string
305
	 * @return	string
306
	 */
307
	protected function _escape_str($str)
308
	{
309
		return pg_escape_string($this->conn_id, $str);
310
	}
311
 
312
	// --------------------------------------------------------------------
313
 
314
	/**
315
	 * "Smart" Escape String
316
	 *
317
	 * Escapes data based on type
318
	 *
319
	 * @param	string	$str
320
	 * @return	mixed
321
	 */
322
	public function escape($str)
323
	{
324
		if (is_php('5.4.4') && (is_string($str) OR (is_object($str) && method_exists($str, '__toString'))))
325
		{
326
			return pg_escape_literal($this->conn_id, $str);
327
		}
328
		elseif (is_bool($str))
329
		{
330
			return ($str) ? 'TRUE' : 'FALSE';
331
		}
332
 
333
		return parent::escape($str);
334
	}
335
 
336
	// --------------------------------------------------------------------
337
 
338
	/**
339
	 * Affected Rows
340
	 *
341
	 * @return	int
342
	 */
343
	public function affected_rows()
344
	{
345
		return pg_affected_rows($this->result_id);
346
	}
347
 
348
	// --------------------------------------------------------------------
349
 
350
	/**
351
	 * Insert ID
352
	 *
353
	 * @return	string
354
	 */
355
	public function insert_id()
356
	{
2242 lars 357
		$v = $this->version();
68 lars 358
 
359
		$table	= (func_num_args() > 0) ? func_get_arg(0) : NULL;
360
		$column	= (func_num_args() > 1) ? func_get_arg(1) : NULL;
361
 
362
		if ($table === NULL && $v >= '8.1')
363
		{
364
			$sql = 'SELECT LASTVAL() AS ins_id';
365
		}
366
		elseif ($table !== NULL)
367
		{
368
			if ($column !== NULL && $v >= '8.0')
369
			{
370
				$sql = 'SELECT pg_get_serial_sequence(\''.$table."', '".$column."') AS seq";
371
				$query = $this->query($sql);
372
				$query = $query->row();
373
				$seq = $query->seq;
374
			}
375
			else
376
			{
377
				// seq_name passed in table parameter
378
				$seq = $table;
379
			}
380
 
381
			$sql = 'SELECT CURRVAL(\''.$seq."') AS ins_id";
382
		}
383
		else
384
		{
385
			return pg_last_oid($this->result_id);
386
		}
387
 
388
		$query = $this->query($sql);
389
		$query = $query->row();
390
		return (int) $query->ins_id;
391
	}
392
 
393
	// --------------------------------------------------------------------
394
 
395
	/**
396
	 * Show table query
397
	 *
398
	 * Generates a platform-specific query string so that the table names can be fetched
399
	 *
400
	 * @param	bool	$prefix_limit
401
	 * @return	string
402
	 */
403
	protected function _list_tables($prefix_limit = FALSE)
404
	{
405
		$sql = 'SELECT "table_name" FROM "information_schema"."tables" WHERE "table_schema" = \''.$this->schema."'";
406
 
407
		if ($prefix_limit !== FALSE && $this->dbprefix !== '')
408
		{
409
			return $sql.' AND "table_name" LIKE \''
410
				.$this->escape_like_str($this->dbprefix)."%' "
411
				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
412
		}
413
 
414
		return $sql;
415
	}
416
 
417
	// --------------------------------------------------------------------
418
 
419
	/**
420
	 * List column query
421
	 *
422
	 * Generates a platform-specific query string so that the column names can be fetched
423
	 *
424
	 * @param	string	$table
425
	 * @return	string
426
	 */
427
	protected function _list_columns($table = '')
428
	{
429
		return 'SELECT "column_name"
430
			FROM "information_schema"."columns"
431
			WHERE LOWER("table_name") = '.$this->escape(strtolower($table));
432
	}
433
 
434
	// --------------------------------------------------------------------
435
 
436
	/**
437
	 * Returns an object with field data
438
	 *
439
	 * @param	string	$table
440
	 * @return	array
441
	 */
442
	public function field_data($table)
443
	{
444
		$sql = 'SELECT "column_name", "data_type", "character_maximum_length", "numeric_precision", "column_default"
445
			FROM "information_schema"."columns"
446
			WHERE LOWER("table_name") = '.$this->escape(strtolower($table));
447
 
448
		if (($query = $this->query($sql)) === FALSE)
449
		{
450
			return FALSE;
451
		}
452
		$query = $query->result_object();
453
 
454
		$retval = array();
455
		for ($i = 0, $c = count($query); $i < $c; $i++)
456
		{
457
			$retval[$i]			= new stdClass();
458
			$retval[$i]->name		= $query[$i]->column_name;
459
			$retval[$i]->type		= $query[$i]->data_type;
460
			$retval[$i]->max_length		= ($query[$i]->character_maximum_length > 0) ? $query[$i]->character_maximum_length : $query[$i]->numeric_precision;
461
			$retval[$i]->default		= $query[$i]->column_default;
462
		}
463
 
464
		return $retval;
465
	}
466
 
467
	// --------------------------------------------------------------------
468
 
469
	/**
470
	 * Error
471
	 *
472
	 * Returns an array containing code and message of the last
2107 lars 473
	 * database error that has occurred.
68 lars 474
	 *
475
	 * @return	array
476
	 */
477
	public function error()
478
	{
479
		return array('code' => '', 'message' => pg_last_error($this->conn_id));
480
	}
481
 
482
	// --------------------------------------------------------------------
483
 
484
	/**
485
	 * ORDER BY
486
	 *
487
	 * @param	string	$orderby
488
	 * @param	string	$direction	ASC, DESC or RANDOM
489
	 * @param	bool	$escape
490
	 * @return	object
491
	 */
492
	public function order_by($orderby, $direction = '', $escape = NULL)
493
	{
494
		$direction = strtoupper(trim($direction));
495
		if ($direction === 'RANDOM')
496
		{
497
			if ( ! is_float($orderby) && ctype_digit((string) $orderby))
498
			{
499
				$orderby = ($orderby > 1)
500
					? (float) '0.'.$orderby
501
					: (float) $orderby;
502
			}
503
 
504
			if (is_float($orderby))
505
			{
506
				$this->simple_query('SET SEED '.$orderby);
507
			}
508
 
509
			$orderby = $this->_random_keyword[0];
510
			$direction = '';
511
			$escape = FALSE;
512
		}
513
 
514
		return parent::order_by($orderby, $direction, $escape);
515
	}
516
 
517
	// --------------------------------------------------------------------
518
 
519
	/**
520
	 * Update statement
521
	 *
522
	 * Generates a platform-specific update string from the supplied data
523
	 *
524
	 * @param	string	$table
525
	 * @param	array	$values
526
	 * @return	string
527
	 */
528
	protected function _update($table, $values)
529
	{
530
		$this->qb_limit = FALSE;
531
		$this->qb_orderby = array();
532
		return parent::_update($table, $values);
533
	}
534
 
535
	// --------------------------------------------------------------------
536
 
537
	/**
538
	 * Update_Batch statement
539
	 *
540
	 * Generates a platform-specific batch update string from the supplied data
541
	 *
542
	 * @param	string	$table	Table name
543
	 * @param	array	$values	Update data
544
	 * @param	string	$index	WHERE key
545
	 * @return	string
546
	 */
547
	protected function _update_batch($table, $values, $index)
548
	{
549
		$ids = array();
550
		foreach ($values as $key => $val)
551
		{
2049 lars 552
			$ids[] = $val[$index]['value'];
68 lars 553
 
554
			foreach (array_keys($val) as $field)
555
			{
556
				if ($field !== $index)
557
				{
2049 lars 558
					$final[$val[$field]['field']][] = 'WHEN '.$val[$index]['value'].' THEN '.$val[$field]['value'];
68 lars 559
				}
560
			}
561
		}
562
 
563
		$cases = '';
564
		foreach ($final as $k => $v)
565
		{
2049 lars 566
			$cases .= $k.' = (CASE '.$val[$index]['field']."\n"
68 lars 567
				.implode("\n", $v)."\n"
568
				.'ELSE '.$k.' END), ';
569
		}
570
 
2049 lars 571
		$this->where($val[$index]['field'].' IN('.implode(',', $ids).')', NULL, FALSE);
68 lars 572
 
573
		return 'UPDATE '.$table.' SET '.substr($cases, 0, -2).$this->_compile_wh('qb_where');
574
	}
575
 
576
	// --------------------------------------------------------------------
577
 
578
	/**
579
	 * Delete statement
580
	 *
581
	 * Generates a platform-specific delete string from the supplied data
582
	 *
583
	 * @param	string	$table
584
	 * @return	string
585
	 */
586
	protected function _delete($table)
587
	{
588
		$this->qb_limit = FALSE;
589
		return parent::_delete($table);
590
	}
591
 
592
	// --------------------------------------------------------------------
593
 
594
	/**
595
	 * LIMIT
596
	 *
597
	 * Generates a platform-specific LIMIT clause
598
	 *
599
	 * @param	string	$sql	SQL Query
600
	 * @return	string
601
	 */
602
	protected function _limit($sql)
603
	{
604
		return $sql.' LIMIT '.$this->qb_limit.($this->qb_offset ? ' OFFSET '.$this->qb_offset : '');
605
	}
606
 
607
	// --------------------------------------------------------------------
608
 
609
	/**
610
	 * Close DB Connection
611
	 *
612
	 * @return	void
613
	 */
614
	protected function _close()
615
	{
616
		pg_close($this->conn_id);
617
	}
618
 
619
}