Maurits van der Schee 5 years ago
parent
commit
40d9dbe73e
1 changed files with 155 additions and 22 deletions
  1. 155
    22
      api.php

+ 155
- 22
api.php View File

@@ -4838,9 +4838,14 @@ namespace Tqdev\PhpCrudApi\Database {
4838 4838
                 return '';
4839 4839
             }
4840 4840
             switch ($this->driver) {
4841
-                case 'mysql':return " LIMIT $offset, $limit";
4842
-                case 'pgsql':return " LIMIT $limit OFFSET $offset";
4843
-                case 'sqlsrv':return " OFFSET $offset ROWS FETCH NEXT $limit ROWS ONLY";
4841
+                case 'mysql':
4842
+                    return " LIMIT $offset, $limit";
4843
+                case 'pgsql':
4844
+                    return " LIMIT $limit OFFSET $offset";
4845
+                case 'sqlsrv':
4846
+                    return " OFFSET $offset ROWS FETCH NEXT $limit ROWS ONLY";
4847
+                case 'sqlite':
4848
+                    return " LIMIT $limit OFFSET $offset";
4844 4849
             }
4845 4850
         }
4846 4851
 
@@ -4890,9 +4895,14 @@ namespace Tqdev\PhpCrudApi\Database {
4890 4895
             $valuesSql = '(' . implode(',', $values) . ')';
4891 4896
             $outputColumn = $this->quoteColumnName($table->getPk());
4892 4897
             switch ($this->driver) {
4893
-                case 'mysql':return "$columnsSql VALUES $valuesSql";
4894
-                case 'pgsql':return "$columnsSql VALUES $valuesSql RETURNING $outputColumn";
4895
-                case 'sqlsrv':return "$columnsSql OUTPUT INSERTED.$outputColumn VALUES $valuesSql";
4898
+                case 'mysql':
4899
+                    return "$columnsSql VALUES $valuesSql";
4900
+                case 'pgsql':
4901
+                    return "$columnsSql VALUES $valuesSql RETURNING $outputColumn";
4902
+                case 'sqlsrv':
4903
+                    return "$columnsSql OUTPUT INSERTED.$outputColumn VALUES $valuesSql";
4904
+                case 'sqlite':
4905
+                    return "$columnsSql VALUES $valuesSql";
4896 4906
             }
4897 4907
         }
4898 4908
 
@@ -5072,17 +5082,28 @@ namespace Tqdev\PhpCrudApi\Database {
5072 5082
         private function getSpatialFunctionName(string $operator): string
5073 5083
         {
5074 5084
             switch ($operator) {
5075
-                case 'co':return 'ST_Contains';
5076
-                case 'cr':return 'ST_Crosses';
5077
-                case 'di':return 'ST_Disjoint';
5078
-                case 'eq':return 'ST_Equals';
5079
-                case 'in':return 'ST_Intersects';
5080
-                case 'ov':return 'ST_Overlaps';
5081
-                case 'to':return 'ST_Touches';
5082
-                case 'wi':return 'ST_Within';
5083
-                case 'ic':return 'ST_IsClosed';
5084
-                case 'is':return 'ST_IsSimple';
5085
-                case 'iv':return 'ST_IsValid';
5085
+                case 'co':
5086
+                    return 'ST_Contains';
5087
+                case 'cr':
5088
+                    return 'ST_Crosses';
5089
+                case 'di':
5090
+                    return 'ST_Disjoint';
5091
+                case 'eq':
5092
+                    return 'ST_Equals';
5093
+                case 'in':
5094
+                    return 'ST_Intersects';
5095
+                case 'ov':
5096
+                    return 'ST_Overlaps';
5097
+                case 'to':
5098
+                    return 'ST_Touches';
5099
+                case 'wi':
5100
+                    return 'ST_Within';
5101
+                case 'ic':
5102
+                    return 'ST_IsClosed';
5103
+                case 'is':
5104
+                    return 'ST_IsSimple';
5105
+                case 'iv':
5106
+                    return 'ST_IsValid';
5086 5107
             }
5087 5108
         }
5088 5109
 
@@ -5102,6 +5123,9 @@ namespace Tqdev\PhpCrudApi\Database {
5102 5123
                     $functionName = str_replace('ST_', 'ST', $functionName);
5103 5124
                     $argument = $hasArgument ? 'geometry::STGeomFromText(?,0)' : '';
5104 5125
                     return "$column.$functionName($argument)=1";
5126
+                case 'sqlite':
5127
+                    $argument = $hasArgument ? '?' : '0';
5128
+                    return "$functionName($column, $argument)=1";
5105 5129
             }
5106 5130
         }
