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 *
9 * Copyright (c) 2014 - 2018, British Columbia Institute of Technology
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/)
32 * @copyright	Copyright (c) 2014 - 2018, British Columbia Institute of Technology (http://bcit.ca/)
33 * @license	http://opensource.org/licenses/MIT	MIT License
34 * @link	https://codeigniter.com
35 * @since	Version 3.0.0
36 * @filesource
37 */
38defined('BASEPATH') OR exit('No direct script access allowed');
39
40/**
41 * Firebird/Interbase 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 */
53class CI_DB_ibase_driver extends CI_DB {
54
55	/**
56	 * Database driver
57	 *
58	 * @var	string
59	 */
60	public $dbdriver = 'ibase';
61
62	// --------------------------------------------------------------------
63
64	/**
65	 * ORDER BY random keyword
66	 *
67	 * @var	array
68	 */
69	protected $_random_keyword = array('RAND()', 'RAND()');
70
71	/**
72	 * IBase Transaction status flag
73	 *
74	 * @var	resource
75	 */
76	protected $_ibase_trans;
77
78	// --------------------------------------------------------------------
79
80	/**
81	 * Non-persistent database connection
82	 *
83	 * @param	bool	$persistent
84	 * @return	resource
85	 */
86	public function db_connect($persistent = FALSE)
87	{
88		return ($persistent === TRUE)
89			? ibase_pconnect($this->hostname.':'.$this->database, $this->username, $this->password, $this->char_set)
90			: ibase_connect($this->hostname.':'.$this->database, $this->username, $this->password, $this->char_set);
91	}
92
93	// --------------------------------------------------------------------
94
95	/**
96	 * Database version number
97	 *
98	 * @return	string
99	 */
100	public function version()
101	{
102		if (isset($this->data_cache['version']))
103		{
104			return $this->data_cache['version'];
105		}
106
107		if (($service = ibase_service_attach($this->hostname, $this->username, $this->password)))
108		{
109			$this->data_cache['version'] = ibase_server_info($service, IBASE_SVC_SERVER_VERSION);
110
111			// Don't keep the service open
112			ibase_service_detach($service);
113			return $this->data_cache['version'];
114		}
115
116		return FALSE;
117	}
118
119	// --------------------------------------------------------------------
120
121	/**
122	 * Execute the query
123	 *
124	 * @param	string	$sql	an SQL query
125	 * @return	resource
126	 */
127	protected function _execute($sql)
128	{
129		return ibase_query(isset($this->_ibase_trans) ? $this->_ibase_trans : $this->conn_id, $sql);
130	}
131
132	// --------------------------------------------------------------------
133
134	/**
135	 * Begin Transaction
136	 *
137	 * @return	bool
138	 */
139	protected function _trans_begin()
140	{
141		if (($trans_handle = ibase_trans($this->conn_id)) === FALSE)
142		{
143			return FALSE;
144		}
145
146		$this->_ibase_trans = $trans_handle;
147		return TRUE;
148	}
149
150	// --------------------------------------------------------------------
151
152	/**
153	 * Commit Transaction
154	 *
155	 * @return	bool
156	 */
157	protected function _trans_commit()
158	{
159		if (ibase_commit($this->_ibase_trans))
160		{
161			$this->_ibase_trans = NULL;
162			return TRUE;
163		}
164
165		return FALSE;
166	}
167
168	// --------------------------------------------------------------------
169
170	/**
171	 * Rollback Transaction
172	 *
173	 * @return	bool
174	 */
175	protected function _trans_rollback()
176	{
177		if (ibase_rollback($this->_ibase_trans))
178		{
179			$this->_ibase_trans = NULL;
180			return TRUE;
181		}
182
183		return FALSE;
184	}
185
186	// --------------------------------------------------------------------
187
188	/**
189	 * Affected Rows
190	 *
191	 * @return	int
192	 */
193	public function affected_rows()
194	{
195		return ibase_affected_rows($this->conn_id);
196	}
197
198	// --------------------------------------------------------------------
199
200	/**
201	 * Insert ID
202	 *
203	 * @param	string	$generator_name
204	 * @param	int	$inc_by
205	 * @return	int
206	 */
207	public function insert_id($generator_name, $inc_by = 0)
208	{
209		//If a generator hasn't been used before it will return 0
210		return ibase_gen_id('"'.$generator_name.'"', $inc_by);
211	}
212
213	// --------------------------------------------------------------------
214
215	/**
216	 * List table query
217	 *
218	 * Generates a platform-specific query string so that the table names can be fetched
219	 *
220	 * @param	bool	$prefix_limit
221	 * @return	string
222	 */
223	protected function _list_tables($prefix_limit = FALSE)
224	{
225		$sql = 'SELECT TRIM("RDB$RELATION_NAME") AS TABLE_NAME FROM "RDB$RELATIONS" WHERE "RDB$RELATION_NAME" NOT LIKE \'RDB$%\' AND "RDB$RELATION_NAME" NOT LIKE \'MON$%\'';
226
227		if ($prefix_limit !== FALSE && $this->dbprefix !== '')
228		{
229			return $sql.' AND TRIM("RDB$RELATION_NAME") AS TABLE_NAME LIKE \''.$this->escape_like_str($this->dbprefix)."%' "
230				.sprintf($this->_like_escape_str, $this->_like_escape_chr);
231		}
232
233		return $sql;
234	}
235
236	// --------------------------------------------------------------------
237
238	/**
239	 * Show column query
240	 *
241	 * Generates a platform-specific query string so that the column names can be fetched
242	 *
243	 * @param	string	$table
244	 * @return	string
245	 */
246	protected function _list_columns($table = '')
247	{
248		return 'SELECT TRIM("RDB$FIELD_NAME") AS COLUMN_NAME FROM "RDB$RELATION_FIELDS" WHERE "RDB$RELATION_NAME" = '.$this->escape($table);
249	}
250
251	// --------------------------------------------------------------------
252
253	/**
254	 * Returns an object with field data
255	 *
256	 * @param	string	$table
257	 * @return	array
258	 */
259	public function field_data($table)
260	{
261		$sql = 'SELECT "rfields"."RDB$FIELD_NAME" AS "name",
262				CASE "fields"."RDB$FIELD_TYPE"
263					WHEN 7 THEN \'SMALLINT\'
264					WHEN 8 THEN \'INTEGER\'
265					WHEN 9 THEN \'QUAD\'
266					WHEN 10 THEN \'FLOAT\'
267					WHEN 11 THEN \'DFLOAT\'
268					WHEN 12 THEN \'DATE\'
269					WHEN 13 THEN \'TIME\'
270					WHEN 14 THEN \'CHAR\'
271					WHEN 16 THEN \'INT64\'
272					WHEN 27 THEN \'DOUBLE\'
273					WHEN 35 THEN \'TIMESTAMP\'
274					WHEN 37 THEN \'VARCHAR\'
275					WHEN 40 THEN \'CSTRING\'
276					WHEN 261 THEN \'BLOB\'
277					ELSE NULL
278				END AS "type",
279				"fields"."RDB$FIELD_LENGTH" AS "max_length",
280				"rfields"."RDB$DEFAULT_VALUE" AS "default"
281			FROM "RDB$RELATION_FIELDS" "rfields"
282				JOIN "RDB$FIELDS" "fields" ON "rfields"."RDB$FIELD_SOURCE" = "fields"."RDB$FIELD_NAME"
283			WHERE "rfields"."RDB$RELATION_NAME" = '.$this->escape($table).'
284			ORDER BY "rfields"."RDB$FIELD_POSITION"';
285
286		return (($query = $this->query($sql)) !== FALSE)
287			? $query->result_object()
288			: FALSE;
289	}
290
291	// --------------------------------------------------------------------
292
293	/**
294	 * Error
295	 *
296	 * Returns an array containing code and message of the last
297	 * database error that has occurred.
298	 *
299	 * @return	array
300	 */
301	public function error()
302	{
303		return array('code' => ibase_errcode(), 'message' => ibase_errmsg());
304	}
305
306	// --------------------------------------------------------------------
307
308	/**
309	 * Update statement
310	 *
311	 * Generates a platform-specific update string from the supplied data
312	 *
313	 * @param	string	$table
314	 * @param	array	$values
315	 * @return	string
316	 */
317	protected function _update($table, $values)
318	{
319		$this->qb_limit = FALSE;
320		return parent::_update($table, $values);
321	}
322
323	// --------------------------------------------------------------------
324
325	/**
326	 * Truncate statement
327	 *
328	 * Generates a platform-specific truncate string from the supplied data
329	 *
330	 * If the database does not support the TRUNCATE statement,
331	 * then this method maps to 'DELETE FROM table'
332	 *
333	 * @param	string	$table
334	 * @return	string
335	 */
336	protected function _truncate($table)
337	{
338		return 'DELETE FROM '.$table;
339	}
340
341	// --------------------------------------------------------------------
342
343	/**
344	 * Delete statement
345	 *
346	 * Generates a platform-specific delete string from the supplied data
347	 *
348	 * @param	string	$table
349	 * @return	string
350	 */
351	protected function _delete($table)
352	{
353		$this->qb_limit = FALSE;
354		return parent::_delete($table);
355	}
356
357	// --------------------------------------------------------------------
358
359	/**
360	 * LIMIT
361	 *
362	 * Generates a platform-specific LIMIT clause
363	 *
364	 * @param	string	$sql	SQL Query
365	 * @return	string
366	 */
367	protected function _limit($sql)
368	{
369		// Limit clause depends on if Interbase or Firebird
370		if (stripos($this->version(), 'firebird') !== FALSE)
371		{
372			$select = 'FIRST '.$this->qb_limit
373				.($this->qb_offset ? ' SKIP '.$this->qb_offset : '');
374		}
375		else
376		{
377			$select = 'ROWS '
378				.($this->qb_offset ? $this->qb_offset.' TO '.($this->qb_limit + $this->qb_offset) : $this->qb_limit);
379		}
380
381		return preg_replace('`SELECT`i', 'SELECT '.$select, $sql, 1);
382	}
383
384	// --------------------------------------------------------------------
385
386	/**
387	 * Insert batch statement
388	 *
389	 * Generates a platform-specific insert string from the supplied data.
390	 *
391	 * @param	string	$table	Table name
392	 * @param	array	$keys	INSERT keys
393	 * @param	array	$values	INSERT values
394	 * @return	string|bool
395	 */
396	protected function _insert_batch($table, $keys, $values)
397	{
398		return ($this->db_debug) ? $this->display_error('db_unsupported_feature') : FALSE;
399	}
400
401	// --------------------------------------------------------------------
402
403	/**
404	 * Close DB Connection
405	 *
406	 * @return	void
407	 */
408	protected function _close()
409	{
410		ibase_close($this->conn_id);
411	}
412
413}
414