chillerlan /
php-database
| 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
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
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
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 |