5107 5131
 
@@ -5157,10 +5181,10 @@ namespace Tqdev\PhpCrudApi\Database {
5157 5181
 
5158 5182
         private function getRecordValueConversion(ReflectedColumn $column): string
5159 5183
         {
5160
-            if (in_array($this->driver, ['mysql', 'sqlsrv']) && $column->isBoolean()) {
5184
+            if (in_array($this->driver, ['mysql', 'sqlsrv', 'sqlite']) && $column->isBoolean()) {
5161 5185
                 return 'boolean';
5162 5186
             }
5163
-            if ($this->driver == 'sqlsrv' && $column->getType() == 'bigint') {
5187
+            if (in_array($this->driver, ['sqlsrv', 'sqlite']) && in_array($column->getType(), ['integer', 'bigint'])) {
5164 5188
                 return 'integer';
5165 5189
             }
5166 5190
             return 'none';
@@ -5255,6 +5279,8 @@ namespace Tqdev\PhpCrudApi\Database {
5255 5279
                     return "$this->driver:host=$this->address port=$this->port dbname=$this->database options='--client_encoding=UTF8'";
5256 5280
                 case 'sqlsrv':
5257 5281
                     return "$this->driver:Server=$this->address,$this->port;Database=$this->database";
5282
+                case 'sqlite':
5283
+                    return "$this->driver:$this->address";
5258 5284
             }
5259 5285
         }
5260 5286
 
@@ -5273,6 +5299,10 @@ namespace Tqdev\PhpCrudApi\Database {
5273 5299
                     ];
5274 5300
                 case 'sqlsrv':
5275 5301
                     return [];
5302
+                case 'sqlite':
5303
+                    return [
5304
+                        'PRAGMA foreign_keys = on;',
5305
+                    ];
5276 5306
             }
5277 5307
         }
5278 5308
 
@@ -5299,6 +5329,8 @@ namespace Tqdev\PhpCrudApi\Database {
5299 5329
                         \PDO::SQLSRV_ATTR_DIRECT_QUERY => false,
5300 5330
                         \PDO::SQLSRV_ATTR_FETCHES_NUMERIC_TYPE => true,
5301 5331
                     ];
5332
+                case 'sqlite':
5333
+                    return $options + [];
5302 5334
             }
5303 5335
         }
5304 5336
 
@@ -5406,11 +5438,17 @@ namespace Tqdev\PhpCrudApi\Database {
5406 5438
                 case 'mysql':
5407 5439
                     $stmt = $this->query('SELECT LAST_INSERT_ID()', []);
5408 5440
                     break;
5441
+                case 'sqlite':
5442
+                    $stmt = $this->query('SELECT LAST_INSERT_ROWID()', []);
5443
+                    break;
5409 5444
             }
5410 5445
             $pkValue = $stmt->fetchColumn(0);
5411 5446
             if ($this->driver == 'sqlsrv' && $table->getPk()->getType() == 'bigint') {
5412 5447
                 return (int) $pkValue;
5413 5448
             }
5449
+            if ($this->driver == 'sqlite' && in_array($table->getPk()->getType(), ['integer', 'bigint'])) {
5450
+                return (int) $pkValue;
5451
+            }
5414 5452
             return $pkValue;
