Firebird   A
last analyzed

Complexity

Total Complexity 34

Size/Duplication

Total Lines 191
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 93
dl 0
loc 191
rs 9.68
c 1
b 0
f 0
wmc 34

7 Methods

Rating   Name   Duplication   Size   Complexity  
A showTables() 0 3 1
A select() 0 30 6
A showDatabases() 0 3 1
A dropTable() 0 21 1
B createTable() 0 36 7
A truncate() 0 5 1
D fieldspec() 0 71 17
1
<?php
2
/**
3
 * Class Firebird
4
 *
5
 * @filesource   Firebird.php
6
 * @created      11.01.2018
7
 * @package      chillerlan\Database\Dialects
8
 * @author       Smiley <[email protected]>
9
 * @copyright    2018 Smiley
10
 * @license      MIT
11
 */
12
13
namespace chillerlan\Database\Dialects;
14
15
class Firebird extends DialectAbstract{
16
17
	/** @inheritdoc */
18
	public function select(array $cols, array $from, string $where = null, $limit = null, $offset = null, bool $distinct = null, array $groupby = null, array $orderby = null):array{
19
		$sql = ['SELECT'];
20
21
		if($limit !== null){
22
			$sql[] = 'FIRST ? SKIP ?';
23
		}
24
25
		if($distinct){
26
			$sql[] = 'DISTINCT';
27
		}
28
29
		!empty($cols)
30
			? $sql[] = implode(', ', $cols)
31
			: $sql[] = '*';
32
33
		$sql[] = 'FROM';
34
		$sql[] = implode(', ', $from);
35
		$sql[] = $where;
36
37
		if(!empty($groupby)){
38
			$sql[] = 'GROUP BY';
39
			$sql[] = implode(', ', $groupby);
40
		}
41
42
		if(!empty($orderby)){
43
			$sql[] = 'ORDER BY';
44
			$sql[] = implode(', ', $orderby);
45
		}
46
47
		return $sql;
48
	}
49
50
	/** @inheritdoc */
51
	public function createTable(string $table, array $cols, string $primaryKey = null, bool $ifNotExists = null, bool $temp = null, string $dir = null):array{
52
		$sql = [$ifNotExists ? 'RECREATE' : 'CREATE']; // nasty
53
54
		if($temp){
55
			$sql[] = 'GLOBAL TEMPORARY';
56
		}
57
58
		$sql[] = 'TABLE';
59
60
		$n = explode('.', $table);
61
62
		$sql[] = $this->quote($n[count($n) - 1]);
63
64
		$_cols = [];
65
66
		if(!empty($cols)){
67
68
			foreach($cols as $name => $col){
69
70
				if(strtolower($name) === strtolower($primaryKey)){
0 ignored issues
show
Bug introduced by
It seems like $primaryKey can also be of type null; however, parameter $string of strtolower() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

70
				if(strtolower($name) === strtolower(/** @scrutinizer ignore-type */ $primaryKey)){
Loading history...
71
					$x = explode(' NOT NULL', $col, 2);
72
73
					if(count($x) > 0){
74
						$col = $x[0].' NOT NULL PRIMARY KEY';
75
						$col .= $x[1] ?? '';
76
					}
77
78
				}
79
80
				$_cols[] = $col;
81
			}
82
83
			$sql[] = '('.implode(', ', $_cols).')';
84
		}
85
86
		return $sql;
87
	}
88
89
	/** @inheritdoc */
90
	public function dropTable(string $table, bool $ifExists):array{
91
/*
92
		if($ifExists){
93
			$sql[] = '
94
EXECUTE BLOCK AS  BEGIN
95
    IF  ( EXISTS ( SELECT 1 FROM RDB$RELATIONS WHERE RDB$RELATION_NAME = '.$this->quote($table).' ))
96
    THEN  BEGIN
97
        EXECUTE STATEMENT \'DROP TABLE '.$this->quote($table).';\' ;
98
    END
99
END ^
100
';
101
		}
102
		else{
103
104
		}
105
*/
106
			$sql = ['DROP TABLE'];
107
			$sql[] = $this->quote($table);
108
109
110
		return $sql;
111
	}
112
113
	/** @inheritdoc */
114
	public function fieldspec(string $name, string $type, $length = null, string $attribute = null, string $collation = null, bool $isNull = null, string $defaultType = null, $defaultValue = null, string $extra = null):string{
115
		$type = strtoupper(trim($type));
116
117
		$field = [$this->quote(trim($name))];
118
119
		$type_translation = [
120
			'TINYINT'    => 'SMALLINT',
121
			'MEDIUMINT'  => 'INT',
122
			'BIGINT'     => 'INT64',
123
			'REAL'       => 'DOUBLE PRECISION',
124
			'DOUBLE'     => 'DOUBLE PRECISION',
125
			'BOOLEAN'    => 'CHAR(1)',
126
			'BINARY'     => 'CHAR',
127
			'VARBINARY'  => 'CHAR',
128
			'TINYTEXT'   => 'VARCHAR(255)',
129
			'DATETIME'   => 'TIMESTAMP',
130
			'IMAGE'      => 'BLOB',
131
			'TEXT'       => 'BLOB SUB_TYPE TEXT',
132
			'MEDIUMTEXT' => 'BLOB SUB_TYPE TEXT',
133
			'LONGTEXT'   => 'BLOB SUB_TYPE TEXT',
134
		][$type] ?? false;
135
136
		if($type_translation){
137
			$field[] = $type_translation;
138
		}
139
		elseif(in_array($type, ['CHAR', 'VARCHAR', 'DECIMAL', 'NUMERIC'], true)){
140
			$field[] = $type.'('.$length.')';
141
		}
142
		else{
143
			$field[] = $type;
144
		}
145
146
		if($isNull === false && !in_array($type, ['DATE', 'TIME', 'TIMESTAMP'], true)){
147
			$field[] = 'NOT NULL';
148
		}
149
150
		$defaultType = strtoupper($defaultType);
0 ignored issues
show
Bug introduced by
It seems like $defaultType can also be of type null; however, parameter $string of strtoupper() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

150
		$defaultType = strtoupper(/** @scrutinizer ignore-type */ $defaultType);
Loading history...
151
152
		if($defaultType === 'USER_DEFINED'){
153
154
			switch(true){
155
				case $type === 'TIMESTAMP' && intval($defaultValue) === 0:
156
					$field[] = 'DEFAULT 0';
157
					break;
158
				case strtoupper($defaultValue) === 'NULL' && $isNull === true:
159
					$field[] = 'DEFAULT NULL';
160
					break;
161
				case $type === 'BOOLEAN':
162
					$field[] = 'DEFAULT '.(preg_match('/^1|T|TRUE|YES$/i', $defaultValue) ? '1' : '0');
163
					break;
164
				default:
165
					$field[] = 'DEFAULT \''.$defaultValue.'\'';
166
			}
167
168
		}
169
		elseif($defaultType === 'CURRENT_TIMESTAMP'){
170
			$field[] = 'DEFAULT CURRENT_TIMESTAMP';
171
		}
172
		elseif($defaultType === 'NULL' && $isNull === true){
173
			$field[] = 'DEFAULT NULL';
174
		}
175
176
		if($attribute){
177
			$field[] = $attribute;
178
		}
179
180
		if($extra){
181
			$field[] = $extra;
182
		}
183
184
		return implode(' ', $field);
185
	}
186
187
	/** @inheritdoc */
188
	public function truncate(string $table):array{
189
		$sql = ['DELETE FROM'];// RECREATE TABLE [table spec] ...stupid firebird 2.5
190
		$sql[] = $this->quote($table);
191
192
		return $sql;
193
	}
194
195
	/** @inheritdoc */
196
	public function showDatabases():array{
197
		/** @noinspection SqlResolve */
198
		return ['SELECT TRIM(LOWER(MON$DATABASE_NAME)) AS "Database" FROM MON$DATABASE'];
199
	}
200
201
202
	/** @inheritdoc */
203
	public function showTables(string $database = null, string $pattern = null, string $where = null):array{
204
		/** @noinspection SqlResolve */
205
		return ['SELECT TRIM(RDB$RELATION_NAME) AS "tablename" FROM RDB$RELATIONS WHERE RDB$VIEW_BLR IS NULL AND (RDB$SYSTEM_FLAG IS NULL OR RDB$SYSTEM_FLAG = 0)'];
206
	}
207
208
	/**
209
	 * this is such a hack. i hate firebird so much.
210
	 *
211
	 * @link https://stackoverflow.com/a/12074601
212
	 * @param string $table
213
	 *
214
	 * @return array
215
	 * @noinspection SqlResolve
216
	 */
217
/*	public function showCreateTable(string $table):array{
218
219
		$def = $this->db->prepared('
220
			SELECT
221
				RF.RDB$FIELD_POSITION AS "id",
222
				TRIM(RF.RDB$FIELD_NAME) AS "name",
223
				(CASE F.RDB$FIELD_TYPE
224
					WHEN 7 THEN
225
						CASE F.RDB$FIELD_SUB_TYPE
226
							WHEN 0 THEN \'SMALLINT\'
227
							WHEN 1 THEN \'NUMERIC(\' || F.RDB$FIELD_PRECISION || \',\' || (-F.RDB$FIELD_SCALE) || \')\'
228
							WHEN 2 THEN \'DECIMAL(\' || F.RDB$FIELD_PRECISION || \',\' || (-F.RDB$FIELD_SCALE) || \')\'
229
						END
230
					WHEN 8 THEN
231
						CASE F.RDB$FIELD_SUB_TYPE
232
							WHEN 0 THEN \'INTEGER\'
233
							WHEN 1 THEN \'NUMERIC(\'  || F.RDB$FIELD_PRECISION || \',\' || (-F.RDB$FIELD_SCALE) || \')\'
234
							WHEN 2 THEN \'DECIMAL(\'  || F.RDB$FIELD_PRECISION || \',\' || (-F.RDB$FIELD_SCALE) || \')\'
235
						END
236
					WHEN 9 THEN \'QUAD\'
237
					WHEN 10 THEN \'FLOAT\'
238
					WHEN 12 THEN \'DATE\'
239
					WHEN 13 THEN \'TIME\'
240
					WHEN 14 THEN \'CHAR(\' || (TRUNC(F.RDB$FIELD_LENGTH / CH.RDB$BYTES_PER_CHARACTER)) || \') \'
241
					WHEN 16 THEN
242
						CASE F.RDB$FIELD_SUB_TYPE
243
							WHEN 0 THEN \'BIGINT\'
244
							WHEN 1 THEN \'NUMERIC(\' || F.RDB$FIELD_PRECISION || \', \' || (-F.RDB$FIELD_SCALE) || \')\'
245
							WHEN 2 THEN \'DECIMAL(\' || F.RDB$FIELD_PRECISION || \', \' || (-F.RDB$FIELD_SCALE) || \')\'
246
						END
247
					WHEN 27 THEN \'DOUBLE\'
248
					WHEN 35 THEN \'TIMESTAMP\'
249
					WHEN 37 THEN \'VARCHAR(\' || (TRUNC(F.RDB$FIELD_LENGTH / CH.RDB$BYTES_PER_CHARACTER)) || \')\'
250
					WHEN 40 THEN \'CSTRING\' || (TRUNC(F.RDB$FIELD_LENGTH / CH.RDB$BYTES_PER_CHARACTER)) || \')\'
251
					WHEN 45 THEN \'BLOB_ID\'
252
					WHEN 261 THEN \'BLOB SUB_TYPE \' || F.RDB$FIELD_SUB_TYPE
253
					ELSE \'RDB$FIELD_TYPE: \' || F.RDB$FIELD_TYPE || \'?\'
254
				END) AS "type",
255
				IIF(COALESCE(RF.RDB$NULL_FLAG, 0) = 0, NULL, \'NOT NULL\') AS "isnull",
256
				COALESCE(RF.RDB$DEFAULT_SOURCE, F.RDB$DEFAULT_SOURCE) AS "default",
257
				TRIM(CH.RDB$CHARACTER_SET_NAME) AS "charset",
258
				TRIM(DCO.RDB$COLLATION_NAME) AS "collation",
259
				TRIM(F.RDB$VALIDATION_SOURCE) AS "check",
260
				TRIM(RF.RDB$DESCRIPTION) AS "desc"
261
			FROM RDB$RELATION_FIELDS RF
262
				JOIN RDB$FIELDS F ON (F.RDB$FIELD_NAME = RF.RDB$FIELD_SOURCE)
263
				LEFT OUTER JOIN RDB$CHARACTER_SETS CH ON (CH.RDB$CHARACTER_SET_ID = F.RDB$CHARACTER_SET_ID)
264
				LEFT OUTER JOIN RDB$COLLATIONS DCO ON ((DCO.RDB$COLLATION_ID = F.RDB$COLLATION_ID) AND (DCO.RDB$CHARACTER_SET_ID = F.RDB$CHARACTER_SET_ID))
265
			WHERE (RF.RDB$RELATION_NAME = ?) AND (COALESCE(RF.RDB$SYSTEM_FLAG, 0) = 0)
266
			ORDER BY RF.RDB$FIELD_POSITION
267
			', [$table]);
268
269
		$idx = $this->db->prepared('SELECT S.RDB$FIELD_POSITION AS "pos", S.RDB$FIELD_NAME AS "field", I.RDB$INDEX_ID AS "id", I.RDB$UNIQUE_FLAG AS "unique" FROM RDB$INDEX_SEGMENTS AS S, RDB$INDICES AS I WHERE S.RDB$INDEX_NAME = I.RDB$INDEX_NAME AND I.RDB$RELATION_NAME = ?', [$table]);
270
271
		$fields = [];
272
		if($def instanceof ResultInterface && $def->length > 0){
273
274
			foreach($def as $field){
275
				$index = $idx[$field->id]['id'] ?? false;
276
277
				if($index){
278
					$index = $index === 1 ? 'PRIMARY KEY' : 'UNIQUE'; // @todo
279
				}
280
281
				$fields[] = $this->fieldspec(trim($field->name), trim($field->type), null, null, null, $field->isnull !== 'NOT NULL', null, null, trim($field->default.$index));
282
			}
283
284
		}
285
286
		$this->db->prepared('RECREATE GLOBAL TEMPORARY TABLE TEMP$SQL_CREATE ("name" BLOB SUB_TYPE TEXT CHARACTER SET UTF8 NOT NULL, "create" BLOB SUB_TYPE TEXT CHARACTER SET UTF8 NOT NULL) ON COMMIT PRESERVE ROWS');
287
288
		$create = sprintf('CREATE TABLE %1$s (%2$s)', $this->quote($table), PHP_EOL.implode(','.PHP_EOL, $fields).PHP_EOL);
289
290
		$this->db->prepared('INSERT INTO TEMP$SQL_CREATE ("name", "create") VALUES (?, ?)', [$table, $create]);
291
292
		return ['SELECT "name" AS "Table", "create" AS "Create Table" FROM TEMP$SQL_CREATE'];
293
	}
294
*/
295
}
296