5415 5453
         }
5416 5454
 
@@ -5996,6 +6034,51 @@ namespace Tqdev\PhpCrudApi\Database {
5996 6034
             $this->typeConverter = new TypeConverter($driver);
5997 6035
         }
5998 6036
 
6037
+        private function createSqlLiteReflectionTables() /*: void */
6038
+        {
6039
+            $reflection = $this->query('SELECT "name" FROM "sqlite_master" WHERE "type" = \'table\' and name like \'sys/%\';', []);
6040
+            if (count($reflection) == 0) {
6041
+                //create reflection tables
6042
+                $this->query('CREATE table "sys/version" ("version" integer);', []);
6043
+                $this->query('CREATE table "sys/tables" ("name" text, "type" text);', []);
6044
+                $this->query('CREATE table "sys/columns" ("self" text,"cid" integer,"name" text,"type" integer,"notnull" integer,"dflt_value" integer,"pk" integer);', []);
6045
+                $this->query('CREATE table "sys/foreign_keys" ("self" text,"id" integer,"seq" integer,"table" text,"from" text,"to" text,"on_update" text,"on_delete" text,"match" text);', []);
6046
+            }
6047
+            $version = $this->query('pragma schema_version;', [])[0]["schema_version"];
6048
+            $current = $this->query('SELECT "version" from "sys/version";', []);
6049
+            if (!$current || count($current) == 0 || !isset($current[0]["schema_version"]) || $version != $current[0]["schema_version"]) {
6050
+                // reflection may take a while
6051
+                set_time_limit(3600);
6052
+                // update version data
6053
+                $this->query('DELETE FROM "sys/version";', []);
6054
+                $this->query('INSERT into "sys/version" ("version") VALUES (?);', [$version]);
6055
+
6056
+                // update tables data
6057
+                $this->query('DELETE FROM "sys/tables";', []);
6058
+                $result = $this->query('SELECT "name", "type" FROM sqlite_master WHERE ("type" = \'table\' or "type" = \'view\') and name not like "sys/%" and name<>"sqlite_sequence";', []);
6059
+                $tables = array();
6060
+                foreach ($result as $row) {
6061
+                    $tables[] = $row['name'];
6062
+                    $this->query('INSERT into "sys/tables" ("name", "type") VALUES (?, ?);', [$row['name'], $row['type']]);
6063
+                }
6064
+                // update columns and foreign_keys data
6065
+                $this->query('DELETE FROM "sys/columns";', []);
6066
+                $this->query('DELETE FROM "sys/foreign_keys";', []);
6067
+                foreach ($tables as $table) {
6068
+                    $result = $this->query("pragma table_info(`$table`);", []);
6069
+                    foreach ($result as $row) {
6070
+                        array_unshift($row, $table);
6071
+                        $this->query('INSERT into "sys/columns" ("self","cid","name","type","notnull","dflt_value","pk") VALUES (?,?,?,?,?,?,?);', array_values($row));
6072
+                    }
6073
+                    $result = $this->query("pragma foreign_key_list(`$table`);", []);
6074
+                    foreach ($result as $row) {
6075
+                        array_unshift($row, $table);
6076
+                        $this->query('INSERT into "sys/foreign_keys" ("self","id","seq","table","from","to","on_update","on_delete","match") VALUES (?,?,?,?,?,?,?,?,?);', array_values($row));
6077
+                    }
6078
+                }
6079
+            }
6080
+        }
6081
+
5999 6082
         public function getIgnoredTables(): array
6000 6083
         {
6001 6084
             switch ($this->driver) {
@@ -6005,6 +6088,8 @@ namespace Tqdev\PhpCrudApi\Database {
6005 6088
                     return ['spatial_ref_sys', 'raster_columns', 'raster_overviews', 'geography_columns', 'geometry_columns'];
6006 6089
                 case 'sqlsrv':
6007 6090
                     return [];
6091
+                case 'sqlite':
6092
+                    return ['sys/version', 'sys/tables', 'sys/columns', 'sys/foreign_keys'];
6008 6093
             }
6009 6094
         }
6010 6095
 
@@ -6017,6 +6102,9 @@ namespace Tqdev\PhpCrudApi\Database {
6017 6102
                     return 'SELECT c.relname as "TABLE_NAME", c.relkind as "TABLE_TYPE" FROM pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace WHERE c.relkind IN (\'r\', \'v\') AND n.nspname <> \'pg_catalog\' AND n.nspname <> \'information_schema\' AND n.nspname !~ \'^pg_toast\' AND pg_catalog.pg_table_is_visible(c.oid) AND \'\' <> ? ORDER BY "TABLE_NAME";';
6018 6103
                 case 'sqlsrv':
6019 6104
                     return 'SELECT o.name as "TABLE_NAME", o.xtype as "TABLE_TYPE" FROM sysobjects o WHERE o.xtype IN (\'U\', \'V\') ORDER BY "TABLE_NAME"';
6105
+                case 'sqlite':
6106
+                    $this->createSqlLiteReflectionTables();
6107
+                    return 'SELECT t.name as "TABLE_NAME", t.type as "TABLE_TYPE" FROM "sys/tables" t WHERE t.type IN (\'table\', \'view\') AND \'\' <> ? ORDER BY "TABLE_NAME"';
6020 6108
             }
6021 6109
         }
6022 6110
 
@@ -6029,6 +6117,8 @@ namespace Tqdev\PhpCrudApi\Database {
6029 6117
                     return 'SELECT a.attname AS "COLUMN_NAME", case when a.attnotnull then \'NO\' else \'YES\' end as "IS_NULLABLE", pg_catalog.format_type(a.atttypid, -1) as "DATA_TYPE", case when a.atttypmod < 0 then NULL else a.atttypmod-4 end as "CHARACTER_MAXIMUM_LENGTH", case when a.atttypid != 1700 then NULL else ((a.atttypmod - 4) >> 16) & 65535 end as "NUMERIC_PRECISION", case when a.atttypid != 1700 then NULL else (a.atttypmod - 4) & 65535 end as "NUMERIC_SCALE", \'\' AS "COLUMN_TYPE" FROM pg_attribute a JOIN pg_class pgc ON pgc.oid = a.attrelid WHERE pgc.relname = ? AND \'\' <> ? AND a.attnum > 0 AND NOT a.attisdropped;';
6030 6118
                 case 'sqlsrv':
6031 6119
                     return 'SELECT c.name AS "COLUMN_NAME", c.is_nullable AS "IS_NULLABLE", t.Name AS "DATA_TYPE", (c.max_length/2) AS "CHARACTER_MAXIMUM_LENGTH", c.precision AS "NUMERIC_PRECISION", c.scale AS "NUMERIC_SCALE", \'\' AS "COLUMN_TYPE" FROM sys.columns c INNER JOIN sys.types t ON c.user_type_id = t.user_type_id WHERE c.object_id = OBJECT_ID(?) AND \'\' <> ?';
6120
+                case 'sqlite':
6121
+                    return 'SELECT "name" AS "COLUMN_NAME", case when "notnull"==1 then \'no\' else \'yes\' end as "IS_NULLABLE", "type" AS "DATA_TYPE", 2147483647 AS "CHARACTER_MAXIMUM_LENGTH", 0 AS "NUMERIC_PRECISION", 0 AS "NUMERIC_SCALE", \'\' AS "COLUMN_TYPE" FROM "sys/columns" WHERE "self" = ? AND \'\' <> ?';
6032 6122
             }
6033 6123
         }
6034 6124
 
@@ -6041,6 +6131,8 @@ namespace Tqdev\PhpCrudApi\Database {
6041 6131
                     return 'SELECT a.attname AS "COLUMN_NAME" FROM pg_attribute a JOIN pg_constraint c ON (c.conrelid, c.conkey[1]) = (a.attrelid, a.attnum) JOIN pg_class pgc ON pgc.oid = a.attrelid WHERE pgc.relname = ? AND \'\' <> ? AND c.contype = \'p\'';
6042 6132
                 case 'sqlsrv':
6043 6133
                     return 'SELECT c.NAME as "COLUMN_NAME" FROM sys.key_constraints kc inner join sys.objects t on t.object_id = kc.parent_object_id INNER JOIN sys.index_columns ic ON kc.parent_object_id = ic.object_id and kc.unique_index_id = ic.index_id INNER JOIN sys.columns c ON ic.object_id = c.object_id AND ic.column_id = c.column_id WHERE kc.type = \'PK\' and t.object_id = OBJECT_ID(?) and \'\' <> ?';
6134
+                case 'sqlite':
6135
+                    return 'SELECT "name" as "COLUMN_NAME" FROM "sys/columns" WHERE "pk"=1 AND "self"=? AND \'\' <> ?';
6044 6136
             }
6045 6137
         }
6046 6138
 
@@ -6053,6 +6145,8 @@ namespace Tqdev\PhpCrudApi\Database {
6053 6145
                     return 'SELECT a.attname AS "COLUMN_NAME", c.confrelid::regclass::text AS "REFERENCED_TABLE_NAME" FROM pg_attribute a JOIN pg_constraint c ON (c.conrelid, c.conkey[1]) = (a.attrelid, a.attnum) JOIN pg_class pgc ON pgc.oid = a.attrelid WHERE pgc.relname = ? AND \'\' <> ? AND c.contype  = \'f\'';
6054 6146
                 case 'sqlsrv':
6055 6147
                     return 'SELECT COL_NAME(fc.parent_object_id, fc.parent_column_id) AS "COLUMN_NAME", OBJECT_NAME (f.referenced_object_id) AS "REFERENCED_TABLE_NAME" FROM sys.foreign_keys AS f INNER JOIN sys.foreign_key_columns AS fc ON f.OBJECT_ID = fc.constraint_object_id WHERE f.parent_object_id = OBJECT_ID(?) and \'\' <> ?';
6148
+                case 'sqlite':
6149
+                    return 'SELECT "from" AS "COLUMN_NAME", "table" AS "REFERENCED_TABLE_NAME" FROM "sys/foreign_keys" WHERE "self" = ? AND \'\' <> ?';
6056 6150
             }
6057 6151
         }
6058 6152
 
@@ -6070,20 +6164,22 @@ namespace Tqdev\PhpCrudApi\Database {
6070 6164
                 return !$tables || in_array($v['TABLE_NAME'], $tables);
6071 6165
             });
6072 6166
             foreach ($results as &$result) {
6167
+                $map = [];
6073 6168
                 switch ($this->driver) {
6074 6169
                     case 'mysql':
6075 6170
                         $map = ['BASE TABLE' => 'table', 'VIEW' => 'view'];
6076
-                        $result['TABLE_TYPE'] = $map[$result['TABLE_TYPE']];
6077 6171
                         break;
6078 6172
                     case 'pgsql':
6079 6173
                         $map = ['r' => 'table', 'v' => 'view'];
6080
-                        $result['TABLE_TYPE'] = $map[$result['TABLE_TYPE']];
6081 6174
                         break;
6082 6175
                     case 'sqlsrv':
6083 6176
                         $map = ['U' => 'table', 'V' => 'view'];
6084
-                        $result['TABLE_TYPE'] = $map[trim($result['TABLE_TYPE'])];
6177
+                        break;
6178
+                    case 'sqlite':
6179
+                        $map = ['table' => 'table', 'view' => 'view'];
6085 6180
                         break;
6086 6181
                 }
6182
+                $result['TABLE_TYPE'] = $map[trim($result['TABLE_TYPE'])];
6087 6183
             }
6088 6184
             return $results;
6089 6185
         }
@@ -6112,6 +6208,23 @@ namespace Tqdev\PhpCrudApi\Database {
6112 6208
                     }
6113 6209
                 }
6114 6210
             }
6211
+            if ($this->driver == 'sqlite') {
6212
+                foreach ($results as &$result) {
6213
+                    // mysql does not properly reflect display width of types
6214
+                    preg_match('|([a-z]+)(\(([0-9]+)(,([0-9]+))?\))?|', $result['DATA_TYPE'], $matches);
6215
+                    if (isset($matches[1])) {
6216
+                        $result['DATA_TYPE'] = $matches[1];
6217
+                    } else {
6218
+                        $result['DATA_TYPE'] = 'integer';
6219
+                    }
6220
+                    if (isset($matches[5])) {
6221
+                        $result['NUMERIC_PRECISION'] = $matches[3];
6222
+                        $result['NUMERIC_SCALE'] = $matches[5];
6223
+                    } else if (isset($matches[3])) {
6224
+                        $result['CHARACTER_MAXIMUM_LENGTH'] = $matches[3];
6225
+                    }
6226
+                }
6227
+            }
6115 6228
             return $results;
6116 6229
         }
6117 6230
 
@@ -6399,6 +6512,20 @@ namespace Tqdev\PhpCrudApi\Database {
6399 6512
                 'uniqueidentifier' => 'char',
6400 6513
                 'xml' => 'clob',
6401 6514
             ],
6515
+            'sqlite' => [
6516
+                'tinytext' => 'clob',
6517
+                'text' => 'clob',
6518
+                'mediumtext' => 'clob',
6519
+                'longtext' => 'clob',
6520
+                'mediumint' => 'integer',
6521
+                'int' => 'integer',
6522
+                'bigint' => 'bigint',
6523
+                'int2' => 'smallint',
6524
+                'int4' => 'integer',
6525
+                'int8' => 'bigint',
6526
+                'double precision' => 'double',
6527
+                'datetime' => 'timestamp'
6528
+            ],
6402 6529
         ];
6403 6530
 
6404 6531
         // source: https://docs.oracle.com/javase/9/docs/api/java/sql/Types.html
@@ -6964,6 +7091,8 @@ namespace Tqdev\PhpCrudApi\Middleware\Router {
6964 7091
             } catch (\PDOException $e) {
6965 7092
                 if (strpos(strtolower($e->getMessage()), 'duplicate') !== false) {
6966 7093
                     $response = $this->responder->error(ErrorCode::DUPLICATE_KEY_EXCEPTION, '');
7094
+                } elseif (strpos(strtolower($e->getMessage()), 'unique constraint') !== false) {
7095
+                    $response = $this->responder->error(ErrorCode::DUPLICATE_KEY_EXCEPTION, '');
6967 7096
                 } elseif (strpos(strtolower($e->getMessage()), 'default value') !== false) {
6968 7097
                     $response = $this->responder->error(ErrorCode::DATA_INTEGRITY_VIOLATION, '');
6969 7098
                 } elseif (strpos(strtolower($e->getMessage()), 'allow nulls') !== false) {
@@ -10269,6 +10398,8 @@ namespace Tqdev\PhpCrudApi {
10269 10398
                     return 5432;
10270 10399
                 case 'sqlsrv':
10271 10400
                     return 1433;
10401
+                case 'sqlite':
10402
+                    return 0;
10272 10403
             }
10273 10404
         }
10274 10405
 
@@ -10281,6 +10412,8 @@ namespace Tqdev\PhpCrudApi {
10281 10412
                     return 'localhost';
10282 10413
                 case 'sqlsrv':
10283 10414
                     return 'localhost';
10415
+                case 'sqlite':
10416
+                    return 'data.db';
10284 10417
             }
10285 10418
         }
10286 10419
 

Loading…
Cancel
Save